Skip to content

异步函数式API变更

概要

  • 函数式组件必须写成纯函数形式
    • 移除{ functional: true }选项
    • 不再支持<template functional>
  • 异步函数式组件必须通过指定的API创建

基本示例

js
import {h} from 'vue'

const FunctionalComp = props => {
    return h('div', `Hello! ${props.name}`)
}
js
import {defineAsyncComponent} from 'vue'

const AsyncComp = defineAsyncComponent(() => import('./Foo.vue'))

动机

简化函数式组件

在2.x中,函数式组件必须通过以下格式创建:

js
const FunctionalComp = {
    functional: true,
    render(h) {
        return h('div', `Hello! ${props.name}`)
    }
}

这会有以下问题:

  • 当组件不需要任何选项,仅需要render函数时,也必须传递functional: true
  • 一些选项支持(propsinject),但是另外一些不支持(components )。但是,用户经常认为所有的选项都会支持,因为目前的函数式组件看起来像普通有状态的组件(尤其是当他们在SFC中使用<template functional> 时)

另外一方面的原因是,我们注意到用户使用函数式组件仅仅是因为它的性能。例如:在SFC中使用<template functional> 时,请求实现者在组件中支持更多有状态的选项。但是,我并不认为这是我们需要花时间研究的事情。

在v3中,有状态组件和无状态组件之间的性能差异将会变得更小,而且在一些用例中没有任何差别。因此,不再有仅仅为了性能而使用函数式组件的强烈动机,这也证明支持 <template functional> 的维护成本是不合理的。在v3中的函数式组件应该出于使用简单的目的去使用,而不是性能。

详细设计

在3.x中,我们有意仅仅通过纯函数支持函数式组件:

js
import {h} from 'vue'

const FunctionalComp = (props, {slots, attrs, emit}) => {
    return h('div', `Hello! ${props.name}`)
}
  • 移除functional选项,不再支持对象格式的{ functional: true }
  • SFCs不再支持<template functional>-如果你想要使用组件中的任意选项而不单是一个函数,那么请使用普通组件。
  • 函数入参发生改变:
    • h现在通过全局导出
    • 函数接收两个参数:props和一个暴露出slots, attrsemit属性的上下文对象。等价于有状态组件中的带$属性的等价物。

与旧语法之间的比较

新的参数列表需要完全具备可替换当前函数式组件参数的能力:

  • propsslots值与旧语法保持一;
  • datachildren不再是必须的(使用props和slot即可);
  • listeners将会被包含在attrs中;
  • injections将会被新APIinject替换(组合式API的一部分):
js
import {inject} from 'vue'
import {themeSymbol} from './ThemeProvider'

const FunctionalComp = props => {
    const theme = inject(themeSymbol)
    return h('div', `Using theme ${theme}`)
}
  • 将会移除parent的访问权限。这是一些内部用例的逃生舱 - 在用户侧,props和injections应该是首选。

可选的props声明

为了在简单用例中使用更加方便,在3.x中函数式组件中,props的声明不再是必须的:

js
const Foo = props => h('div', props.msg)
js
<Foo msg="hello!"/>

不再需要明确的(explicit)props声明,第一个参数props将会包含所有通过父组件传递的属性。

明确的props声明

需要明确的props声明时,可以将props与函数本身联系起来:

js
const FunctionalComp = props => {
    return h('div', `Hello! ${props.name}`)
}

FunctionalComp.props = {
    name: String
}

异步组件创建

还在被讨论中(译者注:在本RFC实现的同时,也在讨论异步组件创建的PR,并且在翻译本文之前已经实现)。

缺点

  • 迁移成本

可替代方案

N/A

采取的策略

  • 对于函数式组件,可以提供逐个迁移的兼容模式。
  • 使用<template functional>的SFC应该被转换为普通SFC。