1.Vue3 中的动定ce动定 v-bind 指令:你不知道的那些工作原理
2.vue双向绑定原理实现
3.v2双项数据绑定?
4.vue早期源码学习系列之四:如何实现动态数据绑定
5.vue3ä¸çç¼è¯å¨åçåä¼åçç¥
Vue3 中的 v-bind 指令:你不知道的那些工作原理
Vue3中的v-bind指令,以其灵活的态绑态绑使用方式让人熟知。本文将深入探讨其工作原理,源码以debug源码解析其多变的写法绑定形式是如何实现的。
首先,动定ce动定我们通过一个简单的态绑态绑idea查询源码示例来展示v-bind指令的使用:将变量title绑定到div的title属性上。编译后的源码代码显示,无论使用何种写法(如:v-bind:title,写法 v-bind:title=, 或者vue3.4新引入的写法),最终的动定ce动定props对象都是{ title: $setup.title },确保了属性绑定的态绑态绑正确性。
继续深入,源码v-bind指令的写法处理过程在transformElement函数中进行,这是动定ce动定在编译阶段处理内置指令如v-for和v-model的函数之一。通过debug工具,态绑态绑我们发现v-bind指令会在transformElement的源码执行中生成对应的props对象。
在buildProps函数中,v-bind指令的属性被解析和合并到props对象中,随后在transformBind函数里,根据指令的写法和值(或省略的值)进行处理,生成包含key和value的属性对象。例如,当省略值时,会将变量名转换并补全,形成最终的net core源码解读键值对。
总结来说,v-bind指令在Vue3的编译流程中,通过transformElement和其内部函数,无论写法如何变化,都能确保属性绑定的准确性和灵活性,为开发者提供了强大的动态绑定能力。
vue双向绑定原理实现
数据双向绑定的核心在于实现视图与数据之间的实时同步更新,达到一种动态响应的效果。Vue通过实现MVVM模式,实现这一目标。
Vue的双向绑定,其原理主要依赖于Object.defineProperty()方法,重新定义对象属性的获取和设置操作。以此,当数据发生改变时,视图能实时响应并更新,反之亦然。
在Vue中,数据双向绑定的实现需借助三个关键组件:Observer、Watcher和Compile。
Observer组件作为数据监听器,通过Object.defineProperty()方法,对所有属性进行劫持监听。网页版象棋源码当属性值发生变化时,会通知订阅者Watcher进行更新。这里引入了Dep消息订阅器,用于收集所有Watcher,并进行统一管理。此组件将数据变化与视图更新之间的联系紧密绑定。
Watcher组件在接收到属性变化通知后,执行相应的更新函数,从而更新视图。这样的机制确保了数据与视图之间的实时同步。
Compile组件负责解析HTML模板中的指令,初始化数据和订阅者Watcher,并将模型数据与视图组件绑定。通过指令解析,Vue能够实现数据的实时绑定和视图的动态更新。
数据双向绑定的流程如下:首先,使用Observer对数据进行劫持监听,监测数据变化。然后,通过Watcher处理数据变化通知,触发视图更新。最后,Compile解析模板中的framework源码编译打包指令,初始化数据和订阅者,实现数据与视图之间的动态绑定。
Vue源码中,v-model的实现涉及Observer、Watcher和Compile三个组件协作,共同完成数据与视图之间的双向绑定,实现高效的数据驱动视图更新。
v2双项数据绑定?
vue数据双向绑定原理
1、vue数据双向绑定是通过数据劫持结合发布者-订阅者模式的方式来实现的,其中比较关键的是数据劫持,下面咱们看一个例子。
2、视图交互变化(input)-数据model变更的双向绑定效果。v-model是什么?怎么使用?vue中标签怎么绑定事件?可以实现双向绑定,指令(v-class、v-for、v-if、v-show、v-on)。vue的model层的data属性。
3、响应式原理:每个组件实例都对应一个watcher实例,它会在组件渲染的不亏本指标源码过程中把“接触”过的数据property记录为依赖。之后当依赖项的setter触发时,会通知watcher,从而使它关联的组件重新渲染。
4、于是vue中就是每当有这样的可能用到双向绑定的指令,就在一个Dep中增加一个订阅者,其订阅者只是更新自己的指令对应的数据,也就是v-model=name和{ { name}}有两个对应的订阅者,各自管理自己的地方。
vue的v-once指令怎么用v-cloak和CSS规则如[v-cloak]{ display:none}一起用时,这个指令可以隐藏未编译的Mustache标签直到组件实例准备完毕,主要用于解决闪动问题,现在Vue3一般不会出现这个问题了。不会显示,直到编译结束。
v-once:该指令后面不需要跟任何表达式(v-for后面接表达式),该指令表示元素和组件只渲染一次,不会随着数据的改变而改变。v-html:v-html会将数据当html标签解析后输出。
vue的常用指令v-bind:用于绑定属性,通过v-bind:绑定过的属性,可以直接在属性值写表达式。可以简写为:v-on:用于绑定事件,通过v-on:绑定过的事件,可以指定vue实例定义的方法。
使用v-on指令监听DOM事件,并在触发时运行一些JavaScript代码。
Vue.js为v-on提供了事件修饰符来处理DOM事件细节,如:event.preventDefault()或event.stopPropagation()。Vue.js通过由点(.)表示的指令后缀来调用修饰符。
本小节我们介绍Vue中如何进行事件处理。在章节2中我们已经介绍了指令v-on,本章节我们将详细介绍在v-on的一些用法。包括如何传递参数、如何使用事件修饰符等。其中,事件修饰符是本章节的难点。
实现双向数据绑定在上篇文章当中,我们实现了单向数据绑定,那么接下来,咱们一步一步来实现双向数据绑定。
一般来说要实现这种双向数据绑定,在前端我目前了解的有三种形式:目前angular,regular的实现都是基于脏检查。当发生某些特定的事情的时候,框架会调用相关的digest方法。内部逻辑就是遍历所有的watcher,对监控的属性做对比。
Vue主要通过以下4个步骤来实现数据双向绑定的:实现一个监听器Observer:对数据对象进行遍历,包括子属性对象的属性,利用Object.defineProperty()对属性都加上setter和getter。
vue实现数据双向绑定主要是:采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应监听回调。
单个v-model数据绑定默认情况下,组件上的v-model便用modelvalue作为prop和update:modelvalu作为事件。
vuex源码分析(二)——双向数据绑定
通过Compile来解析编译模板指令(vue中是用来解析{ { }}),最终利用watcher搭起observer和Compile之间的通信桥梁,达到数据变化—视图更新;视图交互变化(input)—数据model变更双向绑定效果。
vue双向数据绑定是通过数据劫持结合发布订阅模式实现的,数据和视图同步,即数据发生变化,视图跟着变化,视图变化,数据也随之发生改变核心:Object.defineProperty()参数:obj:要定义属性的对象。
于是vue中就是每当有这样的可能用到双向绑定的指令,就在一个Dep中增加一个订阅者,其订阅者只是更新自己的指令对应的数据,也就是v-model=name和{ { name}}有两个对应的订阅者,各自管理自己的地方。
vue早期源码学习系列之四:如何实现动态数据绑定
本系列更多文章,请查阅我的博客:GitHub - youngwind/blog: 梁少峰的个人博客。或扫描下方二维码关注公众号“前端控”,欢迎交流讨论!
在前一篇中,我们学习了如何监听数据变化以及使用观察者模式和事件传递响应变化事件。今天,我们将探讨如何基于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ä¸çç¼è¯å¨åçåä¼åçç¥
å¦ä¹ ç®æ
ç¼è¯å¨åç
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è¿æ¯ä»ä¹ææå¢ï¼æ ¹æ®å°¤é¨æºªæ¬äººç解æï¼ä»è¯´ï¼æ ¹æ®ä»çç»è®¡é£ä¸ªå¨æçé¨åæå¤åªæä¸åä¹ä¸ï¼åºæ¬ä¸é½æ¯éæé¨åï¼æ以å¨ç¼è¯çè¿ç¨ä¸ï¼è½ä¸è½åç°é£ä¸ªæ¯è¾å°çå¨æé¨åï¼æå®æ¾å°æ¯è¾é ä¸