1.vue早期源码学习系列之四:如何实现动态数据绑定
2.浅谈vue3的编译优化
3.Vue 3 全方位详解
4.问起Vue的原理时不要再只说defineProperty了
5.vue3çå¦ä¹ _ä»ç»ä»
6.5分钟让你读懂Vue双向数据绑定的原理解析及代码实现
vue早期源码学习系列之四:如何实现动态数据绑定
本系列更多文章,请查阅我的博客:GitHub - youngwind/blog: 梁少峰的个人博客。或扫描下方二维码关注公众号“前端控”,欢迎交流讨论!
在前一篇中,我们学习了如何监听数据变化以及使用观察者模式和事件传递响应变化事件。uboot网络源码下载今天,我们将探讨如何基于watch库实现动态数据绑定。
我们可以将问题具体化为一个例子:如何实现当user.name或user.age发生变化时,HTML上的DOM元素也能相应地改变?
最初,我采取了以下方法:在数据顶层注册一个事件,当任意属性改变时,重新遍历DOM模板,将{ { user.name}}等转换为实际值,在内存中拼接成fragment,最后将新fragment替换掉原有的DOM结构。这种方法虽然简单,但存在不少问题。
基于上述问题,我们需要改进这种做法。为此,我们引入了Directive(指令)的概念,其构造函数和原型方法如下所示。关键实现思路是:在遍历DOM模板的过程中,当遍历到文本节点:"{ { name}}"时,先将其中的表达式"name"匹配出来,然后新建一个空的textNode,插入到这个文本节点的前面,最后移除这个文本节点。这样,就实现了用一个程序生成的textNode代替原来的textNode,从而实现每个textNode都跟它的页面查源码表达式一一对应起来。
然而,这种方法仍然存在问题。为了解决这些问题,我们引入了Binding和Watcher这两个“类”。Binding用于解决键值索引,Watcher用于解决$watch。Binding、Watcher和Directive三者之间的关系如下:(此处插入)
从图中可以看出,有一个_rootBind对象,其属性是按照DOM模板中用到的数据层层深入排列下去的。在每个属性上都有一个_subs数组,这个数组表示订阅的意思,里面存放的是一系列Watcher。Watcher既可以装载Directive,也可以装载$watch。这就是vue实现动态数据绑定的三大核心概念。
学习Vue.js,我checkout的是vue的这个版本。相比于之前的学习,代码量大幅增加,从七八百行增加到差不多五千行。特别是Binding、Watcher和Directive这几个核心概念,一开始让人难以理解。经过多日的思考和不断调试,我才逐渐明白。
此外,通过Binding、Watcher、Directive构建的动态数据绑定体系还存在一个重大缺陷,我们将在下一篇文章中专门阐述。番薯借阅源码
浅谈vue3的编译优化
编译优化:编译器将模版编译为渲染函数的过程中,尽可能地提取关键信息,并以此指导生成最优代码的过程。优化的方向:尽可能地区分动态内容和静态内容,并针对不同的内容采用不同的优化策略
1.动态节点收集与补丁标志1.1传统diff算法的问题比对新旧两棵虚拟DOM树的时候,总是要按照虚拟DOM的层级结构“一层一层”地遍历
<divid="foo"><pclass="bar">{ { text}}</p></div>上面这段代码中,当响应式数据text值发生变化的时候,最高效的更新方式是直接设置p标签的文本内容
传统Diff算法做不到如此高效,当text值发生变化的时候,会产生一颗新的虚拟DOM树,对比新旧虚拟DOM过程如下:
对比div节点,以及该节点的属性和子节点
对比p节点,以及该节点的属性和子节点
对比p节点的文本子节点,如果文本子节点的内容变了,则更新,否则什么都不做
可以发现,有很多无意义的对比操作。
总结:
传统diff算法的问题:无法利用编译时提取到的任何关键信息,导致渲染器在运行时不会去做相关的优化。
vue3的编译器会将编译得到的关键信息“附着”在它生成的虚拟DOM上,传递给渲染器,执行“快捷路径”。
1.2Block与PatchFlags传统Diff算法无法避免新旧虚拟DOM树间无用的比较操作,是因为运行时得不到足够的关键信息,从而无法区分动态内容和静态内容。换句话说,只要运行时能够区分动态内容和静态内容,就可以实现极简的优化策略
举个例子:
<div><div>foo</div><p>{ { bar}}</p></div>只有{ { bar}}是动态的内容。理想情况下,当数据bar的值变化时,只需要更新p标签的问诊系统源码文本节点即可。为了实现这个目标,需要提供信息给运行时
//传统虚拟DOM描述constvnode={ tag:'div',children:[{ tag:'div',children:'foo'},{ tag:'p',children:ctx.bar},]}//编译优化后constvnode={ tag:'div',children:[{ tag:'div',children:'foo'},{ tag:'p',children:ctx.bar,patchFlag:1},//这是动态节点]}可以发现,虚拟节点多了一个额外的属性,即patchFlag(补丁标志),存在该属性,就认为是动态节点
patchFlag(补丁标志)可以理解为一系列的数字标记,含义如下
constPatchFlags={ TEXT:1,//代表节点有动态的textContentCLASS:2,//代表元素有动态的class绑定STYLE:3//其他。。。}可以在虚拟节点的创建阶段,把它的动态子节点提取出来,并存储到该虚拟节点的dynamicChildren数组中
constvnode={ tag:'div',children:[{ tag:'div',children:'foo'},{ tag:'p',children:ctx.bar,patchFlag:1},//这是动态节点],//将children中的动态节点提取到dynamicChildren数组中dynamicChildren:[{ tag:'p',children:ctx.bar,patchFlag:PatchFlags.TEXT}]}Block定义:带有dynamicChildren属性的虚拟节点称为“块”,即(Block)
一个Block本质上也是一个虚拟DOM,比普通的虚拟节点多处一个用来存储动态节点的dynamicChildren属性。(能够收集所有的动态子代节点)
渲染器的更新操作会以Block为维度。当渲染器在更新一个Block时,会忽略虚拟节点的children数组,直接找到dynamicChildren数组,并只更新该数组中的动态节点。跳过了静态内容,只更新动态内容。同时,由于存在对应的补丁标志,也能够做到靶向更新。
Block节点有哪些:模版根节点、带有v-for、v-if/v-else-if/v-else等指令的节点
1.3收集动态节点编译器生成的渲染函数代码中,不会直接包含用来描述虚拟节点的数据结构,而是包含着用来创建虚拟DOM节点的辅助函数,如下
render(){ returncreateVNode('div',{ id:'foo'},[createVNode('p',null,'text')])}functioncreateVNode(tag,props,children){ constkey=props&&props.keyprops&&deleteprops.key//省略部分代码return{ tag,props,children,key}}createVNode的返回值是一个虚拟DOM节点
举个例子:
<divid="foo"><pclass="bar">{ { bar}}</p></div>上面模版生成带有补丁标志的渲染函数如下:
render(){ returncreateVNode('div',{ id:'foo'},[createVNode('p',{ class:'bar'},text,PatchFlags.TEXT)])}怎么将根节点变成一个Block,如何将动态子代节点收集到该Block的dynamicChildren数组中?
可以发现,在渲染函数内,编译链源码对createVNode函数的调用是层层嵌套结构,执行顺序是内层先执行,外层再执行,当外层createVNode函数执行时,内层的createVNode函数已经执行完毕了。因此,为了让外层Block节点能够收集到内层动态节点,需要一个栈结构的数据来临时存储内层的动态节点。代码实现如下:
//动态节点constdynamicChildrenStack=[]//当前动态节点集合letcurrentDynamicChildren=null//openBlock用来创建一个新的动态节点集合,并将该集合压入栈中functionopenBlock(){ dynamicChildrenStack.push((currentDynamicChildren=[]))}//closeBlock用来通过openBlock创建的动态节点集合从栈中弹出functioncloseBlock(){ currentDynamicChildren=dynamicChildrenStack.pop()}然后调整createVNode函数
<div><div>foo</div><p>{ { bar}}</p></div>0接着调整
<div><div>foo</div><p>{ { bar}}</p></div>.4.渲染器的运行时支持传统的节点更新方式如下:
<div><div>foo</div><p>{ { bar}}</p></div>2优化后的更新方式,直接对比动态节点
<div><div>foo</div><p>{ { bar}}</p></div>3存在对应的补丁标志,可以针对性地完成靶向更新
<div><div>foo</div><p>{ { bar}}</p></div>.Block树除了模版的根节点是Block外,带有结构化指令的节点,如:v-if、v-for,也都应该是Block
2.1带有v-if指令的节点<div><div>foo</div><p>{ { bar}}</p></div>5假设只有最外层的div标签会作为Block,那么变量foo的值为true还是false,block收集到的动态节点都是一样的,如下:
<div><div>foo</div><p>{ { bar}}</p></div>6这意味着,在Diff阶段不会更新。显然,foo不同值下,一个是section,一个是div,是不同标签,是需要更新的。
再举个例子:
<div><div>foo</div><p>{ { bar}}</p></div>7一样会导致更新失败
问题在于:dynamicChildren收集的动态节点是忽略虚拟DOM树层级的,结构化指令会导致更新前后模版的结构发生变化,即模版结构不稳定
解决方法:让带有v-if/v-else-if/v-else等结构化指令的节点也作为Block即可,如下所示
<div><div>foo</div><p>{ { bar}}</p></div>8<div><div>foo</div><p>{ { bar}}</p></div>9在Diff过程中,渲染器根据key值区分,使用新的Block替换旧的Block
2.2带有v-for指令的节点带有v-for指令的节点也会让虚拟DOM树变得不稳定
例子:
//传统虚拟DOM描述constvnode={ tag:'div',children:[{ tag:'div',children:'foo'},{ tag:'p',children:ctx.bar},]}0list的值由[1,2]变成[1]
更新前后对应的Block树如下:
//传统虚拟DOM描述constvnode={ tag:'div',children:[{ tag:'div',children:'foo'},{ tag:'p',children:ctx.bar},]}1更新前后,动态节点数量不一致,无法进行diff操作(diff操作的前提是:操作的节点必须是同层级节点,dynamicChildren不一定是同层级的)
解决方法:让v-for指令的标签也作为Block角色,保证虚拟DOM树具有稳定的结构,无论v-for在运行时怎样变化。如下:
//传统虚拟DOM描述constvnode={ tag:'div',children:[{ tag:'div',children:'foo'},{ tag:'p',children:ctx.bar},]}2由于v-for指令渲染的是一个片段,所以类型用Fragment
2.3Fragment的稳定性//传统虚拟DOM描述constvnode={ tag:'div',children:[{ tag:'div',children:'foo'},{ tag:'p',children:ctx.bar},]}3发现Fragment本身收集的动态节点存在结构是不稳定的情况
结构不稳定:指更新前后一个block的dynamicChildren数组中收集的动态节点的数量或顺序不一致
这种情况无法直接进行靶向更新
解决方法:回退到传统虚拟DOM的Diff手段,即直接使用Fragment的children而非dynamicChildren来进行Diff操作
Fragment的子节点仍然可以是由Block组成的数组
//传统虚拟DOM描述constvnode={ tag:'div',children:[{ tag:'div',children:'foo'},{ tag:'p',children:ctx.bar},]}4当Fragment的子节点更新时,就可以恢复优化模式
有稳定的Fragment吗?如下:
//传统虚拟DOM描述constvnode={ tag:'div',children:[{ tag:'div',children:'foo'},{ tag:'p',children:ctx.bar},]}5稳定的Fragment,可以使用优化模式
vue3模版中的多个根节点,也是稳定的Fragment
//传统虚拟DOM描述constvnode={ tag:'div',children:[{ tag:'div',children:'foo'},{ tag:'p',children:ctx.bar},]}.静态提升减少更新时创建虚拟DOM带来的性能开销和内存占用
如:
//传统虚拟DOM描述constvnode={ tag:'div',children:[{ tag:'div',children:'foo'},{ tag:'p',children:ctx.bar},]}7没有静态提升时,渲染函数是:
//传统虚拟DOM描述constvnode={ tag:'div',children:[{ tag:'div',children:'foo'},{ tag:'p',children:ctx.bar},]}8响应式数据title变化后,整个渲染函数会重新执行
把纯静态的节点提升到渲染函数之外
//传统虚拟DOM描述constvnode={ tag:'div',children:[{ tag:'div',children:'foo'},{ tag:'p',children:ctx.bar},]}9响应式数据title变化后,不会重新创建静态的虚拟节点
注:静态提升是以树为单位的
包含动态绑定的节点本身不会被提升,但是该节点上的静态属性是可以被提升的
//编译优化后constvnode={ tag:'div',children:[{ tag:'div',children:'foo'},{ tag:'p',children:ctx.bar,patchFlag:1},//这是动态节点]}0可以减少创建虚拟DOM产生的开销以及内存占用
4.预字符串化基于静态提升,进一步采用预字符串化优化。
//编译优化后constvnode={ tag:'div',children:[{ tag:'div',children:'foo'},{ tag:'p',children:ctx.bar,patchFlag:1},//这是动态节点]}1采用静态提升优化策略后
//编译优化后constvnode={ tag:'div',children:[{ tag:'div',children:'foo'},{ tag:'p',children:ctx.bar,patchFlag:1},//这是动态节点]}2采用预字符串化将这些静态节点序列化为字符串,并生成一个Static类型的VNode
//编译优化后constvnode={ tag:'div',children:[{ tag:'div',children:'foo'},{ tag:'p',children:ctx.bar,patchFlag:1},//这是动态节点]}3优势:
大块的静态内容可以通过innerHTML设置,在性能上有一定优势
减少创建虚拟节点产生的性能开销
减少内存占用
5.缓存内联事件处理函数//编译优化后constvnode={ tag:'div',children:[{ tag:'div',children:'foo'},{ tag:'p',children:ctx.bar,patchFlag:1},//这是动态节点]}4//编译优化后constvnode={ tag:'div',children:[{ tag:'div',children:'foo'},{ tag:'p',children:ctx.bar,patchFlag:1},//这是动态节点]}5每次重新渲染时,都会为Com组件创建一个全新的props对象。同时,props对象中onChange属性的值也会是全新的函数。造成额外的性能开销
//编译优化后constvnode={ tag:'div',children:[{ tag:'div',children:'foo'},{ tag:'p',children:ctx.bar,patchFlag:1},//这是动态节点]}.v-oncev-once可以对虚拟DOM进行缓存
//编译优化后constvnode={ tag:'div',children:[{ tag:'div',children:'foo'},{ tag:'p',children:ctx.bar,patchFlag:1},//这是动态节点]}7由于节点被缓存,意味着更新前后的虚拟节点不会发生变化,因此也就不需要这些被缓存的虚拟节点参与Diff操作了。编译后的结果如下:
//编译优化后constvnode={ tag:'div',children:[{ tag:'div',children:'foo'},{ tag:'p',children:ctx.bar,patchFlag:1},//这是动态节点]}8v-once包裹的动态节点不会被父级Block收集,因此不会参与Diff操作
v-once指令通常用于不会发生改变的动态绑定中,例如绑定一个常量
//编译优化后constvnode={ tag:'div',children:[{ tag:'div',children:'foo'},{ tag:'p',children:ctx.bar,patchFlag:1},//这是动态节点]}9v-once带来的性能提升
避免组件更新时重新创建虚拟DOM带来的性能开销。因为虚拟DOM被缓存了,所以更新时无需重新创建
避免无用的Diff开销。因为被v-once标记的虚拟DOM树不会被父级Block节点收集
7.总结1.vue3提出了Block的概念,利用Block树及补丁标志
2.静态提升:可以减少更新时创建虚拟DOM产生的性能开销和内存占用
3.预字符串化:在静态提升的基础上,对静态节点进行字符串化。这样做能够减少创建虚拟节点产生的性能开销以及内存占用
4.缓存内联事件处理函数:避免造成不必要的组件更新
5.v-once指令:缓存全部或部分虚拟节点,能够避免组件更新时重新创建虚拟DOM带来的性能开销,也可以避免无用的Diff操作
原文:/post/Vue 3 全方位详解
这是我整理的学习资料,非常系统和完善,欢迎一起学习
Vue简介
Vue是一个用于构建用户界面和单页面应用的渐进式框架。相比其他框架,Vue具有以下特点:
Vue 3于年9月发布,是一次重要升级。本文将全面介绍其新特性和用法。
基础Vite项目创建
使用Vite可以快速创建Vue 3项目:
Vite具有以下特点:
相比Webpack,大大提升了开发体验。
应用入口
应用入口通过createApp创建:
等同于Vue 2中的:
组件编写
组件不再使用选项式API,而是 introduced Composition API:
setup取代了beforeCreate和created钩子,不需要this上下文。
响应系统响应式原理
Vue通过响应系统实现数据驱动视图动态更新:
数据变化驱动视图自动更新。
响应式APIref
ref用于创建基础类型的响应式数据:
ref内部通过Object.defineProperty的get和set来实现响应式。
reactive
reactive用于创建对象(或数组)类型的响应式数据:
reactive通过Proxy对象实现响应式,可以直接操作属性而无需.value。
实现区别
reactive可以检测更多数据操作,实现深层响应式。
Composition API
Composition API是Vue 3中的重要加强之一。
setup函数
setup是Composition API的入口:
setup类似于Vue 2的beforeCreate钩子,不需要this上下文。
setup返回的值可以在模板中使用。
响应式编程
在setup中通过ref和reactive创建响应式状态:
修改数据时,视图会进行响应更新。
计算属性
通过computed创建计算属性:
计算属性会根据依赖响应式进行缓存和更新。
监听器
watch用来监听数据变化:
生命周期钩子
Composition API提供新的生命周期钩子:
自定义Hooks
可以将共享逻辑抽取到自定义Hooks中:
然后在组件中使用自定义Hook:
高级功能浅层响应式
shallowReactive和shallowRef只处理对象最外层属性的响应式,深层次属性不会转换为响应式。
只读数据
readonly可以创建一个深层只读响应式数据,无法修改。
toRefs和toRef
toRefs和toRef用来创建对响应式对象属性的ref引用。
自定义Ref
customRef允许自定义Ref的获取和设置行为:
用于实现自定义的响应式行为。
新组件Fragment
Fragment允许组件没有根节点:
避免添加无意义的根节点。
Teleport
Teleport可以将组件挂载到DOM中的任意位置:
常用于构建弹窗、提示等组件。
Suspense
Suspense组件可以控制组件等待加载状态:
等待内部异步组件加载完成后再渲染。
Vue生态状态管理
provide/inject提供依赖注入:
Vuex和Pinia提供更完整的状态管理解决方案。
路由管理
Vue Router提供路由系统:
支持动态路由、嵌套路由等。
UI框架
常用的Vue UI框架:
测试方案
常用的Vue测试工具:
等等。
其他改变总结
Vue 3进行了诸多改进,使得Vue的核心更简洁高效,拓展性更强。
Vue 3为未来Web应用开发提供了坚实基础。其文档完善,生态繁荣,可以考虑逐步迁移项目到Vue 3。
问起Vue的原理时不要再只说defineProperty了
深入浅出,剖析Vue原理
面试提及Vue原理时,常言通过`Object.defineProperty`修改属性的`get`和`set`方法,以此实现数据变动。然而,Vue的MVVM架构核心作用远不止于此,它依托于`Observer`、`Dep`、`Watcher`和`Compile`四个关键类,以及辅助类`CpompileUtil`协同工作。本文旨在简明解读从初始化DOM和数据到数据渲染的整个流程,探究Vue背后的操作机制。
首先,构建Vue实例需定义DOM元素与数据源`data`。在`Observer`类中,通过`observe`方法遍历数据源,若属性非对象,则返回;否则,调用`defineReactive`方法拦截`get`和`set`方法,并创建依赖收集器`Dep`。
`Dep`类维护一个`Watcher`数组。在`defineReactive`方法中,当属性`get`方法被修改时,根据`Dep.target`的存在与否决定将`Watcher`添加到数组中。当属性通过`set`方法改变时,调用该属性的`Dep.notify`方法通知所有订阅该属性的`Watcher`更新状态。
`Compile`(在Vue中,此步骤由`virtualDOM`处理)负责编译绑定的`el`节点。首先,将`el`的`childNodes`创建为一个`fragment`,以避免频繁访问真实DOM带来的性能损耗。接着,根据`fragment`中的每个`Node`类型进行判断。
若`Node`为`Element`类型,获取其`attributes`,查找与指令`v-xx`相关的`CompileUtil`方法,并将其添加到`Dep`中,递归调用`compile`方法直至文本节点`text`为止。若`Node`为文本类型,则调用`compileText`方法,获取`textContent`表达式,调用相应`CompileUtil`方法操作该表达式。
`CompileUtil`类包含`updateFn`方法,使用数组`reduce`函数拆解`expr`并获取属性,实现表达式的解析。初始化时,只获取数据值进行DOM渲染,此时`Dep.target`为`null`,无需依赖收集;数据变动时,通过`Dep`的`notify`方法通知相关`Watcher`更新DOM。
综上所述,Vue的MVVM架构通过这些核心组件协同工作,实现数据与视图的高效同步,提供简洁且高效的响应式数据绑定机制。
vue3çå¦ä¹ _ä»ç»ä»
vue3带æ¥äºä»ä¹ï¼
æ§è½çæå
æå 大å°åå°%
å渲æå¿«%ï¼æ´æ°æ¸²æ%
å ååå°%
æºç çæå
使ç¨proxy代æ¿definePropertyå®ç°ååºå¼
éåèæDOMçå®ç°åTree-Shaking
æ¥æ±TypeScript
vue3å¯ä»¥æ´å¥½çæ¯ætypescript
æ°çç¹æ§
æ°ççå½å¨æé©å
dataé项åºå§ç»è¢«å£°æ为ä¸ä¸ªå½æ°
移é¤keyCodeæ¯æä½ä¸ºv-onç修饰符
Fragment
Teleport
Suspense
setupé ç½®
refåreactive
watchä¸watchEffect
provideä¸inject
1.compositionAPI(ç»åAPI)
2.æ°çå ç½®ç»ä»¶
3.å ¶ä»æ¹å
å建vue3å·¥ç¨æ¹æ³ä¸ï¼ä½¿ç¨vue-cliå建
#æ¥çvue-cliçæ¬ç¡®ä¿é½å¨4.5.0以ä¸vue?--version#å®è£ æåçº§ä½ ç@vue-clinpm?install?-g?@vue/cli#å建vue?create?vue_test#å¯å¨cd?vue_testnpm?run?serveræå
æ¹æ³äºï¼ä½¿ç¨viteå建
#å建项ç®npm?init?vite-app?<project-name>#è¿å ¥å·¥ç¨ç®å½cd?<project-name>#å®è£ ä¾èµnpm?install#è¿è¡npm?run?devMain.jsçä¸å//?å¼å ¥çä¸åæ¯vueæé å½æ°äºï¼å¼å ¥çæ¯ä¸ä¸ªå为createAppçå·¥åå½æ°import?{ ?createApp?}?from?'vue'import?App?from?'./App.vue'//?å建åºç¨å®ä¾å¯¹è±¡ââapp(类似äºä¹åVue2ä¸çvm,ä½appæ¯vmæ´è½»)//?createApp(App).mount('#app')const?app?=?createApp(App);console.log('@@@',app);app.mount('#app');å ³éè¯æ³æ£æ¥vue3ç»ä»¶æ¨¡æ¿ç»æå¯ä»¥æ²¡ææ ¹æ ç¾Vue3-dev-toolsæ件å®è£ 1.æå¼åºå¹çsetupç解ï¼Vue3.0ä¸ä¸ä¸ªæ°çé 置项ï¼å¼ä¸ºä¸ä¸ªå½æ°ã
setupæ¯ææCompositionAPIï¼ç»åAPIï¼â表æ¼çèå°âã
ç»ä»¶ä¸æç¨å°çï¼æ°æ®ãæ¹æ³ççï¼åè¦é ç½®å¨setupä¸ã
setupå½æ°ç两ç§è¿åå¼ï¼
è¥è¿åä¸ä¸ªå¯¹è±¡ï¼å对象ä¸çå±æ§ãæ¹æ³,å¨æ¨¡æ¿ä¸åå¯ä»¥ç´æ¥ä½¿ç¨ãï¼éç¹å ³æ³¨ï¼ï¼
è¥è¿åä¸ä¸ªæ¸²æå½æ°ï¼åå¯ä»¥èªå®ä¹æ¸²æå 容ãï¼äºè§£ï¼
注æç¹ï¼
Vue2.xé ç½®ï¼dataãmethosãcomputed...ï¼ä¸å¯ä»¥è®¿é®å°setupä¸çå±æ§ãæ¹æ³ã
ä½å¨setupä¸ä¸è½è®¿é®å°Vue2.xé ç½®ï¼dataãmethosãcomputed...ï¼ã
å¦ææéå,setupä¼å ã
å°½éä¸è¦ä¸Vue2.xé 置混ç¨
setupä¸è½æ¯ä¸ä¸ªasyncå½æ°ï¼å 为è¿åå¼ä¸åæ¯returnç对象,èæ¯promise,模æ¿çä¸å°return对象ä¸çå±æ§ãï¼åæä¹å¯ä»¥è¿åä¸ä¸ªPromiseå®ä¾ï¼ä½éè¦Suspenseåå¼æ¥ç»ä»¶çé åï¼
2.ååºå¼æ°æ®RefImpl-referenceå¼ç¨implementå®ç°
refå å·¥çæçæ¯ä¸ä¸ªå¼ç¨å®ç°çå®ä¾å¯¹è±¡=ãå¼ç¨å¯¹è±¡
æµç¨1ï¼
æµç¨2ï¼
æµç¨3ï¼
å¼ç¨å¯¹è±¡å¨å¼ç¨æ°æ°å¼å对象æ¶çç»æåæ
2.1refå½æ°ä½ç¨:å®ä¹ä¸ä¸ªååºå¼çæ°æ®
è¯æ³:?constxxx=ref(initValue)
å建ä¸ä¸ªå å«ååºå¼æ°æ®çå¼ç¨å¯¹è±¡ï¼reference对象ï¼ç®ç§°ref对象ï¼ã
JSä¸æä½æ°æ®ï¼?xxx.value
模æ¿ä¸è¯»åæ°æ®:ä¸éè¦.valueï¼ç´æ¥ï¼<div>{ { xxx}}</div>
å¤æ³¨ï¼
æ¥æ¶çæ°æ®å¯ä»¥æ¯ï¼åºæ¬ç±»åãä¹å¯ä»¥æ¯å¯¹è±¡ç±»åã
åºæ¬ç±»åçæ°æ®ï¼ååºå¼ä¾ç¶æ¯é Object.defineProperty()çgetä¸setå®æçã
对象类åçæ°æ®ï¼å é¨?âæ±å©â?äºVue3.0ä¸çä¸ä¸ªæ°å½æ°ââ?reactiveå½æ°ã
2.2reactiveå½æ°ä½ç¨:å®ä¹ä¸ä¸ªå¯¹è±¡ç±»åçååºå¼æ°æ®ï¼åºæ¬ç±»åä¸è¦ç¨å®ï¼è¦ç¨refå½æ°ï¼
è¯æ³ï¼const代ç对象=reactive(æºå¯¹è±¡)æ¥æ¶ä¸ä¸ªå¯¹è±¡ï¼ææ°ç»ï¼ï¼è¿åä¸ä¸ªä»£ç对象ï¼Proxyçå®ä¾å¯¹è±¡ï¼ç®ç§°proxy对象ï¼
reactiveå®ä¹çååºå¼æ°æ®æ¯âæ·±å±æ¬¡çâã
å é¨åºäºES6çProxyå®ç°ï¼éè¿ä»£ç对象æä½æºå¯¹è±¡å é¨æ°æ®è¿è¡æä½ã
4.Vue3.0ä¸çååºå¼åçvue2.xçååºå¼å®ç°åçï¼
Object.defineProperty(data,?'count',?{ get?()?{ },?set?()?{ }})对象类åï¼éè¿Object.defineProperty()对å±æ§ç读åãä¿®æ¹è¿è¡æ¦æªï¼æ°æ®å«æï¼ã
æ°ç»ç±»åï¼éè¿éåæ´æ°æ°ç»çä¸ç³»åæ¹æ³æ¥å®ç°æ¦æªãï¼å¯¹æ°ç»çåæ´æ¹æ³è¿è¡äºå 裹ï¼ã
åå¨é®é¢ï¼
æ°å¢å±æ§ãå é¤å±æ§,çé¢ä¸ä¼æ´æ°ã
ç´æ¥éè¿ä¸æ ä¿®æ¹æ°ç»,çé¢ä¸ä¼èªå¨æ´æ°ã
解å³æ¹æ³(æ·»å å±æ§ï¼
this.$set(obj,key,value);
importVuefrom'vue'/Vue.set(obj,key,value)
解å³æ¹æ³ï¼å é¤å±æ§ï¼
this.$delete(obj,key)
importVuefrom'vue'/Vue.delete(obj,key,value)
解å³æ¹æ³ï¼ä¿®æ¹æ°ç»ï¼
this.$set(Arr,index,value)
this.Arr.splice(index,num)
Vue3.0çååºå¼å®ç°åç:
new?Proxy(data,?{ //?æ¦æªè¯»åå±æ§å¼get?(target,?prop)?{ return?Reflect.get(target,?prop)},//?æ¦æªè®¾ç½®å±æ§å¼ææ·»å æ°å±æ§set?(target,?prop,?value)?{ return?Reflect.set(target,?prop,?value)},//?æ¦æªå é¤å±æ§deleteProperty?(target,?prop)?{ return?Reflect.deleteProperty(target,?prop)}})proxy.name?=?'tom'模ævue3ä¸å®ç°ååºå¼ä½¿ç¨reflectæä½æºå¯¹è±¡å é¨çæ°æ®
//?模ævue3ä¸å®ç°ååºå¼const?p?=?new?Proxy(person,{ get(target,propName){ ?//?æ人读åpçæ个å±æ§æ¶è°ç¨?return?Reflect.get(target,propName)},set(target,propName,value){ ?//?æ人æ¹åpçå±æ§æ¶è°ç¨?return?Reflect.set(target,propName,value);},deleteProperty(target,propName){ ?//æ人å é¤pçæ个å±æ§æ¶è°ç¨?return?Reflect.deleteProperty(target,propName)}})Proxyï¼/post/
5分钟让你读懂Vue双向数据绑定的原理解析及代码实现
5分钟快速理解Vue双向数据绑定原理和代码示例
当你刚开始一天的工作,前端同事就分享了一篇关于Vue双向数据绑定的解析文章。虽然标题看似复杂,但别担心,让我们一起简化理解。 首先,双向绑定的关键在于Vue的`Object.defineProperty()`方法。它通过`get`和`set`函数,实现数据的读取和修改同步到视图。例如,当文本框内容改变时,`set`方法会被调用,更新数据的同时更新显示在`span`中的内容。 实现基本的双向绑定,我们可以通过监听`keyup`事件,当输入变化时,设置方法会更新数据并反映到DOM中。下面是一个简单示例: 通过监听输入框变化,双向同步数据和视图:输入框内容改变→数据更新→视图更新。 在Vue中,真正的双向绑定更为复杂,涉及到`DocumentFragment`的使用,以提高性能。我们分解任务:绑定数据到输入框和文本节点,同时处理数据变化和视图更新,利用`Dep`和`Watcher`实现通知机制。 最后,双向数据绑定的机制包括:数据监听(`observe`)、视图更新(`Dep.notify()`),以及使用`Watcher`确保视图始终与数据保持同步。 总的来说,理解Vue的双向数据绑定,关键在于掌握访问器属性和事件驱动的更新机制。通过实际操作和实践,你将更深入地掌握这一核心概念。vue3ä¸çç¼è¯å¨åçåä¼åçç¥
å¦ä¹ ç®æ
ç¼è¯å¨åç
vue3ç¼è¯è¿ç¨åæ
vue3ç¼è¯ä¼åçç¥
å¨åå§åä¹åå¯è½æç¼è¯çè¿ç¨ï¼æç»ç产ç©æ¯ä¸ªæ¸²æå½æ°ï¼æ们ç¥é渲æå½æ°è¿åçå¼æ¯ä¸ä¸ªèæDOM(vnode),é£ä¹è¿ä¸ªèæDOMå¨æ们åç»çæ´æ°è¿ç¨ä¸å°åºæä»ä¹ä½ç¨å¢ï¼æ们ä»å¤©å°±æ¥æ¢è®¨ä¸ä¸ã
ç¼è¯å¨åç1.æ¦å¿µå¹¿ä¹ä¸çç¼è¯åçï¼ç¼è¯å¨æ¯å°æºä»£ç 转åææºå¨ç ç软件ï¼æ以ç¼è¯çè¿ç¨åæ¯å°æºä»£ç 转åææºå¨ç çè¿ç¨ï¼ä¹å°±æ¯cpuå¯æ§è¡çäºè¿å¶ä»£ç ãä¾å¦ä½¿ç¨é«çº§è¯è¨javaç¼åçç¨åºéè¦ç¼è¯ææ们çä¸æä½è®¡ç®æºè½çæççåèç ã
å¦æäºè§£è¿ç¼è¯å¨çå·¥ä½æµç¨çåå¦åºè¯¥ç¥éï¼ä¸ä¸ªå®æ´çç¼è¯å¨çå·¥ä½æµç¨ä¼æ¯è¿æ ·ï¼
é¦å ï¼parse解æåå§ä»£ç å符串ï¼çææ½è±¡è¯æ³æ ASTã
å ¶æ¬¡ï¼transform转åæ½è±¡è¯æ³æ ï¼è®©å®åææ´è´´è¿ç®æ ãDSLãçç»æã
æåï¼codegenæ ¹æ®è½¬ååçæ½è±¡è¯æ³æ çæç®æ ãDSLãçå¯æ§è¡ä»£ç ã
2.vueä¸çç¼è¯å¨vueéä¹æç¼è¯çè¿ç¨ï¼æ们ç»å¸¸åçé£ä¸ªHTML模çï¼å¨çæ£å·¥ä½çæ¶åï¼å¹¶ä¸æ¯é£ä¸ªHTML模çï¼å®å®é ä¸æ¯ä¸ä¸ªæ¸²æå½æ°ï¼å¨è¿ä¸ªè¿ç¨ä¸å°±åçäºè½¬æ¢ï¼ä¹å°±æ¯ç¼è¯ï¼ä¹å°±æ¯é£ä¸ªå符串ç模çæç»ä¼åæä¸ä¸ªJSå½æ°ï¼å«renderå½æ°ãæ以å¨è¿ä¸ªè¿ç¨ä¸æ们就éè¦å¼å ¥ç¼è¯å¨çæ¦å¿µãå¨è®¡ç®æºä¸å½ä¸ç§ä¸è¥¿ä»ä¸ç§å½¢æå°å¦ä¸ç§å½¢æè¿è¡è½¬æ¢çæ¶åï¼å°±éè¦ç¼è¯ãç¼è¯å¨ï¼ç¨æ¥å°æ¨¡æ¿å符串ç¼è¯æ为JavaScript渲æå½æ°ç代ç
é£ä¹vueä¸çç¼è¯åçå¨ä»ä¹æ¶åå¢ï¼
è¿ä¸ªæ¶åæ们就éè¦è¿ä¸æ¥äºè§£vueå çä¸åçæ¬çä¸ååè½äºãvueææºå¸¦ç¼è¯å¨åä¸æºå¸¦ç¼è¯çå ï¼å¯¹ä¸åæ建çæ¬ç解éï¼ã
3.è¿è¡æ¶ç¼è¯å¨ä½¿ç¨æºå¸¦ç¼è¯å¨(compiler)çvueå çæ¶åï¼vueç¼è¯çæ¶å»æ¯åçå¨æè½½($mount)çæ¶åã
4.è¿è¡æ¶ä¸ç¼è¯å¦æ使ç¨æªæºå¸¦ç¼è¯å¨çvueå çæ¶åï¼vueå¨è¿è¡æ¶æ¯ä¸ä¼è¿è¡ç¼è¯çãé£ä¹å®çç¼è¯ååçå¨ä»ä¹æ¶åå¢ï¼ä½¿ç¨æªæºå¸¦ç¼è¯å¨çvueå çæ¶åï¼éè¦è¿è¡é¢ç¼è¯ï¼ä¹å°±æ¯åºäºæå»ºå·¥å ·ä½¿ç¨ï¼å°±æ¯æ们平æ¶ä½¿ç¨çvue-cliè¿è¡æ建ç项ç®ï¼å°±æ¯ä½¿ç¨webpackè°ç¨vue-loaderè¿è¡é¢ç¼è¯ï¼å°æævueæ件ï¼å°±æ¯SFCï¼å°éé¢çtemplate模çé¨å转æ¢ærenderå½æ°ãè¿æ ·åç好å¤å°±æ¯vueçå ä½ç§¯åå°äºï¼æ§è¡çæ¶åé度æ´å¿«äºï¼å 为ä¸éè¦è¿è¡ç¼è¯äºã
vueç¼è¯å¨åçç®åæ¥è¯´å°±æ¯ï¼å å°template模ç转æ¢æastæ½è±¡è¯æ³æ ï¼astå转æ¢æ渲æå½æ°renderã
é£ä¹ä»ä¹æ¯æ¯astæ½è±¡è¯æ³æ å¢ï¼
1.astæ½è±¡è¯æ³æ å¨template模çårenderå½æ°ä¹é´æä¸ä¸ªä¸é´äº§ç©å«åastæ½è±¡è¯æ³æ ãå®å°±æ¯ä¸ªjs对象ï¼å®è½å¤æè¿°å½å模ççç»æä¿¡æ¯ï¼è·vnodeå¾ç±»ä¼¼ã注æï¼aståªæ¯ç¨åºè¿è¡è¿ç¨ä¸ç¼è¯äº§ççï¼å®è·æ们æç»ç¨åºçè¿è¡æ¯æ²¡æä»»ä½å ³ç³»çãä¹å°±æ¯å½è¿ä¸ªæ¸²æå½æ°çæä¹åï¼astççå½å¨æå°±ç»æäºï¼ä¸åéè¦äºï¼èé£ä¸ªèæDOMåä¼´éæ´ä¸ªç¨åºççå½å¨æãè¿ä¸ªå°±æ¯aståèæDOMçæ¬è´¨åºå«ã
2.为ä»ä¹éè¦astå¢å¨ast转æ¢ærenderå½æ°çè¿ç¨ä¸ï¼éè¦è¿è¡ç¹å«çæä½ã第ä¸æ¬¡ï¼å°template转æçastæ¯ä¸ªé常ç²ç³çjs对象ï¼æ¯ä¸æ¬¡é常ç²ç³ç转æ¢ï¼ç±»ä¼¼æ£å表达å¼çå¹é ï¼ç¶åæ们çtemplate模çä¸è¿æå¾å¤è¡¨è¾¾å¼ï¼æ令ï¼äºä»¶éè¦éæ°è§£æï¼ç»è¿è¿äºå ·ä½çæ·±å å·¥ç解æ(transform)ä¹åä¼å¾å°ä¸ä¸ªç»æastï¼ç¶åè¿ä¸ªå¯¹è¿ä¸ªç»æastè¿è¡generateï¼çærenderå½æ°
template=>ast=>transform=>ast=>render3.miniçvueç¼è¯å¨ä¸é¢æ们æ¥çä¸ä¸ªminiççvueç¼è¯å¨ï¼å ·ä½ä»£ç å·²çç¥ï¼å ·ä½ä»£ç æå·²ç»æ¾å¨Githubä¸äºï¼mini-vue-compiler
functiontokenizer(input){ ...}functionparse(template){ consttokens=tokenizer(template)...}functiontransform(ast){ ...}functiontraverse(ast,context){ ...}functiongenerate(ast){ ...}functioncompile(template){ //1.解æconstast=parse(template)console.log(JSON.stringify(ast,null,2))//2.转æ¢transform(ast)//3.çæconstcode=generate(ast)console.log(code)//returnfunctionrender(ctx){ //returnh("h3",{ },//ctx.title//)}returnnewFunction(code)()}lettmpl=`<h3>{ { title}}</h3>`compile(tmpl)大æ¦æ以ä¸æä½ï¼å ¶ä¸parseå½æ°å°±æ¯åçå¨ætemplate转æ¢æastçè¿è¿ç¨ï¼å ·ä½æ¯éè¿ä¸äºæ£å表达å¼çå¹é templateä¸çå符串ãæ¯å¦å°
xxx转æast对象ï¼é£ä¹å°±æ¯éè¿æ£å表达å¼å¹é å¦ææ¯
é£ä¹å°±è®¾ç½®ä¸ä¸ªå¼å§æ è®°ï¼åå¾åé¢å¹é å°xxxå 容ï¼ç¶å就设置ä¸ä¸ªåå ç´ ï¼æåå¹é å°é£ä¹å°±è®¾ç½®ä¸ä¸ªç»ææ è®°ï¼ä»¥æ¤ç±»æ¨ãparse解æä¹åå¾å°çæ¯ä¸ä¸ªç²ç³çast对象ãç»è¿parse解æå¾å°ä¸ä¸ªç²ç³çast对象ä¹åï¼å°±ç¨transformè¿è¡æ·±å å·¥ï¼æåè¦ç»è¿generateçæ代ç ã
Vue3ç¼è¯è¿ç¨åææè½½çæ¶åå ætemplateç¼è¯ærenderå½æ°ï¼å¨å建å®ä¾ä¹åï¼ç´æ¥è°ç¨ç»ä»¶å®ä¾çrenderå½æ°å建è¿ä¸ªç»ä»¶ççå®DOMï¼ç¶å继ç»åä¸åéå½ã
1.vue2.xåvue3.xçç¼è¯å¯¹æ¯Vue2.xä¸çCompileè¿ç¨ä¼æ¯è¿æ ·ï¼
parseè¯æ³åæï¼ç¼è¯æ¨¡æ¿çæåå§ç²ç³çASTã
optimizeä¼ååå§ASTï¼æ è®°ASTElement为éææ ¹èç¹æéæèç¹ã
generateæ ¹æ®ä¼ååçASTï¼çæå¯æ§è¡ä»£ç ï¼ä¾å¦_cã_lä¹ç±»çã
å¨Vue3ä¸ï¼æ´ä½çCompileè¿ç¨ä»ç¶æ¯ä¸ä¸ªé¶æ®µï¼ä½æ¯ä¸åäºVue2.xçæ¯ï¼ç¬¬äºä¸ªé¶æ®µæ¢æäºæ£å¸¸ç¼è¯å¨é½ä¼åå¨çé¶æ®µtransformã
parseè¯æ³åæï¼ç¼è¯æ¨¡æ¿çæåå§ç²ç³çASTã
transforméåAST,对æ¯ä¸ä¸ªASTelementè¿è¡è½¬åï¼ä¾å¦ææ¬å ç´ ãæ令å ç´ ãå¨æå ç´ ççç转å
generateæ ¹æ®ä¼ååçASTï¼çæå¯æ§è¡ä»£ç å½æ°ã
2.æºç ç¼è¯å ¥å£æ们å ä»ä¸ä¸ªå ¥å£æ¥å¼å§æ们çæºç é 读ï¼packages/vue/index.tsã
//webå¹³å°ç¹æç¼è¯å½æ°functioncompileToFunction(template:string|HTMLElement,options?:CompilerOptions):RenderFunction{ //çç¥...if(template[0]==='#'){ //è·å模çå 容constel=document.querySelector(template)//çç¥...template=el?el.innerHTML:''}//ç¼è¯const{ code}=compile(template,extend({ //çç¥...},options))constrender=(__GLOBAL__?newFunction(code)():newFunction('Vue',code)(runtimeDom))asRenderFunction//çç¥...return(compileCache[key]=render)}//注åç¼è¯å½æ°registerRuntimeCompiler(compileToFunction)export{ compileToFunctionascompile}è¿ä¸ªå ¥å£æ件ç代ç æ¯è¾ç®åï¼åªæä¸ä¸ªcompileToFunctionå½æ°ï¼ä½å½æ°ä½å çå 容å´åæ¯è¾å ³é®ï¼ä¸»è¦æ¯ç»å以ä¸æ¥éª¤ï¼
ä¾èµæ³¨å ¥ç¼è¯å½æ°è³runtimeregisterRuntimeCompiler(compileToFunction)
runtimeè°ç¨ç¼è¯å½æ°compileToFunction
è°ç¨compileå½æ°
è¿åå å«codeçç¼è¯ç»æ
å°codeä½ä¸ºåæ°ä¼ å ¥Functionçæé å½æ°å°çæçå½æ°èµå¼ç»renderåé
å°renderå½æ°ä½ä¸ºç¼è¯ç»æè¿å
3.templateè·åapp.mount()è·åäºtemplatepackages/runtime-dom/src/index.ts
4.ç¼è¯templatecompileå°ä¼ ?templateç¼è¯ä¸ºrenderå½æ°ï¼packages/runtime-core/src/component.ts
å®é æ§?çæ¯baseCompileï¼packages/compiler-core/src/compile.ts
第?æ¥è§£æ-parseï¼è§£æå符串template为æ½è±¡è¯æ³æ ast
第?æ¥è½¬æ¢-transformï¼è§£æå±æ§ãæ ·å¼ãæ令ç
第ä¸æ¥?æ-generateï¼å°ast转æ¢ä¸ºæ¸²æå½æ°
Vue3ç¼è¯å¨ä¼åçç¥è¿æ¯ä¸ä¸ªéå¸¸å ¸åçç¨å åæ¢æ¶é´çæä½
1.éæèç¹æå<div><div>{ { msg}}</div><p>coboy</p><p>coboy</p><p>coboy</p></div>以ä¸è¿ä¸ªæ®µtemplateå¦æ没æå¼å¯éæèç¹æåå®ç¼è¯åæ¯è¿æ ·çï¼
import{ toDisplayStringas_toDisplayString,createVNodeas_createVNode,openBlockas_openBlock,createBlockas_createBlock}from"vue"exportfunctionrender(_ctx,_cache,$props,$setup,$data,$options){ return(_openBlock(),_createBlock("div",null,[_createVNode("div",null,_toDisplayString(_ctx.msg),1/*TEXT*/),_createVNode("p",null,"coboy"),_createVNode("p",null,"coboy"),_createVNode("p",null,"coboy")]))}å¦æå¼å¯äºéæèç¹æåä¹åå®ç¼è¯ååæ¯è¿æ ·çï¼
import{ toDisplayStringas_toDisplayString,createVNodeas_createVNode,openBlockas_openBlock,createBlockas_createBlock}from"vue"const_hoisted_1=/*#__PURE__*/_createVNode("p",null,"coboy",-1/*HOISTED*/)const_hoisted_2=/*#__PURE__*/_createVNode("p",null,"coboy",-1/*HOISTED*/)const_hoisted_3=/*#__PURE__*/_createVNode("p",null,"coboy",-1/*HOISTED*/)exportfunctionrender(_ctx,_cache,$props,$setup,$data,$options){ return(_openBlock(),_createBlock("div",null,[_createVNode("div",null,_toDisplayString(_ctx.msg),1/*TEXT*/),_hoisted_1,_hoisted_2,_hoisted_3]))}æ们å¯ä»¥çå°templateéåå¨å¤§éçä¸ä¼åçpæ ç¾ï¼æ以å½è¿ä¸ªç»ä»¶éæ°æ¸²æçæ¶åï¼è¿äºéæçä¸ä¼åçæ ç¾å°±ä¸åºè¯¥å次å建äºãæ以vue3å°±æè¿äºéæçä¸ä¼åçæ ç¾çVNodeæ¾å¨äºrenderå½æ°ä½ç¨åçå¤é¢ï¼å¨ä¸æ¬¡renderå½æ°å次æ§è¡çæ¶åï¼é£äºéææ ç¾çVNodeå·²ç»å¨å åéäºï¼ä¸éè¦éæ°å建äºãç¸å½äºå ç¨å½åæºå¨çå åï¼é¿å éå¤å建VNodeï¼ç¨å åæ¥æ¢æ¶é´ã大家ä»ç»æé ä¸çªéææåçåç¼ï¼éæäºåæ们å¯ä»¥ä¸çï¼ä½æ¯æåäºåï¼ç´ææ¬æå°è¡¨è¾¾åºå®ï¼éæèç¹ï¼è¢«æé«äºã
2.è¡¥ä¸æ è®°åå¨æå±æ§è®°å½<div><div:title="title">coboy</div></div>ææå°±æ¯å¨ç¼è¯çè¿ç¨ä¸ï¼å人ç¼ä¸æ ·å¯¹æ¨¡çè¿è¡æ«æçåªäºä¸è¥¿æ¯å¨æçï¼ç¶åæåæè¿äºå¨æçä¸è¥¿æåä¿åèµ·æ¥ï¼ä½ä¸ªæ è®°åè®°å½ï¼çä¸æ¬¡æ´æ°çæ¶åï¼åªæ´æ°è¿äºä¿åèµ·æ¥çå¨æçè®°å½ãæ¯å¦ä¸é¢æ¨¡ççtitleæ¯å¨æçï¼æåå个æ è®°åè®°å½ï¼æ´æ°çæ¶åå°±åªæ´æ°titleé¨åçå 容ã
import{ createVNodeas_createVNode,openBlockas_openBlock,createBlockas_createBlock}from"vue"exportfunctionrender(_ctx,_cache,$props,$setup,$data,$options){ return(_openBlock(),_createBlock("div",null,[_createVNode("div",{ title:_ctx.title},"coboy",8/*PROPS*/,["title"])]))}<div><div:title="title">{ { text}}</div></div>import{ toDisplayStringas_toDisplayString,createVNodeas_createVNode,openBlockas_openBlock,createBlockas_createBlock}from"vue"exportfunctionrender(_ctx,_cache,$props,$setup,$data,$options){ return(_openBlock(),_createBlock("div",null,[_createVNode("div",{ title:_ctx.title},_toDisplayString(_ctx.text),9/*TEXT,PROPS*/,["title"])]))}æ们å¯ä»¥è§å¯å°å¨_createVNodeå½æ°ç第å个åæ°æ¯ä¸ª9ï¼åé¢æ¯ä¸ä¸ªæ³¨éï¼/TEXT,PROPS/ï¼è¿ä¸ªæ¯è¡¨ç¤ºå¨å½åçèç¹éé¢æ两个ä¸è¥¿æ¯å¨æçï¼ä¸ä¸ªæ¯å é¨çææ¬ï¼ä¸ä¸ªæ¯å±æ§ï¼ç¶åå ·ä½æ¯åªä¸ªå±æ§ï¼å¨ç¬¬äºä¸ªåæ°çæ°ç»éé¢åè®°å½äºä¸æ¥["title"]ï¼æ个titleçå±æ§æ¯å¨æçã
å¨å°æ¥è¿è¡patchæ´æ°çæ¶åï¼å°±å¯ä»¥æ ¹æ®å½åè®°å½çä¿¡æ¯ï¼è¿è¡æ´æ°ï¼ç¼©åæ´æ°è¿ç¨åæä½ï¼å¯ä»¥é常精确å°åªè¿è¡titleåææ¬çæ´æ°ã
å¦ædivæ ç¾éæ¯éæææ¬çè¯ï¼_createVNodeå½æ°ç第å个åæ°ååæäº8ï¼åé¢ç注éåæäºï¼/PROPS/ï¼åé¢ç第äºä¸ªåæ°æ°æ®ä¸åã
_createVNodeå½æ°ç第å个åæ°çæ°åå ¶å®æ¯ä¸ä¸ªäºè¿å¶æ°å转æåè¿å¶çæ°åã
8çäºè¿å¶æ¯ï¼9çäºè¿å¶æ¯ï¼å¾å®¹æå¯ä»¥çåºäºè¿å¶çæ¯ä¸ä½çæ°åé½ä»£è¡¨çç¹æ®çå«ä¹ãè¿äºæ°åå°±æ¯patchFlagï¼é£ä¹ä»ä¹æ¯patchFlagå¢ï¼
ä»ä¹æ¯patchFlagpatchFlagæ¯complieræ¶çtransformé¶æ®µè§£æASTElementæä¸çè¡¥ä¸æ è®°ãå®ä¼ä¸ºruntimeæ¶çpatchVNodeæä¾ä¾æ®ï¼ä»èå®ç°é¶åæ´æ°VNodeåéææåçææã
patchFlag被å®ä¹ä¸ºä¸ä¸ªæ°åæ举类åï¼å®çæ¯ä¸ä¸ªæ举å¼å¯¹åºçæ è¯æä¹æ¯ï¼
TEXT=1å¨æææ¬çå ç´
CLASS=2å¨æç»å®classçå ç´
STYLE=4å¨æç»å®styleçå ç´
PROPS=8å¨æpropsçå ç´ ï¼ä¸ä¸å«æclassãstyleç»å®
FULL_PROPS=å¨æpropså带ækeyå¼ç»å®çå ç´
HYDRATE_EVENTS=äºä»¶çå¬çå ç´
STABLE_FRAGMENT=åå ç´ ç订é ä¸ä¼æ¹åçFragmentå ç´
KEYED_FRAGMENT=èªå·±æåå ç´ å¸¦ækeyå¼ç»å®çFragmentå ç´
UNKEYED_FRAGMENT=没ækeyå¼ç»å®çFragmentå ç´
NEED_PATCH=带ærefãæ令çå ç´
DYNAMIC_SLOTS=å¨æslotçç»ä»¶å ç´
HOISTED=-1éæçå ç´
BAIL=-2ä¸æ¯renderå½æ°çæçä¸äºå ç´ ï¼ä¾å¦renderSlot
æ´ä½ä¸patchFlagçå为两大类ï¼
å½patchFlagçå¼å¤§äº0æ¶ï¼ä»£è¡¨æ对åºçå ç´ å¨patchVNodeæ¶ærenderæ¶æ¯å¯ä»¥è¢«ä¼åçæææ´æ°ç
å½patchFlagçå¼å°äº0æ¶ï¼ä»£è¡¨æ对åºçå ç´ å¨patchVNodeæ¶ï¼æ¯éè¦è¢«fulldiffï¼å³è¿è¡éå½éåVNodetreeçæ¯è¾æ´æ°è¿ç¨ã
以ä¸å°±æ¯vue3çä¸ä¸ªé常é«æçä¼åçç¥å«è¡¥ä¸æ è®°åå¨æå±æ§è®°å½ã
3.ç¼åäºä»¶å¤çç¨åºfunctiontokenizer(input){ ...}functionparse(template){ consttokens=tokenizer(template)...}functiontransform(ast){ ...}functiontraverse(ast,context){ ...}functiongenerate(ast){ ...}functioncompile(template){ //1.解æconstast=parse(template)console.log(JSON.stringify(ast,null,2))//2.转æ¢transform(ast)//3.çæconstcode=generate(ast)console.log(code)//returnfunctionrender(ctx){ //returnh("h3",{ },//ctx.title//)}returnnewFunction(code)()}lettmpl=`<h3>{ { title}}</h3>`compile(tmpl)0å°æ¥æ¡æ¶ä¼åreacté£æ ·æ@click="onClick"åæ@click="()=>onClick()"ï¼æåå¯è½æ¯è¿æ ·çä¸ä¸ªç®å¤´å½æ°ãé£å°±æå³çæ¯æ¬¡onClickçå½æ°é½æ¯ä¸ä¸ªå ¨æ°çå½æ°ï¼é£å°±ä¼é æè¿ä¸ªåè°å½æ°ææ没æåï¼é½ä¼è¢«è®¤ä¸ºåäºï¼é£å°±å¿ é¡»è¿è¡ä¸ç³»åçæ´æ°ï¼é£ä¹å¦æè½æè¿ä¸ªåè°å½æ°ç¼åèµ·æ¥ï¼æ´æ°çæ¶åï¼å°±ä¸è¦åå建äºã
æªè¿è¡ç¼åäºä»¶å¤çç¨åºä¹åçç¼è¯
functiontokenizer(input){ ...}functionparse(template){ consttokens=tokenizer(template)...}functiontransform(ast){ ...}functiontraverse(ast,context){ ...}functiongenerate(ast){ ...}functioncompile(template){ //1.解æconstast=parse(template)console.log(JSON.stringify(ast,null,2))//2.转æ¢transform(ast)//3.çæconstcode=generate(ast)console.log(code)//returnfunctionrender(ctx){ //returnh("h3",{ },//ctx.title//)}returnnewFunction(code)()}lettmpl=`<h3>{ { title}}</h3>`compile(tmpl)1è¿è¡ç¼åäºä»¶å¤çç¨åºä¹åçç¼è¯
functiontokenizer(input){ ...}functionparse(template){ consttokens=tokenizer(template)...}functiontransform(ast){ ...}functiontraverse(ast,context){ ...}functiongenerate(ast){ ...}functioncompile(template){ //1.解æconstast=parse(template)console.log(JSON.stringify(ast,null,2))//2.转æ¢transform(ast)//3.çæconstcode=generate(ast)console.log(code)//returnfunctionrender(ctx){ //returnh("h3",{ },//ctx.title//)}returnnewFunction(code)()}lettmpl=`<h3>{ { title}}</h3>`compile(tmpl).åblockè¿æ¯ä»ä¹ææå¢ï¼æ ¹æ®å°¤é¨æºªæ¬äººç解æï¼ä»è¯´ï¼æ ¹æ®ä»çç»è®¡é£ä¸ªå¨æçé¨åæå¤åªæä¸åä¹ä¸ï¼åºæ¬ä¸é½æ¯éæé¨åï¼æ以å¨ç¼è¯çè¿ç¨ä¸ï¼è½ä¸è½åç°é£ä¸ªæ¯è¾å°çå¨æé¨åï¼æå®æ¾å°æ¯è¾é ä¸