• 5.1 组件两种注册方式
    • 5.1.1 全局注册
    • 5.1.2 局部注册
    • 5.1.3 注册过程

    5.1 组件两种注册方式

    熟悉Vue开发流程的都知道,Vue组件在使用之前需要进行注册,而注册的方式有两种,全局注册和局部注册。在进入源码分析之前,我们先回忆一下两者的用法,以便后续掌握两者的差异。

    5.1.1 全局注册

    1. Vue.component('my-test', {
    2. template: '<div>{{test}}</div>',
    3. data () {
    4. return {
    5. test: 1212
    6. }
    7. }
    8. })
    9. var vm = new Vue({
    10. el: '#app',
    11. template: '<div id="app"><my-test><my-test/></div>'
    12. })

    其中组件的全局注册需要在全局实例化Vue前调用,注册之后可以用在任何新创建的Vue实例中调用。

    5.1.2 局部注册

    1. var myTest = {
    2. template: '<div>{{test}}</div>',
    3. data () {
    4. return {
    5. test: 1212
    6. }
    7. }
    8. }
    9. var vm = new Vue({
    10. el: '#app',
    11. component: {
    12. myTest
    13. }
    14. })

    当只需要在某个局部用到某个组件时,可以使用局部注册的方式进行组件注册,此时局部注册的组件只能在注册该组件内部使用。

    5.1.3 注册过程

    在简单回顾组件的两种注册方式后,我们来看注册过程到底发生了什么,我们以全局组件注册为例。它通过Vue.component(name, {...})进行组件注册,Vue.component是在Vue源码引入阶段定义的静态方法。

    1. // 初始化全局api
    2. initAssetRegisters(Vue);
    3. var ASSET_TYPES = [
    4. 'component',
    5. 'directive',
    6. 'filter'
    7. ];
    8. function initAssetRegisters(Vue){
    9. // 定义ASSET_TYPES中每个属性的方法,其中包括component
    10. ASSET_TYPES.forEach(function (type) {
    11. // type: component,directive,filter
    12. Vue[type] = function (id,definition) {
    13. if (!definition) {
    14. // 直接返回注册组件的构造函数
    15. return this.options[type + 's'][id]
    16. }
    17. ...
    18. if (type === 'component') {
    19. // 验证component组件名字是否合法
    20. validateComponentName(id);
    21. }
    22. if (type === 'component' && isPlainObject(definition)) {
    23. // 组件名称设置
    24. definition.name = definition.name || id;
    25. // Vue.extend() 创建子组件,返回子类构造器
    26. definition = this.options._base.extend(definition);
    27. }
    28. // 为Vue.options 上的component属性添加将子类构造器
    29. this.options[type + 's'][id] = definition;
    30. return definition
    31. }
    32. });
    33. }

    Vue.components有两个参数,一个是需要注册组件的组件名,另一个是组件选项,如果第二个参数没有传递,则会直接返回注册过的组件选项。否则意味着需要对该组件进行注册,注册过程先会对组件名的合法性进行检测,要求组件名不允许出现非法的标签,包括Vue内置的组件名,如slot, component等。

    1. function validateComponentName(name) {
    2. if (!new RegExp(("^[a-zA-Z][\\-\\.0-9_" + (unicodeRegExp.source) + "]*$")).test(name)) {
    3. // 正则判断检测是否为非法的标签
    4. warn(
    5. 'Invalid component name: "' + name + '". Component names ' +
    6. 'should conform to valid custom element name in html5 specification.'
    7. );
    8. }
    9. // 不能使用Vue自身自定义的组件名,如slot, component,不能使用html的保留标签,如 h1, svg等
    10. if (isBuiltInTag(name) || config.isReservedTag(name)) {
    11. warn(
    12. 'Do not use built-in or reserved HTML elements as component ' +
    13. 'id: ' + name
    14. );
    15. }
    16. }

    在经过组件名的合法性检测后,会调用extend方法为组件创建一个子类构造器,此时的this.options._base代表的就是Vue构造器。extend方法的定义在介绍选项合并章节有重点介绍过,它会基于父类去创建一个子类,此时的父类是Vue,并且创建过程子类会继承父类的方法,并会和父类的选项进行合并,最终返回一个子类构造器。

    代码处还有一个逻辑,Vue.component()默认会把第一个参数作为组件名称,但是如果组件选项有name属性时,name属性值会将组件名覆盖。

    总结起来,全局注册组件就是Vue实例化前创建一个基于Vue的子类构造器,并将组件的信息加载到实例options.components对象中。

    接下来自然而然会想到一个问题,局部注册和全局注册在实现上的区别体现在哪里?我们不急着分析局部组件的注册流程,先以全局注册的组件为基础,看看作为组件,它的挂载流程有什么不同。