原理
Vue 采用数据劫持结合发布者-订阅者模式的方法,通过 Object.defineProperty()来劫持各个属性的 setter,getter 属性,在数据变动话,通知订阅者,触发更新回调函数,重新渲染视图

关键要素
- observer 实现对 vue 各个属性进行监听
function observer(obj, vm) {
Object.keys(obj).forEach(function (key) {
defineReactive(vm, key, obj[key])
})
}
// Object.defineProperty改写各个属性
function defineReactive(obj, key, val) {
// 每个属性建立个依赖收集对象,get中收集依赖,set中触发依赖,调用更新函数
var dep = new Dep()
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function () {
// 收集依赖 Dep.target标志
Dep.target && dep.addSub(Dep.target)
return val
},
set: function (newVal) {
if (newVal === val) return
// 触发依赖
dep.notify()
val = newVal
},
})
}
- Dep 实现
function Dep() {
this.subs = []
}
Dep.prototype = {
constructor: Dep,
addSub: function (sub) {
this.subs.push(sub)
},
notify: function () {
this.subs.forEach(function (sub) {
sub.update() // 调用的Watcher的update方法
})
},
}
- compiler 实现对 vue 各个指令模板的解析器,生成 AST 抽象语法树,编译成 Virtual Dom,渲染视图
// 编译器
function compiler(node, vm) {
var reg = /\{\{(.*)\}\}/
// 节点类型为元素
if (node.nodeType === 1) {
var attr = node.attributes
// 解析属性
for (var i = 0; i < attr.length; i++) {
if (attr[i].nodeName == 'v-model') {
var _value = attr[i].nodeValue
node.addEventListener('input', function (e) {
//给相应的data属性赋值,触发修改属性的setter
vm[_value] = e.target.value
})
node.value = vm[_value] // 将data的值赋值给node
node.removeAttribute('v-model')
}
}
new Watcher(vm, node, _value, 'input')
}
// 节点类型为text
if (node.nodeType === 3) {
if (reg.test(node.nodeValue)) {
var name = RegExp.$1
name = name.trim()
new Watcher(vm, node, name, 'input')
}
}
}
- Watcher 连接 observer 和 compiler,接受每个属性变动的通知,绑定更新函数,更新视图
function Watcher(vm, node, name, nodeType) {
Dep.target = this // this为watcher实例
this.name = name
this.node = node
this.vm = vm
this.nodeType = nodeType
this.update() // 绑定更新函数
Dep.target = null //绑定完后注销 标志
}
Watcher.prototype = {
get: function () {
this.value = this.vm[this.name] //触发observer中的getter监听
},
update: function () {
this.get()
if (this.nodeType == 'text') {
this.node.nodeValue = this.value
}
if (this.nodeType == 'input') {
this.node.value = this.value
}
},
}
完整实现
function Vue(options) {
this.date = options.data
var data = this.data
observer(data, this) // 监测
var id = options.el
var dom = nodeToFragment(document.getElmentById(id), this) //生成Virtual Dom
// 编译完成后,生成视图
document.getElementById(id).appendChild(dom)
}
function nodeToFragment(node, vm) {
var flag = document.createDocumentFragment()
var child
while ((child = node.firstChild)) {
compiler(cild, vm)
flag.appendChild(child)
}
return flag
}
// 调用
var vm = new Vue({
el: 'app',
data: {
msg: 'hello word',
},
})