深入Vue响应式原理
Posted yangjunhua
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入Vue响应式原理相关的知识,希望对你有一定的参考价值。
深入Vue.js响应式原理
一、创建一个Vue应用
new Vue( data() return name: ‘yjh‘, ; , router, store, render: h => h(App), ).$mount(‘#app‘);
二、实例化一个Vue应用到底发生了什么?
- this._init()
- callHook(vm, ‘beforeCreate‘)
- observe(vm._data)
vm._data = vm.$options.data()
proxy(vm,
_data, key) 代理到vm上访问
function proxy(vm, _data, key)() Object.defineProperty(target, key, get() return vm._data.key , set(val) vm._data.key = val )
- callHook(vm, ‘created‘)
- mountComponent(vm.$mount执行后执行mountComponent)
- callHook(vm, ‘beforeMount‘)
- new Watcher(vm, updateComponent)
const updateComponent = () => // 创建虚拟dom const vnode = vm._render() // 创建虚拟dom的过程等同于如下代码行 // const vnode = vm.$options.render.call(vm, vm.$createElement) // 更新$el vm._update(vnode)
- callHook(vm, ‘mount‘)
在以上发生的行为当中,第3步与第7步两者相辅相成;也是我们最需要关心的,弄清楚这两者,vue响应式原理就基本掌握了
三、如何追踪数据变化
我们都知道 数据发生变化视图也随之更新,那么首先我们得知道如何监听数据的变化
class Observer constructor(value) this.value = value this.walk(value) walk(obj) const keys = Object.keys(obj) for (let i = 0; i < keys.length; i++) defineReactive(obj, keys[i]) function defineReactive(obj, key) Object.defineProperty(obj, key, get() // 数据被访问 return obj.key , set(val) if (val === obj.key) return // 数据更新了 obj.key = val )
四、定义一个发布订阅的Dep类
当我们在创建虚拟dom的过程中,也就是执行vm.$createElement方法,可能会在多个地方使用到同一个数据字段(如:vm.name),即多个订阅者订阅了name的更新,因此在Vue中定义了一个发布订阅的Dep类
class Dep constructor() this.subs = [] addSub(sub) this.subs.push(sub) depend() if (Dep.target) this.addSub(Dep.target) notify() this.subs.forEach(sub => sub.update()) removeSub(sub) const i = this.subs.findIndex(sub) if (i > -1) this.subs.splice(i, 1)
五、数据订阅者
订阅数据更新的到底是谁,我们先看看如下场景
<!-- 场景1 --> <div>名字: userInfo.name ,全名: fullName </div>
export default data() return userInfo: name: ‘junhua‘, , , mounted() // 场景2 this.$watch(‘name‘, (newVal, val) => // ... ) , // 场景2 watch: name(newVal, val) // ... , computed() // 场景3 fullName() return `yang$this.userInfo.name`
从上面示例代码看,订阅数据更新的场景有:
- 模版插值 :
new Watcher(vm, updateComponent)
数据发生变化,更新组件 - vm.$watch : 监听单个数据做一些逻辑操作
- computed使用场景:计算属性
因此数据订阅者包含一个参数expOrFn([Function|String]
),数据更新后需要执行的callback,如下:
class Watcher constructor(vm, expOrFn, cb) this.vm = vm if (typeof expOrFn === ‘function‘) this.getter = expOrFn else this.getter = parsePath(expOrFn) this.cb = cb || () => this.value = this.get() get() Dep.target = this const value = this.getter.call(this.vm, this.vm) Dep.target = undefined return value update() const val = this.value const newVal = this.get() this.cb.call(this.vm, newVal, val)
六、最终的观察者Observer
class Observer constructor(value) this.value = value this.walk(value) walk(obj) const keys = Object.keys(obj) for (let i = 0; i < keys.length; i++) defineReactive(obj, keys[i],) function defineReactive(obj, key) const dep = new Dep() Object.defineProperty(obj, key, get() // 依赖收集,收集订阅者Watcher实例 dep.depend() // 数据被访问 return obj.key , set(val) if (val === obj.key) return // 数据更新了 obj.key = val // 通知订阅者Watcher实例更新 dep.notify() )
七、总结
我们再来回顾下实例化Vue应用的最重要的两点
observe(vm._data) // vm.$mount() const componentUpdateWatcher = new Watcher(vm, updateComponent)
updateComponent在更新渲染组件时,会访问1或多个数据模版插值,当访问数据时,将通过getter拦截器把componentUpdateWatcher作为订阅者添加到多个依赖中,每当其中一个数据有更新,将执行setter函数,对应的依赖将会通知订阅者componentUpdateWatcher执行update,即执行updateComponent;至此Vue数据响应式目的已达到,再来看官网的这张图片就很好理解了
github地址 文章来源:博客园-杨君华,转载请注明出处:杨君华
以上是关于深入Vue响应式原理的主要内容,如果未能解决你的问题,请参考以下文章