异步组件(vue3.x新增)
vue3.x
- 由于函数式组件被定义为纯函数,因此异步组件的定义需要通过将其包装在新的
defineAsyncComponent
助手方法中来显式地定义
import { defineAsyncComponent } from 'vue'
import ErrorComponent from './components/ErrorComponent.vue'
import LoadingComponent from './components/LoadingComponent.vue'
// 不带选项的异步组件
const asyncPage = defineAsyncComponent(() => import('./NextPage.vue'))
// 带选项的异步组件
const asyncPageWithOptions = defineAsyncComponent({
loader: () => import('./NextPage.vue'),
delay: 200,
timeout: 3000,
errorComponent: ErrorComponent,
loadingComponent: LoadingComponent
})
-
component
选项现在被重命名为loader
,loader
函数不再接收resolve
和reject
参数,且必须返回promise
const asyncComponent = defineAsyncComponent(
()=>new Promise((resolve,reject)=>{
/*...*/
})
)
片段(vue3.x新增)
vue3.x
组件可以有多个根节点
// 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
指令可以绑定数组的数据来渲染列表
<div v-for="item in items" :key="item.message">
{{ item.message }}
</div>
vue3.x
从单个绑定获取多个ref
,ref
会通过迭代的key
被设置(新特性)
<div v-for="item in list" :ref="setItemRef"></div>
自定义元素交互
vue2.x
通过Vue.config.ignoredElements
配置自定义元素白名单
Vue.config.ignoredElements = ['plastic-button']
vue3.x
在模板编译期间执行指示编译器将<plastic-button>
视为自定义元素
- 如果使用生成步骤:将
isCustomElement
传递给 Vue模板编译器,如果使用vue-loader
,则应通过 vue-loader 的compilerOptions
选项传递
rules: [
{
test: /\.vue$/,
use: 'vue-loader',
options: {
compilerOptions: {
isCustomElement: tag => tag === 'plastic-button'
}
}
}
]
- 如果使用动态模板编译,通过
app.config.isCustomElement
传递
const app = Vue.createApp({})
app.config.isCustomElement = tag => tag === 'plastic-button'
自定义内置元素的方法是向内置元素添加is
属性
v-is
要使用注册名称来渲染组件,其值应为 JavaScript
字符串文本
<tr v-is="'blog-post-row'"></tr>
Data选项
vue2.x
可以自定义data选项是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选项只接受返回object
的function
<script>
import { createApp } from 'vue'
createApp({
data() {
return {
num: '123'
}
}
}).mount('#app')
</script>
全局API
vue2.x
有许多全局API和配置,会全局改变vue的行为
vue3.x
调用createApp
返回一个应用实例
import {createApp} from 'vue'
const app = createApp({})
config.productionTip
移除
config.ignoredElements
替换为 config.isCustomElement
下表为2.x与3.x的对比
2.x 全局 API | 3.x 实例 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()
是一个全局的 API 直接暴露在单个 Vue 对象上,回调的this
上下文自动绑定到当前实例
webpack
支持tree-shaking,但Vue 2.x 的全局 API 比如 nextTick 无法被 TreeShake,所以就算没有用到这些 API,它们还是会被打包到你的生产版本的代码包里
vue3.x
全局和内部API进行了重构,支持使用tree-shaking
需要注意的是:
当使用全局 API 时,需要主动将其导入到目标文件中
import { nextTick } from 'vue';
nextTick(() => {
// 和 DOM 有关的一些操作
});
如果直接调用Vue.nextTick()
,会导致报错: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会自动生成唯一的key
<div v-if="hhhh">YES</div>
<div v-else>NO</div>
按键修饰符
vue2.x
- 支持keyCodes作为修改
v-on
的方法 - 可以通过全局config.keyCodes
<!-- 键码版本 -->
<input v-on:keyup.13="submit" />
<!-- 别名版本 -->
<input v-on:keyup.enter="submit" />
vue3.x
- 不再支持使用数字 (即键码) 作为
v-on
修饰符,建议使用kebab-cased大小写名称 - 不再支持 config.keyCodes
<input v-on:keyup.delete="confirmDelete" />
在 prop 的默认函数中访问this
vue3.x
生成 prop 默认值的工厂函数不再能访问 this
渲染函数API
vue2.x
- render函数参数
render
函数自动接收h
函数作为参数
// vue2.x
export default
render(h){
return h('div')
}
}
- render函数签名更改
render
函数自动接收诸如 h 之类的参数
// vue2.x
export default{
render(h){
return h('div')
}
}
vue3.x
- render函数参数
h
是全局引入的,而不是作为参数自动传递
// vue 3.x
import {h} from 'vue'
export default{
render(){
return h('div')
}
}
- render函数签名更改
render
函数不再接收任何参数,将主要用于setup()
内部,可以访问作用域中声明的响应式状态和函数以及传递给setup()
的参数
import { h, reactive } from 'vue'
export default {
setup(props, { slots, attrs, emit }) {
const state = reactive({
count: 0
})
function increment() {
state.count++
}
// 返回render函数
return () =>
h(
'div',
{
onClick: increment
},
state.count
)
}
}
slot统一
vue2.x
在内容节点上定义slot data property
h(LayoutComponent,[
h('div',{slot:'header'},this.header),
h('div',{slot:'content'},this.content)
])
// 引用时
this.$scopedSlots.header
vue3.x
- 插槽被定义为当前节点的子对象
h(LayoutComponent,{},{
header:()=>h('div',this.header),
content:()=>h('div',this.content)
})
- 当需要以编程方式引用作用域slot时,被统一到
$slot
选项中
this.$slots.header
过渡类名更改
vue3.x
过渡类名 v-enter
修改为 v-enter-from
、过渡类名v-leave
修改为v-leave-from
<transition>
组件相关属性名也发生了变化:
leave-class
已经被重命名为leave-from-class
(在渲染函数或 JSX 中可以写为:leaveFromClass
)
enter-class
已经被重命名为enter-from-class
(在渲染函数或 JSX 中可以写为:enterFromClass
)
v-model
vue2.x
- 使用
v-model
指令必须使用value
的prop
;
<ChildComponent v-model="pageTitle" />
- 如果出于不同的目的使用其他的
prop
,需要使用v-bind.sync
<ChildComponent :title.sync="pageTitle" />
vue3.x
- 如果要改变绑定的属性名,而不是更改组件内绑定的选项,只需要给 v-model 传递一个参数就可以了;
<ChildComponent v-model:title = 'pageitle' />
- 可以自定义多个v-model;
<ChildComponent v-model:title="pageTitle" v-model:content="pageContent" />
- 支持自定义修饰符
<ChildComponent v-model.capitalize="pageTitle" />
v-if与v-for的优先级比较
vue2.x
在同一个元素上同时使用v-if
和v-for
,v-for
会优先使用;
vue3.x
v-if
总是优先于v-for
生效
v-bind合并行为
vue2.x
如果一个元素同时定义了v-bind="object"
和一个相同的单独的property
,那么这个单独的 property
总是会覆盖object
中的绑定
<div id="red" v-bind="{ id: 'blue' }"></div>
// 结果
<div id="red"></div>
vue3.x
如果一个元素同时定义了v-bind="object"
和一个相同的单独的property
,那么声明绑定的顺序决定了它们如何合并
<div id="red" v-bind="{ id: 'blue' }"></div>
// 结果
<div id="blue"></div>
<div v-bind="{ id: 'blue' }" id="red"></div>
// 结果
<div id="red"></div>
函数式组件
vue2.x
- 作为性能优化
- 返回多个根节点
export default {
functional: true,
props: ['level'],
render(h, { props, data, children }) {
return h(`h${props.level}`, data, children)
}
}
vue3.x
- 通过函数创建组件
所有的函数式组件都是用普通函数创建的,不需要定义{function:true}
组件选项
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
现在作为$attrs
的一部分传递,可以删除
下表为vue3.x已移除的api:
已移除的api | vue2.x | vue3.x |
---|---|---|
$children | 可以使用this.$children 直接访问当前实例的子组件 |
$children 已移除,如需访问子组件,建议使用$refs
|
事件API | vue实例可用于触发通过事件触发API强制附加发处理程序已创建全局事件监听器 | 移除了$on ,$off ,$once 方法,但仍可用$emit 触发由父组件以声明方式附加的事件处理程序 |
过滤器 | 可以使用过滤器来处理通用文本格式 | 过滤器已删除,可以用方法调用或计算属性替换过滤器 |
内联模块Attribute | Vue 为子组件提供了inline-template attribute,以便将其内部内容用作模板,而不是将其作为分发内容 |
不再支持此功能,所有模板写在HTML页面中 |