Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

vue专题 - 派发更新 #38

Open
zhuzhh opened this issue Apr 6, 2020 · 0 comments
Open

vue专题 - 派发更新 #38

zhuzhh opened this issue Apr 6, 2020 · 0 comments

Comments

@zhuzhh
Copy link
Owner

zhuzhh commented Apr 6, 2020

vue响应式的实现,包含了数据劫持、依赖收集、派发更新三部分,本章节单独讲解下派发更新

1、某一对象属性赋值,触发setter方法,setter方法做的几件事:

1、判断新旧值是否一样,如果一样,则结束函数,不一样往下执行
2、新值赋给旧值(val = newVal)
3、判断新值是否是个对象,如果是对象,则把该对象变成一个响应式的
4、执行dep.notify(),(派发更新的起点)

2、Dep.prototype.nitify() 的实现,它只做了两件事情

1、subs数组排序(从小到达排序,组件更新是父到子)
2、执行sub.update方法(循环遍历subs执行,sub指向的是一个watcher实例)

3、Watcher.prototype.update() 的实现

判断三种情况
1、this.lazy ->和计算属性相关,暂不介绍
2、this.sync -> 是否是同步执行的watcher,如果是,则立即执行this.run()
3、以上情况都不符合,执行queueWatcher方法,把当前watcher推入到一个queue中 

4、queueWatcher() 的实现

4.1 判断当前watcher是否在缓存中

const id = watcher.id
if (has[id] == null) {
 // ...
}

这里 has是一个对象,存在于闭包中。
如果存在,则说明该watcher已经被push进队列了,啥也不用做;
如果不存在,则把该watcher push到一个数组中;
(watcher Id相同的场景,一个组件内,同时修改了两个变量,而且这两个变量都会触发页面更新)

4.2 判断flushing, 如果没有,则直接把watcher 放进 queue数组中

4.3 判断waiting 是否是false(waiting初始值为false),则把waiting = true,在没有执行resetSchedulerState前,nextTick方法只执行一次

if (!waiting) {
      waiting = true
      // ......
      nextTick(flushSchedulerQueue)
 }

5、执行nextTick方法,nextTick方法执行timeFunc,返回一个Promise,代码如下

function nextTick(cb) {
 if (!pending) {
    pending = true
    timerFunc()
  }
 // 如果没有传cb,并Promise可用,则返回一个promise实例
 if (!cb && typeof Promise !== 'undefined') {
    return new Promise(resolve => {
      _resolve = resolve
    })
  }
}

5.1 timerFunc是个懒函数,初始化的时候,只执行一次;它主要功能是判断使用哪种nextTick方案,分别判断promiseMutationObserversetImmediatesetTimeout

6 flushSchedulerQueue方法的实现

6.1 queue从小到大排序;排序的作用有:1、先更新父组件,后更新子组件;2、user watcher 先与 render watcher执行(因为user watcher先于render watcher创建);3、如果一个组件被销毁,父组件正在被执行 watcher run,当前watcher会被跳过

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

6.2循环遍历执行queue中的watcher

for(index = 0; index < queue.length; index ++) {
    watcher = queue[index]
    if (watcher.before) {
      watcher.before()
    }
    id = watcher.id
    has[id] = null
    watcher.run()
}

6.3 执行resetSchedulerState方法

function resetSchedulerState () {
  index = queue.length = activatedChildren.length = 0
  has = {}
  if (process.env.NODE_ENV !== 'production') {
    circular = {}
  }
  waiting = flushing = false
}

总结:

  • 同一个组件中,user watcher先与渲染watcher执行
  • 一个组件只有一个渲染watcher
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant