1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
| <div id="app"> <h1>{{name}}</h1> <input type="text" v-model="name" /> <h2>v-text</h2> <div v-text="name"></div> <div v-html="name"></div> <div> <p>年龄:{{age}}</p> <p>地址:{{address}}</p> </div> </div> <script> class LVue2 { constructor(option) { this.$option = option this.$data = option.data this.observer() this.compile() } observer() { const keys = Object.keys(this.$data) keys.forEach(keyName => { const dep = new Dep() let value = this.$data[keyName] Object.defineProperty(this.$data, keyName, { configurable: true, enumerable: true, get() { if (Dep.target) { dep.addSub(Dep.target) } return value }, set(newValue) { dep.notify(newValue) value = newValue }, }) }) } compile() { const el = document.querySelector(this.$option.el) this.compileNodes(el) } compileNodes(el) { const childNodes = el.childNodes childNodes.forEach(node => { if (node.nodeType === 1) { let attrs = node.attributes ;[...attrs].forEach(attr => { const attrName = attr.name const attrValue = attr.value if (attrName === 'v-model') { node.value = this.$data[attrValue] node.addEventListener('input', e => { this.$data[attrValue] = e.target.value }) } else if (attrName === 'v-text') { node.innerText = this.$data[attrValue] new Watcher(this.$data, attrValue, newValue => { node.innerText = newValue }) } else if (attrName === 'v-html') { node.innerHTML = this.$data[attrValue] new Watcher(this.$data, attrValue, newValue => { node.innerHTML = newValue }) } }) if (node.childNodes.length > 0) { this.compileNodes(node) } } else if (node.nodeType === 3) { const textContent = node.textContent const reg = /\{\{\s*([^\{\}\s]+)\s*\}\}/g if (reg.test(textContent)) { const valueName = RegExp.$1 node.textContent = node.textContent.replace(reg, this.$data[valueName]) new Watcher(this.$data, valueName, newValue => { const oldValue = this.$data[valueName] node.textContent = node.textContent.replace(oldValue, newValue) }) } } }) } }
class Dep { constructor() { this.subs = [] } addSub(sub) { this.subs.push(sub) } notify(newValue) { this.subs.forEach(sub => { sub.update(newValue) }) } }
class Watcher { constructor(data, key, cb) { this.cb = cb Dep.target = this data[key] Dep.target = null } update(newValue) { this.cb(newValue) } }
const lvue2 = new LVue2({ el: '#app', data: { name: '梁又文', age: 23, address: '广州市荔湾区', }, }) setTimeout(() => (lvue2.$data.name = '梁文文'), 1000) setTimeout(() => (lvue2.$data.name = '我是v-text的内容'), 2000) setTimeout(() => (lvue2.$data.name = '<h3>我是v-html的内容</h3>'), 3000) </script>
|