export function initUse (Vue: GlobalAPI) { // 这里的Vue是构造器函数. // 通过以下源码: // vue-dev/src/core/global-api/index.js initGlobalAPI()中 // vue-dev/src/core/index.js 这里执行了initGlobalAPI() => 初始化一些全局api // Vue.use(): 安装Vue.js的插件 // 如果插件是一个对象,必须提供 install 方法 // 如果插件是一个函数,它会被作为 install 方法 // install 方法调用时,会将 Vue 作为参数传入 Vue.use = function (plugin: Function | Object) { // installedPlugins存储install后的插件 const installedPlugins = (this._installedPlugins || (this._installedPlugins = [])) if (installedPlugins.indexOf(plugin) > -1) { // 同一个插件只会安装一次 return this } // additional parameters // 除了插件外的其他参数 Vue.use(MyPlugin, { someOption: true }) const args = toArray(arguments, 1) // 往args存储Vue构造器, 供插件的install方法使用 args.unshift(this) // 分情况执行插件的install方法, 把this(Vue), 参数抛回给install方法 // 所以我们常说, install这个方法的第一个参数是 Vue 构造器,第二个参数是一个可选的选项对象: if (typeof plugin.install === 'function') { // plugin是一个对象 plugin.install.apply(plugin, args) } else if (typeof plugin === 'function') { // plugin是一个函数 plugin.apply(null, args) } // install之后会存储该插件避免重复安装 installedPlugins.push(plugin) return this } }
Vuex源码
我们都知道开发一个Vue.js 的插件应该暴露一个 install 方法。这个方法的第一个参数是 Vue 构造器,第二个参数是一个可选的选项对象:
那么我们首先就是看Vuex的install方法是怎么实现的
源码位置: vuex-dev/src/store.js
let Vue // bind on install // install: 装载vuex到vue, Vue.use(Vuex)也是执行install方法 // 关于Vue.use()源码. vue-dev/src/core/global-api/use.js export function install (_Vue) { if (Vue && _Vue === Vue) { if (process.env.NODE_ENV !== 'production') { console.error( '[vuex] already installed. Vue.use(Vuex) should be called only once.' ) } return } // 首次安装插件, 会把局部的Vue缓存到全局的window.Vue. 主要为了避免重复调用Vue.use() Vue = _Vue applyMixin(Vue) }
applyMixin()
源码位置: vuex/src/mixin.js
export default function (Vue) { const version = Number(Vue.version.split('.')[0]) if (version >= 2) { // 如果是2.x.x以上版本,注入一个全局mixin, 执行vueInit方法 Vue.mixin({ beforeCreate: vuexInit }) } else { // override init and inject vuex init procedure // for 1.x backwards compatibility. // 重写Vue原型上的_init方法, 注入vueinit方法 _init方法见 vue-dev/src/core/instance/init.js const _init = Vue.prototype._init // 作为缓存变量 Vue.prototype._init = function (options = {}) { options.init = options.init ? [vuexInit].concat(options.init) : vuexInit // 重新执行_init _init.call(this, options) } } /** * Vuex init hook, injected into each instances init hooks list. */ // 注入store到Vue构造器 function vuexInit () { // 这里的this. 指的是Vue构造器 /** * new Vue({ * ..., * store, * route * }) */ // options: 就是new Vue(options) // 源码见 vue-dev/src/core/instance/init.js initMixin方法 const options = this.$options // store injection // store是我们使用new Vuex.Store(options)的实例 // 注入store到Vue构造函数上的$store属性上, 所以我们在Vue组件里面使用this.$store来使用 if (options.store) { // options.store为真说明是根节点root this.$store = typeof options.store === 'function' ? options.store() : options.store } else if (options.parent && options.parent.$store) { // 子组件直接从父组件中获取$store,这样就保证了所有组件都公用了全局的同一份store this.$store = options.parent.$store } } }
至于install方法Vuex是如果执行的?
export class Store { constructor (options = {}) { // 浏览器环境下安装vuex if (!Vue && typeof window !== 'undefined' && window.Vue) { install(window.Vue) } ... } }