异步组件(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
...
...
// vue3.x
...
...
v-for
vue2.x
v-for
指令可以绑定数组的数据来渲染列表
{{ item.message }}
vue3.x
从单个绑定获取多个ref
,ref
会通过迭代的key
被设置(新特性)
自定义元素交互
vue2.x
通过Vue.config.ignoredElements
配置自定义元素白名单
Vue.config.ignoredElements = ['plastic-button']
vue3.x
在模板编译期间执行指示编译器将
视为自定义元素
- 如果使用生成步骤:将
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
字符串文本
Data选项
vue2.x
可以自定义data选项是object
或function
// object 声明
// function 声明
vue3.x
data选项只接受返回object
的function
全局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
YES
NO
vue3.x
vue会自动生成唯一的key
YES
NO
按键修饰符
vue2.x
- 支持keyCodes作为修改
v-on
的方法 - 可以通过全局config.keyCodes
vue3.x
- 不再支持使用数字 (即键码) 作为
v-on
修饰符,建议使用kebab-cased大小写名称 - 不再支持 config.keyCodes
在 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
组件相关属性名也发生了变化:
leave-class
已经被重命名为leave-from-class
(在渲染函数或 JSX 中可以写为:leaveFromClass
)
enter-class
已经被重命名为enter-from-class
(在渲染函数或 JSX 中可以写为:enterFromClass
)
v-model
vue2.x
- 使用
v-model
指令必须使用value
的prop
;
- 如果出于不同的目的使用其他的
prop
,需要使用v-bind.sync
vue3.x
- 如果要改变绑定的属性名,而不是更改组件内绑定的选项,只需要给 v-model 传递一个参数就可以了;
- 可以自定义多个v-model;
- 支持自定义修饰符
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
中的绑定
// 结果
vue3.x
如果一个元素同时定义了v-bind="object"
和一个相同的单独的property
,那么声明绑定的顺序决定了它们如何合并
// 结果
// 结果
函数式组件
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
- 单文件组件
function
attribute 在中移除
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页面中 |