Vue源码学习(二)-----自定义组件

1、Vue插件的功能

  • 添加全局方法或者属性,例如vue-custom-element;
  • 添加全局资源:指令/过滤器或者过渡等;
  • 通过全局混入来添加一些组件选项,例如vue-router;
  • 添加vue实例方法,将其加到原型上;
  • 一个组件库,提供自己的API,同时提供上面提到的一个或者多个功能,例如Element-ui提供自己的API;

2、Vue的钩子函数

  • 源码中(/src/core/instance/index.js),Vue是一个构造函数,所以我们需要使用new的方式创建一个关于Vue的实例,如果是生产环境或者不是Vue的实例,将发出警告,然后通过Vue实例上的_init()方法进行Vue的初始化;
    function Vue (options) {
      if (process.env.NODE_ENV !== 'production' &&
        !(this instanceof Vue)
      ) {
        warn('Vue is a constructor and should be called with the `new` keyword')
      }
      this._init(options)
    }
  • _init()方法是Vue通过prototype来实现的一个原型属性,Vue先调用了initLifecycle(vm)、initEvents(vm)、initRender(vm)这三个方法,用于初始化生命周期、事件和渲染函数,且发生在beforeCreated钩子函数之前,在Vue中几乎所有的钩子(errorCaptured除外)函数执行都是通过callHook(vm: Component, hook: string) 来调用的;
    Vue.prototype._init = function (options?: Object) {
        ...
        // expose real self
        vm._self = vm
        initLifecycle(vm)
        initEvents(vm)
        initRender(vm)
        callHook(vm, 'beforeCreate')
        initInjections(vm) // resolve injections before data/props
        initState(vm)
        initProvide(vm) // resolve provide after data/props
        callHook(vm, 'created')
        ...
        if (vm.$options.el) {
          vm.$mount(vm.$options.el)
        }
     }
  • callHook()方法根据传入的hook从实例中拿到对应的回调函数数组(/packages/vue-template-compiler/browser.js下的 LIFECYCLE_HOOKS),然后再执行,步骤如下:
    • 把所有同类钩子先合并成数组(一个实例通过mixins可能有很多个相同钩子),然后存放在 vm.$options,类似于mixin,然后遍历执行;
    • 使用
      //callHook的作用就是执行用户自定义的钩子函数,并将钩子中this指向指为当前的组件实例
      export function callHook(vm:Component, hook:String) {
          //vm为当前vue实例,hook为钩子函数名称
          pushTarget(); //为了避免在某些生命周期钩子中使用 props 数据导致收集冗余的依赖
        //获取对相应的钩子函数内容
          var handlers = vm.$options[hook];
          if (handlers) {
              for (var i = 0, j = handlers.length; i < j; i++) {
                  try {
                      // 直接调用
                      handlers[i].call(vm);
                  } catch (e) {
                      handleError(e, vm, (hook + " hook"));
                  }
              }
          }
          // 钩子函数事件emit
          if (vm._hasHookEvent) {
              vm.$emit('hook:' + hook);
          }
          popTarget();//为了避免在某些生命周期钩子中使用 props 数据导致收集冗余的依赖
      }

3、插件的开发流程

  • 如果插件是一个对象,必须暴露一个install方法,如果是一个函数,自己则会被作为install方法
    Myplugin.install = (Vue,options) => {
    //添加全局方法或者属性
    Vue.globalMethod = () => {}
    //添加全局资源
    Vue.directive(directiveName,{})
    //注入组件选项
    Vue.mixin({})
    //添加实例方法
    Vue.prototype.commonMethod = () => {}

          注意:首先根据installed属性来判断插件是否被注册,防止二次注册,然后利用Vue.mixin使插件在beforeCreate(即组件实例化之前)注册,在destoryed中销毁组件,最后利用defineProperty;让复制关系为已读,且不会被人为修改,结尾还是用Vue.component方法注册router-view与router-link两个组件;

  • 在new Vue()(即组件实例化之前)使用全局方法Vue.use(),执行时自动执行install方法,用来安装插件,多次调用,只会注册一次;
    注意:Vue上定义use方法,参数可以是Object或者Function,逻辑:先判断插件是否注册过,防止重复注册,然后使用工具类的方法toArray拿到use中从1-剩余的参数,最后根据是
    Object或者Function执行对应的install方法,并记录该组件已经被注册过(将参数放入一个数组中)。

4、遇到的问题

  暂无

原文地址:https://www.cnblogs.com/wxh0929/p/13839758.html