太久没有拓展自己的知识,这次从简单的Vue响应式开始。
以下是简单的模拟一个利用Object.defineProperty
方法进行数据绑定,实现响应式的方法。
// dependency 订阅者收集
// 用于收集属性的订阅者
class Dep {
constructor () {
this.subs = [];
}
addSub(sub) {
this.subs.push(sub)
}
// 通知订阅者
notify() {
this.subs.forEach(sub => {
sub.update()
})
}
}
Dep.target = null; // 初始化时用,详细看后面的get()
// 工厂方法,将对象属性修改为响应式
function observe(obj) {
if (!obj || typeof obj !== 'object') {
return;
}
Object.keys(obj).forEach(key => {
defineReactive(obj, key, obj[key])
})
}
function defineReactive(obj, key, val) {
observe(val); // 递归对象子属性
let dep = new Dep(); // 创建当前属性依赖收集
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get () {
// 当创建watcher后,访问对象属性,此时为对象属性添加订阅者
if (Dep.target) {
dep.addSub(Dep.target)
}
return val
},
set (newVal) {
val = newVal
dep.notify() // 设置新值,触发收集到的订阅者的回调函数
}
})
}
// 订阅者
class Watcher {
// new Watcher时 ,将watcher实例添加到属性的订阅者收集当中
constructor(obj, key, cb) {
Dep.target = this; // Watcher实例添加Dep.target
this.cb = cb;
this.obj = obj;
this.key = key;
this.value = obj[key] // 触发属性的访问,将Watcher添加到了属性的dep当中
Dep.target = null // 移除标记
}
update() {
this.value = this.obj[this.key]
this.cb(this.value)
}
}
// 响应式框架
class Vue {
constructor(options) {
this._data = options.data()
observe(this._data)
this._init()
}
// 初始化
_init() {
// ...do something
this._compile(this._data)
}
// 模拟编译模板,并根据模板指令创建观察者和相关回调函数
// 传入初始化时的数据对象
_compile(data) {
simulateDirective(data)
}
}
// 模拟模板编译,创建相关回调函数,根据需要递归处理对象属性,这里就不做模拟了
function simulateDirective(data) {
function updateNameDiv(val) {
console.log('div1 name update:', val)
} // 模拟div1 中的{{ name }}绑定
function updateNameDiv2(val) {
console.log('div2 name update:', val)
} // 模拟div2 中的{{ name }}绑定
function updateAgeText (val) {
console.log('div3 update: ', val)
} // 模拟div3 {{age}}绑定
// 添加第一个watcher
new Watcher(data, 'name', updateNameDiv)
// 添加第二个watcher
new Watcher(data, 'name', updateNameDiv2)
// 添加age属性的watcher
new Watcher(data, 'age', updateAgeText);
}
// 配置数据属性
const options = {
data() {
return {
name: 'xiong',
age: 18
}
}
}
// 初始化
let vm = new Vue(options)
console.log('-------modify age--------')
vm._data.age = 19
console.log('-------modify name-------')
vm._data.name = 'foo'
网友评论