Vue.use 详解

一,基本用法

插件通常为 Vue 来添加全局功能。而插件的应用范围没有明确规定,一般包括:

  • 添加全局方法或属性。例如:vue-custom-element
  • 添加全局资源:指令/过滤器/过渡等。如:v-touch
  • 通过全局混入来添加一些组件选项。如:vue-router
  • 添加 Vue 实例方法,通过把他们添加到 Vue.prototype 上实现
  • 一个库,提供自己的 API,同时提供上面提到的一个或多个功能。如:vue-router

开发一个插件需要暴露一个 install方法。这个方法的第一个参数是 Vue 构造器,第二个参数一个可选的选项对象。

编写一个组件:

import LoadingComponent from './LoadingComponent.vue';

const MyPlugin = {};

MyPlugin.install = function(Vue, options) {
  // 1, 添加全局方法或属性
  Vue.myGlobalVariable = "hahha";
  Vue.myGlobalMethods = function(){ //... }
    
  // 2, 添加全局资源
  // 注册一个全局组件 `loading`, 不用 `import` 可以在任何位置使用
  Vue.component('Loading', LoadingComponent)
  Vue.directive('my-directive', {
    bind(el, binding, vnode, oldnode){ // logic }
  })
    
  // 3, mixins
  Vue.mixin({
    created(){ // logic },
    mounted(){ // logic }
  })
  
  // 4, 在原型上添加实例方法,被各个实例继承
  Vue.prototype.$MyMethods = function(mOptions) { // logic }
}

把 Vue 引到一个新的位置,添加全局方法,组件,属性等内容,其实这些内容也可以通过其他方式写,那么这个API的用处在哪儿

如何使用:

// MyPlugin.vue


export default {
  // other logic
  mounted(){
    // 1, 添加全局方法或属性 (TODO: 这种方式似乎不能访问 ??????)
    const myGlobalVariable = this.myGlobalVariable;    // undefined
    this.myGlobalMethods();  // "this.myGlobalMethods is not a function"
    
    // 3, mixins
    
    // 4, 在原型上添加实例方法,被各个实例继承
    this.$MyMethods();   // run logic
  }
}
二,源码解析
// ./vue/src/core/global-api/use.js

import { toArray } from "../utils/index";

/* @flow */

import { toArray } from '../util/index'

export function initUse (Vue: GlobalAPI) {
  // `plugin: Function | Object` 这种使FLOW写法,facebook 出品的一款Javascript静态类型检查工具。
  // use 方法接受的参数为 function 或 object, 若为function, 默认为 install 方法,若为object, 里面必      须含有一个install 方法。
  
  Vue.use = function (plugin: Function | Object) {
    const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))
    
    // 插件只能注册一次
    if (installedPlugins.indexOf(plugin) > -1) {
      return this
    }

    // 处理附加参数
    // `toArray`: arguments是参数列表的一个类数组(不知道是不是这么说),toArray 将它转变成一个真的数组,截取除第一以外的所有参数。 ① 这些参数会在调用install 方法时传入;
    const args = toArray(arguments, 1)
    args.unshift(this)
    if (typeof plugin.install === 'function') {
      plugin.install.apply(plugin, args)
    } else if (typeof plugin === 'function') {
      plugin.apply(null, args)
    }
    installedPlugins.push(plugin)
    return this
  }
}

所以我们总结下,Vue.use 的好处:

  1. 统一在一个位置注册全局内容,避免main.js过度臃肿;
  2. 全局注册,避免重复引入问题;
  3. 避免重复注册组件。
三,常见的几种用法
  • mixins
    // 一个简单的栗子,可以用于 validation
    const myMixin = {
      install(Vue, options) {
        // 这里 this 指向 myMixin
        
        Vue.mixin({
          // 每个组件创建都会执行 created 方法
          created(){
            const rules = this.$options.rules || null;
            if(rules) {
              Object.keys(rules).forEach(key => {
                const ruleItem = rules[key];
                
                // $watch 接受两个参数:监测的属性名,callback(变化后的值)
                this.$watch(key, newValue => {
                  const isValid = ruleItem.validate(newValue);
                  
                  if(!isValid) {
                    console.log(ruleItem.message)
                  }
                })
              })
            }
          }
        })
      }
    }
    
    const vue = new Vue({
      data: {
        foo: 10
      },
      // 检验规则 ③
      rules: {
        foo: {
          validate: value => value > 1,
          message: "foo 必须大于 1"
        },
        // more rules
      }
    })
    
    // * 需要注意的是, ③处的 rules 只能在根组件通过 this.options.rules 访问,子组件的话得看嵌套关系,可能是 this.$options.parent.$options....parent.$options.rules 这种方式访问该属性
    
  • directives
    // 注册一个全局自定义指令:`v-focus`
    
    // plugin 内部
    function Myplugin(Vue){
      Vue.directive('focus', {
        // 当被绑定元素插入到DOM中时,自动聚焦
        inserted(el){
          el.focus();
        }
      })
    }
    
    // template 内部
      // 自动聚焦
    
    
  • axios

    axios 不能直接用Vue.use, 以为它本身没有暴露 install 方法。所以使用axios只能是下面两种方法:

    • 使用的地方进行 import

      import axios from 'axios';
      
      export const login = params => {
        return axios
          .post(`localhost:3000/login`, params)
          .then(res => res)
      }
      
    • 使用 vue-axios :

      // npm i -S axios vue-axios
      
      // main.js
      import Vue from 'vue';
      import axios from 'axios';
      import VueAxios from 'vue-axios';
      
      // 由之前的 `vue.use` 用法我们知道,它可以接收第二个 `options` 参数,所以这里本质是将 `axios` 通过 `VueAxios` 包装以符合 "install 规范"
      Vue.use(VueAxios, axios);
      
      // usage
      this.axios.get(params).then(res => res);
      Vue.axios.get(params).then(res => res);
      this.$http.get(params).then(res => res);
      
      
  • vue-router
    // ./route/index.js
    import Router from 'vue-router';
    Vue.use(Router);
    
    let router = new Router({
      routes: [
        {
          path: "./home",
          component: homeComp
        }
      ]
    })
    
    export default router;
    
    // main.js
    import router from ....;
    const vue = new Vue({
      // other logic
      router
    })
    
  • Vuex

    用法与vue-router 等同,不赘述

四,常见问题
  • 为什么要先 Vue.usenew Vue() ?

    首先回顾下 Vue.use 做了什么:

    • 检测是否注册过此插件
    • 插件注入到 Vue 构造函数 (TODO:不知道是不是这么说)
    • 存储注册过的插件

    而在 new Vue(options) 时,会执行 Vue.prototype._init 进行初始化,将Vue 本身属性与options合并,然后进行事件,生命周期初始化。(比如:vue-router, vuex 等需要将 store, router 选项插入options)

    所以,如果 Vue.usenew Vue() 之前执行,那么调用_init()时,插件中使用的内容还没有注入到 Vue.options.component, Vue.options.directives 等属性中,初始化的实例也无从访问。

你可能感兴趣的:(Vue.use 详解)