vue 响应式原理

vue

响应式原理

简单点讲 vue 的响应式是通过 Object.defineProperty 和 观察者模式来实现的。

vue 初始化的时候 watcher 构造函数通过 Object.defineProperty 方法对 data 属性进行递归遍历,设置 get、set,初始化编译的时候会触发 getter 函数,进行依赖收集,将观察者 watcher 添加到目标对象 dep 中。改变数据的时候会触发 set, 执行 notify 方法,调用 dep 中 watcher 对象的 update 方法,update 方法将 watcher 添加到 watcher 队列中, 通过调用 nextTick 异步执行,触发更新。

至于实现的细节,现在细细道来...

我们需要分析的大致是以下源码文件:

  • observer

    • dep.js
    • index.js
    • scheduler.js
    • watcher.js

先看 index.js

export class Observer { // 定义 Observer 构造函数

value: any;

dep: Dep;

vmCount: number; // number of vms that have this object as root $data

constructor (value: any) {

this.value = value

this.dep = new Dep()

this.vmCount = 0

def(value, '__ob__', this)

if (Array.isArray(value)) { // 如果是数组

if (hasProto) {

protoAugment(value, arrayMethods)

} else {

copyAugment(value, arrayMethods, arrayKeys)

}

this.observeArray(value)

} else {

this.walk(value)

}

}

/**

* Walk through all properties and convert them into

* getter/setters. This method should only be called when

* value type is Object.

*/

walk (obj: Object) {

const keys = Object.keys(obj)

for (let i = 0; i < keys.length; i++) { // 对属性进行遍历

defineReactive(obj, keys[i])

}

}

/**

* Observe a list of Array items.

*/

observeArray (items: Array<any>) {

for (let i = 0, l = items.length; i < l; i++) {

observe(items[i])

}

}

}

...

...

...

/**

* Define a reactive property on an Object.

*/

export function defineReactive ( // 把对象的属性变成响应式

obj: Object,

key: string,

val: any,

customSetter?: ?Function,

shallow?: boolean

) {

const dep = new Dep()

const property = Object.getOwnPropertyDescriptor(obj, key) // 获取obj对象key属性的数据属性

if (property && property.configurable === false) { // 如果该属性是不能修改或删除的,则直接返回

return

}

// cater for pre-defined getter/setters

const getter = property && property.get //尝试拿到该对象原生的get属性,保存到getter中

const setter = property && property.set //尝试拿到该对象原生的set属性,保存到setter中

if ((!getter || setter) && arguments.length === 2) { //如果getter不存在或者setter存在,且参数只有两个

val = obj[key] //则直接通过obj[ke]获取值,并保存到val中

}

let childOb = !shallow && observe(val) //递归调用observe:当某个对象的属性还是对象时会进入

Object.defineProperty(obj, key, { //调用Object.defineProperty设置obj对象的访问器属性

enumerable: true,

configurable: true,

get: function reactiveGetter () {

const value = getter ? getter.call(obj) : val

if (Dep.target) {

dep.depend() //调用depend()收集依赖,

if (childOb) { // 如果childOb存在

childOb.dep.depend() //则调用childOb.dep.depend()收集依赖

if (Array.isArray(value)) { // 如果 val 是数组则调用小下面的函数处理

dependArray(value)

}

}

}

return value

},

set: function reactiveSetter (newVal) {

const value = getter ? getter.call(obj) : val //如果之前有定义getter,则调用getter获取值,否则就赋值为val

/* eslint-disable no-self-compare */

if (newVal === value || (newVal !== newVal && value !== value)) { //如果value没有改变就直接返回

return

}

/* eslint-enable no-self-compare */

if (process.env.NODE_ENV !== 'production' && customSetter) {

customSetter()

}

// #7981: for accessor properties without setter

if (getter && !setter) return

if (setter) {

setter.call(obj, newVal)

} else {

val = newVal

}

childOb = !shallow && observe(newVal) // newVal调用observe处理,newVal为数组或对象其属性也是响应式

dep.notify() // 通知订阅的 watcher 做更新

}

})

}

接下来看 dep.js

 notify () {

// stabilize the subscriber list first

const subs = this.subs.slice()

if (process.env.NODE_ENV !== 'production' && !config.async) {

// subs aren't sorted in scheduler if not running async

// we need to sort them now to make sure they fire in correct

// order

subs.sort((a, b) => a.id - b.id)

}

for (let i = 0, l = subs.length; i < l; i++) { // subs 中存的 wather,循环执行 watcher 中的 update 方法

subs[i].update()

}

}

接着看 watcher.js

/**

* Subscriber interface.

* Will be called when a dependency changes.

*/

update () {

/* istanbul ignore else */

if (this.lazy) {

this.dirty = true

} else if (this.sync) {

this.run()

} else {

queueWatcher(this) // 执行 watcher 队列 queueWatcher 方法

}

}

scheduler.js:

export function queueWatcher (watcher: Watcher) {

const id = watcher.id

if (has[id] == null) { // 相同的 watcher 只添加进队列一次

has[id] = true

if (!flushing) {

queue.push(watcher)

} else {

// if already flushing, splice the watcher based on its id

// if already past its id, it will be run next immediately.

let i = queue.length - 1

while (i > index && queue[i].id > watcher.id) {

i--

}

queue.splice(i + 1, 0, watcher)

}

// queue the flush

if (!waiting) {

waiting = true

if (process.env.NODE_ENV !== 'production' && !config.async) {

flushSchedulerQueue()

return

}

nextTick(flushSchedulerQueue) // 调用异步 nestTick 处理 watcher 队列

}

}

}

以上是 vue 响应式原理 的全部内容, 来源链接: utcz.com/z/379903.html

回到顶部