MVVM框架-vue基础原理实现
/* 定义一个类,用来创建Vue实例 */ class Vue{ constructor(options = {}){ // 给Vue实例添加属性 this.$el = options.el; this.$data = options.data; this.$methods = options.methods; // 如果指定el,对el进行解析 if(this.$el){ //compile负责编译解析模板内容 // 需要:数据和模板 new Compile(this.$el, this) } } }
定义vue实例,添加属性,对指定模板进行解析;
/* Compile专门负责解析模板内容 */ class Compile { // 参数一:模板 // 参数二:Vue实例 constructor(el, vm){ // el:new vue传递的选择器 this.el = typeof(el) === "string" ? document.querySelector(el) : el; // vm: new vue传递的实例 this.vm = vm; //编译模板 if(this.el){ //1.把el中所有子节点都放入内存中, fragment let fragment = this.node2fragment(this.el); //2.在内存中编译fragment this.compile(fragment); //3.把fragment一次性加到页面中 this.el.appendChild(fragment); } } /* 核心方法 */ node2fragment(node){ let fragment = document.createDocumentFragment(); //获取所有的子节点 let childNodes = node.childNodes; this.toArray(childNodes).forEach(node => { //把el中所有子节点添加到文档碎片fragment中 fragment.appendChild(node) }) return fragment; } /** * 编译文档碎片(内存中) * @param {*} fragment */ compile(fragment){ let childNodes = fragment.childNodes; //编译子节点 this.toArray(childNodes).forEach( node => { if(this.isElementNode(node)){ //如果是元素,需要解析指令 this.compileElement(node); } if(this.isTextNode(node)){ //如果是文本节点,需要解析差值表达式 this.compileText(node); } // 如果当前节点还有子节点,需要继续解析 if(node.childNodes && node.childNodes.length > 0 ){ this.compile(node) } }) } // 解析html标签 compileElement(node){ //console.log(node) // 1.获取当前节点下所有的属性 let attributes = node.attributes; this.toArray(attributes).forEach(attr => { // 2.解析vue的指令(所有以v-开头的属性) let attrName = attr.name; if(this.isDirective(attrName)){ let type = attrName.slice(2); let expr = attr.value; // 如果是v-on指令 if(this.isEventDirective(type)){ CompileUtil['eventHandler'](node, this.vm, type, expr) }else{ CompileUtil[type](node, this.vm, expr ) } } }) } // 解析文本节点 compileText(node){ let txt = node.textContent; let reg = /\{\{(.+)\}\}/; if(reg.test(txt)){ let expr = RegExp.$1; node.textContent = txt.replace(reg, this.vm.$data[expr]); } } /* 工具方法 */ //转换数组方法 toArray(likeArray){ return [].slice.call(likeArray); } // 判断是否元素 isElementNode(node){ return node.nodeType === 1; } // 判断是否文本节点 isTextNode(node){ return node.nodeType === 3; } // 判断是否为指令 isDirective(attrName){ return attrName.startsWith('v-'); } // isEventDirective(type){ return type.split(':')[0] === "on"; } } let CompileUtil = { // 处理v-text指令 text(node, vm, expr){ node.textContent = vm.$data[expr]; }, html(node, vm, expr){ node.innerHTML = vm.$data[expr]; }, model(node, vm, expr){ node.value = vm.$data[expr]; }, eventHandler(node, vm, type, expr){ // 给当前元素注册事件即可 let eventType = type.split(":")[1]; let fn = vm.$methods && vm.$methods[expr] if(eventType && fn){ node.addEventListener(eventType, fn.bind(vm)); } } }
解析vue中差值表达式和一些基础指令;学习总结如下:
1.fragment文档片段,内存中存在的元素,不会画到页面上,影响性能;
2.一些基础JS原生方法:
- let fragment = document.createDocumentFragment();//创建fragment
-
let childNodes = node.childNodes;//获取所有的子节点
-
let attributes = node.attributes;//获取当前节点下所有的属性
-
正则的分组RegExp.$1,和reg.test()方法
3.Compile专门负责解析模板中的内容

浙公网安备 33010602011771号