Vue3官網(wǎng)文檔
1窘奏、Vue3.0 新變化
- Proxy:不只是解決了 defineProperty 的局限性
- Performance:性能比Vue 2.x快1.2~2倍
- Tree shaking support:可以將無用模塊“剪輯”跷车,僅打包需要的躏升,按需編譯,體積比Vue2.x更小
- Composition API: 組合API(類似React Hooks)
- Better TypeScript support:更優(yōu)秀的 Ts 支持
- Custom Renderer API:暴露了自定義渲染API
- Fragment, Teleport(Protal), Suspense:更先進的組件,“碎片”拥褂,Teleport 即 Protal 傳送門掌唾,“懸念”。
2男翰、Vue3.0是如何變快
2.1 雙向綁定
2.0現(xiàn)有限制:
- 無法檢測到新的屬性添加/刪除
- 無法監(jiān)聽數(shù)組的變化
- 需要深度遍歷,浪費內(nèi)存
3.0優(yōu)化:
- 使用 ES6的Proxy 作為其觀察者機制纽乱,取代之前使用的Object.defineProperty奏篙。Proxy默認可以支持數(shù)組
- 允許框架攔截對象上的操作
- 多層對象嵌套,使用懶代理
Object.defineProperty -> Proxy
Object.defineProperty是一個相對比較昂貴的操作迫淹,因為它直接操作對象的屬性,顆粒度比較小为严。將它替換為es6的Proxy敛熬,在目標對象之上架了一層攔截,代理的是對象而不是對象的屬性第股。這樣可以將原本對對象屬性的操作變?yōu)閷φ麄€對象的操作应民,顆粒度變大。
javascript引擎在解析的時候希望對象的結(jié)構(gòu)越穩(wěn)定越好夕吻,如果對象一直在變诲锹,可優(yōu)化性降低,proxy不需要對原始對象做太多操作涉馅。
2.2 虛擬DOM
https://vue-next-template-explorer.netlify.app/
2.0 虛擬 DOM性能瓶頸:
- 雖然vue能夠保證觸發(fā)更新的組件最小化归园,但單個組件部分變化需要遍歷該組件的整個vdom樹
- 傳統(tǒng)vdom性能跟模版大小正相關(guān),跟動態(tài)節(jié)點的數(shù)量無關(guān)
3.0優(yōu)化工作
- 在 vue 3.0 中重新推翻后重寫了虛擬 DOM 的實現(xiàn)稚矿,官方宣稱渲染速度最快可以翻倍庸诱。更多編譯時的優(yōu)化以減少運行時的開銷
diff算法優(yōu)化
Vue2中的虛擬dom是進行全量的對比捻浦,即數(shù)據(jù)更新后在虛擬DOM中每個標簽內(nèi)容都會對比有沒有發(fā)生變化
-
Vue3新增了靜態(tài)標記(PatchFlag)
在創(chuàng)建虛擬DOM的時候會根據(jù)DOM中的內(nèi)容會不會發(fā)生變化添加靜態(tài)標記
-
數(shù)據(jù)更新后,只對比帶有patch flag的節(jié)點
<div> <p>我是一個標題</p> <p>{{msg}}</p> <!-- 雙向綁定 --> </div>
// 虛擬DOM export function render(_ctx, _cache, $props, $setup, $data, $options) { return (_openBlock(), _createBlock("div", null, [ _createVNode("p", null, "我是一個標題"), // 不標記 _createVNode("p", null, _toDisplayString(_ctx.msg) + "}", 1 /* TEXT */) // 標記 ])) }
-
并且可以通過flag的信息得知當前節(jié)點要對比的具體內(nèi)容
export const enum PatchFlags { TEXT = 1,// 動態(tài)文本節(jié)點 CLASS = 1 << 1, // 2 // 動態(tài) class STYLE = 1 << 2, // 4 // 動態(tài) style PROPS = 1 << 3, // 8 // 動態(tài)屬性桥爽,但不包含類名和樣式 FULL_PROPS = 1 << 4, // 16 // 具有動態(tài) key 屬性朱灿,當 key 改變時,需要進行完整的 diff 比較钠四。 HYDRATE_EVENTS = 1 << 5, // 32 // 帶有監(jiān)聽事件的節(jié)點 STABLE_FRAGMENT = 1 << 6, // 64 // 一個不會改變子節(jié)點順序的 fragment KEYED_FRAGMENT = 1 << 7, // 128 // 帶有 key 屬性的 fragment 或部分子字節(jié)有 key UNKEYED_FRAGMENT = 1 << 8, // 256 // 子節(jié)點沒有 key 的 fragment NEED_PATCH = 1 << 9, // 512 // 一個節(jié)點只會進行非 props 比較 DYNAMIC_SLOTS = 1 << 10, // 1024 // 動態(tài) slot HOISTED = -1, // 靜態(tài)節(jié)點 // 指示在 diff 過程應(yīng)該要退出優(yōu)化模式 BAIL = -2 }
2.2 hoistStatic 靜態(tài)提升
- Vue2中無論元素是否參與更新, 每次都會重新創(chuàng)建, 然后再渲染
- Vue3中對于不參與更新的元素, 會做靜態(tài)提升, 只會被創(chuàng)建一次, 在渲染時直接復(fù)用即可
<div>
<p>我是一個標題</p>
<p>{{msg}}</p>
</div>
// 靜態(tài)提升之前
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createBlock("div", null, [
_createVNode("p", null, "我是一個標題"), // 每次都會創(chuàng)建一個虛擬節(jié)點
_createVNode("p", null, _toDisplayString(_ctx.msg) + "}", 1 /* TEXT */)
]))
}
// 靜態(tài)提升之后
const _hoisted_1 = /*#__PURE__*/_createVNode("p", null, "我是一個標題", -1 /* HOISTED */) // 定義了一個全局變量盗扒,只會創(chuàng)建一次
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createBlock("div", null, [
_hoisted_1,
_createVNode("p", null, _toDisplayString(_ctx.msg) + "}", 1 /* TEXT */)
]))
}
2.3 事件監(jiān)聽緩存
默認情況下onClick會被視為動態(tài)綁定, 所以每次都會去追蹤它的變化,但是因為是同一個函數(shù)缀去,所以沒有追蹤變化, 直接緩存起來復(fù)用即可
<div>
<button @click="onClick">按鈕</button>
</div>
// 開啟事件監(jiān)聽緩存之前
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createBlock("div", null, [
_createVNode("button", { onClick: _ctx.onClick }, "按鈕", 8 /* PROPS */, ["onClick"])
])) // 有靜態(tài)標記
}
// 開啟事件監(jiān)聽緩存之后
export function render(_ctx, _cache, $props, $setup, $data, $options) {
return (_openBlock(), _createBlock("div", null, [
_createVNode("button", {
onClick: _cache[1] || (_cache[1] = (...args) => (_ctx.onClick(...args)))
}, "按鈕")
])) // 沒有靜態(tài)標記
}
值得注意的新API
1.創(chuàng)建vue3.0項目的方法
(1)使用vite
npm init vite-app 項目名
cd 項目名
npm run dev
使用這種方式的優(yōu)缺點:
① 創(chuàng)建速度快侣灶,不通過webpack編譯
② 還需要自己安裝vue-router、vuex (vue-router以及vuex都要相應(yīng)的升級為4.0版本)
vite是什么朵耕?
vite是基于vue3單文件組件的非打包開發(fā)服務(wù)器
vite 是一個基于 Vue3 單文件組件的非打包開發(fā)服務(wù)器炫隶,vite具有以下的優(yōu)點:
可以快速的冷啟動,不需要等待打包阎曹;
即時的熱模塊更新伪阶;
不用等待整個項目編譯完成。
原理:ES module/.vue文件拆分為http請求+type处嫌?=參數(shù)/熱更新(koa/websocket/watcher)
vite使用ES module栅贴,代碼以模塊的方式引入到文件;即在瀏覽器使用import/export方式導(dǎo)入/導(dǎo)出熏迹,webpack使用require方式導(dǎo)入檐薯。
vite使用koa構(gòu)建的服務(wù)端,webpack使用webpack-dev-server構(gòu)建服務(wù)端注暗。
(2)利用vue-cli
npm install -g @vue/cli
vue create 項目名
cd 項目名
vue add vue-next //重點 執(zhí)行這行,會把2.0代碼改為3.0的, vue-router, vuex會變成4.0的
npm run serve
介紹完安裝vue3.0坛缕,接下來,咱們就正式進入咱們今天的重點了~
vue3.0的主要變化
響應(yīng)式基本原理:Object.defineProperty -> Proxy捆昏,提高性能
初始化方式:Options api -> composition api赚楚,提供代碼復(fù)用,更好的tree-shaking
初始化項目:vue-cli -> vite骗卜,提高開發(fā)效率
擴展方法:Vue.property.xxx -> config.globalProperties.xxx宠页,更好的tree-shaking
實例化:new Vue -> createApp。
原來的寫法寇仓,如果一個項目有多個Vue實例举户,那么多個實例造成污染:
Vue.use(plugin)
new Vue({el:'app1'});
new Vue({el:'app2'});
而createApp這種方式,每次創(chuàng)建一個vue實例,然后use不同插件
app1 = createApp({});
app2 = createApp({});
app1.use(plugin1)
app2.use(plugin2)
setup內(nèi)部不支持this遍烦,因為setup時組件實例還沒有創(chuàng)建俭嘁,通過setup的第二個參數(shù)context獲取實例
支持ts -> ts的優(yōu)點:類型檢查/編譯器提示/ts擁抱ES6規(guī)范及部分起草規(guī)范
2.相比于vue2.0,vue3.0的新變化
1乳愉、main.js兄淫,新增createApp方法
// vue2.0
import Vue from 'vue';
import App from './App.vue';
import router from './router';
import store from './store';
import './App.scss'
new Vue({
el: '#app',
router,
store,
template: '<App/>',
components: { App }
})
// vue3.0
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
import store from './store';
import './App.scss'
const app = createApp(App)
app.use(router).use(store).mount('#app');
注意: 由于vue3.0 使用的是 import {createApp} from 'vue' 而不是像vue2.0的import Vue from 'vue'屯远。因此之前使用的ui框架以及第三方包可能會因為不支持vue3.0而報錯。
2捕虽、template模板(Fragment)
vue2.0里template只支持單一根節(jié)點慨丐,在vue3.0里可以使用多個根節(jié)點
<template>
<!-- vue3.0組件的根節(jié)點可以有多個,或者使用<Fragment> 空標簽 -->
<div class="login"></div>
<div class="main"></div>
<div></div>
</template>
3泄私、組合式API(Composition API)
composition api是什么房揭?
組合api:將組件屬性公開為函數(shù)api
data -> reactive()/ref()
computed -> computed():創(chuàng)建計算屬性,返回的是ref實例
watch -> watch()
provide/inject -> provide()/inject()
lifeCicle -> on+xxx
options api的優(yōu)點:保證代碼的下限晌端,在指定的地方寫指定的代碼
options api的缺點:同一個邏輯代碼比較分散
composition api的優(yōu)點:自由度高捅暴,復(fù)用性提高
composition api的缺點:對編碼人員的要求比較高
composition API的設(shè)計動機
- 邏輯復(fù)用及代碼整理
vue2.x中代碼復(fù)用是通過mixin提取公共邏輯和通過作用域插槽編寫復(fù)用性組件
mixin的缺點:名稱一樣時會被覆蓋
作用域插槽的缺點:只能在模板中使用
vue3.0中代碼復(fù)用通過composition API。對于業(yè)務(wù)邏輯相同的代碼咧纠,可以抽取到一個js文件蓬痒,然后導(dǎo)入到不同到組件
composition的缺點:自由度高,需要編碼人員有比較高的抽象能力 - 更好的typescript支持
使用傳統(tǒng)的option配置方法寫組件的時候問題漆羔,隨著業(yè)務(wù)復(fù)雜度越來越高梧奢,代碼量會不斷的加大;由于相關(guān)業(yè)務(wù)的代碼需要遵循option的配置寫到特定的區(qū)域演痒,導(dǎo)致后續(xù)維護非常的復(fù)雜亲轨,同時代碼可復(fù)用性不高,而composition-api就是為了解決這個問題而生的鸟顺。
在vue2.0里我們都把.vue文件里的js部分叫做Options API惦蚊, 而在3.0里變?yōu)镃omposition API。
注:Composition API 里有vue3.0的新增的主要新特性:
(1)一個全新的屬性 setup 讯嫂,這是一個組件的入口蹦锋,讓我們可以運用 Vue3.0 暴露的新接口,它運行在組件被實例化時候欧芽,props 屬性被定義之后晕粪,實際上等價于 Vue2.0 版本的 beforeCreate 和 Created 這兩個生命周期,setup 返回的是一個對象渐裸,里面的所有被返回的屬性值,都會被合并到 Vue2.0 的 render 渲染函數(shù)里面装悲,在單文件組件中昏鹃,它將配合 模板的內(nèi)容,完成Model到View之間的綁定诀诊,在未來版本中應(yīng)該還會支持返回 JSX 代碼片段洞渤。
- 組合API的入口函數(shù), 在 beforeCreate 之后属瓣、created 之前執(zhí)行 载迄,主要是實現(xiàn)數(shù)據(jù)和業(yè)務(wù)邏輯不分離
- 無法使用data和methods讯柔,所以為了避免錯誤使用,直接將內(nèi)部的this改成了undefined
- 內(nèi)部的方法只能是同步护昧,不能是異步
(2)setup函數(shù)的第一個參數(shù) props 是父組件傳進來的值魂迄,在 Vue2.0 中我們可以使用 props 屬性值完成父子通信,在這里我們需要定義 props 屬性去定義接受值的類型惋耙,然后我們可以利用 setup 的第一個參數(shù)獲取 props 使用捣炬。
(3) setup 函數(shù)的第二個參數(shù)context是一個上下文對象,這個上下文對象中包含了一些有用的屬性绽榛,這些屬性在 Vue2.0 中需要通過 this 才能訪問到湿酸,在 vue3.0 中,訪問他們變成以下形式:
context.parent--> this.$parent
context.root--> this
context.emit-->this.$emit
context.refs-->this.$refs
context.slots --> this.$slots
程序執(zhí)行setup時灭美,組件尚未被創(chuàng)建推溃,因此能訪問到的有用屬性有: root、parent届腐、refs铁坎、attrs、listeners梯捕、isServer厢呵、ssrContext、emit 于此同時 data傀顾、computed襟铭、methods等是訪問不到的.
(4)setup()不單可以return 對象,還可以返回方法短曾。
(5)利用watchEffect可以監(jiān)聽props寒砖。
// 父組件
<template>
<Child val="測試"/>
</template>
<script>
import {setup} from 'vue';
import Child from './Child';
export default {
name: 'Parent',
components: {
Child
},
setup(props, ctx) {
console.log(ctx); // 在setup()中無法訪問到this
console.log(this); // undefined
}
}
</script>
// 子組件 Child
<template></tempalte>
<script>
import {setup, watchEffect} from 'vue';
export default {
name: 'Child',
// props: ['val'], // 跟vue2.0相同 也可以采用對象形式
props: {
val: String
},
setup(props, context) {
console.log(props); // 在這里可以獲取父組件傳過來的屬性值
watchEffect(() => { // 利用watchEffect監(jiān)聽props
console.log(props.val); // 首次以及props改變才會執(zhí)行這里的代碼
})
return { // setup 返回的對象/方法 可以在模板中被訪問得到
}
}
}
</script>
vue2.0 里數(shù)據(jù)都是在data里定義, 而在vue3.0可以使用reactive, ref
state更名為reactive
reactive等價于 Vue 2.x 的Vue.observable()嫉拐,用于獲取一個對象的響應(yīng)性代理對象
const obj = reactive({ count: 0 });
value更名為ref哩都,并提供isRef和toRefs
const unwrapped = isRef(foo) ? foo.value : foo;
reactive 函數(shù)
- 可以監(jiān)聽復(fù)雜類型(json/Array)的變化
- 實現(xiàn)響應(yīng)式數(shù)據(jù)的方法,Vue2中通過
Object.defineProperty
實現(xiàn)的婉徘,Vue3通過ES6的Proxy
實現(xiàn) - 注意點:如果給 reactive 傳遞了其他對象漠嵌,即不是
json/Array
- 默認情況下修改對象界面不會自動更新
- 如果要更新,可以通過重新賦值的方式
reactive 傳遞基本數(shù)據(jù)類型
import { reactive } from 'vue'
setup() {
let state = reactive(123) // 傳遞基本數(shù)據(jù)類型
function fn() {
state = 666 // 界面不會更新,不是 Proxy 對象
console.log(state) // 666
}
}
import { reactive } from 'vue'
setup() {
let state = reactive({age: 13})
function fn() {
state.age = 20 // 界面更新
console.log(state) // Proxy{age: 20}
}
}
reactive 傳遞其他對象
import { reactive } from 'vue'
setup() {
let state = reactive({time: new Date()}) // 其他對象
function fn() {
state.time.setDate(state.time.getDate() + 1) // 界面不會更新
console.log(state) // Proxy{...}
}
}
import { reactive } from 'vue'
setup() {
let state = reactive({time: new Date()}) // 其他對象
function fn() {
const newTime = new Date(state.time.getTime())
newTime.setDate(state.time.getDate() + 1)
state.time = newTime // 界面更新
console.log(state) // Proxy{...}
}
}
3.1.2 ref 函數(shù)
-
本質(zhì)還是 reactive 盖呼,當給 ref 函數(shù)傳遞一個值后儒鹿,底層會自動轉(zhuǎn)成 reactive,所以一般都是使用 ref 函數(shù)創(chuàng)建響應(yīng)式數(shù)據(jù)
- ref(10) -> reactive({value: 18})
- 在 setup 函數(shù)內(nèi)部修改 ref 的值時必須通過
.value
的方式
import { ref } from 'vue' setup() { let state = ref(10) function fn() { state.value = 20 console.log(state) // RefImp{...} } }
- 在 template 中使用不用添加
.value
几晤,Vue內(nèi)部已經(jīng)進行了轉(zhuǎn)換
3.1.3 reactive 和 ref 的區(qū)別
Vue在解析數(shù)據(jù)之前约炎,會通過當前數(shù)據(jù)的__v_ref
這個私有屬性判斷這個數(shù)據(jù)是否是ref類型,如果值為true
就是一個ref類型的數(shù)據(jù)
- 在template里使用的是ref類型的數(shù)據(jù),Vue會自動添加
.value
- 在template里使用的是reactive類型的數(shù)據(jù)圾浅,Vue不會自動添加
.value
注意: compisition-api 的本質(zhì)也是將 return 出去的值注入到 option-api 中
return {state, remStu}
// 等同
data() {
return {
state: ''
}
},
methods: {
remStu() {}
}
3.1.4 markRaw 函數(shù)
數(shù)據(jù)永遠不會被追蹤
setup() {
let obj = {name: 'zs', age: 18}
obj = markRaw(obj) // 此時無論怎么修改都不會更新數(shù)據(jù)
let state = reactive(obj)
}
3.1.5 toRef 函數(shù)
如果利用ref將某一個對象中的屬性變成響應(yīng)式的數(shù)據(jù)掠手,修改響應(yīng)式的數(shù)據(jù)是不會影響到原始數(shù)據(jù)的
setup() {
let obj = {name: 'zs'}
let state = ref(obj.name)
function fn() {
state.value = 'ls'
console.log(obj) // {name: 'zs'}
console.log(state.value) // ls
}
}
利用 toRef 將一個對象的某一個屬性變成響應(yīng)式的數(shù)據(jù),修改響應(yīng)式數(shù)據(jù)會影響原始數(shù)據(jù)狸捕,但不會更新視圖喷鸽,只是引用了原始數(shù)據(jù)
setup() {
let obj = {name: 'zs'}
let state = toRef(obj, 'name')
function fn() {
state.value = 'ls'
console.log(obj) // {name: 'ls'}
console.log(state.value) // ls
}
}
應(yīng)用場景:
如果想讓響應(yīng)式數(shù)據(jù)和原始數(shù)據(jù)關(guān)聯(lián)起來,并且修改數(shù)據(jù)之后不想更新UI界面府寒,那么就可以使用
3.1.6 toRefs 函數(shù)
將對象的全部屬性變成響應(yīng)式數(shù)據(jù)魁衙,修改后不更新視圖
setup() {
let obj = {name: 'zs', age: 18}
let state = toRefs(obj)
function fn() {
state.name.value = 'ls'
state.age.value = 20
console.log(obj) // {name: 'ls', age: 20}
console.log(state.name.value) // ls
}
}
3.1.7 toRaw 函數(shù)
ref 和 reactive 數(shù)據(jù)類型每次修改都會被追蹤,都會更新UI界面株搔,這樣會非常消耗性能剖淀,有時候一些數(shù)據(jù)不需要追蹤就可以通過toRaw方法拿到它的原始數(shù)據(jù),對原始數(shù)據(jù)進行修改就不會被追蹤
setup() {
let obj = {name: 'zs', age: 18}
let state = reactive(obj) // 包裝后的對象
let obj2 = toRaw(state)
/*
* let state = ref(obj)
* let obj2 = toRaw(state.value)
*/
console.log(obj === obj2) // true
/*
* state 和 obj 的關(guān)系:
* state 本質(zhì)是一個 Proxy 對象纤房,只是引用了 obj
*/
console.log(obj === state) // false
// 修改原始數(shù)據(jù)
function fn() {
obj2.name = 'ls' // 這里只是在內(nèi)存里發(fā)生了改變纵隔,并不會更新視圖
// obj.name = 'ls' 這樣寫才會更新視圖
}
}
data 函數(shù)
在 rfcs 中就有提到 data() 將不在支持作為一個對象,只能作為一個 function 返回一個對象炮姨。雖然在 Vue 2.x 中可以返回一個狀態(tài)進行狀態(tài)共享捌刮,但是這勢必會引發(fā)一些問題,而且如果要實現(xiàn)這種狀態(tài)共享舒岸,function 完全可以替代绅作,通過 function 返回對象,v-bind 給子組件就可以實現(xiàn)狀態(tài)共享蛾派,使用 object 會對邏輯造成紊亂俄认,并且需要示例去說明,更加重了學(xué)習(xí)者的心智負擔(dān)洪乍!所以在 Vue 3.0 中不再支持 object 方式眯杏,強行使用編譯不會通過,并且給出警告:[Vue warn]: data() should return an object.
watch函數(shù)
import { reactive, watch } from "vue";
import store from "../stores";
export default {
setup() {
const state = reactive({
searchValue: '',
});
// 監(jiān)聽搜索框的值
watch(
() => {
return state.searchValue;
},
() => {
// 存儲輸入框到狀態(tài) store 中心壳澳,用于組件通信
store.setSearchValue(state.searchValue);
}
);
return {
...toRefs(state)
};
}
};
computed
跟 Vue2.0 的使用方式很相近岂贩,同樣需要按需導(dǎo)入該模塊 , 計算屬性分為兩種,只讀 以及可讀可寫
<tempalte>
<div>{{addCount}}</div>
<div>{{addCount2}}</div>
</tempalte>
<script>
import {setup, reactive, computed} from 'vue';
export default {
setup(props, context) {
const count = ref(0);
const addCount = coomputed(() => count.value + 1); // 只讀
const addCount2 = computed(() => {
get: () => count.value + 1, // 取值函數(shù)
set: (value) => count.value = value; // 賦值函數(shù)
})
return {
count,
addCount,
addCount2
}
}
}
</script>
生命周期lifecycle hooks
① vue2.0的生命周期的鉤子函數(shù)有: beforeCreate巷波、created萎津、beforeMount、mounted抹镊、beforeUpdate姜性、updated、beforeDestroy髓考、destroyed
② vue3.0的生命周期在vue2.0的基礎(chǔ)上做了些修改, 新版本的都是以onXxx()函數(shù)注冊使用弃酌,其中 beforeCreate氨菇、created 這兩個函數(shù)可以由setup()代替
import { onMounted, onUpdated, onUnmounted } from "@vue/composition-api";//引入鉤子
export default {
setup(props, ctx) {
// `props` 屬性被定義之后儡炼,實際上等價于 `Vue2.0` 版本的 `beforeCreate` 和 `Created` 這兩個生命周期
const loadMore = () => {};
onMounted(() => {
loadMore();
});
onUpdated(() => {
console.log('updated!')
})
onUnmounted(() => {
console.log('unmounted!')
})
return {
loadMore
};
}
};
新舊版本生命周期對比 :
Options API Hook inside inside setup
beforeCreate Not needed*
created Not needed*
beforeCreate --> use setup()
created --> use setup()
beforeMount --> onBeforeMount
mounted --> onMounted
beforeUpdate --> onBeforeUpdate
updated --> onUpdated
beforeDestory --> onBeforeUnmount
destoryed --> onUnmounted
errorCaptured --> onErrorCaptured
同時vue3.0 還提供了2個全新的生命周期幫助我們?nèi)フ{(diào)試代碼:
onRenderTracked
onRenderTriggered
3.2 遞歸監(jiān)聽
默認情況下,ref和reactive創(chuàng)建的數(shù)據(jù)都是遞歸監(jiān)聽查蓉,即無論里面套多少層都可以監(jiān)聽得到
const state = ref({
a: 1,
b: {
c: 2,
d: {
e: 3
}
}
})
ref.b.c.value = 3 // 可以監(jiān)聽乌询,且會更新界面
缺點:當數(shù)據(jù)量比較大時,會非常消耗性能豌研,因為每一層都是一個Proxy對象
3.3 非遞歸監(jiān)聽
只能監(jiān)聽第一層妹田,不能監(jiān)聽其它層,只有第一層數(shù)據(jù)改變了鹃共,其它層才會被監(jiān)聽鬼佣,可以通過 shallowReactive、shallRef 創(chuàng)建非遞歸監(jiān)聽數(shù)據(jù)
3.3.1 shallowReactive
import { shallowReactive } from 'vue'
const state = shallowReactive({
a: 1,
b: {
c: 2,
d: {
e: 3
}
}
})
// 這樣可以被監(jiān)聽
state.a = 'a' /* 如果第一層不變霜浴,則視圖不會更新 */
state.b.c = 'c'
state.b.d.e = 'e'
3.3.2 shallowRef
本質(zhì)是 shallowReactive 晶衷,當給 shallowRef 函數(shù)傳遞一個值后,底層會自動轉(zhuǎn)成 shallowReactive阴孟,所以 shallowRef 的第一層是.value
import { shallowRef } from 'vue'
const state = shallowRef({
a: 1,
b: {
c: 2,
d: {
e: 3
}
}
})
// shallowRef 的第一層是 state.value晌纫,只有state.value 發(fā)生變化才會更新視圖
state.value = {
a: 2,
b: {
c: 3,
d: {
e: 4
}
}
}
3.3.2 triggerRef
自動觸發(fā)一次shallowRef的數(shù)據(jù)更新,沒有triggerReactive函數(shù)
import { shallowRef, triggerRef } from 'vue'
const state = shallowRef({
a: 1,
b: {
c: 2,
d: {
e: 3
}
}
})
state.b.d.e = 'e'
triggerRef(state) // 此時視圖會更新
3.1.1 刪除數(shù)據(jù)
import { reactive } from 'vue'
export default {
name: 'App',
setup() {
let { state, remStu } = useRemoveStudent()
return { state, remStu }
}
}
function useRemoveStudent() {
let state = reactive({
stus:[
{id:1, name:'zs', age:10},
{id:2, name:'ls', age:20},
{id:3, name:'ww', age:30}
]
})
function remStu(index) {
state.stus = state.stus.filter((stu, idx) => idx !== index)
}
return {state, remStu}
}
<ul>
<li v-for="(stu, index) in state.stus"
:key="stu.id"
@click="remStu(index)">
{{stu.name}} - {{stu.age}}
</li>
</ul>
3.1.2 新增數(shù)據(jù)
import { reactive } from 'vue'
export default {
name: 'App',
setup() {
let {state, remStu} = useRemoveStudent();
let {state2, addStu} = useAddStudent(state);
return {state, remStu, state2, addStu}
}
}
function useAddStudent(state) {
let state2 = reactive({
stu:{
id:'',
name:'',
age:''
}
})
function addStu() {
const stu = Object.assign({}, state2.stu)
state.stus.push(stu)
state2.stu.id = ''
state2.stu.name = ''
state2.stu.age = ''
}
return {state2, addStu}
}
<form>
<input type="text" v-model="state2.stu.id">
<input type="text" v-model="state2.stu.name">
<input type="text" v-model="state2.stu.age">
<input type="submit" @click.prevent="addStu">
</form>
Tree-shaking Global API
Tree-shaking的本質(zhì)是消除無用的js代碼永丝。無用代碼消除在廣泛存在于傳統(tǒng)的編程語言編譯器中锹漱,編譯器可以判斷出某些代碼根本不影響輸出,然后消除這些代碼慕嚷,這個稱之為DCE(dead code elimination)哥牍。
Tree-shaking 是 DCE 的一種新的實現(xiàn),Javascript同傳統(tǒng)的編程語言不同的是闯冷,javascript絕大多數(shù)情況需要通過網(wǎng)絡(luò)進行加載砂心,然后執(zhí)行,加載的文件大小越小蛇耀,整體執(zhí)行時間更短辩诞,所以去除無用代碼以減少文件體積,對javascript來說更有意義纺涤。
Tree-shaking 和傳統(tǒng)的 DCE的方法又不太一樣译暂,傳統(tǒng)的DCE 消滅不可能執(zhí)行的代碼,而Tree-shaking 更關(guān)注宇消除沒有用到的代碼撩炊。
在 Vue2.x 的版本中外永,在 Vue.prototype 定義了全局 API,如下:這會導(dǎo)致 Vue 庫的整體體積較大拧咳,部署生產(chǎn)時即使未用到的 API 伯顶,也會被打包。
import Vue from 'vue'
Vue.nextTick(() => {})
const obj = Vue.observable({})
Vue 3.0 在平衡功能和包體積大小做了一定的努力,力圖在 Vue 包做到更新并且不限制其功能祭衩。Vue 3.0 中使用了 組合式 API灶体,通過 ES Modules 的靜態(tài)分析的設(shè)計,與現(xiàn)代的 捆綁器 webpack 和 rollup 相結(jié)合掐暮,Tree-shaking 中剔除了那些未在項目使用卻導(dǎo)出 ES Module API蝎抽,如此,用戶只會使用到那些 import 的 API.
需要注意的是: 在使用模塊打包器構(gòu)建 ES Module 包時可以 Tree-shaking路克,在打包 UMD 構(gòu)建包時還是會全局打包 API 至 Vue 全局變量中.
teleport 傳送
<teleport> 組件的實現(xiàn)動機時樟结,解決了組件樹的一個弱點, 能夠?qū)⒔M件中的模板遷移帶 DOM 的其他位置精算,在沒有我們的DOM樹的情況下瓢宦,將其從深層嵌套的位置中分解出來。
example:
<body>
<div id="app">
<h1>Move the #content with the portal component</h1>
<teleport to="#endofbody">
<div id="content">
<p>
this will be moved to #endofbody.<br />
Pretend that it's a modal
</p>
<Child />
</div>
</teleport>
</div>
<div id="endofbody"></div>
<script>
new Vue({
el: "#app",
components: {
Child: { template: "<div>Placeholder</div>" }
}
});
</script>
</body>
result:
<div id="app">
<!-- -->
</div>
<div id="endofbody">
<div id="content">
<p>
this will be moved to #endofbody.<br />
Pretend that it's a modal
</p>
<div>Placeholder</div>
</div>
</div>
渲染函數(shù)
const app = Vue.createApp({})
app.component('anchored-heading', {
render() {
const { h } = Vue
return h(
'h' + this.level, // tag name
{}, // props/attributes
this.$slots.default() // array of children
)
},
props: {
level: {
type: Number,
required: true
}
}
})
遷移升級v3 地址
注意:vue3.0兼容vue2.0 vue版本升級之后不需要更改關(guān)于vue部分的代碼殖妇,但是項目中使用的相關(guān)的插件就不一定了~
醬醬醬~目前vue3.0的新特性就這些刁笙,后期有更新的話,繼續(xù)補充