vue2升级vue3之组件新变化

210 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

函数式组件

在 Vue 2 中,函数式组件主要有两个应用场景:

  • 作为性能优化,因为它们的初始化速度比有状态组件快得多
  • 返回多个根节点

然而,在 Vue 3 中,有状态组件的性能已经提高到它们之间的区别可以忽略不计的程度。此外,有状态组件现在也支持返回多个根节点。

因此,函数式组件剩下的唯一应用场景就是简单组件,比如创建动态标题的组件。否则,建议你像平常一样使用有状态组件。

vue2.x语法

使用 <dynamic-heading> 组件,负责提供适当的标题 (即:h1h2h3 等等),在 2.x 中,这可以通过单文件组件编写:

 // Vue 2 函数式组件示例
 export default {
   functional: true,
   props: ['level'],
   render(h, { props, data, children }) {
     return h(`h${props.level}`, data, children)
   }
 }

或者,对于喜欢在单文件组件中使用 <template> 的用户:

 <!-- Vue 2 结合 <template> 的函数式组件示例 -->
 <template functional>
   <component
     :is="`h${props.level}`"
     v-bind="attrs"
     v-on="listeners"
   />
 </template>
 ​
 <script>
 export default {
   props: ['level']
 }
 </script>

需要在template上定义functional属性。

vue3.x语法

现在,在 Vue 3 中,所有的函数式组件都是用普通函数创建的。换句话说,不需要定义 { functional: true } 组件选项。

它们将接收两个参数:propscontextcontext 参数是一个对象,包含组件的 attrsslotsemit property。

此外,h 现在是全局导入的,而不是在 render 函数中隐式提供。

以前面提到的 <dynamic-heading> 组件为例,下面是它现在的样子。

 import { h } from 'vue'
 ​
 const DynamicHeading = (props, context) => {
   return h(`h${props.level}`, context.attrs, context.slots)
 }
 ​
 DynamicHeading.props = ['level']
 ​
 export default DynamicHeading

单文件组件(SFC)

在 3.x 中,有状态组件和函数式组件之间的性能差异已经大大减少,并且在大多数用例中是微不足道的。因此,在单文件组件上使用 functional 的开发者的迁移路径是删除该 attribute,并将 props 的所有引用重命名为 $props,以及将 attrs 重命名为 $attrs

 <template>
   <component
     v-bind:is="`h${$props.level}`"
     v-bind="$attrs"
   />
 </template>
 ​
 <script>
 export default {
   props: ['level']
 }
 </script>

异步组件

介绍

以前,异步组件是通过将组件定义为返回 Promise 的函数来创建的,例如:

 const asyncModal = () => import('./Modal.vue')

或者,对于带有选项的更高阶的组件语法:

 const asyncModal = {
   component: () => import('./Modal.vue'),
   delay: 200,
   timeout: 3000,
   error: ErrorComponent,
   loading: LoadingComponent
 }

3.x语法

现在,在 Vue 3 中,由于函数式组件被定义为纯函数,因此异步组件需要通过将其包裹在新的 defineAsyncComponent 助手方法中来显式地定义:

  • 新的 defineAsyncComponent 助手方法,用于显式地定义异步组件
  • component 选项被重命名为 loader
  • Loader 函数本身不再接收 resolvereject 参数,且必须返回一个 Promise
 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
 })

实例

 <script setup>
 import { defineAsyncComponent } from 'vue'
 ​
 const AdminPage = defineAsyncComponent(() =>
   import('./components/AdminPageComponent.vue')
 )
 </script>
 ​
 <template>
   <AdminPage />
 </template>

emits选项

Vue 3 现在提供一个 emits 选项,和现有的 props 选项类似。这个选项可以用来定义一个组件可以向其父组件触发的事件

vue2.x

在 Vue 2 中,你可以定义一个组件可接收的 prop,但是你无法声明它可以触发哪些事件:

 <template>
   <div>
     <p>{{ text }}</p>
     <button v-on:click="$emit('accepted')">OK</button>
   </div>
 </template>
 <script>
   export default {
     props: ['text']
   }
 </script>
 ​

vue3.x

和 prop 类似,现在可以通过 emits 选项来定义组件可触发的事件:

 <template>
   <div>
     <p>{{ text }}</p>
     <button v-on:click="$emit('accepted')">OK</button>
   </div>
 </template>
 <script>
   export default {
     props: ['text'],
     emits: ['accepted']
   }
 </script>

在setup中使用defineProps和defineEmits获取:

 <script setup>
 const props = defineProps({
   foo: String
 })
 ​
 const emit = defineEmits(['change', 'delete'])
 // setup 代码
 </script>