简单代码例子

const { createApp, defineComponent, computed, watch, ref, reactive, effect } =
Vue
const app = createApp({
components: [],
template: `
<div ref="testRef">
<div
key="i"
v-for="(item111, index222, haha333) in list"
@click="list.push({id: list.length, name: 'll'})"
>
<div>first</div>
<li>second</li>
<span>third</span>
<span>third</span>
{{m2}}
</div>
<button @click="show = !show">switch message</button>
<div v-if="show">3333 {{message}}</div>
</div>
`,
mounted() {
console.log('mounted')
},
beforeDestroy() {
console.log('beforeDestroy')
},
data() {
return {
message: 'Hello Vue!',
eventName: 'click',
list: [
{ id: 1, name: 'nzz' },
{ id: 2, name: 'ryy' },
{ id: 3, name: 'lhc' }
],
show: false
}
},
methods: {
handleCLick() {
console.log('handleCLick')
}
},
computed: {
m2() {
return this.message + '33'
}
}
}).mount('#app')

上述 例子 简要的运行流程

第一次渲染

  • createApp(...).mount('#app')

    1. createVNode(...) 创建 vnode

      • guardReactiveProps 若 props 是响应式对象,则拷贝其数据再操作
      • normalizeClass normalizeStyle 对 class 和 style 的对象和数组模式进行处理
      • 根据 type 创建 shapeFlag
      • normalizeChildren
        • shapeFlag 再处理
    2. render(...) 根据 vnode 创建元素并挂载到指定位置。绑定数据和视图的关系。

      • patch vnode 到指定根元素(这里的 vnode 是组件 vnode)。

        • mountComponent 挂载组件。

          • setupComponent 执行时用 reactive 创建响应式数据

            • setupStatefulComponent 设置有状态的组件

              • finishComponentSetup 完成组件设置

                • compileToFunction 将模板解析成函数

                  • compile

                    1. baseParse 解析 template,生成 ast。
                    2. transform 转换优化数据。
                    3. generate 生成渲染函数 (是字符串形式)。
                  • 使用 new Function 将渲染函数字符串变成函数。
                • applyOptions
                  • reactive 创建响应式对象
          • setupRenderEffect 设置渲染 effect,并挂载组件。
            1. 创建 componentUpdateFn。用 componentUpdateFn 创建 reactiveEffect。
            2. 调用 reactiveEffect 的 run 方法。这会去执行 componentUpdateFn。

              componentUpdateFn 会 patch 标签或组件到指定位置。patch 之前以及之后会调用生命周期函数。

              patch 过程中会访问到响应式数据的属性值,会收集上述 reactiveEffect 到 targetMap 中对应响应式数据所映射的 Map 的对应属性所映射的 Dep 中。

点击按钮,改变响应式对象属性值引发重新渲染

  • 点击按钮 响应式对象属性值改变

    • setter 监听到属性值改变,调用 trigger 方法

      • 找到 targetMap 中对应对象的对应属性的对应 Dep,执行所有的 reactiveEffect。

        • 执行时会调用 componentUpdateFn。

          componentUpdateFn 会 patch 组件。

diff 算法

patchUnkeyedChildren

同时遍历各新旧 children,patch 相同索引位置的节点。

若新 children 多出节点,挂载。

若新 children 少了的节点,就卸载。

patchKeyedChildren

  1. sync from start

    从头往后遍历,若是 SameVNodeType,那么 patch 这俩节点。

    若不是,则退出遍历。

    (a b) c

    (a b) d e

  2. sync from end

    从后往前遍历,若是 SameVNodeType,那么 patch 这俩节点。

    若不是,则退出遍历。

    a (b c)

    d e (b c)

  3. common sequence + mount

    若遍历的位置超出旧 children,但是在新 children 范围内。

    这说明新 children 比就节点多了一些节点。

    需要挂载这些多出来的节点。

    (a b)

    (a b) c

    i = 2, e1 = 1, e2 = 2

    (a b)

    c (a b)

    i = 0, e1 = -1, e2 = 0

  4. common sequence + unmount

    若遍历的位置在旧 children 范围内,但是超出新 children。

    这说明新 children 比旧 children 少了一些节点。

    需要卸载这些多出来的节点。

    (a b) c

    (a b)

    i = 2, e1 = 2, e2 = 1

    a (b c)

    (b c)

    i = 0, e1 = 0, e2 = -1

  5. unknown sequence

    若非上述的集中情况,那么就不是节点边缘发生了改变,而是节点中间有变化。

    [i ... e1 + 1]: a b [c d e] f g

    [i ... e2 + 1]: a b [e d c h] f g

    i = 2, e1 = 4, e2 = 5

5.1 build key:index map for newChildren

将新 children 的 key 和 index 关系保存到 keyToNewIndexMap 中。

5.2 loop through old children left to be patched and try to patch

matching nodes & remove nodes that are no longer present

遍历旧 children,移除在新 children 中不再出现的节点。

判断该节点是否出现?

若有 key,则看是否有 key 匹配;

若无 key,则看是否 isSameVNodeType(key 和 type 都相等)。

若在新 children 中再次出现,

则 patch 之。

5.3 move and mount

generate longest stable subsequence only when nodes have moved

找到相对顺序不变的最大的子序列。

移动非这个子序列中的元素,到正确的位置。

比较 patchUnkeyedChildren 和 patchKeyedChildren

某节点有大量子孙节点,且该节点的位置经过移动。

此时,若节点有 key,会比无 key,更新时所消耗的资源要小。

无 key 的情况,需要卸载大量子孙节点,再在指定位置处,再大量创建子孙节点。

有 key 的情况,则移动该节点即可。少去了卸载和创建的操作。

那是否有某种情况,无 key 会比有 key 更优呢?

查找不用移动的最长子序列调用的方法 getSequence,貌似需要消耗一些资源。

若,children 是大量普通 div,只有一层,内容是字符串。然后打乱顺序。

这种情况下,无 key 应该会比有 key 更优吧?

如何测试看实际效果呢?

const unkeyedl1 = [
{ name: '1' },
{ name: '2', children: [{ name: '21' }, { name: '22' }] },
{ name: '3' },
{ name: '4' },
{ name: '5' }
]
const unkeyedl2 = [
{ name: '1' },
{ name: '3333' },
{ name: '4444' },
{ name: '2222', children: [{ name: '222221' }, { name: '222222' }] },
{ name: '5' }
]
const keyedl1 = [
{ key: 1, name: '1' },
{
key: 'b',
name: '2',
children: [
{ key: 'b1', name: '21' },
{ key: 'b2', name: '22' }
]
},
{ key: 'c', name: '3' },
{ key: 'd', name: '4' },
{ key: 5, name: '5' }
]
const keyedl2 = [
{ key: 1, name: '1' },
{ key: 'c', name: '3333' },
{ key: 'd', name: '4444' },
{
key: 'b',
name: '2222',
children: [
{ key: 'b1', name: '222221' },
{ key: 'b2', name: '222222' }
]
},
{ key: 5, name: '5' }
]

Vue3源码阅读梳理的更多相关文章

  1. 【面试】足够“忽悠”面试官的『Spring事务管理器』源码阅读梳理(建议珍藏)

    PS:文章内容涉及源码,请耐心阅读. 理论实践,相辅相成 伟大领袖毛主席告诉我们实践出真知.这是无比正确的.但是也会很辛苦. 就像淘金一样,从大量沙子中淘出金子一定是一个无比艰辛的过程.但如果真能淘出 ...

  2. 【面试】足够应付面试的Spring事务源码阅读梳理(建议珍藏)

    Starting from a joke 问:把大象放冰箱里,分几步? 答:三步啊,第一.把冰箱门打开,第二.把大象放进去,第三.把冰箱门带上. 问:实现Spring事务,分几步? 答:三步啊,第一. ...

  3. Hive cli源码阅读和梳理

    对Cli的重新认识*). hive cli有两种模式, 本地模式: 采用持有的driver对象来处理, 远程模式: 通过连接HiveServer来实现, 由此可见之前的架构图中的描述还是模糊且带有误导 ...

  4. 【原】AFNetworking源码阅读(三)

    [原]AFNetworking源码阅读(三) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇的话,主要是讲了如何通过构建一个request来生成一个data tas ...

  5. CI框架源码阅读笔记4 引导文件CodeIgniter.php

    到了这里,终于进入CI框架的核心了.既然是“引导”文件,那么就是对用户的请求.参数等做相应的导向,让用户请求和数据流按照正确的线路各就各位.例如,用户的请求url: http://you.host.c ...

  6. 【 js 基础 】【 源码学习 】backbone 源码阅读(一)

    最近看完了 backbone.js 的源码,这里对于源码的细节就不再赘述了,大家可以 star 我的源码阅读项目(https://github.com/JiayiLi/source-code-stud ...

  7. Mask RCNN 源码阅读(update)

    之前看了Google官网的object_dectect 的源码,感觉Google大神写的还不错.最近想玩下Mask RCNN,就看了下源码,这里刚好当做总结和梳理.链接如下: Google官网的obj ...

  8. SpringMVC源码阅读系列汇总

    1.前言 1.1 导入 SpringMVC是基于Servlet和Spring框架设计的Web框架,做JavaWeb的同学应该都知道 本文基于Spring4.3.7源码分析,(不要被图片欺骗了,手动滑稽 ...

  9. Pytorch版本yolov3源码阅读

    目录 Pytorch版本yolov3源码阅读 1. 阅读test.py 1.1 参数解读 1.2 data文件解析 1.3 cfg文件解析 1.4 根据cfg文件创建模块 1.5 YOLOLayer ...

  10. Bert源码阅读

    前言 对Google开源出来的bert代码,来阅读下.不纠结于代码组织形式,而只是梳理下其训练集的生成,训练的self-attention和multi-head的具体实现. 训练集的生成 主要实现在c ...

随机推荐

  1. Redis的攻击手法

    目录 Redis概述 Redis未授权 漏洞发现 漏洞验证 Redis写shell 漏洞利用 Redis写公钥 漏洞利用 主从复制RCE 漏洞简介: 漏洞利用 计划任务反弹shell 漏洞利用 Red ...

  2. 关于Intent.setDataAndType参数问题

    关于Intent.setDataAndType参数问题 install取设置属于和类型,数据就是获取到的uri,更具文件类型不同,type参数也不相同,具体参考下表 {后缀名,MIME类型} ​ {& ...

  3. VM安装Centos7操作系统

    个人名片: 对人间的热爱与歌颂,可抵岁月冗长 Github‍:念舒_C.ying CSDN主页️:念舒_C.ying 个人博客 :念舒_C.ying 视频教程:https://live.csdn.ne ...

  4. UBOOT编译--- UBOOT全部目标的编译过程详解(九)

    1. 前言 UBOOT版本:uboot2018.03,开发板myimx8mmek240. 2. 概述 本文接续上篇文章,采用自下而上的方法,先从最原始的依赖开始,一步一步,执行命令生成目标.这里先把上 ...

  5. (工具) 交叉编译 gperftools及使用

    交叉编译gperftools及使用 sudo apt-get install kcachegrind # 导出为 callgrind 格式时需要 sudo apt install doxygen-la ...

  6. CopyOnWriteArrayList 是如何保证线程安全的?

    本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问. 前言 大家好,我是小彭. 在上一篇文章里,我们聊到了ArrayList 的线程安全问题,其中提到了 Copy ...

  7. 【大数据面试】sqoop:空值、数据一致性、列式存储导出、数据量、数据倾斜

    一.有没有遇到过问题,怎么进行解决的 1.空值问题 本质:hive底层存储空数据使用\n<==>MySQL存储空数据使用null 解决:双向导入均分别使用两个参数☆,之前讲过 2.数据一致 ...

  8. (四) 一文搞懂 JMM - 内存模型

    4.JMM - 内存模型 1.JMM内存模型 JMM与happen-before 1.可见性问题产生原因 下图为x86架构下CPU缓存的布局,即在一个CPU 4核下,L1.L2.L3三级缓存与主内存的 ...

  9. 软件开发目录规范、python常用内置模块

    编程思想的转变 1.面条版阶段 所有的代码全部堆叠在一起.可以看成是直接将所有的数据放在C盘        视频.音频.文本.图片 2.函数版阶段 根据功能的不同封装不同的函数.可以看成是将C盘下的数 ...

  10. maven 项目依赖自动导入失败(pom.xml 文件爆红),解决--手动导入

    idea 报错信息提示:Dependency 'xxx' not found 解决方法:可以通过更换仓库的镜像配置解决,但是一般咱都在配置maven的时候,设置成阿里云仓库镜像了,更换成其他的,可能出 ...