>[success] # 简单的响应式模型(二) * 有这么一个 Vue 对象。修改了`data`中`text3`的数据,但是因为视图中并不需要用到`text3`,因此其实`text3`并不需要做响应劫持,上个章节时候我们是同一将所有数据都做了劫持,这是需要优化点之一 ~~~ new Vue({ template: `<div> <span>{{text1}}</span> <span>{{text2}}</span> <div>`, data: { text1: 'text1', text2: 'text2', text3: 'text3' } }); // 修改数据 this.text3 = 'modify text3'; ~~~ * 也会遇到一个数据对应多个视图情况 ~~~ let globalObj = { text1: 'text1' }; let o1 = new Vue({ template: `<div> <span>{{text1}}</span> <div>`, data: globalObj }); let o2 = new Vue({ template: `<div> <span>{{text1}}</span> <div>`, data: globalObj }); // 执行 globalObj.text1 = 'hello,text1'; ~~~ >[danger] ##### 简单的构想 ![](https://img.kancloud.cn/eb/b5/ebb5142a0119611bfc362adb0da03738_908x486.png) 1. 创建Vue 对象时候 开始调用 `observer `给每个对象属性分配`getter `和` setter`,此时只是会先注册需要具有响应依赖的 发布者对象 ,并给每个属性 分配并不会触发getter 和setter中的观察者模,getter 和setter 只有触发时候才会调用现在只是声明 2. 在创建 我们要做的观察者对象`new Watcher()`,这里很巧妙 因为我们是先注册 `observer` 后注册 观察者对象,你此时并不能将观察者对象倒序传入`observer`。但在`Watcher `时候我们其实创建一个属于发布者的静态属性,该属性指向了我们订阅者 3. 此时开始渲染视图,只有视图中使用的属性才会进行 发布订阅的劫持,此时是第一次触发`getter `,将观察者注入到发布者,当我们进行`setter `赋值的时候 会触发发布者 统一广播调用观察者的视图更新 * 注实际上一个 `Watcher` 对象可能会在多个 `Dep` 中,并不是每次 **addSub 都是一个新的 Watcher 对象**,需依赖 `Dep.target` 进行收集(**实际上 Dep.target 也是通过 Watcher 对象的 get 方法调用 pushTarget 将自身赋值给 Dep.target**)。 ~~~ // 发布者 class Dep { constructor() { this.subs = [] } // 收集 addSubs(sub) { this.subs.push(sub) } // 触发 notify() { this.subs.forEach((sub) => { sub.update() }) } } // 观察者 关于Dep.target = this // this 指向的就是 new watcher 生成的watcher实例 // 这样全局任何地方都可调用 class Watcher { constructor() { Dep.target = this // this 指向的就是 new watcher 生成的watcher实例 } /* 更新视图的方法 */ update() { console.log('视图更新啦~') } } class Vue { constructor(opt) { this._data = opt.data observer(this._data) /* 新建一个Watcher观察者对象,这时候Dep.target会指向这个Watcher对象 */ new Watcher() /* 在这里模拟render的过程,为了触发test属性的get函数 */ console.log('render~', this._data.test) } } function defineReactive(obj, key, val) { /* 一个Dep类对象 */ const dep = new Dep() Object.defineProperty(obj, key, { enumerable: true, configurable: true, get: function reactiveGetter() { /* 将Dep.target(即当前的Watcher对象存入dep的subs中) */ dep.addSub(Dep.target) return val }, set: function reactiveSetter(newVal) { if (newVal === val) return /* 在set的时候触发dep的notify来通知所有的Watcher对象更新视图 */ dep.notify() }, }) } ~~~ >[danger] ##### 参考 [剖析 Vue.js 内部运行机制](https://juejin.cn/book/6844733705089449991)