Vue组件的使用 - Component

前言:在单页面应用开发中,大多数情况下只有一个html页面,那么单页面的页面跳转,其实就是各组件之间的切换,那么这个组件其实就充当的是一个一个独立的html页面的角色,只不过这个组件之间可以复用,传值,随意调用,在vue中通常由(vue-router)和组件(component)来配合完成,那么这个component就是我们下来将要学习的组件。

什么是组件?

官方说组件是可复用的 Vue 实例,且带有一个名字,它与 new Vue 拥有相同的选项,比如它拥有生命周期钩子、datacomputedmethods 等,与 new Vue 的唯一区别是不拥有 el 这样的根实例选项。

注册组件的两种方式

全局注册

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

局部注册

var ComponentA = { /*....*/ }

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

无论是全局注册还是局部注册,每个组件都需要有个名字。比如在全局注册的my-component-name和局部注册的component-a都是组件的名称, 组件名称推荐的命名方式hello-word的方式,也就是字母全小写,不同字母使用连字符

全局注册组件的使用场景

    <div id="app">
      <btn-success></btn-success>
    </div>
    
    <script type="text/javascript">
      Vue.component('btn-success', {
        template: '<div> <button class="btn-success" @click="onSuccessClick">成功</button> </div>',
        methods:{
          onSuccessClick: function(){
            alert('点击了Success按钮');
          }
        }
      });
      
      var vm = new Vue({
        el: "#app",
      })
      
    </script>

上面的案例中我们注册了一个全局组件btn-success,可以看到我们为这个组件提供了一个模板template,这个模板里的内容其实就是这个组件最终要渲染的内容,并给它添加了一个点击事件 onSuccessClick,点击 btn-success 就会触发 onSuccessClick 方法。

这就是一个单独模块,它可以独立去维护自己的点击事件以及生命周期钩子,当我们需要使用它时,只需要通过 <btn-success></btn-success> 标签名的方式就可以把它展示在我们的 el 中,在使用VueCli构建工具开发的时候,组件将会被拆分成.vue后缀的单独文件,并且各个组件相互独立,互不干扰。

需要注意的是,组件中的data选项的指向不再是一个对象{},而变成了一个方法funvtion(){return {}},并通过return返回一个独立的对象。

举个例子  我们将刚才上边的例子改造为一个累加的效果,点击按钮一次加一!

   <div id="app">
      <!-- 调用全局组件 -->
      <btn-success></btn-success>
      <btn-success></btn-success>
      <btn-success></btn-success>
    </div>
    
    <script type="text/javascript">
      // 定义全局组件btn-success
      Vue.component('btn-success', {
        template: '<div> 当前值:{{ count }}  <button class="btn-success" @click="onSuccessClick">成功</button> </div>',
        data:function(){
          return {
            count: 0
          }
        },
        methods:{
          onSuccessClick: function(){
            this.count += 1
          }
        }
      });
      
      var vm = new Vue({
        el: "#app",
      })
      
    </script>

注意:此时在组建中定义的count值目前只能在组件中调用,el中不能调用,还需要注意的是通过全局注册的组件,无论是否对它进行了引用,它总会包含在最终的构建结果中,试想一下,当我们费尽心思来优化工具配置,希望最终构建结果尽可能精简时,这是不是一件非常令人绝望的事情?

基于此,vue更推荐使用局部注册的方式来进行组件的定义。

局部注册组件的使用场景

依然已刚才的示例为底,我们改造一下变成局部注册组件的方式,完成累加任务!

   <div id="app">
      <!-- 调用全局组件 -->
      <btn-success></btn-success>
      <btn-success></btn-success>
      <btn-success></btn-success>
    </div>
    
    <script type="text/javascript">
      var btnCountAdd = {
        template: '<div><span>当前count的值:{{count}}</span><button @click="onCountAddClick">count++</button></div>',
        data: function () {
                return {
                    count: 0
                }
              },
        methods: {
          onCountAddClick: function () {
            this.count += 1;
          }
        }
      };
      
      
      var vm = new Vue({
        el: "#app",
        components:{
          'btn-success': btnCountAdd
        }
      })
      
    </script>

在上面的代码中首先声明了一个变量 btnCountAdd,并为它指定了 templatedatamethods 选项,然后在 new Vue 中通过 components 选项来注册了一个 btn-success 组件使其指向 btnCountAdd,这样就完成了一个组件的局部注册

这似乎和全局注册组件没有很好的区分,这是因为,在实际项目中我们一般不这样用,一般是通过模块化的方式来引入组件,我们来模拟一下真实环境!

// 项目结构

├── index.html
├── btn-count-add.js
└── main.js

 

// index.html

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

 <div id="app">
    <btn-count-add></btn-count-add> 
    <btn-count-add></btn-count-add> 
    <btn-count-add></btn-count-add> 
    <btn-count-add></btn-count-add> 
  </div>
// 这里通过 ES6 module的方式导入模块,type类型必须设置为module
<script src="./main.js" type="module"></script>

 

// main.js

import { btnCountAdd } from './btn-count-add.js'
var vm = new Vue({
    el: '#app',
    components: {
        'btn-count-add': btnCountAdd
    }
});

 

// btn-count-add.js

 var btnCountAdd = {
    template: '<div><span>当前count的值:{{count}}</span><button @click="onCountAddClick">count++</button></div>',
    data: function () {
        return {
            count: 0
        }
    },
    methods: {
        onCountAddClick: function () {
            this.count += 1;
        }
    }
};

// ES6的导出语法,将其导出
export { btnCountAdd };

我们把 btnCountAdd 抽象进btn-count-add.js文件中,并通过es6的导出语法将其导出,这就形成了一个单独的模块,当需要使用 btnCountAdd 的时候,便可以通过 import { btnCountAdd } from './btn-count-add.js' 来引入 btnCountAdd,并通过 components 选项来把它注册到 Vue 中。

这种引入方式在真实的项目环境中会经常使用,特别是当我们使用一些大搞工具(如:webpack, gulp)的时候,这种情况尤其普遍。

当使用局部注册的方式来注册组件的时候,虽然这种方式可以方便对项目进行模块化,并且有利于对打包的结果进行优化,但是同样会有一个小的问题——那就是每使用一个组件都需要通过 import 来把它进行引用。

当组件过多的时候,这不免是一个很麻烦的事情。为此 Vue 提供了一个更为便利的方式用于自动全局注册,当我们使用 Webpack+Vue-CLI 来构建项目的时候,可以在 main.jsnew Vue 之前这样做:

// 需要 npm import --save lodash
import upperFirst from 'lodash/upperFirst'
import camelCase from 'lodash/camelCase'

const requireComponent = require.context(
  // 其组件目录的相对路径
  './components',
  // 是否查询其子目录
  false,
  // 匹配基础组件文件名的正则表达式,获取.vue结尾的文件
  /.vue$/
)


requireComponent.keys().forEach(fileName => {
  // 获取组件配置
  const componentConfig = requireComponent(fileName)

  // 获取组件的 PascalCase 命名
  const componentName = upperFirst(
    camelCase(
      // 剥去文件名开头的 `./` 和结尾的扩展名
      fileName.replace(/^\.\/(.*)\.\w+$/, '$1')
    )
  )

  // 全局注册组件
  Vue.component(
    componentName,
    // 如果这个组件选项是通过 `export default` 导出的,
    // 那么就会优先使用 `.default`,
    // 否则回退到使用模块的根。
    componentConfig.default || componentConfig
  )
});

比如 BtnSuccess.vue 则可以直接通过 <btn-success></btn-success> 来直接使用,并且未被使用到的组件并不会打包到构建结果之中。