注册全局和局部组件

我们已通过 Vue.component()、普通 JavaScript 对象或单文件组件引擎创建了众多组件。其中部分属于全局组件,另一些则是局部组件。例如,前文在 /components/ 目录中重构的所有组件都属于局部组件,而 "什么是组件?" 章节创建的组件则属于全局组件。无论全局还是局部组件,使用时都必须进行注册——有些在创建时即完成注册,有些则需要手动注册。

在接下来的章节中,您将学习:

  • 如何进行全局注册与局部注册

  • 两种注册方式对应用的影响差异

  • Vue 组件的注册机制(而非组件传递方式)

我们将重点探讨 Vue 组件的注册体系,而非组件间的数据传递机制。值得注意的是,全局组件通过 Vue.component() 方法注册后可在任意 Vue 实例中使用,而局部组件则需通过 components 选项在特定父组件中显式注册才能使用。这种设计既保证了常用组件的全局可用性,又能有效控制专用组件的使用范围。

在 Vue 中注册全局组件

全局组件,顾名思义,可以在整个应用中全局使用。它们通过 Vue.component() 方法进行全局注册:

Vue.component('my-component-name', { ... })

全局组件必须在根 Vue 实例实例化之前完成注册。注册后,它们就可以在根 Vue 实例的模板中使用,例如:

Vue.component('component-x', { ... })
Vue.component('component-y', { ... })
Vue.component('component-z', { ... })

new Vue({ el: '#app' })
<div id="app">
  <component-x></component-x>
  <component-y></component-y>
  <component-z></component-z>
</div>

可以看到,全局组件的注册非常简单——您甚至可能在创建时都没有意识到注册过程。我们稍后将在 "在 Nuxt 中注册全局组件" 章节探讨这种注册方式在 Nuxt 中的应用。不过现在,让我们先学习如何注册局部组件。

在 Vue/Nuxt 中注册局部组件

我们在本章的 VueNuxt 应用中已经接触并使用过局部组件。这些组件通过普通 JavaScript 对象创建:

var ComponentX = { ... }
var ComponentY = { ... }
var ComponentZ = { ... }

然后通过 components 选项进行注册:

new Vue({
  el: '#app',
  components: {
    'component-x': ComponentX,
    'component-y': ComponentY,
    'component-z': ComponentZ
  }
})

还记得在 "使用 props 向子组件传递数据" 章节开头创建的 Vue 应用吗?(位于本书 GitHub 仓库的 /chapter-5/vue/component/basic.html 文件)。该应用中的 user-item 组件是个全局组件,现在让我们将其重构为局部组件:

  1. 移除以下全局组件声明:

    Vue.component('user-item', {
      props: ['user'],
      template: '<li>{{ user.name }}</li>'
    })
  2. 替换为局部组件:

    const UserItem = {
      props: ['user'],
      template: '<li>{{ user.name }}</li>'
    }
  3. 通过 components 选项注册局部组件:

    new Vue({
      el: '#app',
      data: {
        users: [
          { id: 0, name: 'John Doe' },
          //...
        ]
      },
      components: {
        'user-item': UserItem
      }
    })

应用功能保持不变,唯一区别是 user-item 组件不再全局可用。这意味着它不能在其他子组件中使用。例如,若要让 ComponentXComponentZ 中可用,需要手动 "附加":

var ComponentX = { ... }

var ComponentZ = {
  components: {
    'component-x': ComponentX
  }
}

如果使用 babelwebpack 编写 ES2015 模块,可以将 ComponentX 作为单文件组件导入:

// components/ComponentZ.vue
import ComponentX from './ComponentX.vue'

export default {
  components: {
    'component-x': ComponentX
  }
}

<component-x
  v-for="item in items"
  ...
></component-x>

还可以省略 components 选项中的 component-x 键名,直接使用 ComponentX 变量:

// components/ComponentZ.vue
export default {
  components: {
    ComponentX
  }
}

ES2015+ 中,这种写法等价于 ComponentX: ComponentX。由于 component-x 未注册,模板中需要使用 <ComponentX> 而非 <component-x>

<ComponentX
  v-for="item in items"
  ...
></ComponentX>

上述单文件组件的 ES2015 写法与我们在 Nuxt 中编写 .vue 文件的方式完全一致。至此您应该意识到,我们在 Nuxt 应用中(如 /components/copyright.vue/components/nav.vue)编写的都是局部组件。那么在 Nuxt 中如何编写全局组件呢?这就需要用到 /plugins/ 目录了,我们将在下一节详细探讨。

您可以在本书 GitHub 仓库的 /chapter-5/vue/component/registering-local-components.html 找到这个示例应用。

在 Nuxt 中注册全局组件

我们在第 2 章《Nuxt 入门》中学习过目录结构,其中 /plugins/ 目录可用于存放需要在根 Vue 应用实例化之前运行的 JavaScript 文件。因此,这里正是注册全局组件的最佳位置。

让我们创建第一个全局组件:

  1. /plugins/ 目录创建简单 Vue 组件:

    <!-- components/global/sample-1.vue -->
    <template>
      <p>{{ message }}</p>
    </template>
    <script>
    export default {
      data () {
        return {
          message: 'A message from sample global component 1.'
        }
      }
    }
    </script>
  2. /plugins/ 目录创建 .js 文件并导入组件:

    // plugins/global-components.js
    import Vue from 'vue'
    import Sample from '~/components/global/sample-1.vue'
    Vue.component('sample-1', Sample)
  3. 也可以在 /plugins/global-components.js 中直接创建第二个全局组件:

    Vue.component('sample-2', {
      render (createElement) {
        return createElement('p', 'A message from sample global component 2.')
      }
    })
  4. Nuxt 配置文件中设置这些插件优先运行:

    // nuxt.config.js
    plugins: [
      '~/plugins/global-components.js',
    ]

    请注意,这个组件将在你的 Nuxt 应用的客户端和服务器端都可用。如果你只想在特定的端运行这个组件,例如只在客户端,那么你可以像下面这样注册它:

    // 仅客户端
    { src: '~/plugins/global-components.js', mode: 'client' }
    // 仅服务端
    { src: '~/plugins/global-components.js', mode: 'server' }
  5. 全局组件无需重复导入即可随处使用:

    <!-- pages/about.vue -->
    <sample-1 />
    <sample-2 />
  6. 运行应用后将显示:

    <p>A message from sample global component 1.</p>
    <p>A message from sample global component 2.</p>

就这样!这就是你如何在 Nuxt 中通过涉及各种文件来注册全局组件的方法。全局注册的本质是使用 Vue.component,就像我们在 Vue 应用中做的那样。然而,全局注册通常不是理想的选择,就像它的 “近亲” ——全局混入(我们将在下一节中介绍)一样。例如,在大多数情况下不需要全局注册组件对于服务器端和客户端来说都是不必要的。现在,让我们继续看看什么是混入以及如何编写它们。

您可以在本书 GitHub 仓库的 /chapter-5/nuxt-universal/global-components/ 目录找到此示例。接下来,我们将探讨混入(mixins)的概念与实现方法。