html部分
<div id="app">
<input type="text" v-model="myText">
{{myText}}
<input type="text" v-model.lazy="myText">
</div>
js部分
function defineReactive(vm, key, value) {
let dep = new Dep()
Object.defineProperty(vm, key, {
get() {
if (Dep.target) {
dep.addSub(Dep.target)
}
return value
},
set(newVal) {
if (newVal === value) return
value = newVal
// 通知更新
dep.notify()
}
})
}
function observe(vm, data) {
Object.keys(data).forEach(key => {
defineReactive(vm, key, data[key])
})
}
function Dep() {
this.subs = []
}
Dep.prototype.addSub = function (watcher) {
this.subs.push(watcher)
}
Dep.prototype.notify = function () {
this.subs.forEach(watcher => {
watcher.update()
})
}
function Watcher(vm, node, name) {
// 实例化时标记此Watcher,并触发get()方法,将 Watcher 添加到订阅器 Dep 中
Dep.target = this
this.vm = vm
this.node = node
this.name = name
this.get()
this.update()
// 添加完成,删除标记
Dep.target = null
}
Watcher.prototype.get = function () {
this.value = this.vm[this.name] // 触发 defineReactive 中get方法,将Watcher添加到Dep中
}
Watcher.prototype.update = function () {
let value = this.vm[this.name]
// 更新文本
this.node.nodeValue = value
// 更新input框
window.vModelObj[this.name].forEach(node => {
node.value = value
})
}
function compile(node, vm) {
if (!window.vModelObj) window.vModelObj = {}
// 元素节点 - 解析指令
if (node.nodeType === 1) {
let attrs = node.attributes
for (let i = 0; i < attrs.length; i++) {
let attr = attrs[i]
if (attr.nodeName.includes('v-model')) {
if (window.vModelObj[attr.nodeValue]) {
window.vModelObj[attr.nodeValue].push(node)
} else {
window.vModelObj[attr.nodeValue] = [node]
}
// 解析修饰符 并 添加监听事件
node.addEventListener(attr.nodeName.includes('.lazy') ? 'change' : 'input', function (e) {
vm[attr.nodeValue] = e.target.value
})
node.value = vm[attr.nodeValue]
}
}
}
// 文本节点 - 绑定订阅者 Watcher
if (node.nodeType === 3) {
let reg = /\{\{(.*)\}\}/;
// 正则匹配模版中 {{}} 中的内容
if (reg.test(node.nodeValue)) {
let name = RegExp.$1
new Watcher(vm, node, name)
}
}
}
function nodeToFragment(vm, dom) {
let fragment = document.createDocumentFragment()
let child = dom.firstChild
while (child) {
compile(child, vm)
fragment.appendChild(child)
child = dom.firstChild
}
return fragment
}
function Vue(options) {
let data = options.data
// data 为函数类型判断
if (Object.prototype.toString.call(options.data) === '[object Function]') {
data = options.data()
}
observe(this, data)
let dom = document.querySelector(options.el)
let newDom = nodeToFragment(this, dom)
dom.appendChild(newDom)
}
new Vue({
el: '#app',
data() {
return {
myText: 'TOM'
}
}
})