小型mvvm实现原理
关键词:
defineReactive:数据劫持,用来监听获取数据set和get,其中,get将实现发布订阅直接的绑定,set则发布事件,实现订阅者更新
Watcher:观察者,绑定Dept,在dept调用notify时候,触发Watcher回调
Dept:依赖关系,在数据更新的时候(set),调用自身的观察回调函数
具体代码如下
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>MVVM</title> </head> <body> <div></div> </body> <script> // 通过 Dep 解耦 class Dep { constructor() { this.subs = [] } addSub(sub) { // sub 是 Watcher 实例 this.subs.push(sub) } notify() { this.subs.forEach(sub => { sub.update() }) } } // 全局属性,通过该属性配置 Watcher Dep.target = null function update(value) { document.querySelector('div').innerText = value } class Watcher { constructor(obj, key, cb) { // 将 Dep.target 指向自己 // 然后触发属性的 getter 添加监听 // 最后将 Dep.target 置空 Dep.target = this this.cb = cb this.obj = obj this.key = key this.value = obj[key] // 触发obj的get方法,与dep联系起来 Dep.target = null } update() { // 获得新值 this.value = this.obj[this.key] // 调用 update 方法更新 Dom this.cb(this.value) } } 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 dp = new Dep() Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter() { console.log('get value') // 将 Watcher 添加到订阅 if (Dep.target) { dp.addSub(Dep.target) } return val }, set: function reactiveSetter(newVal) { console.log('change value') val = newVal // 执行 watcher 的 update 方法 dp.notify() } }) } var data = { name: 'yck' } observe(data) // // 模拟解析到 `{{name}}` 触发的操作 new Watcher(data, 'name', update) // // update Dom innerText // data.name = 'yyy' </script> </html>
具体逻辑分析如下