浅谈Vue 性能优化之深挖数组(4)
我们再捋一下思路,首先在 initState 里面调用 initData,initData 得到用户配置的 data 对象后调用了 observe,observe 函数里面会实例化 Observer 类,在其构造函数里面,首先将对象的 _ ob _ 属性指向 Observer 实例(这一步是为了检测到对象添加或者删除属性之后,能触发响应式的伏笔),之后遍历当前对象的键值,调用 defineReactive 去转换成 getter / setter。
所以,来分析下 defineReactive。
// 如果是数组,先篡改数组的一些方法(push,splice,shift等等),使其能够支持响应式 if (Array.isArray(value)) { if (hasProto) { protoAugment(value, arrayMethods) } else { copyAugment(value, arrayMethods, arrayKeys) } // 数组里面的元素还是数组或者对象,递归地调用 observe 函数,使其成为响应式数据 this.observeArray(value) } else { // 遍历对象,使其每个键值也能成为响应式数据 this.walk(value) } walk (obj: Object) { const keys = Object.keys(obj) for (let i = 0; i < keys.length; i++) { // 将对象的键值转换成 getter / setter, // getter 收集依赖 // setter 通知 watcher 更新 defineReactive(obj, keys[i]) } } observeArray (items: Array<any>) { for (let i = 0, l = items.length; i < l; i++) { observe(items[i]) } }
首先,我们从 defineReactive 可以看出,每个响应式属性都有一个 Dep 实例,这个是用来收集 watcher 的。由于 getter 与 setter 都是函数,并且引用了 dep,所以形成了闭包,dep 一直存在于内存当中。因此,假如在渲染组件的时候,如果使用了响应式属性 a,就会走到上述的语句1,dep 实例就会收集组件这个 renderWatcher,因为在对 a 进行 setter 赋值操作的时候,会调用 dep.notify() 去 通知 renderWatcher 去更新,进而触发响应式数据收集新一轮的 watcher。
那么语句2与3,到底是什么作用呢
我们举个栗子分析
<div>{{person}}<div>
export default { data () { return { person: { name: '张三', age: 18 } } } } this.person.gender = '男' // 组件视图不会更新
因为 Vue 是无法探测到对象增添属性,所以也没有一个时机去触发 renderWatcher 的更新。
为此, Vue 提供了一个 API, this.$set
,它是 Vue.set
的别名。
export function set (target: Array<any> | Object, key: any, val: any): any { if (Array.isArray(target) && isValidArrayIndex(key)) { target.length = Math.max(target.length, key) target.splice(key, 1, val) return val } if (key in target && !(key in Object.prototype)) { target[key] = val return val } const ob = (target: any).__ob__ if (target._isVue || (ob && ob.vmCount)) { process.env.NODE_ENV !== 'production' && warn( 'Avoid adding reactive properties to a Vue instance or its root $data ' + 'at runtime - declare it upfront in the data option.' ) return val } if (!ob) { target[key] = val return val } defineReactive(ob.value, key, val) ob.dep.notify() return val }
内容版权声明:除非注明,否则皆为本站原创文章。