本教程讲解es6语法将用于编写创建MiniVue.js文件//创建一个MVVM类classMVVM{//构造函数constructor(option){//缓存重要属性这个.$vm=这个;this.$el=option.el;this.$data=option.data;}}MVVM类的作用:解析视图模板,将相应的数据渲染到视图中。首先判断视图是否存在。如果视图存在这时候,创建一个模板编译器来解析视图类MVVM{//构造函数constructor(option){//缓存重要属性this.$vm=this;this.$el=option.el;这个。$数据=选项。数据;//判断视图是否存在if(this.$el){//创建一个模板编译器来解析视图this.$compiler=newTemplateCompiler(this.$el,this.$vm)}}}让我们去创建一个文件TemplateCompiler.js,输入以下内容//创建模板编译工具classTemplateCompiler{//el视图//vm全局vm对象constructor(el,vm){//缓存重要属性this.el=document.querySelector(el);这个.vm=虚拟机;}}缓存重要属性后,需要对模板进行解析。步骤是分三步将模板内容放入内存(内存片段)。解析模板,将内存的结果放回模板类TemplateCompiler{//elview//vmglobalvmobjectconstructor(el,vm){//缓存重要属性this.el=document.querySelector(el);这个.vm=虚拟机;//1.将模板内容放入内存(内存片段)letfragment=this.node2fragment(this.el);//2.解析模板this.compile(fragment);//3.将内存结果放回模板this.el.appendChild(fragment);}}下面定义node2fragment()方法和compile()方法下面来实现分析TemplateCompiler类有两类方法:工具方法和核心方法classTemplateCompiler{//elview//vmglobalvmobjectconstructor(el,vm){//缓存重要属性this.el=document.querySelector(el);这个.vm=虚拟机;//1.将模板内容放入内存(内存片段)letfragment=this.node2fragment(this.el)//2.解析模板this.compile(fragment);//3。将记忆结果放回模板中this.el.appendChild(fragment);}}//工具方法isElementNode(node){//1.元素节点2.属性节点3.文本节点returnnode.nodeType===1;}isTextNode(node){返回node.nodeType===3;}//核心方法node2fragment(node){//1.创建一个内存片段letfragment=document.createDocumentFragment();//2.将模板放入内存letchild;while(child=node.firstChild){fragment.appendChild(child);}//3.返回返回片段;}编译(节点){}}关于createDocumentFragment的说明:DocumentFragments是不属于主DOM树的DOM节点。通常的用例是创建文档片段,将元素附加到文档片段,然后将文档片段附加到DOM树。在DOM树中,文档片段被其所有子元素替换。因为文档片段存在于内存中,而不是DOM树中,所以在文档片段中插入子元素不会造成页面回流(计算元素位置和几何)。因此,使用文档片段通常会带来更好的性能。解析解析模板compile()方法实现方法先获取每个子节点,然后遍历每个节点,再判断节点类型。下面的childNode是一个没有数组方法的类数组。使用展开运算符(spread)[...childNode]进行转换Compile(parent){//1.获取子节点letchildNode=parent.childNodes;//2.遍历每个节点[...childNode].forEach(node=>{//3.判断节点类型if(this.isElementNode(node)){//解析元素节点的说明this.compileElement(node);}})}下面是实现compileElement()方法。当前调用的是元素节点,下一步是获取元素的所有属性,然后遍历所有属性,然后判断属性是否是命令v-text是vue命令,比如ng-xxx命令不是收集,判断为命令时收集结果compileElement(node){//1.获取当前节点的所有属性letattrs=node.attributes;//2.遍历当前元素的所有属性[...attrs].forEach(attr=>{letattrName=attr.name//3.判断属性是否为Directiveif(this.isDirective(attrName)){//4.收集lettype=attrName.substr(2);//v-text//指令的值是表达式letexpr=attr.value;//解析指令CompilerUtils.text(node,this.vm,expr);}})}实现CompilerUtils类CompilerUtils={//解析文本指令text(node,vm,expr){//1.找到更新方法letupdaterFn=this.updater['textUpdater'];//执行方法updaterFn&&updaterFn(node,vm.$data[expr]);},//更新规则对象updater:{//文本更新方法textUpdater(node,value){node.textContent=value;}}}在TemplateCompiler类工具方法下添加判断属性是否为指令的方法isDirective()isDirective(attrName){//判断属性是否为指令returnattrName.indexOf('v-')>=0;}实现(v-text)完整代码//创建模板编译工具类TemplateCompiler{//el视图//vm全局vm对象constructor(el,vm){//缓存重要属性this.el=document.querySelector(el);这个.vm=虚拟机;//1.将模板内容放入内存(内存片段)letfragment=this.node2fragment(this.el)//2.解析模板this.compile(fragment);//3.将内存结果返回给模板this.el.appendChild(fragment);}//工具方法isElementNode(node){//1.元素节点2.属性节点3.文本节点returnnode.nodeType===1;}isTextNode(node){返回node.nodeType===3;}isDirective(attrName){//判断属性是否为指令returnattrName.indexOf('v-')>=0;}//核Heartmethodnode2fragment(node){//1.创建内存碎片letfragment=document.createDocumentFragment();//2.将模板内容放入内存letchild;while(child=node.firstChild){fragment.appendChild(child);}//3.返回返回片段;}compile(parent){//1.获取子节点letchildNode=parent.childNodes;//2.遍历每个节点[...childNode].forEach(node=>{//3.判断节点类型if(this.isElementNode(node)){//元素节点(解析指令)this.compileElement(node);}})}//指令compilingElement(node){//1.获取当前节点的所有属性letattrs=node.attributes;//2.遍历当前元素的所有属性[...attrs].forEach(attr=>{letattrName=attr.name//3.判断属性是否为指令if(this.isDirective(attrName)){//4.Collectlettype=attrName.substr(2);//v-text//指令的值是表达式letexpr=attr.value;CompilerUtils.text(node,this.vm,expr);}})}//解析表达式compileText(){}}CompilerUtils={//解析文本命令text(node,vm,expr){//1.找到更新方法letupdaterFn=this.updater['textUpdater'];//执行方法updaterFn&&updaterFn(node,vm.$data[expr]);},//更新规则对象updater:{//文本更新方法textUpdater(node,value){node.textContent=value;}}}实现(v-model)修改如下代码compileElement(node){//1.获取当前节点的所有属性letattrs=node.attributes;//2.遍历当前元素的所有属性[...attrs].forEach(attr=>{letattrName=attr.name//3.判断属性是否为指令if(this.isDirective(attrName)){//4.Collectionlettype=attrName.substr(2);//v-text//指令的值是表达式letexpr=attr.value;//----------------------修改代码开始--------------------//CompilerUtils.text(node,this.vm,expr);CompilerUtils[type](node,this.vm,expr);//------------------------修改代码结束---------------------}})}添加实现CompilerUtils={...//--------在CompilerUtils类中-------------新方法--------------------//解析模型命令model(node,vm,expr){//1.找到更新方法letupdaterFn=this.updater['modelUpdater'];//执行方法updaterFn&&updaterFn(node,vm.$data[expr]);},//更新规则对象updater:{...//--------------------新方法------------------------//输入框更新方法modelUpdater(node,value){node.value=value}}}实现(v-html)由读者自行添加相应的方法,形式类似(v-model)来实现下面的解析表达式,在compile中添加如下代码Importanthowtouseverificationexpressioncompile(parent){//1.获取子节点letchildNode=parent.childNodes;//2.遍历每个节点[...childNode].forEach(node=>{//3.判断节点类型if(this.isElementNode(node)){//元素节点(解析指令)this.compileElement(node);//-----------------新代码--------------------//文本node}elseif(this.isTextNode(node)){//表达式分析//定义表达式正则校验规则lettextReg=/\{\{(.+)\}\}/;letexpr=node.textContent;//根据规则验证内容if(textReg.test(expr)){//获取分组内容expr=RegExp.$1;//调用方法编译this.compileText(node,expr);}}})}让我们来实现文本解析器,它类似于表达式解析,通过分析(v-text)//解析表达式compileText(node,expr){CompilerUtils.text(node,this.vm,expr);}完整实现代码//创建模板编译工具类TemplateCompiler{//elview//vmglobalvmobjectconstructor(el,vm){//缓存重要属性this.el=document.查询选择器(EL);这个.vm=虚拟机;//1.将模板内容放入内存(内存片段)letfragment=this.node2fragment(this.el)//2.解析模板this.compile(fragment);//3.将内存结果返回给模板this.el.appendChild(fragment);}//工具方法isElementNode(node){//1.元素节点2.属性节点3.文本节点returnnode.nodeType===1;}isTextNode(node){返回node.nodeType===3;}isDirective(attrName){//判断属性是否为指令returnattrName.indexOf('v-')>=0;}//核心方法node2fragment(node){//1.创建一个内存片段letfragment=document.createDocumentFragment();//2.将模板内容放入内存letchild;while(child=node.firstChild){fragment.appendChild(child);}//3.返回返回片段;}compile(parent){//1.获取子节点letchildNode=parent.childNodes;//2.遍历每个节点[...childNode].forEach(node=>{//3.判断节点类型if(this.isElementNode(node)){//元素节点(解析指令)this.compileElement(node);}elseif(this.isTextNode(node)){//表达式解析//定义表达式正则验证规则lettextReg=/\{\{(.+)\}\}/;letexpr=node.textContent;//根据规则验证内容if(textReg.test(expr)){expr=RegExp.$1;//调用编译方法this.compileText(node,expr);}}})}//解析元素节点的命令compileElement(node){//1.获取当前节点的所有属性letattrs=node.attributes;//2.遍历当前元素的所有属性[...attrs].forEach(attr=>{letattrName=attr.name;//3.判断属性是否为指令if(this.isDirective(attrName)){//4.Collectlettype=attrName.substr(2);//v-text//指令的值是表达式letexpr=attr.value;//CompilerUtils.text(node,this.vm,expr);CompilerUtils[type](node,this.vm,expr);}})}//解析表达式compileText(node,expr){CompilerUtils.text(node,this.vm,expr);}}CompilerUtils={//解析文本命令text(node,vm,expr){//1.找到更新方法letupdaterFn=this.updater['textUpdater'];//执行方法updaterFn&&updaterFn(node,vm.$data[expr]);},//解析模型指令model(node,vm,expr){//1.找到更新方法letupdaterFn=this.updater['modelUpdater'];//执行方法updaterFn&&updaterFn(node,vm.$data[expr]);},//更新规则对象updater:{//文本更新方法textUpdater(node,value){node.textContent=value;},//输入框更新方法modelUpdater(node,value){node.value=value;}}}数据双向绑定(完)更多精彩后续内容
