【手写 Vue2.x 源码】第二十一篇 - 依赖收集的过程分析

247 阅读3分钟

一,前言

上篇,使用真实节点替换原始节点,主要涉及以下几点:

  • 新老节点的更新方案
  • 虚拟节点与真实节点映射
  • 实现新老节点的替换

本篇,依赖收集的过程分析;


二,依赖收集

1,前情回顾

截止至上一篇,已经完成了Vue的两大核心部分:响应式数据 和 数据渲染,即完成了整个Vue的初始化流程;

new Vue时,执行_init初始化,通过mountComponent做组件挂载:

  • vm._render:调用 render 方法,创建虚拟节点
  • vm._render:更新逻辑,将虚拟节点渲染成为真实 dom
    • patch:根据虚拟节点生成真实节点,新节点替换老节点

Vue2 和 Vue3 的初渲染流程上是大致相同的;

从本篇开始,将进入Vue的更新流程;

2,Vue 的响应式特性

备注:当前仅考虑单个根组件的更新,暂不涉及多组件更新;

Vue特性:当响应式数据发生变化时,会触发对应视图的更新;

示例:同一数据可能被多个视图(页面或组件)所共享,比如Vuex中的数据:

  • A 组件,使用了数据 name;
  • B 组件,使用了数据 name;
  • 这样,A,B 两个组件就都依赖了数据 name;
  • 当数据发生变化时,两个组件都会触发对应视图更新操作;

分析:这就需要知道数据和视图间的对应关系,从而准确触发该数据对应视图的更新操作;从设计模式上看就用到了观察者模式;

这里就涉及到了Vue的依赖收集,下面介绍Vue的依赖收集的过程;

3,dep 和 watcher

Vue中,依赖收集的实现使用了观察者模式:

  • watcher函数:每个组件或页面所对应的的渲染函数;
  • dep属性:每个数据都具有一个dep属性,用于记录使用该数据的组件或页面的视图渲染函数watcher

当数据发生变化时,dep属性中存放的多个watcher将会被通知,watcher通过调用自身对应的更新方法update,完成页面的重新渲染;

  • name添加属性dep:用于收集组件 A组件 B的渲染逻辑watcher Awatcher B
  • watcher Awatcher B添加各自的更新方法update
  • 当数据发生变化时,通知dep中存放的watcher Awatcher B触发各自的更新方法update

depwatcher为多对多关系:

  • 由于同一个数据可能在多个页面或组件中被渲染,所以一个dep可以对应多个watcher
  • 由于同一个视图可能包含多个数据,所以一个watcher 可以对应多个dep

回想之前的内容:

  • 由于vm._update(vm._render()) 执行了数据渲染和更新操作
  • 所以watcher中的update方法,便是触发vm._update(vm._render())重新进行数据渲染和视图更新
  • 所以,需要将vm._update(vm._render())改造为可以通过watcher调用的方法

4, 其他

  • 数据响应式过程中,为每个属性扩展dep,用于收集watcher,在数据渲染时记录watcher
  • 当同一数据在同一视图中被多次使用时,在dep中需要对watcher进行查重,确保相同watcher仅记录一次;
  • 防止只要数据变化就会渲染视图的情况:当数据在视图中没有被使用时,数据的变化不应触发watcher渲染,需要在视图渲染时进行依赖收集,知道哪些数据被“真正”使用了;

三,结尾

本篇,主要介绍了 Vue 依赖收集的过程分析;

  • 介绍了 Vue 的响应式特性
  • 介绍了 Vue 的依赖收集过程
  • 介绍了 dep 和 watcher 以及观察者模式;

下一篇,Vue 依赖收集的实现


更新记录

  • 20230201:调整目录结构,添加内容中的代码高亮,调整少量内容描述,更新文章摘要;