html <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title></title> </head> <body> <div id="app"><input type="text" v-model="text" /> {{text}} ...
html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title></title> </head> <body> <div id="app"><input type="text" v-model="text" /> {{text}}</div> </body> <script src="./Mvvm.js"></script> <script type="text/javascript"> var vm = new Vue({ el: 'app', data: { text: '超哥哥', }, }) vm.data.text = '超哥哥To' </script> </html>
js
class Vue { constructor(options) { this.data = options.data //創建data 數據 this.optionsTo = options this.observe(this.data, this) //對數據數據進行雙向綁定 let id = options.el // 獲取掛載點 let dom = this.nodeToFrament(document.getElementById(id), this) //創建文檔碎片,並處理 document.getElementById(id).appendChild(dom) //將處理好的文檔碎片添加到 文檔中 } nodeToFrament(node, vm) { let fragment = document.createDocumentFragment() //創建文檔碎片 let child while ((child = node.firstChild)) { // 這裡是一個判斷條件條件,當node.firstChild 為null 的時候會停下來 this.compile(child, vm) //執行對 節點的處理 fragment.appendChild(child) // 將 處理完的數據添加到 文檔碎片容器中 } return fragment } compile(node, vm) { let reg = /\{\{(.*)\}\}/ //創建去除雙花括弧的 正則 if (node.nodeType === 1) { //如果 節點類型等於 1, 那麼代表是 元素節點 let attr = node.attributes //拿到 元素節點的所有屬性 for (let i = 0; i < attr.length; i++) { if (attr[i].nodeName == 'v-model') { //如果 元素節點中出現 v-model 屬性 let name = attr[i].nodeValue //拿到 屬性對應的 值 node.addEventListener('input', function (e) { vm.data[name] = e.target.value }) new Watcher(vm, node, name) // node.value = vm.data[name] //去data 裡面查找對應的 數據並賦值給對應 元素 node.removeAttribute('v-model') } } } if (node.nodeType === 3) { //如果是 文本節點 if (reg.test(node.nodeValue)) { let name = RegExp.$1 //調用正則 name = name.trim() //去掉前後空格 // node.nodeValue = vm.data[name] //去data 裡面查找對應的 數據並賦值給對應 元素 new Watcher(vm, node, name) //實例化Watcher 監聽數據 } } } observe(obj) { if (!obj || typeof obj !== 'object') { return } else { Object.keys(obj).forEach((key) => { this.defneReactive(obj, key, obj[key]) }) } } defneReactive(obj, key, val) { let dep = new Dep() //Dep 函數相當於是一個中間件,橋梁 this.observe(val) Object.defineProperty(obj, key, { get() { if (Dep.target) { dep.addSub(Dep.target) //只要每次獲取數據 都將全局變數裡面的數據存儲到dep 裡面,以便設置數據時調用 } return val }, set(newVal) { if (newVal === val) { return } else { val = newVal dep.notify() //觸發notify 方法,dep 裡面的數據呈現到頁面 } }, }) } } class Dep { //Dep 函數相當於是一個中間件,橋梁 constructor() { this.subs = [] } addSub(sub) { this.subs.push(sub) } notify() { this.subs.forEach((sub) => { sub.update() }) } } class Watcher { constructor(vm, node, name) { Dep.target = this //Dep.target 是一個全局變數,存儲的是 this指向的 Vue 函數裡面的數據 this.vm = vm this.node = node this.name = name this.update() Dep.target = null //將 Dep.target 變數清空,從而保證Dep.target 裡面的數據每次的是最新的 } update() { if (this.node.nodeType === 3) { this.node.nodeValue = this.vm.data[this.name] //去data 裡面查找對應的 數據並賦值給對應 元素 } else if (this.node.nodeType === 1) { this.node.value = this.vm.data[this.name] } } }