组件的注册

定义好组件后,需要注册才能使用。Vue 提供了两种注册方式:全局注册和局部注册。选择哪种方式,取决于组件的使用范围和项目规模。

全局注册

使用 Vue.component() 注册的组件是全局组件,可以在任何 Vue 实例的模板中使用。

基本用法

// 注册全局组件
Vue.component('my-component', {
  template: '<div>全局组件</div>'
})

// 创建 Vue 实例
new Vue({
  el: '#app'
})
<div id="app">
  <my-component></my-component>
</div>

全局可用

全局组件可以在任何地方使用,包括其他组件内部:

Vue.component('component-a', {
  template: '<div>组件 A</div>'
})

Vue.component('component-b', {
  template: `
    <div>
      <p>组件 B</p>
      <component-a></component-a>
    </div>
  `
})

new Vue({
  el: '#app',
  template: `
    <div>
      <component-a></component-a>
      <component-b></component-b>
    </div>
  `
})

适用场景

适合全局注册的组件:

  • 基础组件:按钮、输入框、图标等
  • 通用组件:弹窗、提示、加载动画等
  • 布局组件:头部、底部、侧边栏等

缺点

全局注册的问题:

  1. 打包体积大:即使不使用,也会被打包
  2. 命名冲突:多个开发者可能创建同名组件
  3. 难以维护:组件来源不明确

局部注册

使用组件的 components 选项注册局部组件,只能在当前组件中使用。

基本用法

// 定义组件
var ComponentA = {
  template: '<div>组件 A</div>'
}

var ComponentB = {
  template: '<div>组件 B</div>'
}

// 在 Vue 实例中局部注册
new Vue({
  el: '#app',
  components: {
    'component-a': ComponentA,
    'component-b': ComponentB
  }
})
<div id="app">
  <component-a></component-a>
  <component-b></component-b>
</div>

在组件中注册

var ComponentA = {
  template: '<div>组件 A</div>'
}

var ComponentB = {
  template: `
    <div>
      <p>组件 B</p>
      <component-a></component-a>
    </div>
  `,
  components: {
    'component-a': ComponentA
  }
}

ES6 模块化写法

实际项目中,通常使用 ES6 模块:

// ComponentA.js
export default {
  template: '<div>组件 A</div>'
}

// ComponentB.js
import ComponentA from './ComponentA.js'

export default {
  template: `
    <div>
      <p>组件 B</p>
      <component-a></component-a>
    </div>
  `,
  components: {
    ComponentA
  }
}

ES6 简写语法

当组件名与变量名相同时,可以简写:

components: {
  ComponentA  // 等同于 ComponentA: ComponentA
}

Vue 会自动将 ComponentA 转换为 component-a 使用。

适用场景

适合局部注册的组件:

  • 业务组件:用户列表、订单详情等
  • 页面组件:首页、关于页等
  • 特定功能组件:只在某处使用的组件

优点

局部注册的好处:

  1. 按需加载:不使用的组件不会被打包
  2. 依赖明确:组件关系清晰
  3. 避免冲突:不同作用域可以使用同名组件

全局 vs 局部对比

特性全局注册局部注册
使用范围所有 Vue 实例当前组件
打包体积全部打包按需打包
命名冲突容易冲突不会冲突
依赖关系不明确明确
适用组件基础、通用组件业务、页面组件

实战示例

基础组件全局注册

// utils/register-components.js
import Vue from 'vue'
import AppButton from './components/AppButton.vue'
import AppInput from './components/AppInput.vue'
import AppIcon from './components/AppIcon.vue'
import AppLoading from './components/AppLoading.vue'

// 批量注册基础组件
const baseComponents = {
  AppButton,
  AppInput,
  AppIcon,
  AppLoading
}

Object.keys(baseComponents).forEach(key => {
  Vue.component(key, baseComponents[key])
})
// main.js
import Vue from 'vue'
import './utils/register-components'
import App from './App.vue'

new Vue({
  render: h => h(App)
}).$mount('#app')

业务组件局部注册

<template>
  <div class="user-page">
    <user-header :user="user"></user-header>
    <user-profile :user="user"></user-profile>
    <user-posts :posts="posts"></user-posts>
  </div>
</template>

<script>
import UserHeader from './UserHeader.vue'
import UserProfile from './UserProfile.vue'
import UserPosts from './UserPosts.vue'

export default {
  components: {
    UserHeader,
    UserProfile,
    UserPosts
  },
  data() {
    return {
      user: {},
      posts: []
    }
  }
}
</script>

自动全局注册

对于大量基础组件,可以使用自动化注册:

// utils/register-components.js
import Vue from 'vue'
import upperFirst from 'lodash/upperFirst'
import camelCase from 'lodash/camelCase'

// 查找所有 .vue 文件
const requireComponent = require.context(
  './components/base',
  false,
  /Base[A-Z]\w+\.(vue|js)$/
)

requireComponent.keys().forEach(fileName => {
  const componentConfig = requireComponent(fileName)
  
  const componentName = upperFirst(
    camelCase(
      fileName.replace(/^\.\/(.*)\.\w+$/, '$1')
    )
  )
  
  Vue.component(componentName, componentConfig.default || componentConfig)
})

这样,components/base 目录下所有 BaseXxx.vue 文件都会自动注册。

单文件组件中的注册

.vue 文件中,推荐使用局部注册:

<template>
  <div>
    <app-button @click="handleClick">按钮</app-button>
    <app-input v-model="value"></app-input>
  </div>
</template>

<script>
import AppButton from '@/components/base/AppButton.vue'
import AppInput from '@/components/base/AppInput.vue'

export default {
  name: 'MyComponent',
  components: {
    AppButton,
    AppInput
  },
  data() {
    return {
      value: ''
    }
  },
  methods: {
    handleClick() {
      console.log('clicked')
    }
  }
}
</script>

注意事项

局部组件不能在子组件中使用

var ComponentA = {
  template: '<div>组件 A</div>'
}

new Vue({
  el: '#app',
  components: {
    'component-a': ComponentA
  }
})

// ComponentA 只能在 #app 的模板中使用
// 不能在其他全局组件中使用

如果需要在多个组件中使用,考虑全局注册或在每个组件中分别注册。

组件命名冲突

// 全局组件
Vue.component('my-component', {
  template: '<div>全局</div>'
})

// 局部组件
new Vue({
  el: '#app',
  components: {
    'my-component': {
      template: '<div>局部</div>'
    }
  }
})

// 局部组件会覆盖全局组件

最佳实践

  1. 优先使用局部注册:除非确定需要全局使用
  2. 基础组件全局注册:按钮、图标等通用组件
  3. 业务组件局部注册:保持依赖清晰
  4. 使用自动化工具:大量组件时自动注册

小结

组件注册要点:

  1. 全局注册:简单方便,适合基础组件
  2. 局部注册:依赖明确,适合业务组件
  3. 合理选择:根据使用范围决定注册方式
  4. 避免滥用全局:减少打包体积,避免命名冲突