浅谈vue.use()方法从源码到使用


Posted in Javascript onMay 12, 2019

关于 vue.use 我们都知道些什么?

在做 vue 开发的时候大家一定经常接触 Vue.use() 方法,官网给出的解释是: 通过全局方法 Vue.use() 使用插件;我觉得把使用理解成注册更合适一些,首先看下面常见的注册场景。

import Router from 'vue-router'
Vue.use(Router)

import Vuex from 'vuex'
Vue.use(Vuex)

import Echarts from 'echarts'
Vue.prototype.$echarts = Echarts

关于 echarts 的注册很简单,直接挂在 Vue 方法的原型上,通过原型链继承的关系可以在任意一个组件里通过 this.$echarts 访问到 echarts 实例,我们来写一个简单的例子证明一下。

function myVue(title){
 this.title = title
}
myVue.prototype.myUse = '在原型上添加公共属性'
const A = new myVue('我是实例A')
const B = new myVue('我是实例B')
console.log(A.title, B.title, A.myVue, B.myVue, )
// 我是实例A 我是实例B 在原型上添加公共属性 在原型上添加公共属性

而 Router 和 Vuex 的注册就要去分析 Vue.use() 的源码了,在分析源码之前先总结一下官方对 Vue.use() 方法的说明:

  • 通过全局方法 Vue.use() 使用插件
  • Vue.use 会自动阻止多次注册相同插件
  • 它需要在你调用 new Vue() 启动应用之前完成
  • Vue.use() 方法至少传入一个参数,该参数类型必须是 Object 或 Function,如果是 Object 那么这个 Object 需要定义一个 install 方法,如果是 Function 那么这个函数就被当做 install 方法。在 Vue.use() 执行时 install 会默认执行,当 install 执行时第一个参数就是 Vue,其他参数是 Vue.use() 执行时传入的其他参数。

官网说 Vue.use() 是用来使用插件的,那么传入的 Router 和 Vuex 就是这里指的插件,而这个插件本质上又是一个 install 方法。至于 install 方法内部实现了什么逻辑就由插件自身的业务决定了。

源码分析

首先说一下 Flow,vue源码中那些奇怪的写法 Vue.use = function (plugin: Function | Object) 是 Flow 的语法,Flow 是 facebook 出品的 JavaScript 静态类型检查工具。JavaScript 是动态类型语言,它的灵活性有目共睹,但是过于灵活的副作用是很容易就写出非常隐蔽的隐患代码,在编译期甚至看上去都不会报错,但在运行阶段就可能出现各种奇怪的 bug。

下面我们正式开始分析源码,Vue.use() 的源码很简单30行都不到,首先看 src/core/global-api/use.js 下 Vue.use() 方法的定义:

import { toArray } from '../util/index'
export function initUse (Vue: GlobalAPI) {
 Vue.use = function (plugin: Function | Object) {
  const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))
  if (installedPlugins.indexOf(plugin) > -1) {
   return this
  }
  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
 }
}

上面源码中使用了工具函数 toArray ,该函数定义在 src/shared/util.js

export function toArray (list: any, start?: number): Array<any> {
 start = start || 0
 let i = list.length - start
 const ret: Array<any> = new Array(i)
 while (i--) {
  ret[i] = list[i + start]
 }
 return ret
}

了解一下源码实现了什么逻辑

Vue.use = function (plugin: Function | Object) {

在全局api Vue 上定义了 use 方法,接收一个 plugin 参数可以是 Function 也可以是 Object,这就和前面官方规定的 Vue.use() 第一个参数要求的类型对应上了。

if (installedPlugins.indexOf(plugin) > -1) {

用来判断该插件是不是已经注册过,防止重复注册。

const args = toArray(arguments, 1)

arguments是 Vue.use() 方法的参数列表是一个类数组,后面的 1 先理解成一个常量,toArray 方法的作用就是把第一个 Array 参数从下标为1截取到最后。也就拿到了 Vue.use() 方法除去第一个之外的其他参数,这些参数准备在调用 instll 方法的时候传入。

if (typeof plugin.install === 'function') {
} else if (typeof plugin === 'function') {

这里的if语句是判断 Vue.use() 传入的第一个参数是 Object 还是 Function。

plugin.install.apply(plugin, args)
plugin.apply(null, args)

判断完之后执行那个对应的 install 方法,用 apply 改变 this 指向,并把 toArray 得到的剩余参数传入。

installedPlugins.push(plugin)

最后记录该组件已经注册过了

现在我们发现 Vue.use() 的注册本质上就是执行了一个 install 方法,install 里的内容由开发者自己定义,通俗讲就是一个钩子可能更贴近语义化而已。

Vue.use()有什么用

在 install 里我们可以拿到 Vue 那么和 Vue 相关的周边工作都可以考虑放在 Vue.use() 方法里,比如:

  • directive注册
  • mixin注册
  • filters注册
  • components注册
  • prototype挂载
  • ...

echarts 用 Vue.use() 来注册

main.js

import Vue from 'vue'
import echarts from './echarts.js'
Vue.use(echarts)

new Vue({
 ...
})

echarts.js

import Echarts from 'echarts'
export default {
 install(Vue){
  Vue.prototype.$echarts = Echarts
 }
}

这样写的好处是可以在 install 的文件里做更多配置相关的工作,main.js 不会变的臃肿,更方便管理。

全局组件用 Vue.use() 来注册

base.js

import a from './a'
import b from './b'
let components = { a, b }
const installBase = {
 install (Vue) {
  Object.keys(components).map(key => Vue.component(key, components[key]))
 }
}

main.js

import Vue from 'vue'
import base from './base.js'
Vue.use(base)

new Vue({
 ...
})

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
js 代码优化点滴记录
Feb 19 Javascript
JavaScript利用构造函数和原型的方式模拟C#类的功能
Mar 06 Javascript
js实现带关闭按钮始终显示在网页最底部工具条的方法
Mar 02 Javascript
jQuery滚动条插件nanoscroller使用指南
Apr 21 Javascript
js实现的简洁网页滑动tab菜单效果代码
Aug 24 Javascript
JS实现登录页面记住密码和enter键登录方法推荐
May 10 Javascript
JS实现经典的中国地区三级联动下拉菜单功能实例【测试可用】
Jun 06 Javascript
bootstrap时间插件daterangepicker使用详解
Oct 19 Javascript
JS判断用户用的哪个浏览器实例详解
Oct 09 Javascript
详解微信小程序与内嵌网页交互实现支付功能
Oct 22 Javascript
使用layui日期控件laydate对开始和结束时间进行联动控制的方法
Sep 06 Javascript
使用VScode 插件debugger for chrome 调试react源码的方法
Sep 13 Javascript
Vue安装浏览器开发工具的步骤详解
May 12 #Javascript
微信小程序缓存过期时间的使用详情
May 12 #Javascript
从0到1搭建element后台框架优化篇(打包优化)
May 12 #Javascript
Vue项目服务器部署之子目录部署方法
May 12 #Javascript
vue配置接口域名方法总结
May 12 #Javascript
详解babel升级到7.X采坑总结
May 12 #Javascript
babel7.x和webpack4.x配置vue项目的方法步骤
May 12 #Javascript
You might like
PHP中实现图片的锐化
2006/10/09 PHP
php zend 相对路径问题
2009/01/12 PHP
无法在发生错误时创建会话,请检查 PHP 或网站服务器日志,并正确配置 PHP 安装(win+linux)
2012/05/05 PHP
PHP递归调用的小技巧讲解
2013/02/19 PHP
解析PHP中的正则表达式以及模式匹配
2013/06/19 PHP
PHP网页游戏学习之Xnova(ogame)源码解读(一)
2014/06/23 PHP
php 算法之实现相对路径的实例
2017/10/17 PHP
Jquery之Bind方法参数传递与接收的三种方法
2014/06/24 Javascript
web 屏蔽BackSpace键实例代码
2016/12/24 Javascript
gulp加批处理(.bat)实现ng多应用一键自动化构建
2017/02/16 Javascript
jQuery序列化后的表单值转换成Json
2017/06/16 jQuery
JavaScript提升机制Hoisting详解
2019/10/23 Javascript
解决Vue-cli3没有vue.config.js文件夹及配置vue项目域名的问题
2020/12/04 Vue.js
[48:53]2014 DOTA2华西杯精英邀请赛 5 25 LGD VS VG第一场
2014/05/26 DOTA
[00:11]战神迅矛
2019/03/06 DOTA
[01:14:19]NAVI vs Mineski 2019国际邀请赛淘汰赛 败者组BO1 8.20.mp4
2020/07/19 DOTA
Python实现网站文件的全备份和差异备份
2014/11/30 Python
python利用lxml读写xml格式的文件
2017/08/10 Python
Python数据分析之如何利用pandas查询数据示例代码
2017/09/01 Python
基于Python os模块常用命令介绍
2017/11/03 Python
关于Django显示时间你应该知道的一些问题
2017/12/25 Python
Python利用Django如何写restful api接口详解
2018/06/08 Python
Python实现繁体中文与简体中文相互转换的方法示例
2018/12/18 Python
Django自带日志 settings.py文件配置方法
2019/08/30 Python
python多进程并行代码实例
2019/09/30 Python
python中的函数递归和迭代原理解析
2019/11/14 Python
在Tensorflow中实现leakyRelu操作详解(高效)
2020/06/30 Python
PyTorch如何搭建一个简单的网络
2020/08/24 Python
python3.8.3安装教程及环境配置的详细教程(64-bit)
2020/11/28 Python
python 实现简易的记事本
2020/11/30 Python
matplotlib事件处理基础(事件绑定、事件属性)
2021/02/03 Python
绘画设计学生的个人自我评价
2013/09/20 职场文书
女娲补天教学反思
2014/02/05 职场文书
2016年元旦致辞
2015/08/01 职场文书
解决redis sentinel 频繁主备切换的问题
2021/04/12 Redis
Android自定义scrollview实现回弹效果
2022/04/01 Java/Android