函数式组件

函数式组件是常规 Vue.js 组件的子集。它们没有状态或组件实例。它们可以被认为是传递 props 的渲染(render)函数(如本章前面所示)。

我们可以将组件标记为函数式的(functional),这意味着它们是无状态的(没有反应数据)和无实例的(没有 this 上下文)。

有关更多信息,请参阅 Vue.js 文档 ( https://vuejs.org/v2/guide/render-function.html#Functional-Components )。

函数式组件只能访问从其父组件传递的 props、children、slots 和作用域 slot。他们还会收到 parents 和 listeners 的引用。

以下是一个 Greet 组件(在 Greet.vue 文件中)。注意模板(template)中的 functional:

<template functional>
    <div>Functional Component: {{ props.greeting }} {{
    props.audience }}</div>
</template>

函数式组件必须通过 props.propName 访问 props。函数式组件也可以用 function: true 布尔值表示,并与渲染(render)函数一起使用:

export default {
    functional: true,
    render(h, context) {
        return h(
            'h2',
            `Functional Render: ${context.props.greeting}${context.props.audience}`
        )
    }
}

我们可以在 App.vue 文件中使用这两个函数式组件:

<template>
    <div id="app">
        <Greet greeting="Hello" audience="World" />
        <GreetRender greeting="Hello" audience="World" />
    </div>
</template>

<script>
import Greet from './components/Greet.vue'
import GreetRender from './components/GreetRender.js'

export default {
    components: {
        Greet,
        GreetRender
    }
}
</script>

这将向浏览器渲染以下内容:

image 2023 10 13 23 51 31 029
Figure 1. Figure 5.21: Functional component rendering

函数式组件是封装仅渲染功能的好方法。也就是说,他们从 props 中派生出模板。它们的性能比常规组件稍好,因为它们没有任何关联的反应状态或组件实例。

我们针对非函数式组件介绍的一个常见用例是发出事件,可以按如下方式完成:

<template>
    <input
        type="submit"
        @click="$emit('click', $event)"
    />
</template>

要使用函数式组件发出事件,我们可以将元素绑定到 listeners 对象中的属性。

要将所有事件委托给子级,我们可以使用 v-on="listeners":

<template functional>
    <input
        v-on="listeners"
        v-bind="data.attrs"
    />
</template>

要绑定特定的监听器,我们可以使用 v-on:eventName="listeners.listenerName",其中 listenerName 是函数式组件的父级绑定的监听器:

<template functional>
    <input
        type="submit"
        v-on:click="listeners.click"
        v-bind="data.attrs"
    />
</template>

绑定到不存在的侦听器属性将导致错误。为了避免这种情况,我们可以使用 listeners.listenerName || (() => {}) 表达式。

至此,我们学习了如何使用具有 .vue 组件模板变体和渲染函数的函数式组件来实现常见的 Vue.js 组件模式。

总结

Vue.js中的函数式组件是一种特殊类型的组件,通常用于那些只依赖于传递给它的props而不维护本地状态的简单组件。函数式组件在性能上具有一些优势,因为它们不需要实例化Vue组件,并且可以更轻量地渲染。

函数式组件的定义方式如下:

<template functional>
  <!-- 这里是组件的模板内容 -->
</template>

<script>
export default {
  functional: true,
  props: {
    // 这里定义组件的props
  },
  render(h, { props }) {
    // 这里编写函数式组件的渲染逻辑
  }
}
</script>

以下是有关函数式组件的一些重要概念:

  1. <template functional> 标签:函数式组件必须以 <template functional> 开头,以指示Vue这是一个函数式组件。

  2. functional: true:在组件的选项中,你需要将 functional 设置为 true,以告诉Vue这是一个函数式组件。

  3. props:你可以像常规组件一样定义props,以接收从父组件传递的数据。

  4. render 方法:函数式组件使用 render 方法来渲染内容。这个方法接受`h`(createElement函数)和一个上下文对象(包括props和其他属性)。你可以在`render` 方法中编写组件的渲染逻辑。

下面是一个示例,演示如何创建一个简单的函数式组件:

<template functional>
  <div>
    <h1>{{ props.title }}</h1>
    <p>{{ props.content }}</p>
  </div>
</template>

<script>
export default {
  functional: true,
  props: {
    title: String,
    content: String
  },
  render(h, { props }) {
    return (
      <div>
        <h1>{props.title}</h1>
        <p>{props.content}</p>
      </div>
    );
  }
}
</script>

这个函数式组件接受 titlecontent 两个props,并在渲染时直接使用它们。

函数式组件通常适用于不需要状态管理或生命周期方法的简单显示组件,因为它们不会维护本地状态,也不会有生命周期。函数式组件在性能上更高效,因为它们不需要Vue的实例化过程。

我们现在将构建一个待办事项应用程序,该应用程序使用我们在本章中讨论过的所有模式。

活动 5.01:使用插件和可重用组件构建 Vue.js 应用程序

在本活动中,我们将构建一个集成 jsonplaceholder 作为数据源的待办事项应用程序。

我们的待办事项应用程序将加载待办事项并将其显示为列表。它将根据待办事项是否已完成以及待办事项的名称显示一个复选框。

在核对待办事项时,应用程序会将其同步到应用程序接口(API)。

我们将注入 axios 作为插件来查询 https://jsonplaceholder.typicode.com

请按照以下步骤完成此活动:

  1. 将 axios 安装到项目中。

  2. 要将 axios 作为属性注入到此组件实例中,请创建一个 src/plugins/axios.js 插件文件,该文件在安装(install)时意味着组件实例具有 axios 属性。

  3. 要使插件正常工作,请将其导入并注册到 src/main.js 中。

  4. 我们还希望将 API 的 baseUrl 注入到所有组件中。我们将在 src/main.js 文件中创建一个内联插件来执行此操作。

  5. 我们现在想要从 src/App.vue 中获取所有待办事项。执行此操作的一个好地方是在 mounted 的生命周期方法中。

  6. 为了显示待办事项列表,我们将在 src/components/TodoList.vue 中创建一个 TodoList 函数式组件,该组件接收待办事项 prop,循环浏览项目,并将待办事项的渲染延迟到绑定了待办事项的待办事项范围槽下。

  7. 现在,我们可以使用 TodoList 组件来渲染我们已经在 src/App.vue 中获取的待办事项。

  8. 我们现在需要创建一个 TodoEntry 组件,我们将在其中实现大部分特定于待办事项的逻辑。对于组件来说,一个好的做法是让 props 非常具体地针对组件的角色。在本例中,我们要处理的 todo 对象的属性是 id、title 和 Completed,因此这些应该是我们的 TodoEntry 组件接收的 props。 我们不会将 TodoEntry 设为函数式组件,因为我们需要一个组件实例来创建 HTTP 请求。

  9. 然后,我们将更新 src/App.vue,以便它消耗 TodoEntry(确保绑定 id、title 和 completed)。

  10. 添加切换待办事项的完成(Complete)状态的功能。我们将在 src/components/TodoEntry.vue 中实现其中的大部分内容。我们将监听 input 更改事件。在更改时,我们需要读取新值并向 /todos/{todoId} 发送一个 PATCH 请求,其中包含一个包含已完成设置的新值的对象。我们还希望在 Vue.js 中发出 completedChange 事件,以便 App 组件可以更新内存中的数据。

  11. 在 App.vue 中,我们希望在触发 completeChange 时更新相关的待办事项。由于 completeChange 不包含待办事项的 ID,因此在设置 handleCompleteChange 函数以侦听 completeChange 时,我们需要从上下文中读取该 ID。

预期输出如下:

image 2023 10 14 09 09 08 982
Figure 2. Figure 5.22: Our to-do app using jsonplaceholder data

总结

在本章中,我们研究了全局组合模式和高级组件设置,它们可用于减少 Vue.js 应用程序中的重复。

首先,我们了解了 mixin,它显式地共享功能,同时让组件拥有最终发言权,并看到了此规则的例外情况。然后我们看到插件如何成为多个 Vue.js 原语的绝佳挂钩。

接下来,我们研究了规范模式如何最大化 Vue.js 中的组件可重用性。展示了一些想法,例如利用 props 来委托数据、插槽来委托模板,以及实现允许组件与 Vue 惯用速记方式(例如 v-model)一起使用的接口。

我们还深入研究了 .vue 文件之外的 Vue.js 组件。我们通过引入字符串模板、渲染函数和 JSX 以及每种工作方法的要求,深入研究了 Vue.js 组件是什么。 component 标签和 keep-alive 展示了另一种基于响应性数据在 Vue.js 应用程序中动态渲染组件的方法。最后,我们看到了函数式组件如何巩固我们如何使用 .vue 文件定义组件。

到目前为止,我们已经学习了如何使用组件、mixin 和插件来构建应用程序。为了构建跨多个页面的应用程序,我们需要实现路由。这就是我们将在下一章中解决的问题。