Logo
Section 1 / 3

无渲染组件与继承

探索 Vue 的无渲染组件和继承机制。

导入

无渲染组件与继承

探索 Vue 的无渲染组件和继承机制。

组件并不一定需要模板。不需要模板在某些情况下非常有用。例如,我们可能需要一个简单的验证器组件,用于为所有表单字段添加监听器,但又不希望为其额外包裹一个 <div>。或者,我们可能有一个抽象的表单字段定义,它无法用单一模板渲染,但却提供了一些方法和数据。又或者,我们只是想将某些逻辑抽象出来。

标准组件与无渲染组件之间的关键区别在于:无渲染组件没有模板,也没有样式。虽然 .vue 文件可以包含这些内容,但无渲染组件仅提供脚本部分,并且不需要是 .vue 文件,一个 .js 文件也可以是一个 Vue 组件。可以说,无渲染组件是组件最基础的形式,而普通的 .vue 组件则是功能更丰富的增强版本。

定义无渲染组件

无渲染组件在一个单独的 .js 文件中定义。最基础的形式如下:

1export default {}

一个 Vue 组件本质上就是一个导出的、简单的空 JS 对象。其余部分都是可选的。它具备我们所熟知的所有 Vue 组件特性:可以有 props(稍后会讲到)、计算属性、数据,可以使用生命周期钩子,并拥有状态。

注意:它也可以继承其他组件。如果父组件(在继承链中)提供了模板,那么无渲染组件也会继承该模板,这意味着它不再保持“无渲染”状态。如果父组件本身也是无渲染组件,那么子组件仍然保持无渲染。

使用无渲染组件来继承功能

一个组件可以扩展另一个组件,从而继承其全部内容,包括模板。要扩展一个组件,只需在子组件中添加 extends 键。

1<template> 2 <div><!-- --></div> 3</template> 4 5<script> 6import Parent from './Parent.vue' 7 8export default { 9 extends: Parent 10} 11</script>

当子组件中也定义了相同的内容时,这些内容会覆盖父组件中的对应项;而有些内容则不会被覆盖。让我们通过实践来练习继承。我们准备了一个小应用,其中包含三个表单控件组件——一个简单的文本输入框、一个复选框和一个文本区域。请查看它们,并尝试将共通逻辑抽象到无渲染组件 components/FormControl.js 中。

No configuration found for ID: 69edeb8aaae608df68e791663ef26dcf3b9689d33b0f43afceb7e5dc6102bc49

一个重要点是:组件可以继承 props、data 和方法。

继承与生命周期钩子

当我们讨论继承时,通常遵循覆盖原则——这是面向对象编程(OOP)的标准规则。无论是 Java、PHP、C#、Smalltalk 还是 TypeScript,都是一样的:子组件中定义的内容会自动覆盖父组件中的同名内容。

然而,生命周期钩子却打破了这一规则。它们是堆叠的。让我们研究一下在继承链中钩子的行为。我们已经准备了一个包含三个组件的小应用——parent、child 和 grandchild,继承关系已建立。请尝试实验这些钩子。它们按什么顺序被调用?如果是 before* 类型(如 beforeDestroy),与 *ed 类型(如 mounted)的顺序是否不同?

No configuration found for ID: 1b12b6e7af94b4c7d51c3f893f61033552ebdfe2e9000d56b2d36c2ded7123f9

哪些组件应该被继承?

组合优于继承(这是标准的 OOP 原则)同样适用于 Vue。只要发现某些功能可以逻辑上进行抽象,我们就应创建一个抽象的无渲染版本并从中派生——但前提是,如果抽象带来的复杂性过高,我们可能就不应这样做。

过早优化也可能带来问题。如果我们预见到未来会有许多非常相似的组件,就提前进行抽象,但尚未确认这些组件是否真的会被使用,这就会引入不必要的复杂性。

如果多个组件之间共享某些功能,而这些组件本身差异很大(比如获取用户数据的方法),我们或许更适合使用 mixin。

在模板中使用无渲染组件

我们可以在模板中使用任何组件,包括无渲染组件。它们预先为我们实现了很多功能,因此我们可以高效地用它们来封装其他组件的功能,使逻辑更容易抽象。

无渲染组件提供了一个默认插槽,可用于向嵌套在其中的组件传递数据。从技术上讲,这使得组件不再完全“无渲染”,但它仍没有模板,且仍然是一个 .js 文件。

首先,定义一个无渲染组件,并为其添加一个 render 函数。在其中,我们可以使用 $slots 来定义数据。然后,通过 v-slot 属性访问这些数据。我们可以在下面的应用中亲自尝试。接下来,尝试更复杂的结构,比如嵌套多个无渲染组件,观察它们的行为。

No configuration found for ID: e3544c9da921874813b0e62cc20180f9930eba6c5221d41b268524bb52e2cb4f

继承并非总是最佳选择。有时逻辑是共享的,但组件之间差异太大,无法抽象成有意义的单元。在这种情况下,也许使用 mixin,甚至完全解耦的服务类单元,才是存放逻辑的最佳位置。

生命周期钩子

混入(Mixins)