Vue.js的MVVM的響應式原理-指令解析&初始化檢視
阿新 • • 發佈:2021-10-16
Vue.js採用資料劫持結合釋出者-訂閱者模式的放方式,通過Object.defineProperty()來劫持各個屬性的setter,getter,在資料變動時釋出訊息給訂閱者,觸發響應的監聽回撥
Vue.js內部指令解析和檢視初始化
//MVue.js const compileUtil = { getValue(expr, vm){ //[person,name] return expr.split('.').reduce((data,currentVal)=>{ return data[currentVal]; },vm.$data) }, text(node,expr,vm){ //expr:msg 學習MVVM實現原理 //三種情況 v-text="msg" v-text="person.name" {{}} // console.log(expr); let value; if(expr.indexOf("{{") > -1){ //{{Person.name}}--{{Person.age}} value = expr.replace(/\{\{(.+?)\}\}/g, (...args)=>{ return this.getValue(args[1], vm); }) }else{ value = this.getValue(expr, vm); } this.updater.textupdater(node, value); }, html(node,expr,vm){ const value = this.getValue(expr, vm); this.updater.htmlUpdater(node, value); }, model(node,expr,vm){ const value = this.getValue(expr, vm); this.updater.modelUpdater(node, value); }, on(node,expr,vm,eventName){ let fn = vm.$options.methods[expr]; node.addEventListener(eventName, fn.bind(vm), false); }, bind(node, expr, vm, attrName){ // }, //更新的函式 updater:{ textupdater(node, value){ node.textContent = value; }, htmlUpdater(node, value){ node.innerHTML = value; }, modelUpdater(node, value){ node.value = value; } } } class Compile{ constructor(el,vm){ this.el = this.isElementNode(el) ? el : document.querySelector(el); this.vm = vm; //1.獲取文件碎片 放入記憶體中會減少頁面的迴流和重繪 const fragment = this.node2Fragment(this.el); console.log(fragment) //2.編譯模板 this.compile(fragment); //3.追加子元素到根元素 this.el.appendChild(fragment); } compile(fragment){ //1.獲取子節點 const childNodes = fragment.childNodes; [...childNodes].forEach(child=>{ // console.log(child) if(this.isElementNode(child)){ //是元素節點 //編譯元素節點 // console.log("元素節點", child) this.compileElement(child); }else{ //文字節點 //編譯文字節點 // console.log("文字節點", child) this.compileText(child); } //遞迴遍歷內部節點 if(child.childNodes && child.childNodes.length){ this.compile(child); } }) } //編譯元素節點 compileElement(node){ const attributes = node.attributes; // console.log(attributes); [...attributes].forEach((attr)=>{ const {name, value} = attr; // console.log(name); if(this.isDirective(name)){ //是一個指令 v-textv-html v-model v-on:chilk const [,dirctive] = name.split('-'); // text html model on:click const [dirName, eventName] = dirctive.split(':'); // text html model on // 更新資料 資料驅動檢視 compileUtil[dirName](node, value, this.vm, eventName) // 刪除有指令的標籤的屬性 node.removeAttribute('v-'+ dirctive); }else if(this.isEventName(name)){ // @click="handlerClick" let [,eventName] = name.split("@"); compileUtil["on"](node, value, this.vm, eventName); } }) } //判斷是否是@符號開頭 isEventName(attrName){ return attrName.startsWith("@"); } //判斷是不是一個指令 isDirective(attrName){ return attrName.startsWith("v-"); } //編譯文字節點 compileText(node){ // {{}} v-text const content = node.textContent; if(/\{\{(.+?)\}\}/.test(content)){ compileUtil['text'](node, content, this.vm) } } node2Fragment(el){ //建立文件碎片 const f = document.createDocumentFragment(); let firstChild; while(firstChild = el.firstChild){ f.appendChild(firstChild); } return f; } //判斷是否是元素節點 isElementNode(node){ return node.nodeType === 1; } } class MVue{ constructor(options){ this.$el = options.el; this.$data = options.data; this.$options = options; if(this.$el){ //1.實現一個數據的觀察者 //2.實現一個指令解析器Compile new Compile(this.$el, this); } } }
持續更新ing...