目录
- 不兼容的变更之组件与渲染函数
- 1、只能使用普通函数创建函数式组件
- 2、异步组件
- 3、 组件内的emits选项
- 4、渲染函数 API
不兼容的变更之组件与渲染函数
1、只能使用普通函数创建函数式组件
在 3.x 中,2.x 带来的函数式组件的性能提升可以忽略不计,因此我们建议只使用有状态的组件
函数式组件只能由接收 props 和 context (即:slots、attrs、emit) 的普通函数创建
非兼容:functional attribute 已从单文件组件 (SFC) 的 中移除
非兼容:{ functional: true } 选项已从通过函数创建的组件中移除
2.x 语法
// Vue 2 函数式组件示例
export default {
functional: true,
props: ['level'],
render(h, { props, data, children }) {
return h(`h${props.level}`, data, children)
}
}
3.x语法
现在,在 Vue 3 中,所有的函数式组件都是用普通函数创建的。换句话说,不需要定义 { functional: true } 组件选项。
它们将接收两个参数:props 和 context。context 参数是一个对象,包含组件的 attrs、slots 和 emit property。
此外,h 现在是全局导入的,而不是在 render 函数中隐式提供。
import { h } from 'vue'
const DynamicHeading = (props, context) => {
return h(`h${props.level}`, context.attrs, context.slots)
}
DynamicHeading.props = ['level']
export default DynamicHeading
2、异步组件
新的 defineAsyncComponent 助手方法,用于显式地定义异步组件
component 选项被重命名为 loader
Loader 函数本身不再接收 resolve 和 reject 参数,且必须返回一个 Promise
2.x 语法
const asyncModal = () => import('./Modal.vue')
** 或者带有选项
const asyncModal = {
component: () => import('./Modal.vue'),
delay: 200,
timeout: 3000,
error: ErrorComponent,
loading: LoadingComponent
}
3.x语法
import { defineAsyncComponent } from 'vue'
import ErrorComponent from './components/ErrorComponent.vue'
import LoadingComponent from './components/LoadingComponent.vue'
// 不带选项的异步组件
const asyncModal = defineAsyncComponent(() => import('./Modal.vue'))
// 带选项的异步组件
const asyncModalWithOptions = defineAsyncComponent({
loader: () => import('./Modal.vue'),
delay: 200,
timeout: 3000,
errorComponent: ErrorComponent,
loadingComponent: LoadingComponent
})
// () =>
// new Promise((resolve, reject) => {
// })
Vue Router 支持一个类似的机制来异步加载路由组件,也就是俗称的懒加载。尽管类似,但是这个功能和 Vue 所支持的异步组件是不同的。当用 Vue Router 配置路由组件时,你不应该使用 defineAsyncComponent。
关于路由懒加载的传送门
3、 组件内的emits选项
Vue 3 现在提供一个 emits 选项,和现有的 props 选项类似。这个选项可以用来定义一个组件可以向其父组件触发的事件。
<template>
<div>
<p>{{ text }}</p>
<button v-on:click="$emit('accepted')">OK</button>
</div>
</template>
<script>
export default {
props: ['text'],
emits: ['accepted']
}
</script>
强烈建议使用 emits 记录每个组件所触发的所有事件。因为移除了 .native 修饰符。任何未在 emits 中声明的事件监听器都会被算入组件的 $attrs,并将默认绑定到组件的根节点上。
这会导致一些原生事件触发两次,一次是在根元素上的原生事件,一次是组件内部的emit。
4、渲染函数 API
- 渲染函数参数
** 在 2.x 中,render 函数会自动接收 h 函数 (它是 createElement 的惯用别名) 作为参数:
export default {
render(h) {
return h('div')
}
}
** 在 3.x 中,h 函数现在是全局导入的,而不是作为参数自动传递。
import { h } from 'vue'
export default {
render() {
return h('div')
}
}
- 渲染函数签名更改
** 在 2.x 中,render 函数自动接收参数,如 h 函数。
export default {
render(h) {
return h('div')
}
}
** 在 3.x 中,由于 render 函数不再接收任何参数,它将主要在 setup() 函数内部使用。
** 这还有一个好处:可以访问在作用域中声明的响应式状态和函数,以及传递给 setup() 的参数。
import { h, reactive } from 'vue'
export default {
setup(props, { slots, attrs, emit }) {
const state = reactive({
count: 0
})
function increment() {
state.count++
}
// 返回渲染函数
return () =>
h(
'div',
{
onClick: increment
},
state.count
)
}
}
- VNode Prop 格式化
** 在 2.x 中,domProps 包含 VNode prop 中的嵌套列表:
{
staticClass: 'button',
class: { 'is-outlined': isOutlined },
staticStyle: { color: '#34495E' },
style: { backgroundColor: buttonColor },
attrs: { id: 'submit' },
domProps: { innerHTML: '' },
on: { click: submitForm },
key: 'submit-button'
}
** 在 3.x 中,整个 VNode prop 的结构都是扁平的。
{
class: ['button', { 'is-outlined': isOutlined }],
style: [{ color: '#34495E' }, { backgroundColor: buttonColor }],
id: 'submit',
innerHTML: '',
onClick: submitForm,
key: 'submit-button'
}
- 注册组件
** 在 2.x 中,注册一个组件后,把组件名作为字符串传递给渲染函数的第一个参数,它可以正常地工作:
export default {
render(h) {
return h('button-counter')
}
}
** 在 3.x 中,由于 VNode 是上下文无关的,不能再用字符串 ID 隐式查找已注册组件。
** 取而代之的是,需要使用一个导入的 resolveComponent 方法:
import { h, resolveComponent } from 'vue'
export default {
setup() {
const ButtonCounter = resolveComponent('button-counter')
return () => h(ButtonCounter)
}
}