vue3更新流程分析

1、执行下面代码

声明响应式数据,且过1秒钟后,更改一次,看它的更新流程

<script src="../../dist/vue.global.js"></script>
<div id="demo">
  <section>
    <h1>{{count}}</h1>
  </section>
</div>
<script>
  const { createApp, ref } = Vue
  var app = createApp({
    setup() {
      const count = ref(1)
      setInterval(() => {
        debugger
        count.value++
      }, 1000);
      return { count }
    }
  })
  app.mount('#demo')
</script>

2、打断点技巧

runtime-dom/src/nodeOps.ts 下setElementText内打上断点

在html代码setInterval内部打上断点
在这里插入图片描述

3、调用栈分析

首次触发断点时,如下如所示,在渲染视图时候,count依然为1

在这里插入图片描述

跳到下一个断点,如下图,此时触发了setup内部的setInterval函数,再调到下一个断点看发生了什么

在这里插入图片描述

  1. 如下图,此时在访问响应式数据count时候,会触发get和set,
  2. 在get收集依赖,set触发依赖更新,
  3. 在这里get因为没有activeEffect就导致不会收集它的依赖,至于为什么,在下面会有单独的说明,
  4. 再看看set触发依赖更新,此时的依赖其实就是componentUpdateFn,内部执行了 patch(prevTree,nextTree)
  5. 那么此时有个问题,我们什么时候收集的和count相关的componentUpdateFn依赖的呢?
  6. 这时候需要我们给响应式依赖的依赖收集打上断点看他是什么时候给我收集的依赖

在这里插入图片描述

reactivity/src/effect.ts 下trackEffect函数内打上断点,刷新页面
如下图可以看到,是在初次render时候,访问到了count所以会收集到依赖,但是在依赖收集的时候是收集的activeEffect,那么activeEffect是哪里来的,
在这个文件下可以找到ReactiveEffect函数,在new 这个函数的时候,我们会将实例赋值给全局变量activeEffect,所以此时的activeEffect是首次执行componentUpdateFn内new出来的实例

在这里插入图片描述

4、为什么只有写在模板内的响应式数据才会收集依赖,但是写在setup内的响应式数据却不会触发依赖收集呢???

reactivity\src\ref.ts 给RefImpl内的get打上断点,如图所示

在这里插入图片描述

此时会触发两次依赖收集

第一次是在render时候,依赖收集成功

第二依赖收集,在执行trackRefValue时候,此时的activeEffect为undefined

在这里插入图片描述

此时有个疑问activeEffect什么时候给我重置为undefined的,给ReactiveEffect内的run函数分别在这两个位置打上断点,如图所示,在执行render时候,run执行完了会执行finally下的activeEffect = this.parent

在这里插入图片描述

5、初始化挂载流程图如下

在这里插入图片描述

6、patch更新细节

案例一

<script src="../../dist/vue.global.js"></script>
<div id="app">
  <h1>vue3 更新流程</h1>
  <p>{{count}}</p>
  <comp></comp>
</div>
<script>
  var app = createApp({
    data() {
      return {
        count: 1
      }
    },
    mounted() {
      this.count++
    },
    components: {
      comp: {
        template: '<div>comp-comp</div>'
      }
    }
  })
  app.mount('#app')
</script>

案例二

<script src="../../dist/vue.global.js"></script>
<div id="app">
  <h1>vue3 更新流程</h1>
  <p>{{count}}</p>
  <comp></comp>
</div>
<script>
  var app = createApp({
    data() {
      return {
        count: 1
      } 
    },
    mounted() {
      this.count++
    },
    components: {
      comp: {
        template: '<div>comp-comp</div>'
      }
    }
  })
  app.mount('#app')
</script>

案例三

<script src="../../dist/vue.global.js"></script>
<div id="app">
  <div v-for="item in arr" :key="item">{{item}}</div>
</div>
<script>
  var { createApp } = Vue
  var app = createApp({
    data() {
      return {
        arr: ['a', 'b', 'c', 'd', 'e']
      }
    },
    mounted() {
      setTimeout(() => {
        this.arr.splice(2, 0,'f')
      }, 1000);
    }
  })
  app.mount('#app')
</script>

在这里插入图片描述

  • 案例三打断点
  • packages\runtime-core\src\renderer.ts文件下:
  • 搜索patchKeyedChildren(patchUnkeyedChildren( 都打上断点
  • 触发patchKeyedChildren函数
    1. sync from start 相同的头部
    2. sync from end 相同的尾部
    3. common sequence + mount 同一序列,新增未知节点
    4. common sequence + unmount 同一序列,删除未知节点
    5. unknown sequence
export function isSameVNodeType(n1, n2) {
  return n1.type === n2.type && n1.key === n2.key // type是标签名 key是绑定的值
}

流程图
ProcessOn

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值