全体尤大直播的过程,劝退大兄弟已经总结的贼棒了 ,直接移步

抄笔记:尤雨溪在Vue3.0 Beta直播里聊到了这些…

Vue3核心的TypescriptProxy响应式,CompositioZ P P /n解决代码重复横跳都有很棒的文章剖析了, 我总结一下虚拟Dom部分把,并比照一下Reactvdom的重写也是vue3功能如此优异的重要原因

  1. 了解Vue3源码这一篇就够了
  2. Vue 3.0 这个迷人的小h * ] ? N X妖精,到底好在哪里?(更新原理比照)

Vue3的虚拟dom

先说结论,静态M g ? a N o F N符号,upadte功能提高1.3~2倍,ssr提高2~3倍,怎样做到的呢

尤大Vue3.0直播虚拟Dom总结(和React 对比)
尤大Vue3.0直播虚拟Dom总结(和React 对比)

编译模板的静态符号

咱们来看一段很常见s s M !的代码8 W { / l z G [ ]

&5 ` h + d jlt;div id="app">
<h1>技能摸鱼</h1>
<p>今天天气真不错</p&1 w h I # * C 3gt;
<div>{{name}}</div>
</div>4 n T

vu7 % ; { Y _e2中会解析

funcS v Y z @ R L Ytion render() {
with(this) {
return _c('div', {
attrs: {
"id": "app"
}
}, [_c('h1', [_v("技能摸鱼")]), _c('p', [_v("今天天气真不错")]| a | 0 q E . b B), _c('div', [_v(
_s(name))])]3 J e p c ) d _ c)
}
}

其间前面两个标签是彻底静态的,后续的烘托中不会发生任何改变,Vue2中仍然使用8 9 , =_c新建成vdomi . k rdiff的时分需要比照,有一些额外的功能损耗$ ; ~

咱们看下vue3中的解析成果

import { createVNode as _createVNode, toDisplayString as _toDisplayString, openBlock as _i } @ T O ,openBlock, createBlock as _createBlock } frK [ Y G c -om "vue"
export function render(_ctx, _cache) {
retura ^ b ] 8 an (_openBlock(), _createBlock("div", { id: "app"? 5 + ) 2 - }, [
_c! { a e R l v nreateVNode("h1", nO V S 1 $ull, "技能摸鱼"),
_createVNode("p", null, "今天天气真P Q V w ^ m不错"),
_createVNode("div", null, _toDisplayString(_ctx.name), 1 /* TEXT */)
]))
}
// Check the console for the AST
尤大Vue3.0直播虚拟Dom总结(和React 对比)

最后一个_createVN( ] { 0ode第四个参数1,只需带. t & n z , c u这个参数的,才会被真正的追踪,静k $ M / –态节点不需要遍历,这个便是` O L h 8 2 ! *vue3优异功能的首要来源,再看杂乱一点的

<div id="app">
<h1>技能摸鱼</h1>
<p>今天天气真不错</py 7 i t x  s J #>
<div>{{name}}</div>
<div :class="{re/ M E ~ 5 b Ld:isRed}">@ , f ^ U B p 0 I;摸鱼符</div>
<buttonm T ( @click="handleClick"&gS & # t v St;戳我</button>
<input type="text" v-model="name">
</div>

解析的成果 在线预览


import { createVNode as _createVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createBlock as _createBlock } from "vuej [ 9 X r i"
expo5 l 2 # prt function render(_ctx, _cache) {
return (c ?  2 P_openBlock(), _createBloc* | u + H 4 uk("div",= ( n u [ W { id: "app" }, [
_createVNode("h1", null, "技能摸鱼e U 0 ! !& n * A N ^ , u"),
_createVNode("p", null, "今天天气真不错"),
_createVNode("divX t J 4 d E ^ z ,", null, _toDisplayString(_ctx.name), 1 /* TEXT */),
_createVNode( = 5 N n a #"divJ q #", {
class: {red:_ctx.isRed}
}, "摸鱼符", 2 /* CLASS */),
_createVNode("button", { onClick: _ctx.handleClick }, "戳我", 8 /* PROPS */, ["onClick"])
]))
}
// Check the console for the AST

_createVNode出第四个参数呈现了其他数字,依据, = Y p ^后面注释也很容易猜出,依据textprops等不同的符号,这样再diff的时分,只需要比照text或者props,不用再做无畏的props遍历, 优异!
学习一下劝退大兄弟的注释

export const enum PatchFlags {
TEX_ % x f J 4 q  (T = 1,// 表明具有动态textContentE W R F G r L的元素
CLASS = 1 << 1,  // 表明有动态Class的元素
STYLE = 1 << 2,  // 表明动态款式(静Y u m (态如style="color: red",也会提高至动态)
PROPS = 1 << 3,  // 表明具有非类/款式动态道具的元素。
FULL_PROPS = 1 << 4,  // 表明带有动态键的道具的元素,与上面三种相斥
HYDRATE_EVENTS = 1 << 5,  /n X O 3 L/ 表明带有事情监听器的元素
STABLd B )E_FRAGMENT = 1 << 6,   // 表明其子顺序不变的片段(没懂)。 
KEYED_FRAGMENT = 1 << 7, // 表明带有键控或部 d [分键控子元素的片段。
UNKEYED_FRAGMENT = 1 << 8, // 表明带有无key绑定的片段
NEED_PATCH = 1 << 9,   // 表明只需要非特点补丁的元素,例如ref或hooks
DYNAMIC_SLOTS = 1 <&9 | t ?lt; 10,  //; E l - R L M y j 表明具有动态插槽的元素
}n M F

如果一起有propstext的绑定呢, 位运算组合即可


<div id="app">
<h1&F z R = ^ hgt;技能摸鱼</h1>
<p>今天天气真不错</p>
<div :id="user@ I _ B sid"">{{name}}</div>
&l{ Q - M 2t;/div>
import { createV] Z ] l D = + 4 ;Node as _creat~ m F H C . o , 3eVNodex $ Q Y ] V S f, toDisplayS_ : _ c I +tring as _toDisplayString, openBlock as _openBlock, createBlock as _createBlock } from "vue"
e1 t a 3 ~xport function render(_ctx, _cache) {
return (_openBlock(), _createBlock("div", { id: "app" }, [K o 2 K l s %
_createVNode("h1", null, "技能摸鱼"),
_createVNq f B i % n sode("pB q + Z . s T & !", null, "今天天气真不错"),
_createVNode("div", {
id: _ctx.userid,
""": ""
}, _toDisplayv c uString(_ctx.name), 9 /* TEXT, PROPS */, ["id"])
]))
}
// Check the console for the AST

text是1,props? ^ o是8,组合在一起便是9,咱们能够简单的经过位运算0 O 7 ^来断定需要做te3 Z 7 - $xtprops的判断, 按位与即可,只需不是0便是需要比较J / ! |

尤大Vue3.0直播虚拟Dom总结(和React 对比)
尤大Vue3.0直播虚拟Dom总结(和React 对比)

位运算来做类型组合 自身便是一个最佳实践,react大兄弟也是相同的 代码

export const PLUGIN_EVENT_SYSTEM = 1;
export const RESPONDER_EVENT_SYSTEM = 1 &r 0 V Qlt;< 1;
export const USE_EVENT_SYSTEM = 1 <7 / # t Q W w - ,< 2;
exporW 1 jt c$ * l  O } . X Bonst IS_TARGET_PHA ] wASE_ONLY = 1 << 3;
export const IS_PAS, g O kSIVE =o 8 8 l 1 <E  6 @ N< 4;
export const PASSIVE_NOT_SUPPORTED = 1 << 5;
export const IS_REPLAYED = 1 << 6;
export const IS_FIRST_ANCX L ]ESTOR = 1 << 7;
export const LEGACY_= D ] + BFB_SUPPORT = 1 << 8;

事情缓存

绑定的@click会存在缓存里 链接

<div id="app">
<button @click="handleClick">戳我</button>
</div>

export function render(_ctx, _cache) {
return (_openF w 4 V 2Block(), _c [ Q 4 !createBlock("div", { id: "app"Q h P S p E [ }, [
_createVNode("button", {
onClick: _cache[S C j1] || (_: 3 # 9cache[1] = $event => (_ctx.handleClick($event)))
}, "戳我")B | ~ S d 2 ~ [
]))
}

传入的事情会自动生成并缓存一个内联函数再cache里,变为一个静态节点。这样就算咱们自己写内联函数,也不会导致多余的重复烘托 真是优异啊

静态提高

代码

<div id="app">
<f y F = i v o P;h1>技能摸鱼</h1>
<p>今天天气真不错</p>
<div>{{name}}</div>
<div :claF ? e w t 9 ~ss="{red:isRed}">摸鱼符</div>
</div>
const _hoisted_1 = { id: "app" }
const _hoisted_2 = _createVi a Q J ` r / F /Node("h1", null, "技能摸鱼", -1 /* HOISTED */)
const _hoisted_3x % 5 O & = _creaL | + 4 y kteVNode("p", null, "今天天气真不错", -1 /* HOISTED */)
export function render(_ctx, _cache) {
rh = n D Uer u [ qturn (_openBlock(), _createBlock("div", _hoisted_1, [
_hoisted_2,
_hoisted_3,
_createVNode("diP 7 T R j P mv", null, _toDisplayString(_ctx.name), 1 /* TEXT */),
_createVNode("div", {
class: {red:_ctx.isRed}
}, "摸鱼符", 2 /* CLASS */)
]))
}

vue3和react fiber的vdom

很多人吐槽越来越像React,其实越来越像的api,代表着前端的两个方向

Vue1.x

没有vdom,彻底的响应式,每个数据改变,都经过响应式告诉机制来新建Watcher干活,就像独立团z z Z & / e规模小的时分,每个战士入伍和升职,都自动告诉咱老李F = G,办理便利

项目规模: ~ H L R u变大后,过多的Watcher,会导致功能的瓶颈

尤大Vue3.0直播虚拟Dom总结(和React 对比)
尤大Vue3.0直播虚拟Dom总结(和React 对比)
尤大Vue3.0直播虚拟Dom总结(和React 对比)

React15x

React15时代,没有响应式,数据变了,整个新数据和老的数据做diff,算出差异 就知道怎样去修正dom了,就像老李指挥室有一个模型,每次人事~ _ T , 4 9变更,经过比照所有人前后差异,就知道了改变, 看起来有很多计算量,可是& Y | m这种ir ; C # ?mmutable的数据结构对大型项目a _ = p #比较3 F ! % ;友好,? m & ~ C并且Vdop ! Mm抽象成功后,换成其他渠道render成为了或许,无论是打鬼子还是; | 0 e + | z打国8 { E d ! 1 t ;军,都用一个vdom模式

碰到的问题相同,如果dom节点J : ^ Y p e r持续变多,每次diff的时间超过了16ms,就或许会造成卡顿(60fps)

尤大Vue3.0直播虚拟Dom总结(和React 对比)

Vue2.x

引进vdom,控制了颗粒度,组件层面走watcher告诉, 组件内部走vdom做diff,既不会有太多watcher,也不会4 k [ j让vdom的规模过大,diff超过16ms,真是优异啊
就像独立团大了今后,只需营5 q * – l长排长级其他变化,才会告诉老李,内部的自己diff办理了

尤大Vue3.0直播虚拟Dom总结(和React 对比)

React 16 Fiber

React走了别的一条路,已然首要问题是diff导致卡顿| b O a F e xT v p X于是React走了类似cpu调度的逻辑,把vdL _ Mom这棵树,微观变成了链表,使用浏览器的闲暇时间来做diffs @ 6 p Z M H 7如果超过了16ms,有动画或者用户交互的使命,就把主进程控制权还给浏览器,等闲暇了持续,特别像等候女神的备胎

尤大Vue3.0直播虚拟Dom总结(和React 对比)

dif# % F F = U U h 6f的逻辑,变成了单向的链表,任何时分主线程女神有空了,咱们在持续蹭上去接盘做diff,我们研讨下requey L , { 0 A ] xstIdleCallback就知道,从浏览器角度看 是这样的

尤大Vue3.0直播虚拟Dom总结(和React 对比)

大概代码

requestIdelCallback(myNonEssentialWo~ 7 t ! = u S Frk);
// 等候女神闲暇
function myNonEssentialWork (deadline) {
// deadline.tiB } |meRemaining()>0 主线程女神还有事情
// 还有diff使命没算玩
while (d| D ! , C j Peadline.timeRemaining() > 0 && tasks.length > 0) {
doWorkIfNeeded();
}
// 女神没时间了,把女神还回去
if (tasks.length > 0){
requestIdleCall, y 0 C iback(myl L n l 1 h oNonEssentialWork);
}
}

Vue3

这儿的静态提高和事情缓存刚才说过了,就不说了,其实我也很纳闷,这些静态| H符号和事情缓存,React自身也能够做,为啥就不实现了a X ) $ t D,连shouldComponentUpdate都得自己定义,为啥不把默认的组件都变成pure或者memo呢,唉,或许这便是人生把

React给你自由,Vue让你持久,或许也是现在国内Vue和React都如此受欢迎的原因把

Vue3经过Proxy响应式+组件内部vdom+静态符号,把使命颗粒度控制的满足详尽,所以也不太需要time-slice

人生啊,小孩才天天研讨利害, 成年人挑选我都要,也等待React17的新特性

尤大Vue3.0直播虚拟Dom总结(和React 对比)

直播小细节

最后提问期间,强如尤大,也没法回避发量的问题,可惜没引荐啥护发素,我仔细看了一下,好像vue3发布后,尤大发际线确实提高了 囧
祝我们技能提高的一起也能有乌黑的秀发

小广告

欢迎点赞重视,我的首要喜好便是摸鱼,引荐个摸鱼群把,这篇文章便是我上午摸鱼写出来的, 一起来技能摸鱼把,最近准备摸鱼写个vue3源码全剖析系列 群进不去的加我 K } [ S h & N _微信woniu_ppp

尤大Vue3.0直播虚拟Dom总结(和React 对比)