One Piece介紹
2020年9月19日凌晨轻黑,尤雨溪大大正式發(fā)布了 Vue.js 3.0 版本糊肤,代號(hào):One Piece。此框架新的主要版本提供了更好的性能氓鄙、更小的捆綁包體積馆揉、更好的 TypeScript 集成、用于處理大規(guī)模用例的新 API抖拦,并為框架未來的長期迭代奠定了堅(jiān)實(shí)的基礎(chǔ)升酣。
Vue3.0六大亮點(diǎn):
1,性能比Vue2.X版本快1.2 ~ 2倍
2态罪,按需加載噩茄,按需編譯,體積比Vue2.X版本更小
3复颈,新增了組合API(類似于React Hooks)【最大亮點(diǎn)】
4绩聘,更好的TS語法支持(新增Fragment、Teleport券膀、Suspense)
5君纫,暴露了自定義的渲染API
6驯遇,提供了更先進(jìn)的組件定義
回顧下Vue2.0的特點(diǎn):
- 輕量級(jí)框架:只關(guān)注視圖層芹彬,是一個(gè)構(gòu)建數(shù)據(jù)的視圖集合,大小只有幾十kb叉庐;
- 簡單易學(xué):國人開發(fā)舒帮,中文文檔,不存在語言障礙 ,易于理解和學(xué)習(xí)玩郊;
- 雙向數(shù)據(jù)綁定:保留了angular的特點(diǎn)肢执,在數(shù)據(jù)操作方面更為簡單;
- 組件化:保留了react的優(yōu)點(diǎn)译红,實(shí)現(xiàn)了html的封裝和重用预茄,在構(gòu)建單頁面應(yīng)用方面有著獨(dú)特的優(yōu)勢(shì);
- 視圖侦厚,數(shù)據(jù)耻陕,結(jié)構(gòu)分離:使數(shù)據(jù)的更改更為簡單,不需要進(jìn)行邏輯代碼的修改刨沦,只需要操作數(shù)據(jù)就能完成相關(guān)操作诗宣;
- 虛擬DOM:dom操作是非常耗費(fèi)性能的, 不再使用原生的dom操作節(jié)點(diǎn)想诅,極大解放dom操作召庞,但具體操作的還是dom不過是換了另一種方式;
- 運(yùn)行速度更快: 相比較與react而言来破,同樣是操作虛擬dom篮灼,就性能而言,vue存在很大的優(yōu)勢(shì)徘禁。
新版本是如何變快的穿稳?
diff方法的優(yōu)化:
- Vue2.0的虛擬DOM是進(jìn)行全量的對(duì)比
- Vue3.0新增加了靜態(tài)標(biāo)記 (在與上次虛擬節(jié)點(diǎn)進(jìn)行對(duì)比時(shí)候,只對(duì)比帶有patch flag的節(jié)點(diǎn)晌坤,并且可以通過flag的信息得知當(dāng)前節(jié)點(diǎn)要對(duì)比的具體內(nèi)容)=> 減少了比較次數(shù)逢艘,性能提升。
<template>
<div class="about">
<h1>This is an about page</h1>
<p>{{msg}}</p>
</div>
</template>
import { createVNode as _createVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createBlock as _createBlock } from "vue"
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createBlock("template", null, [
_createVNode("div", { class: "about" }, [
_createVNode("h1", null, "This is an about page"),
_createVNode("p", null, _toDisplayString(_ctx.msg), 1 /* TEXT */)
])
]))
}
hoistStatic 靜態(tài)提升:
- Vue2.0中無論元素是否參與更新骤菠,每次都會(huì)重新創(chuàng)建
- Vue3.0中不參與更新的元素它改,只會(huì)被創(chuàng)建一次,之后每次渲染時(shí)候被不停的復(fù)用
<template>
<div class="about-page">
<h1 id='hello'>This is an about 111</h1>
<p>{{msg}}</p>
<button @click='myFn'></button>
</div>
</template>
import { createVNode as _createVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createBlock as _createBlock } from "vue"
const _hoisted_1 = { class: "about-page" }
const _hoisted_2 = /*#__PURE__*/_createVNode("h1", { id: "hello" }, "This is an about 111", -1 /* HOISTED */)
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createBlock("template", null, [
_createVNode("div", _hoisted_1, [
_hoisted_2,
_createVNode("p", null, _toDisplayString(_ctx.msg), 1 /* TEXT */),
_createVNode("button", { onClick: _ctx.myFn }, null, 8 /* PROPS */, ["onClick"])
])
]))
}
cacheHandlers 事件偵聽器緩存
- 默認(rèn)情況下商乎,onClick會(huì)被視為動(dòng)態(tài)綁定央拖,所以每次都會(huì)去追蹤它的變化
- 但因?yàn)槭峭粋€(gè)函數(shù),所以Vue3.0沒有追蹤變化鹉戚,緩存起來直接使用即可
import { createVNode as _createVNode, toDisplayString as _toDisplayString, openBlock as _openBlock, createBlock as _createBlock } from "vue"
const _hoisted_1 = { class: "about-page" }
const _hoisted_2 = /*#__PURE__*/_createVNode("h1", { id: "hello" }, "This is an about 111", -1 /* HOISTED */)
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createBlock("template", null, [
_createVNode("div", _hoisted_1, [
_hoisted_2,
_createVNode("p", null, _toDisplayString(_ctx.msg), 1 /* TEXT */),
_createVNode("button", {
onClick: _cache[1] || (_cache[1] = (...args) => (_ctx.myFn && _ctx.myFn(...args)))
})
])
]))
}
SSR渲染
-
當(dāng)有大量靜態(tài)內(nèi)容時(shí)鲜戒,這些內(nèi)容會(huì)被當(dāng)做純字符串推進(jìn)一個(gè)buffer里面
,即使存在動(dòng)態(tài)(屬性抹凳、函數(shù)和值)的綁定遏餐,會(huì)通過模板插值嵌入進(jìn)去。這樣會(huì)比通過虛擬DOM的渲染快上很多很多
當(dāng)靜態(tài)內(nèi)容大到一定量級(jí)的時(shí)候赢底,會(huì)使用 _createStaticVNode 方法失都,在客戶端生成一個(gè)static node, 這些靜態(tài)node柏蘑,會(huì)被直接innerHtml, 就無需創(chuàng)建對(duì)象,然后根據(jù)對(duì)象來渲染粹庞。
<template>
<div class="about-page">
<h1 class='hello'>This is an about 111</h1>
<h1 class='hello'>This is an about 111</h1>
<h1 class='hello'>This is an about 111</h1>
<h1 class='hello'>This is an about 111</h1>
<h1 class='hello'>This is an about 111</h1>
<h1 class='hello'>This is an about 111</h1>
<p>{{msg}}</p>
<button @click='myFn'></button>
</div>
</template>
import { mergeProps as _mergeProps } from "vue"
import { ssrRenderAttrs as _ssrRenderAttrs, ssrInterpolate as _ssrInterpolate } from "@vue/server-renderer"
export function ssrRender(_ctx, _push, _parent, _attrs, $props, $setup, $data, $options) {
const _cssVars = { style: { color: _ctx.color }}
_push(`<template${
_ssrRenderAttrs(_mergeProps(_attrs, _cssVars))
}><div class="about-page"><h1 class="hello">This is an about 111</h1><h1 class="hello">This is an about 111</h1><h1 class="hello">This is an about 111</h1><h1 class="hello">This is an about 111</h1><h1 class="hello">This is an about 111</h1><h1 class="hello">This is an about 111</h1><p>${
_ssrInterpolate(_ctx.msg)
}</p><button></button></div></template>`)
}
compiler原理
- 靜態(tài)Node不再做更新處理
- 靜態(tài)綁定的class/id不再做更新處理
- 結(jié)合打包標(biāo)記PatchFlag, 進(jìn)行更新分析(動(dòng)態(tài)綁定)
- 事件監(jiān)聽器Cache緩存處理(cacheHandlers)
- hoistStatic自動(dòng)針對(duì)多靜態(tài)節(jié)點(diǎn)進(jìn)行優(yōu)化咳焚,輸出字符串
Vue3.0 快速上手步驟
-
創(chuàng)建Vue3的三種方式:
- Vue-cli
- Webpack
- Vite
-
什么是Vite?
- Vite是vue作者開發(fā)的一款意圖取代webpack的工具
- 其實(shí)現(xiàn)原理是利用ES6的import會(huì)發(fā)送請(qǐng)求去加載文件的特性
- 攔截這些請(qǐng)求庞溜,做了一些預(yù)編譯革半,省去了webpack冗長的打包時(shí)間(大大提升編譯速度)
- 可以在單文件中書寫ES6語法
- 支持熱更新(請(qǐng)求的內(nèi)容才會(huì)被打包、更新)
- RollUp打包模式
-
使用Vite創(chuàng)建項(xiàng)目
1. 安裝Vite:npm install -g create-vite-app 2. 利用Vite創(chuàng)建Vue3項(xiàng)目:create-vite-app [your_name] / npm init vite-app [your_name] 3. 安裝依賴運(yùn)行項(xiàng)目 cd [your_name] npm install npm run dev
如何兼容使用Vue3.0, 有哪些需要注意的點(diǎn)流码?
-
生命周期鉤子函數(shù)的變化:
-
與 2.x 版本生命周期相對(duì)應(yīng)的組合式 API
-
beforeCreate
-> 使用setup()
-
created
-> 使用setup()
-
beforeMount
->onBeforeMount
-
mounted
->onMounted
-
beforeUpdate
->onBeforeUpdate
-
updated
->onUpdated
-
beforeDestroy
->onBeforeUnmount
-
destroyed
->onUnmounted
-
errorCaptured
->onErrorCaptured
-
-
新增的鉤子函數(shù)
除了和 2.x 生命周期等效項(xiàng)之外督惰,組合式 API 還提供了以下調(diào)試鉤子函數(shù):
onRenderTracked
onRenderTriggered
-
-
破壞性的變化\新增的變化
-
vue3中template支持多個(gè)根標(biāo)簽。
<!-- 得益于 `vue3.0` 的特性旅掂,我們現(xiàn)在不比把組件內(nèi)容全部包裹在某一個(gè) `div` 下面了赏胚,一個(gè) `template` 里面可以有多個(gè)根節(jié)點(diǎn)元素,是沒有關(guān)系的商虐。--> <template> <p>{{msg}}</p> <button @click='myFn'></button> </template>
-
異步組件的引用
// before const asyncPage = () => import('./NextPage.vue') // after import { defineAsyncComponent } from 'vue' const asyncPage = defineAsyncComponent(() => import('./NextPage.vue'))
-
main.js 入口文件的變化
//vue2 import Vue from 'vue' import App from './App.vue' Vue.config.productionTip = false new Vue({ render: h => h(App), }).$mount('#app')
//vue3 import { createApp } from 'vue' import App from './App.vue' import './index.css' createApp(App).mount('#app')
- setup(取代data methods) ref
setup函數(shù)返回一個(gè)對(duì)象觉阅,這個(gè)對(duì)象中包含方法和數(shù)據(jù),生命周期鉤子函數(shù)也在setup中運(yùn)行秘车,取代的是vue2中的data典勇,methods。
ref類型的數(shù)據(jù)叮趴,是一種響應(yīng)式的數(shù)據(jù),待續(xù)// Vue2 export default{ props:{ title: String }, data() { return { count: 0 }, methods:{ add(){ return this.count++ } } } } // Vue3 export default { props: { title: String }, setup(props,context) { const count = ref(0) add(){ return count.value ++ } return {count,add} } }
-
-
移除了的特性
- 移除
keyCode
作為v-on
修飾符 -
$on,$off and $once
移除 -
fliters
移除 -
Inline templates attributes
移除
- 移除
新版本的不完全升級(jí)指南
- 90%以上的代碼可與Vue2.0的復(fù)用
- Composition API作為新增的API不會(huì)影響舊的邏輯代碼
- Mixins不再推薦割笙,filters已經(jīng)被遺棄
- TS語法的更多應(yīng)用
- Vue3有專用的遷移版本,對(duì)Vue2進(jìn)行兼容
新老版本雙向數(shù)據(jù)綁定的原理
-
在Vue2.x中是通過defineProperty來實(shí)現(xiàn)響應(yīng)式數(shù)據(jù)的
原理:vue 雙向數(shù)據(jù)綁定是通過 數(shù)據(jù)劫持 結(jié)合 發(fā)布訂閱模式的方式來實(shí)現(xiàn)的眯亦, 也就是說數(shù)據(jù)和視圖同步伤溉,數(shù)據(jù)發(fā)生變化,視圖跟著變化妻率,視圖變化乱顾,數(shù)據(jù)也隨之發(fā)生改變;
核心:關(guān)于VUE雙向數(shù)據(jù)綁定宫静,其核心是 Object.defineProperty()方法 -
在Vue3.x中是通過proxy來實(shí)現(xiàn)響應(yīng)式數(shù)據(jù)的
let obj = { name: "lorretta", age: 18 }; let handler = { // 獲取值 get(obj, key) { console.log(obj, "obj", key, "key"); return obj[key]; }, // 設(shè)置值 set(obj, key, val) { console.log("set proxy: ", obj, key, val); obj[key] = val; console.log("更新了UI界面"); return true }, }; let state = new Proxy(obj, handler); state.name = "rita"; console.log(state);
Composition API(組合API)
專業(yè)版: 通過創(chuàng)建 Vue 組件走净,我們可以將接口的可重復(fù)部分及其功能提取到可重用的代碼段中。僅此一項(xiàng)就可以使我們的應(yīng)用程序在可維護(hù)性和靈活性方面走得更遠(yuǎn)孤里。然而伏伯,我們的經(jīng)驗(yàn)已經(jīng)證明,光靠這一點(diǎn)可能是不夠的捌袜,尤其是當(dāng)你的應(yīng)用程序變得非常大的時(shí)候——想想幾百個(gè)組件说搅。在處理如此大的應(yīng)用程序時(shí),共享和重用代碼變得尤為重要琢蛤。
設(shè)計(jì)動(dòng)機(jī)
維護(hù)大型項(xiàng)目的時(shí)候,特別是在閱讀他人代碼時(shí),uptionAPI會(huì)把完整的邏輯切割到不同選項(xiàng),導(dǎo)致閱讀苦難
組件公共代碼,需要進(jìn)行代碼復(fù)用,vue2.0會(huì)出現(xiàn)比較困難的情況,例如mixin
為了以后使用ts實(shí)現(xiàn)更好的類型推斷
再次回顧下:Vue3的優(yōu)勢(shì)
一蜓堕、Vue 3.0 性能提升主要是通過哪幾方面體現(xiàn)的?
1博其、源碼體積的優(yōu)化
- 重寫了虛擬 dom
2套才、響應(yīng)式系統(tǒng)的升級(jí)
- 用 Proxy 和 Reflect 來代替 vue2 中的 Object.definepeoperty()方法來重寫響應(yīng)式
- vue3 中可以監(jiān)聽動(dòng)態(tài)新增的屬性
- vue3 中可以監(jiān)聽刪除的屬性
- vue3 中可以監(jiān)聽數(shù)組的索引和 length 屬性
3、代碼編譯優(yōu)化
- 使用了 組合 API 來代替 vue2 中的 Options API
- 組件內(nèi)不需要根節(jié)點(diǎn)了慕淡,使用 fragment(代碼片段)代替了背伴,fragment(代碼片段)不會(huì)在頁面顯示
- vue3 中標(biāo)記和提升所有的靜態(tài)根節(jié)點(diǎn),diff 的時(shí)候只需要對(duì)比動(dòng)態(tài)節(jié)點(diǎn)內(nèi)容
二峰髓、Vue 3.0 所采用的 Composition Api 與 Vue 2.x 使用的 Options Api 有什么區(qū)別傻寂?
- 代碼更利于維護(hù)和封裝
- Vue2 中,我們會(huì)在一個(gè) vue 文件的 data,methods携兵,computed疾掰,watch 中定義屬性和方法,共同處理頁面邏輯 ,一個(gè)功能的實(shí)現(xiàn)徐紧,代碼過于分散
- vue3 中,代碼是根據(jù)邏輯功能來組織的静檬,一個(gè)功能的所有 api 會(huì)放在一起(高內(nèi)聚,低耦合)并级,提高可讀性和可維護(hù)性,基于函數(shù)組合的 API 更好的重用邏輯代碼
- Vue3 中用 setup 函數(shù)代替了 Vue2 中的 beforeCreate 和 created
- Vue3 中用 onUnmounted 代替了 Vue2 中的 beforeDestory
- Vue3 中用 unmounted 代替了 Vue2 中的 destroyed
三拂檩、Proxy 相對(duì)于 Object.defineProperty 有哪些優(yōu)點(diǎn)?
- 代碼的執(zhí)行效果更快
- Proxy 可以直接監(jiān)聽對(duì)象而非屬性
- Proxy 可以直接監(jiān)聽數(shù)組的變化
- Proxy 有多達(dá) 13 種攔截方法,不限于 apply嘲碧、ownKeys稻励、deleteProperty、has 等等是 Object.defineProperty 不具備的
- Proxy 返回的是一個(gè)新對(duì)象,我們可以只操作新的對(duì)象達(dá)到目的,而 Object.defineProperty 只能遍歷對(duì)象屬性直接修改
- Proxy 不需要初始化的時(shí)候遍歷所有屬性愈涩,另外有多層屬性嵌套的話望抽,只有訪問某個(gè)屬性的時(shí)候,才會(huì)遞歸處理下一級(jí)的屬性
四履婉、Vue 3.0 在編譯方面有哪些優(yōu)化糠聪?
- diff算法的優(yōu)化:vue3.x 中標(biāo)記和提升所有的靜態(tài)節(jié)點(diǎn),diff 的時(shí)候只需要對(duì)比動(dòng)態(tài)節(jié)點(diǎn)內(nèi)容
- Fragments(升級(jí) vetur 插件): template 中不需要唯一根節(jié)點(diǎn)谐鼎,可以直接放文本或者同級(jí)標(biāo)簽
- 靜態(tài)提升(hoistStatic),當(dāng)使用 hoistStatic 時(shí),所有靜態(tài)的節(jié)點(diǎn)都被提升到 render 方法之外.只會(huì)在應(yīng)用啟動(dòng)的時(shí)候被創(chuàng)建一次,之后使用只需要應(yīng)用提取的靜態(tài)節(jié)點(diǎn)舰蟆,隨著每次的渲染被不停的復(fù)用
- patch flag, 在動(dòng)態(tài)標(biāo)簽?zāi)┪布由舷鄳?yīng)的標(biāo)記,只有帶 patchFlag 的節(jié)點(diǎn)才被認(rèn)為是動(dòng)態(tài)的元素,會(huì)被追蹤屬性的修改,能快速的找到動(dòng)態(tài)節(jié)點(diǎn),而不用逐個(gè)逐層遍歷,提高了虛擬 dom diff 的性能
- 緩存事件處理函數(shù) cacheHandler, 避免每次觸發(fā)都要重新生成全新的 function 去更新之前的函數(shù)
- tree shaking, 通過搖樹優(yōu)化核心庫體積,減少不必要的代碼量
五狸棍、Vue.js 3.0 響應(yīng)式系統(tǒng)的實(shí)現(xiàn)原理身害?
- 通過 2 個(gè)響應(yīng)式 API 函數(shù)的調(diào)用,一個(gè)是 reactive() , 一個(gè)是 ref(), 就可以大致明白了
- reactive 函數(shù)是用來把普通對(duì)象創(chuàng)建成為響應(yīng)式對(duì)象的草戈,函數(shù)內(nèi)通過執(zhí)行 proxy 創(chuàng)建的 get塌鸯、set、deleteProperty 方法來實(shí)現(xiàn)
- get 方法獲取響應(yīng)式數(shù)據(jù)唐片,同時(shí)調(diào)用 track 方法去收集依賴
- set 方法設(shè)置響應(yīng)式數(shù)據(jù)丙猬,同時(shí)調(diào)用 trigger 方法是觸發(fā)響應(yīng)式數(shù)據(jù)的更新
- deleteProperty 方法刪除響應(yīng)式數(shù)據(jù)涨颜,同時(shí)用 trigger 方法是觸發(fā)響應(yīng)式數(shù)據(jù)的更新
- ref 函數(shù)是用來把一般類型的數(shù)據(jù)或者普通對(duì)象創(chuàng)建成為響應(yīng)式對(duì)象的,函數(shù)內(nèi)返回的是一個(gè)對(duì)象
- 對(duì)象內(nèi)的 get 方法獲取響應(yīng)式數(shù)據(jù)茧球,同時(shí)調(diào)用 track 方法去收集依賴
- 對(duì)象內(nèi)的 set 方法設(shè)置響應(yīng)式數(shù)據(jù)庭瑰,同時(shí)調(diào)用 trigger 方法是觸發(fā)響應(yīng)式數(shù)據(jù)的更新
- tarck 函數(shù)內(nèi)通過 targetMap 找到 depsMap 通過 depsMap 找到 dep,最后向 dep 內(nèi)添加 effect()函數(shù)
- trigger 函數(shù)內(nèi)通過 targetMap 找到 depsMap 通過 depsMap 找到 dep抢埋,最后遍歷 dep 數(shù)組弹灭,執(zhí)行里面的 effect()函數(shù)來更新響應(yīng)式數(shù)據(jù)
附錄:一些參考資料和網(wǎng)站
1,PatchFlags
export const enum PatchFlags {Ⅰ
TEXT = 1,1/動(dòng)態(tài)文本節(jié)點(diǎn)
CLASS = 1<<1揪垄,1/ 2// 動(dòng)態(tài) classSTYLE= 1<<2穷吮,// 4//動(dòng)態(tài) style
PROPS = 1<< 3,// 8// 動(dòng)態(tài)屬性饥努,但不包含類名和樣式
FULL_PROPS = 1<<4,// 16 //具有動(dòng)態(tài) key屬性捡鱼,當(dāng)key改變時(shí),需要進(jìn)行完整的 diff 比較酷愧。
HYDRATE_EVENTS = 1<<5堰汉,// 32//帶有監(jiān)聽事件的節(jié)點(diǎn)
STABLE_FRAGMENT = 1<<6,// 64//一個(gè)不會(huì)改變子節(jié)點(diǎn)順序的 fragment
KEYED_FRAGMENT = 1<<7伟墙,// 128//帶有key屬性的 fragment 或部分子字節(jié)有
keyUNKEYED_FRAGMENT = 1<<8翘鸭,// 256//子節(jié)點(diǎn)沒有key 的 fragment
NEED_ PATCH =1<<9,//512//一個(gè)節(jié)點(diǎn)只會(huì)進(jìn)行非 props比較
DYNAMIC_SLOTS = 1 << 10,//1024 // 動(dòng)態(tài)的插槽
// --------- SPECIAL FLAGS (下面是特殊的)---------------
// 以下是特殊的flag,不會(huì)在優(yōu)化中被用到戳葵,是內(nèi)置的特殊flag
HOISTED = -1,
BAIL = -2, // 用來表示一個(gè)節(jié)點(diǎn)的diff應(yīng)該結(jié)束
}
2就乓,驗(yàn)證網(wǎng)址:
https://vue-next-template-explorer.netlify.app/
[圖片上傳失敗...(image-81db52-1629942396026)]
3,參考文檔地址
Vite使用文檔:https://vue3js.cn/vite/
TS學(xué)習(xí)文檔:https://www.tslang.cn/