異步組件(vue3.x新增)
vue3.x
- 由于函數(shù)式組件被定義為純函數(shù)界斜,因此異步組件的定義需要通過(guò)將其包裝在新的
defineAsyncComponent
助手方法中來(lái)顯式地定義
import { defineAsyncComponent } from 'vue'
import ErrorComponent from './components/ErrorComponent.vue'
import LoadingComponent from './components/LoadingComponent.vue'
// 不帶選項(xiàng)的異步組件
const asyncPage = defineAsyncComponent(() => import('./NextPage.vue'))
// 帶選項(xiàng)的異步組件
const asyncPageWithOptions = defineAsyncComponent({
loader: () => import('./NextPage.vue'),
delay: 200,
timeout: 3000,
errorComponent: ErrorComponent,
loadingComponent: LoadingComponent
})
-
component
選項(xiàng)現(xiàn)在被重命名為loader
,loader
函數(shù)不再接收resolve
和reject
參數(shù),且必須返回promise
const asyncComponent = defineAsyncComponent(
()=>new Promise((resolve,reject)=>{
/*...*/
})
)
片段(vue3.x新增)
vue3.x
組件可以有多個(gè)根節(jié)點(diǎn)
// vue2.x
<template>
<div>
<header>...</header>
<main>...</main>
<footer>...</footer>
</div>
</template>
// vue3.x
<template>
<header>...</header>
<main v-bind="$attrs">...</main>
<footer>...</footer>
</template>
v-for
vue2.x
v-for
指令可以綁定數(shù)組的數(shù)據(jù)來(lái)渲染列表
<div v-for="item in items" :key="item.message">
{{ item.message }}
</div>
vue3.x
從單個(gè)綁定獲取多個(gè)ref
,ref
會(huì)通過(guò)迭代的key
被設(shè)置(新特性)
<div v-for="item in list" :ref="setItemRef"></div>
自定義元素交互
vue2.x
通過(guò)Vue.config.ignoredElements
配置自定義元素白名單
Vue.config.ignoredElements = ['plastic-button']
vue3.x
在模板編譯期間執(zhí)行指示編譯器將<plastic-button>
視為自定義元素
- 如果使用生成步驟:將
isCustomElement
傳遞給 Vue模板編譯器挎春,如果使用vue-loader
涯雅,則應(yīng)通過(guò) vue-loader 的compilerOptions
選項(xiàng)傳遞
rules: [
{
test: /\.vue$/,
use: 'vue-loader',
options: {
compilerOptions: {
isCustomElement: tag => tag === 'plastic-button'
}
}
}
]
- 如果使用動(dòng)態(tài)模板編譯揣钦,通過(guò)
app.config.isCustomElement
傳遞
const app = Vue.createApp({})
app.config.isCustomElement = tag => tag === 'plastic-button'
自定義內(nèi)置元素的方法是向內(nèi)置元素添加is
屬性
v-is
要使用注冊(cè)名稱來(lái)渲染組件计贰,其值應(yīng)為 JavaScript
字符串文本
<tr v-is="'blog-post-row'"></tr>
Data選項(xiàng)
vue2.x
可以自定義data選項(xiàng)是object
或function
// object 聲明
<script>
const app = new Vue({
data: {
num: '123'
}
})
</script>
// function 聲明
<script>
const app = new Vue({
data() {
return {
num: '123'
}
}
})
</script>
vue3.x
data選項(xiàng)只接受返回object
的function
<script>
import { createApp } from 'vue'
createApp({
data() {
return {
num: '123'
}
}
}).mount('#app')
</script>
全局API
vue2.x
有許多全局API和配置,會(huì)全局改變vue的行為
vue3.x
調(diào)用createApp
返回一個(gè)應(yīng)用實(shí)例
import {createApp} from 'vue'
const app = createApp({})
config.productionTip
移除
config.ignoredElements
替換為 config.isCustomElement
下表為2.x與3.x的對(duì)比
2.x 全局 API | 3.x 實(shí)例 API (app) |
---|---|
Vue.config | app.config |
Vue.config.productionTip | 已移除 |
Vue.config.ignoredElements | app.config.isCustomElement |
Vue.component | app.component |
Vue.directive | app.directive |
Vue.mixin | app.mixin |
Vue.use | app.use |
全局 API Treeshaking
vue2.x
Vue.nextTick()
是一個(gè)全局的 API 直接暴露在單個(gè) Vue 對(duì)象上,回調(diào)的this
上下文自動(dòng)綁定到當(dāng)前實(shí)例
webpack
支持tree-shaking,但Vue 2.x 的全局 API 比如 nextTick 無(wú)法被 TreeShake,所以就算沒(méi)有用到這些 API孔厉,它們還是會(huì)被打包到你的生產(chǎn)版本的代碼包里
vue3.x
全局和內(nèi)部API進(jìn)行了重構(gòu),支持使用tree-shaking
需要注意的是:
當(dāng)使用全局 API 時(shí)聂抢,需要主動(dòng)將其導(dǎo)入到目標(biāo)文件中
import { nextTick } from 'vue';
nextTick(() => {
// 和 DOM 有關(guān)的一些操作
});
如果直接調(diào)用Vue.nextTick()
钧嘶,會(huì)導(dǎo)致報(bào)錯(cuò):undefined is not a function
key attribute
vue2.x
建議在v-if
/v-else
/v-else-if
的分支中使用key
// vue2.x
<div v-if="hhhh" key = "yes">YES</div>
<div v-else key = "no">NO</div>
vue3.x
vue會(huì)自動(dòng)生成唯一的key
<div v-if="hhhh">YES</div>
<div v-else>NO</div>
按鍵修飾符
vue2.x
- 支持keyCodes作為修改
v-on
的方法 - 可以通過(guò)全局config.keyCodes
<!-- 鍵碼版本 -->
<input v-on:keyup.13="submit" />
<!-- 別名版本 -->
<input v-on:keyup.enter="submit" />
vue3.x
- 不再支持使用數(shù)字 (即鍵碼) 作為
v-on
修飾符,建議使用kebab-cased大小寫(xiě)名稱 - 不再支持 config.keyCodes
<input v-on:keyup.delete="confirmDelete" />
在 prop 的默認(rèn)函數(shù)中訪問(wèn)this
vue3.x
生成 prop 默認(rèn)值的工廠函數(shù)不再能訪問(wèn) this
渲染函數(shù)API
vue2.x
- render函數(shù)參數(shù)
render
函數(shù)自動(dòng)接收h
函數(shù)作為參數(shù)
// vue2.x
export default
render(h){
return h('div')
}
}
- render函數(shù)簽名更改
render
函數(shù)自動(dòng)接收諸如 h 之類的參數(shù)
// vue2.x
export default{
render(h){
return h('div')
}
}
vue3.x
- render函數(shù)參數(shù)
h
是全局引入的,而不是作為參數(shù)自動(dòng)傳遞
// vue 3.x
import {h} from 'vue'
export default{
render(){
return h('div')
}
}
- render函數(shù)簽名更改
render
函數(shù)不再接收任何參數(shù),將主要用于setup()
內(nèi)部,可以訪問(wèn)作用域中聲明的響應(yīng)式狀態(tài)和函數(shù)以及傳遞給setup()
的參數(shù)
import { h, reactive } from 'vue'
export default {
setup(props, { slots, attrs, emit }) {
const state = reactive({
count: 0
})
function increment() {
state.count++
}
// 返回render函數(shù)
return () =>
h(
'div',
{
onClick: increment
},
state.count
)
}
}
slot統(tǒng)一
vue2.x
在內(nèi)容節(jié)點(diǎn)上定義slot data property
h(LayoutComponent,[
h('div',{slot:'header'},this.header),
h('div',{slot:'content'},this.content)
])
// 引用時(shí)
this.$scopedSlots.header
vue3.x
- 插槽被定義為當(dāng)前節(jié)點(diǎn)的子對(duì)象
h(LayoutComponent,{},{
header:()=>h('div',this.header),
content:()=>h('div',this.content)
})
- 當(dāng)需要以編程方式引用作用域slot時(shí),被統(tǒng)一到
$slot
選項(xiàng)中
this.$slots.header
過(guò)渡類名更改
vue3.x
過(guò)渡類名 v-enter
修改為 v-enter-from
、過(guò)渡類名v-leave
修改為v-leave-from
<transition>
組件相關(guān)屬性名也發(fā)生了變化:
leave-class
已經(jīng)被重命名為leave-from-class
(在渲染函數(shù)或 JSX 中可以寫(xiě)為:leaveFromClass
)
enter-class
已經(jīng)被重命名為enter-from-class
(在渲染函數(shù)或 JSX 中可以寫(xiě)為:enterFromClass
)
v-model
vue2.x
- 使用
v-model
指令必須使用value
的prop
;
<ChildComponent v-model="pageTitle" />
- 如果出于不同的目的使用其他的
prop
,需要使用v-bind.sync
<ChildComponent :title.sync="pageTitle" />
vue3.x
- 如果要改變綁定的屬性名琳疏,而不是更改組件內(nèi)綁定的選項(xiàng),只需要給 v-model 傳遞一個(gè)參數(shù)就可以了;
<ChildComponent v-model:title = 'pageitle' />
- 可以自定義多個(gè)v-model;
<ChildComponent v-model:title="pageTitle" v-model:content="pageContent" />
- 支持自定義修飾符
<ChildComponent v-model.capitalize="pageTitle" />
v-if與v-for的優(yōu)先級(jí)比較
vue2.x
在同一個(gè)元素上同時(shí)使用v-if
和v-for
,v-for
會(huì)優(yōu)先使用;
vue3.x
v-if
總是優(yōu)先于v-for
生效
v-bind合并行為
vue2.x
如果一個(gè)元素同時(shí)定義了v-bind="object"
和一個(gè)相同的單獨(dú)的property
有决,那么這個(gè)單獨(dú)的 property
總是會(huì)覆蓋object
中的綁定
<div id="red" v-bind="{ id: 'blue' }"></div>
// 結(jié)果
<div id="red"></div>
vue3.x
如果一個(gè)元素同時(shí)定義了v-bind="object"
和一個(gè)相同的單獨(dú)的property
,那么聲明綁定的順序決定了它們?nèi)绾魏喜?/p>
<div id="red" v-bind="{ id: 'blue' }"></div>
// 結(jié)果
<div id="blue"></div>
<div v-bind="{ id: 'blue' }" id="red"></div>
// 結(jié)果
<div id="red"></div>
函數(shù)式組件
vue2.x
- 作為性能優(yōu)化
- 返回多個(gè)根節(jié)點(diǎn)
export default {
functional: true,
props: ['level'],
render(h, { props, data, children }) {
return h(`h${props.level}`, data, children)
}
}
vue3.x
- 通過(guò)函數(shù)創(chuàng)建組件
所有的函數(shù)式組件都是用普通函數(shù)創(chuàng)建的,不需要定義{function:true}
組件選項(xiàng)
import { h } from 'vue'
const DynamicHeading = (props, context) => {
return h(`h${props.level}`, context.attrs, context.slots)
}
DynamicHeading.props = ['level']
export default DynamicHeading
- 單文件組件
<template>
<component
v-bind:is="`h${$props.level}`"
v-bind="$attrs"
/>
</template>
<script>
export default {
props: ['level']
}
</script>
function
attribute 在<template>
中移除
listeners
現(xiàn)在作為$attrs
的一部分傳遞,可以刪除
下表為vue3.x已移除的api:
已移除的api | vue2.x | vue3.x |
---|---|---|
$children | 可以使用this.$children 直接訪問(wèn)當(dāng)前實(shí)例的子組件 |
$children 已移除,如需訪問(wèn)子組件,建議使用$refs
|
事件API | vue實(shí)例可用于觸發(fā)通過(guò)事件觸發(fā)API強(qiáng)制附加發(fā)處理程序已創(chuàng)建全局事件監(jiān)聽(tīng)器 | 移除了$on ,$off ,$once 方法,但仍可用$emit 觸發(fā)由父組件以聲明方式附加的事件處理程序 |
過(guò)濾器 | 可以使用過(guò)濾器來(lái)處理通用文本格式 | 過(guò)濾器已刪除,可以用方法調(diào)用或計(jì)算屬性替換過(guò)濾器 |
內(nèi)聯(lián)模塊Attribute | Vue 為子組件提供了inline-template attribute空盼,以便將其內(nèi)部?jī)?nèi)容用作模板书幕,而不是將其作為分發(fā)內(nèi)容 |
不再支持此功能,所有模板寫(xiě)在HTML頁(yè)面中 |