手撕VUE源码(一):手写一个MVVM
class Vue{ constructor(options){ this.$el = options.el; //获取元素节点 this.$data = options.data; //获取数据 //若元素节点不为空 if(this.$el){ new Observer(this.$data); //数据劫持 new Compiler(this); //解析节点变为虚拟dom,放到内存中 } } } class Observer{ constructor(data){ this.observer (data); } observer(data){ //判断是否为对象 if(data && typeof data == 'object'){ for(let key in data){ this.reactive(key,data[key],data); //对key属性绑定数据劫持 } } } reactive(key,value,data){ this.observer(value); //对嵌套的子属性继续递归劫持 Object.defineProperties(data,key,{ get(){ return value; }, set: newValue => { // if(newValue != value){ value = newValue; this.observer(newValue);//若新值为一个对象则对新对象再次劫持 } } }) } } class Compiler{ constructor(vm){ this.vm = vm; //判断el是元素节点还是元素ID this.el = this.isElementNode(vm.$el)?el:document.querySelector(el); let fregment = this.getChildNodes(this.el); //将子元素放入内存 //将模板语法转换成绑定的数据 this.compilerToData(fregment); } compilerToData(fregment){ //开始转换 [...fregment.childNodes].forEach(child =>{ //判断子节点类型是元素还是文本节点 if(isElementNode(child)){ this.compilerElementNode(child); //转换元素节点 this.compilerToData(child); //递归遍找到所有的孩子 }else{ this.compilerTextNode(child); //转换文本节点 } }) } compilerElementNode(node){ let attributes = node.attributes; //节点的attributes属性也是个类数组 [...attributes].forEach( attr => { let {name:vueDirect, value:express} = attr; //v-model="jsc" 对象结构,冒号后面代表将属性的值赋给新的变量名 //判断属性是否为vue指令 if(this.isVueDirect(vueDirect)){ let [,vueDirect] = vueDirect.split("-") //对象结构,对“v-model”再做一次分割 CompilerMapUtil[vueDirect](node,this.vm,express); //CompilerMapUtil是一个映射关系,不同的vue指令对应不同的解析处理方式 } }) } compilerTextNode(node){ let textcontent = node.textcontent; //获取文本内容 if(/\{\{(.+?)\}\}/.test(textcontent)) { CompilerMapUtil["text"](node,vm,textcontent) } } isVueDirect(name){ if(name.startsWith("v-")){ return true; } return false; } getChildNodes(el){ let fregment = document.createDocumentFragment(); let firstChilNode; //每次取一个子节点放入内存中,直到取完为止 while(firstChilNode = el.firstChild){ fregment.appendChild(firstChilNode); } return fregment; } isElementNode(node){ return node.nodeType ===1; } } //vue指令及解析处理关系映射 CompilerMapUtil = { model(node,vueObject,express){ //根据model属性的变量名字express,从vue对象vm中取数据(jsc:11111,express的值是jsc) let modelValue = this.getValue(vueObject,express); let fn = this.updater['modelUpdater'] //获取model指令对应的赋值方法 fn(node,modelValue); //将vue对象中的值赋给输入框 }, text(node,vueObject,content){ //正则表达式的g表示全局,意为找出所有{{}}进行替换 //因为箭头函数没有arguments,因此使用...args剩余参数表示法,可以将类数组arguments转换成数组类型变量args接收 let textContent = content.replace(/\{\{(.+?)\}\}/g,(...args)=>{ //replace的回调函数可以设置5个参数,该函数的第一个参数是匹配模式的子串。第二个参数是与模式中子表达式匹配的子串 //args第一个参数是正则表达式匹配的{{jsc}},第二个参数是子表达式匹配到的jsc return this.getValue(vueObject,args[1]); }) let fn = this.updater['textUpdater'] // 获取文本节点对应的赋值方法 fn(node,textContent); //进行赋值操作 }, getValue(vueObject,express){ //express在这里是jsc.name(v-model="jsc.name") //reduce会遍历[jsc,name]这个数组,并把vue的数据对象作为第一次的dataObject参数 //每一次的返回值作为下一次的dataObject入参 express.split(".").reduce( (dataObject,currentKey) => { return dataObject[currentKey]; },vueObject.$data) }, updater:{ modelUpdater(node,modelValue){ node.value = modelValue; }, textUpdater(node, modelValue){ node.textcontent = modelValue } }, for(){ }, if(){ } }