全体尤大直播的过程,劝退大兄弟已经总结的贼棒了 ,直接移步
抄笔记:尤雨溪在Vue3.0 Beta直播里聊到了这些…
Vue3
核心的Typescript
,Proxy
响应式,CompositioZ P P /n
解决代码重复横跳都有很棒的文章剖析了, 我总结一下虚拟Dom
部分把,并比照一下React
, vdom
的重写也是vue3
功能如此优异的重要原因
- 了解Vue3源码这一篇就够了
- Vue 3.0 这个迷人的小h * ] ? N X妖精,到底好在哪里?(更新原理比照)
Vue3的虚拟dom
先说结论,静态M g ? a N o F N符号,upadte
功能提高1.3~2倍,ssr
提高2~3倍,怎样做到的呢
编译模板的静态符号
咱们来看一段很常见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
新建成vdom
,i . k r在diff
的时分需要比照,有一些额外的功能损耗$ ; ~
咱们看下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
最后一个_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 ^后面注释也很容易猜出,依据text
,props
等不同的符号,这样再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
如果一起有props
和text
的绑定呢, 位运算组合即可
<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 - $xt
和props
的判断, 按位与即可,只需不是0便是需要比较J / ! |
位运算来做类型组合 自身便是一个最佳实践,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
,会导致功能的瓶颈
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)
Vue2.x
引进vdom,控制了颗粒度,组件层面走watcher告诉, 组件内部走vdom做diff,既不会有太多watcher,也不会4 k [ j让vdom的规模过大,diff超过16ms,真是优异啊
就像独立团大了今后,只需营5 q * – l长排长级其他变化,才会告诉老李,内部的自己diff办理了
React 16 Fiber
React
走了别的一条路,已然首要问题是diff
导致卡顿| b O a F e x,T v p X于是React
走了类似cpu
调度的逻辑,把vdL _ Mom
这棵树,微观变成了链表,使用浏览器的闲暇时间来做diff
,s @ 6 p Z M H 7如果超过了16ms
,有动画或者用户交互的使命,就把主进程控制权还给浏览器,等闲暇了持续,特别像等候女神的备胎
dif# % F F = U U h 6f
的逻辑,变成了单向的链表,任何时分主线程女神有空了,咱们在持续蹭上去接盘做diff
,我们研讨下requey L , { 0 A ] xstIdleCallback
就知道,从浏览器角度看 是这样的
大概代码
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发布后,尤大发际线确实提高了 囧
祝我们技能提高的一起也能有乌黑的秀发
小广告
欢迎点赞重视,我的首要喜好便是摸鱼,引荐个摸鱼群把,这篇文章便是我上午摸鱼写出来的, 一起来技能摸鱼把,最近准备摸鱼写个vue3源码全剖析系列 群进不去的加我 K } [ S h & N _微信woniu_ppp