关于Vue Component组件注册

  • 更新时间:2020-03-09 12:31:42
  • 编辑:顾滢莹
本站收集了一篇Vue相关的编程文章,网友邵博赡根据主题投稿了本篇教程内容,涉及到Vue、Component、Vue源码解读之Component组件注册的实现相关内容,已被599网友关注,内容中涉及的知识点可以在下方直接下载获取。
  • vue3官方文档指南(带标签) / 89 MB 推荐度:
  • Vue2.0/3.0面试通杀秘籍(2021版) / 2.9 MB 推荐度:
  • 水晶石技法VUE 10完全学习手册 / 114191 MB 推荐度:
  • 《Vue.js前端开发基础与项目实战》项目源码 / 741 MB 推荐度:
  • Vue2实践揭秘 / 204.6 MB 推荐度:
  • Vue源码解读之Component组件注册的实现

    什么是组件?

    组件 (Component) 是 Vue.js 最强大的功能之一。组件可以扩展 HTML 元素,封装可重用的代码。在较高层面上,组件是自定义元素,Vue.js 的编译器为它添加特殊功能。在有些情况下,组件也可以表现为用 is 特性进行了扩展的原生 HTML 元素。

    所有的 Vue 组件同时也都是 Vue 的实例,所以可接受相同的选项对象 (除了一些根级特有的选项) 并提供相同的生命周期钩子。

    Vue可以有全局注册和局部注册两种方式来注册组件。

    全局注册

    注册方式

    全局注册有以下两种注册方式:

    通过Vue.component 直接注册。

    Vue.component('button-counter', {
        //data选项必须是一个函数
        data: function () {
          return {
            count: 0
          }
        },
        template:'#clickBtn'
      })

    通过Vue.extend来注册。

     var buttonComponent = Vue.extend({
        name:'button-counter',
        data: function () {
          return {
            count: 0
          }
        },
        template:'#clickBtn'
      });
     Vue.component('button-counter', buttonComponent);

    具体过程

    Vue初始化时,initGlobalAPI通过调用initAssetRegisters()进行组件注册。

    function initAssetRegisters (Vue) {
     // 创建组件注册的方法
     // ASSET_TYPES在Vue内部定义,var ASSET_TYPES = ['component','directive','filter'];
     ASSET_TYPES.forEach(function (type) {
      Vue[type] = function (
       id,
       definition
      ) {
       //这里的definition指的是定义(Function或Object),是函数或者对象
       //如果definition不存在,直接返回options内type和id对应的
       //这里的options是指全局的组件,指令和过滤器,见图一
       if (!definition) {
        return this.options[type + 's'][id]
       } else {
        /* istanbul ignore if */
        if ("development" !== 'production' && type === 'component') {
         validateComponentName(id);
        }
        // 如果是component(组件)方法,并且definition是对象
        if (type === 'component' && isPlainObject(definition)) {
         definition.name = definition.name || id;
         //通过this.options._base.extend方法(也就是Vue.extend方法)将定义对象转化为构造器。
         //Vue.options._base = Vue;
         definition = this.options._base.extend(definition);
        }
        if (type === 'directive' && typeof definition === 'function') {
         definition = { bind: definition, update: definition };
        }
        // 将构造器赋值给 this.options[‘component'+ 's'][id]
        //全局的组件,指令和过滤器,统一挂在vue.options上。在init的时候利用mergeOptions合并策略侵入实例,供实例使用。
        this.options[type + 's'][id] = definition;
        return definition
       }
      };
     });
    }

    图一:

    Vue源码解读之Component组件注册的实现

    initAssetRegisters里面通过this.options._base.extend方法将定义对象转化为构造器,而options._base.extend其实就是Vue.extend。接下来我们就看一下Vue.extend做了什么。

    Vue.extend = function (extendOptions) {
      extendOptions = extendOptions || {};
      var Super = this;
      var SuperId = Super.cid;
      //组件缓存
      var cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {});
      //如果组件已经被缓存在extendOptions上则直接取出
      if (cachedCtors[SuperId]) {
       return cachedCtors[SuperId]
      }
    
      //如果有name属性,检验name拼写是否合法
      var name = extendOptions.name || Super.options.name;
      if ("development" !== 'production' && name) {
       validateComponentName(name);
      }
    
      var Sub = function VueComponent (options) {
       this._init(options);
      };
      //将vue上原型的方法挂在Sub.prototype中,Sub的实例同时也继承了vue.prototype上的所有属性和方法。
      //关于 prototype的学习:http://www.cnblogs.com/dolphinX/p/3286177.html
      Sub.prototype = Object.create(Super.prototype);
      //Sub构造函数修正,学习于https://www.cnblogs.com/SheilaSun/p/4397918.html
      Sub.prototype.constructor = Sub;
      Sub.cid = cid++;
      //通过vue的合并策略合并添加项到新的构造器上
      Sub.options = mergeOptions(
       Super.options,
       extendOptions
      );
      //缓存父构造器
      Sub['super'] = Super;
    
      // 处理props和computed响应式配置项
      if (Sub.options.props) {
       initProps$1(Sub);
      }
      if (Sub.options.computed) {
       initComputed$1(Sub);
      }
    
      // allow further extension/mixin/plugin usage
      Sub.extend = Super.extend;
      Sub.mixin = Super.mixin;
      Sub.use = Super.use;
    
      //在新的构造器上挂上vue的工具方法
      ASSET_TYPES.forEach(function (type) {
       Sub[type] = Super[type];
      });
      // enable recursive self-lookup
      if (name) {
       Sub.options.components[name] = Sub;
      }
    
      // keep a reference to the super options at extension time.
      // later at instantiation we can check if Super's options have
      // been updated.
      Sub.superOptions = Super.options;
      Sub.extendOptions = extendOptions;
      Sub.sealedOptions = extend({}, Sub.options);
    
      //缓存组件构造器在extendOptions上
      cachedCtors[SuperId] = Sub;
      return Sub
     };

    vue.extend返回了一个带有附加Option的vue构造器。这个构造器被命名为Sub,等待render时候初始化。

    initAssetRegisters完成之后,options下挂载了全局组件button-counter,如图:

    Vue源码解读之Component组件注册的实现

    接下来调用new Vue()渲染vue整体的生命周期

    局部注册

    如果不需要全局注册,或者是让组件使用在其它组件内,可以用选项对象的components属性实现局部注册。

    注册方式

    new Vue({
        el: '#components-demo',
        components:{
          'button-counter':{
            template:'#clickBtn',
            data: function () {
              return {
                count: 0
              }
            }
          }
        }
      })

    具体过程

    Vue局部组件注册也是通过initAssetRegisters()方法调用Vue.extend,不同的是在createComponent()时,initMixin()里面有判断

    if (options && options._isComponent) {
       //因为Vue动态合并策略非常慢,并且内部组件的选项都不需要特殊处理。
       //调用initInternalComponent快捷方法,内部组件实例化。
       initInternalComponent(vm, options);
     }
     else {
       vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
       );
      }
    function initInternalComponent (vm, options) {
     var opts = vm.$options = Object.create(vm.constructor.options);
     // 这样做是因为它比动态枚举更快。
     var parentVnode = options._parentVnode;
     opts.parent = options.parent;
     opts._parentVnode = parentVnode;
    
     var vnodeComponentOptions = parentVnode.componentOptions;
     opts.propsData = vnodeComponentOptions.propsData;
     opts._parentListeners = vnodeComponentOptions.listeners;
     opts._renderChildren = vnodeComponentOptions.children;
     opts._componentTag = vnodeComponentOptions.tag;
    
     if (options.render) {
      opts.render = options.render;
      opts.staticRenderFns = options.staticRenderFns;
     }
    }

    opts的结构如图所示:

    Vue源码解读之Component组件注册的实现

    局部注册和全局注册不同的是,只有该类型的组件才可以访问局部注册的子组件,而全局注册是扩展到 Vue.options 下。在所有组件创建的过程中,都会从全局的 Vue.options.components 扩展到当前组件的 vm.$options.components 下,这就是全局注册的组件能被任意使用的原因。

    组件名定义

    定义组件名的方式有两种:

    使用短横线形式

    Vue.component('button-counter', {})

    引用这个自定义元素时,必须用 <button-counter></button-counter>

    使用驼峰的形式

    Vue.component('buttonCounter', { })

    此时在引用这个自定义元素时,两种命名方法都可以使用。也就是说,<buttonCounter><button-counter> 都是可行的。

    注意,直接在 DOM (即非字符串的模板) 中使用时只有短横线是有效的。如下:

    <div id="components-demo">
        <button-counter></button-counter>
    </div>

    可参考:https://www.jb51.net/article/144050.htm

    以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持码农之家。

    Vue相关教程

    • Vue 使用计时器实现跑马灯效果的实例代码

      这篇文章主要介绍了Vue 使用计时器实现跑马灯效果,本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值 ,需要的朋友可以参考下

      发布时间:2019-09-10

    • 基于Vue+element-ui 的Table二次封装的实现

      这篇文章主要介绍了基于Vue+element-ui 的Table二次封装的实现,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

      发布时间:2019-07-23

    用户留言