Skip to content

组件

Vue 组件与 Web Components 规范的自定义元素非常类似。Vue 的组件设计 (如插槽 API) 在浏览器原生支持该规范前就部分受到了它的影响。两者之间主要的不同在于, Vue 组件的数据模型是作为框架的一部分而设计的,而该框架为构建复杂应用提供了很多必要的附加功能。例如响应式模板和状态管理——这两者都没有被该规范所覆盖。 Vue 也为创建和使用自定义元素提供了很好的支持。

定义组件

组件是对 UI 结构的一种的抽象。在 Vue 中,组件本质上是一个具有预定义选项的 JS 实例。一个基本的 Vue 组件选项包含 template(模板)和 data(数据)。

app.js:
js
// 定义组件
const baseComponents = {
  data() {
    return {
      message: "Hello Vue3"
    }
  },
  template: `<div><h1>{{message}}</h1></div>`
}
// 挂载组件
Vue.createApp(baseComponents).mount("#app")
index.html:
html
<html lang="en">
  <head>
    <!--Vue.js-->
    <script src="https://unpkg.com/vue@3.4.21/dist/vue.global.js"></script>
  </head>
  <body>
    <div id="app"></div>
    <script src="app.js"></script>
  </body>
</html>

template(模板)就是 UI 结构, HTML 布局和外观。可以使用 Vue 的模板语法来声明式地创建组件 UI。组件的 data 选项是一个函数,返回一个对象。data 对象的属性仅在实例首次创建时被添加,因此要确保所需数据都在 data 函数返回的对象中。必要时,要对尚未提供所需值的 property 使用 null、undefined 或其他占位的值。虽然也可以直接将不包含在 data 中的新 property 添加到组件实例中。但由于该 property 不在背后的响应式 $data 对象内,Vue 的响应性系统不会自动跟踪它。

根组件

传递给 createApp 的选项用于配置根组件。应该在 DOM 文档中显示地定义一个元素作为根组件渲染的起点。因此,根组件可以直接在 DOM 文档中定义组件的 template 属性。

app.js:
js
// 定义组件
const baseComponents = {
  data() {
    return {
      message: "Hello Vue3"
    }
  }
}
// 挂载组件
Vue.createApp(baseComponents).mount("#app")
index.html:
html
<html lang="en">
  <head>
    <!--Vue.js-->
    <script src="https://unpkg.com/vue@3.4.21/dist/vue.global.js"></script>
  </head>
  <body>
    <div id="app">
      <h1>{{message}}</h1>
    </div>
    <script src="app.js"></script>
  </body>
</html>

如果根组件的定义声明中与 DOM 文档中都有 template,Vue 会优先使用组件定义中的 template 属性作为渲染模板。

子组件

可以使用 components 属性在父组件中注册子组件。下面是利用组件的 components 属性重构 Hello Vue3 的例子。

常用组件属性

还有各种其他的组件属性,可以将用户定义的组件属性添加到组件实例中,例如 methods,computed,watch,props,inject 和 setup。组件实例的所有属性,无论如何定义,都可以在组件的模板中访问。

methods(方法)

methods 属性用向组件实例添加方法,它应该是一个包含所需方法的对象:

js
// 定义 HelloVue 组件
const HelloVue = {
  data() {
    return {
      message: "Hello Vue3",
      imgsrc: "assets/vuelogo.png",
      styleObject: {
        fontSize: "32px"
      }
    };
  },
  methods: {
    changeFontSize(e) {
      this.styleObject.fontSize = e.type === "mouseover" ? "48px" : "32px";
    }
  },
  template: `<div id="vueapp">
               <img :src="imgsrc" />
               <h1
                 @mouseover="changeFontSize"
                 @mouseout="changeFontSize"
                 :style="styleObject"
               >{{message}}</h1>
             </div>`
};

Vue 自动为 methods 绑定 this,并始终指向组件实例。在定义 methods 时应避免使用箭头函数,因为这会阻止 Vue 绑定恰当的 this 指向。

模板中,methods 属性内定义的方法通常被当做事件监听使用。在上面的例子中,鼠标移入移出时,会调用 changeFontSize 方法。也可以在模板支持 JavaScript 表达式的任何地方调用方法:

js
<span :title="toTitleDate(date)">
  {{ formatDate(date) }}
</span>

computed(计算属性)

页面最终显示的结果可能需要既有数据经过一些复杂的逻辑变换后才能得到,这时可以使用 computed 属性获取计算属性作为页面的最终呈现数据:

计算属性可以通过在表达式中调用方法来达到同样的效果:

index.html:
html
<h1>
  {{ computMessageProps() }}
</h1>
app.js:
js
methods: {
  computMessageProps() {
    let message = "Hello World";
    if (this.radiocheckedValue === "2") {
      message = "Hello Jquery";
    } else if (this.radiocheckedValue === "3") {
      message = "Hello Vue3";
    }
    return message;
  }
}
从最终结果来说,这两种实现方式确实是完全相同的。然而,不同的是计算属性将基于它们的响应依赖关系缓存。计算属性只会在相关响应式依赖发生改变时重新求值。相比之下,每当触发重新渲染时,调用方法将始终会再次执行函数。

watch(侦听器)

watch 属性提供了一个更通用的方法来响应数据的变化。当需要在数据变化时执行异步或开销较大的操作时,这个方式是最有用的。

组件间的通信

组件是独立可复用的,每个组件维持它自己的状态。实际的应用中,各个组件总是相互关联的,或共有一些状态,或共同操作同一状态等等。Vue 提供了一些 API 用于维护组件间的通信。

props

像在元素上注册 attribute 一样,父组件可以在子组件上注册一些自定义 attribute 并传值给该 attribute。子组件通过 props 属性获取父组件上注册的 attribute 并使其成为该组件实例中的一个数据属性。该数据属性的值可以在模板中访问,就像任何其他组件值一样。一个组件可以拥有任意数量的 prop,并且在默认情况下,无论任何值都可以传递给 prop。因此也可以传递一个方法给子组件,并在子组件中触发方法的调用来改变父组件的状态。

Provide / Inject

当组件的有一些深度嵌套的组件,而深层的子组件只需要父组件的部分内容。在这种情况下,如果仍然将 prop 沿着组件链逐级传递下去,可能会很麻烦。对于这种情况,可以使用一对 provide 和 inject。无论组件层次结构有多深,父组件都可以作为其所有子组件的依赖提供者。这个特性有两个部分:父组件有一个 provide 选项来提供数据,子组件有一个 inject 选项来开始使用这些数据。

默认情况下,provide/inject 绑定并不是响应式的。即父组件提供的数据变化后并不会反映到子组件上。我们可以通过传递一个 ref 属性、reactive 对象或者一个组合式 API computed 属性给 provide 来改变这种行为。

js
provide() {
  return {
    dataList: Vue.computed(() => this.dataList)
}

emits

emits 选项在组件上定义发出的事件。父级组件可以像处理原生 DOM 事件一样通过 v-on 或 @ 监听子组件实例的任意通过 emits 选项注册的自定义事件:

生命周期及钩子函数

每个组件在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。同时在这个过程中也会运行一些叫做生命周期钩子的函数,如 mounted、updated 和 unmounted。可以在实例生命周期的不同阶段被调用。

其他

Vue 组件还有很多其他的特性,比如插槽动态组件 & 异步组件模板引用等。具体请参照官方文档。