Skip to content

install

什么是Vuex? Vuex是Vue官方专门为Vue.js应用程序开发的状态管理模式+库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。而Vuex4专门用于适配Vue3。

如何使用?

js

import { createApp } from 'vue'
import { createStore } from 'vuex'

// 创建一个新的store实例
const store = createStore({
  state: {
    count: 0
  },
  mutations: {
    increment(state) {
      state.count++
    }
  }
})

const app = createApp({})

// 将store实例作为插件安装
app.use(store)

app.use的源码

位于core/packages/runtime-core/src/apiCreateApp.ts文件

createAppContext中对use方法进行了定义

ts
export function createAppAPI<HostElement>(
  render: RootRenderFunction<HostElement>,
  hydrate?: RootHydrateFunction,
): CreateAppFunction<HostElement> {
  return function createApp(rootComponent, rootProps = null) {
    
    // ... do something

    const installedPlugins = new WeakSet()

    const app: App = (context.app = {
      _uid: uid++,
      _component: rootComponent as ConcreteComponent,
      _props: rootProps,
      _container: null,
      _context: context,
      _instance: null,

      // ... do something

      use(plugin: Plugin, ...options: any[]) {
        if (installedPlugins.has(plugin)) {
          __DEV__ && warn(`Plugin has already been applied to target app.`)
        } else if (plugin && isFunction(plugin.install)) {
          installedPlugins.add(plugin)
          plugin.install(app, ...options)
        } else if (isFunction(plugin)) {
          installedPlugins.add(plugin)
          plugin(app, ...options)
        } else if (__DEV__) {
          warn(
            `A plugin must either be a function or an object with an "install" ` +
              `function.`,
          )
        }
        return app
      },
    })
    // ... dosomething
    return app
  }
}

app.use方法接收两个参数,分别是plugin和options,options是一个数组,对于plugin的类型,vue进行了一系列定义。

  • 类型 pluginInstallFunction 是一个泛型类型,表示一个函数。Options默认为一个数组类型,且判断传入的Options为数组后将其类型更改为unknow[],否则为unknow。
  • 类型ObjectPlugin是一个泛型类型,表示一个对象,它有一个install属性,类型是pluginInstallFunction<Options>
  • 类型FunctionPlugin 。。。
ts
type PluginInstallFunction<Options = any[]> = Options extends unknown[]
  ? (app: App, ...options: Options) => any
  : (app: App, options: Options) => any
export type ObjectPlugin<Options = any[]> = {
  install: PluginInstallFunction<Options>
}
export type FunctionPlugin<Options = any[]> = PluginInstallFunction<Options> &
  Partial<ObjectPlugin<Options>>

export type Plugin<Options = any[]> =
  | FunctionPlugin<Options>
  | ObjectPlugin<Options>
  • use方法执行后会先进入一系列判断流程
  • 首先判断当前插件是否重复注册,如果是则发出警告。
js
  if (installedPlugins.has(plugin)) {
    __DEV__ && warn(`Plugin has already been applied to target app.`)
  }
  • 其次判断当前插件中的install方法是否为函数,如果是则将当前插件添加到WeakSet中,并执行install方法。
js
  else if (plugin && isFunction(plugin.install)) {
    installedPlugins.add(plugin)
    plugin.install(app, ...options)
  }
  • 其次判断plugin是否为函数,如果是则执行同上一步的流程(只不过执行的是plugin方法)
js
 else if (isFunction(plugin)) {
    installedPlugins.add(plugin)
    plugin(app, ...options)
  }
  • 否则,警告一个插件必须是一个函数或者对象,并且拥有install方法。
js
  else if (__DEV__) {
    warn(
      `A plugin must either be a function or an object with an "install" ` +
        `function.`,
    )
  }

vuex中的install方法

位于/src/store.js文件

该文件主要定义了Store构造器。在Store构造器中存在install方法。

ts
  install (app, injectKey) {
    app.provide(injectKey || storeKey, this)
    app.config.globalProperties.$store = this
    const useDevtools = this._devtools !== undefined
      ? this._devtools
      : __DEV__ || __VUE_PROD_DEVTOOLS__
    if (useDevtools) {
      addDevtools(app, this)
    }
  }

根据上面app.use源码我们可以得知,app.use方法依赖于库中的install方法。在vuex的方法中通过app.provide方法向vue应用全局注入store实例(默认key值为store);并通过app.config.globalProperties向vue全局挂载$store属性,用于获取当前Store实例。同时根据用户配置决定是否启用vue官方开发者工具。

app.provide源码

在上面install方法中使用了app.provide向应用注入store实例,那么app.provide方法又干了些什么呢?

位于 ./packages/runtime-core/src/apiInject.ts文件

ts
import { currentInstance } from './component'
export function provide<T, K = InjectionKey<T> | string | number>(
  key: K,
  value: K extends InjectionKey<infer V> ? V : T,
) {
  if (!currentInstance) {
    if (__DEV__) {
      warn(`provide() can only be used inside setup().`)
    }
  } else {
    let provides = currentInstance.provides
    // by default an instance inherits its parent's provides object
    // but when it needs to provide values of its own, it creates its
    // own provides object using parent provides object as prototype.
    // this way in `inject` we can simply look up injections from direct
    // parent and let the prototype chain do the work.
    const parentProvides =
      currentInstance.parent && currentInstance.parent.provides
    if (parentProvides === provides) {
      provides = currentInstance.provides = Object.create(parentProvides)
    }
    // TS doesn't allow symbol as index type
    provides[key as string] = value
  }
}
  • 获取当前组件实例上的provides
  • 判断父组件是否存在provides
  • 存在则赋值当前provides为父组件provides
  • 保存新增的provides

KESHAOYE-知识星球