Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vue 进阶系列(二)之插件原理及实现 #9

Open
yygmind opened this issue Nov 1, 2018 · 0 comments
Open

Vue 进阶系列(二)之插件原理及实现 #9

yygmind opened this issue Nov 1, 2018 · 0 comments

Comments

@yygmind
Copy link
Owner

yygmind commented Nov 1, 2018

Vue进阶系列汇总如下,欢迎阅读,欢迎加群讨论(文末)。

Vue 进阶系列(一)之响应式原理及实现

Vue 进阶系列(二)之插件原理及实现

使用方法

插件的详细使用方法详情看Vue官网

Vue官网之插件Plugins

概括出来就是

  • 1、通过Vue.use(MyPlugin)使用,本质上是调用MyPlugin.install(Vue)
  • 2、使用插件必须在new Vue()启动应用之前完成,实例化之前就要配置好。
  • 3、如果使用Vue.use多次注册相同插件,那只会注册成功一次。

源码解读

Vue.use源码如下

Vue.use = function (plugin) {   
    // 忽略已注册插件
    if (plugin.installed) {
      return
    }
    
    // 集合转数组,并去除第一个参数
    var args = toArray(arguments, 1);
    
    // 把this(即Vue)添加到数组的第一个参数中
    args.unshift(this);
    
    // 调用install方法
    if (typeof plugin.install === 'function') {
      plugin.install.apply(plugin, args);
    } else if (typeof plugin === 'function') {
      plugin.apply(null, args);
    }
    
    // 注册成功
    plugin.installed = true;
    return this;
  };

Vue.use接受一个对象参数plugin,首先判断是否已注册,如果多次注册相同插件那么只会注册成功一次,在注册成功后设置plugin.installed = true

然后执行toArray(arguments, 1)方法,arguments是一个表示所有参数的类数组对象,需要转换成数组之后才能使用数组的方法。

function toArray (list, start) {
  start = start || 0;
  var i = list.length - start;
  var ret = new Array(i);
  // 循环去除 前start元素
  while (i--) {
    ret[i] = list[i + start];
  }
  return ret
}

上面进行了一次转换,假设list是[1, 2, 3, 4],start是1,首先创建一个包含3个元素的数组,依次执行ret[2] = list[ 2 + 1]ret[1] = list[ 1 + 1]ret[0] = list[ 0 + 1],实际上就是去除arguments的第一个参数然后把剩余的类数组赋值给新的数组,其实就是去除plugin参数,因为调用plugin.install的时候不需要这个参数。

还可以通过如下几种方式实现类数组转换成数组,但是使用slice会阻止某些JavaScript引擎中的优化(参考自MDN)。

// ES5
var args = Array.prototype.slice.call(arguments);
var args = [].slice.call(arguments);

// ES6
const args = Array.from(arguments);
const args = [...arguments];

转换成数组之后调用args.unshift(this),把Vue对象添加到args的第一个参数中,这样就可以在调用plugin.install方法的时候把Vue对象传递过去。

实例:实现一个插件

要求创建一个告诉Vue组件处理自定义rules规则选项的插件,这个rules需要一个对象,该对象指定组件中的数据的验证规则。

示例:

const vm = new Vue({
  data: { foo: 10 },
  rules: {
    foo: {
      validate: value => value > 1,
      message: 'foo must be greater than one'
    }
  }
})

vm.foo = 0 // 输出 foo must be greater than one

第一步先不考虑插件,在已有的VueAPI中是没有rules这个公共方法的,如果要简单实现的话可以通过钩子函数来,即在created里面验证逻辑。

const vm = new Vue({
    data: { foo: 10 },
    rules: {
        foo: {
          validate: value => value > 1,
          message: 'foo must be greater than one'
        }
    },
    created: function () {
      
        // 验证逻辑
        const rules = this.$options.rules
        if (rules) {
          Object.keys(rules).forEach(key => {
          
            // 取得所有规则
            const { validate, message } = rules[key]
            
            // 监听,键是变量,值是函数
            this.$watch(key, newValue => {
            
              // 验证规则
              const valid = validate(newValue)
              if (!valid) {
                console.log(message)
              }
            })
          })
        }
      }
    
})

可以通过this.$options.rules获取到自定义的rules对象,然后对所有规则遍历,使用自定义的validate(newValue)验证规则。

第二步实现这个rules插件,为了在Vue中直接使用,可以通过Vue.mixin注入到Vue组件中,这样所有的Vue实例都可以使用。

按照插件的开发流程,应该有一个公开方法install,在install里面使用全局的mixin方法添加一些组件选项,mixin方法包含一个created钩子函数,在钩子函数中验证this.$options.rules

实现代码如下:

import Vue from 'vue'

// 定义插件
const RulesPlugin = {

  // 插件应该有一个公开方法install
  // 第一个参数是Vue 构造器
  // 第二个参数是一个可选的选项对象
  install (Vue) {
  
    // 注入组件
    Vue.mixin({
    
      // 钩子函数
      created: function () {
      
        // 验证逻辑
        const rules = this.$options.rules
        if (rules) {
          Object.keys(rules).forEach(key => {
          
            // 取得所有规则
            const { validate, message } = rules[key]
            
            // 监听,键是变量,值是函数
            this.$watch(key, newValue => {
            
              // 验证规则
              const valid = validate(newValue)
              if (!valid) {
                console.log(message)
              }
            })
          })
        }
      }
    })
  }
}

// 调用插件,实际上就是调用插件的install方法
// 即RulesPlugin.install(Vue)
Vue.use(RulesPlugin)

参考

Vue官网之插件Plugins
MDN之Arguments 对象

交流

本人Github链接如下,欢迎各位Star

http://github.com/yygmind/blog

我是木易杨,现在是网易高级前端工程师,目前维护了一个高级前端进阶群,欢迎加入。接下来让我带你走进高级前端的世界,在进阶的路上,共勉!

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant