在看服务端渲染的时候,看到在store.js中也要使用Vue.use(vuex)
,就想看看这一行代码到底做了啥,翻看了源码:github.com/vuejs/vuex/...
(没事干的记录,大佬轻喷,叠甲保命)
扯淡的打包
其rollup.config.js
中
javascript
const configs = [
{
input: 'src/index.js',
file: 'dist/vuex.esm-browser.js',
format: 'es',
browser: true,
env: 'development'
},
{
input: 'src/index.js',
file: 'dist/vuex.esm-browser.prod.js',
format: 'es',
browser: true,
env: 'production'
},
// ...
{
input: 'src/index.cjs.js',
file: 'dist/vuex.global.prod.js',
format: 'iife',
minify: true,
env: 'production'
},
]
入口只有index.cjs.js
和index.js
,但是这俩的导出:
javascript
export {
Store,
storeKey,
createStore,
useStore,
mapState,
mapMutations,
mapGetters,
mapActions,
createNamespacedHelpers,
createLogger
}
压根没有install
方法,最后全局搜索才发现在src/index.mjs
中的导出
javascript
export {
Vuex as default,
version,
Store,
storeKey,
createStore,
install,
useStore,
mapState,
mapMutations,
mapGetters,
mapActions,
createNamespacedHelpers,
createLogger
}
才有导出的install
方法,但是index.mjs
并未出现在rollup.config.js
中,直到在script/build
中
javascript
async function run() {
await Promise.all([build(), copy()])
checkAllSizes()
}
async function copy() {
await fs.copy('src/index.mjs', 'dist/vuex.mjs')
}
说白了就是overwrite if exist一个dist/vuex.mjs
文件,内容和src/index.mjs
一样。
但是仍然存在一个问题,vuex
并没有挂载install
方法,install
方法是实现在Store
类中的。并且实际项目运行了后发现,
Vuex
确实没有install
方法。那为啥会使用Vue.use(Vuex)
?
答案就是:
Vue.use(Vuex)
是Vue 2 接入 Vuex 3.x的用法,而Vue 3 接入 的是Vuex 4,没有挂载install
方法的是Vuex 4(但是代码里却依旧留着install
的导出。。。不知道为了啥)
Vuex 3.x的install方法
回顾Vuex 3.x的使用方式
javascript
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({/* ... */})
new Vue({
el: '#app',
store
})
javascript
export function install (_Vue) {
if (Vue && _Vue === Vue) {
if (__DEV__) {
console.error(
'[vuex] already installed. Vue.use(Vuex) should be called only once.'
)
}
return
}
Vue = _Vue
applyMixin(Vue)
}
// mixin.js
export default function (Vue) {
const version = Number(Vue.version.split('.')[0])
if (version >= 2) {
Vue.mixin({ beforeCreate: vuexInit })
} else {
// override init and inject vuex init procedure
// for 1.x backwards compatibility.
const _init = Vue.prototype._init
Vue.prototype._init = function (options = {}) {
options.init = options.init
? [vuexInit].concat(options.init)
: vuexInit
_init.call(this, options)
}
}
/**
* Vuex init hook, injected into each instances init hooks list.
*/
function vuexInit () {
const options = this.$options
// store injection
if (options.store) {
this.$store = typeof options.store === 'function'
? options.store()
: options.store
} else if (options.parent && options.parent.$store) {
this.$store = options.parent.$store
}
}
}
-
vue.use(Vuex)
时是将vuexInit
作为beforeCreate
回调 -
如果每个非根app组件的Option都没有设置store属性,那么就会子组件使用父组件的store,然后由于根app组件传递了store属性,那么就会走if上半判断,将store挂载到根app组件的实例上,然后app的子组件将使用app的store,然后一层一层地这样传下去(好家伙,手动provide inject是吧)
-
至于为啥vue 2明明有
provide
和inject
,还要使用这种手动的方式进行,是因为这俩api是vue
2.2+ 版本以后才有的,一切为了兼容性咯
Vuex 4.x的install方法
在理解Vue 4的install 方法之前,需要先回顾Vue 4的使用方式
javascript
import { createApp } from 'vue'
import { createStore } from 'vuex'// Create a new store instance.
const store = createStore({ /* ... */})
const app = createApp({ /* your root component */ })// Install the store instance as a plugin
app.use(store)
// in some component
const store = useStore()
kotlin
class Store {
// ....
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)
}
}
}
// injectKey.js
import { inject } from 'vue'
export const storeKey = 'store'
export function useStore (key = null) {
return inject(key !== null ? key : storeKey)
}
可以看出来,
- 组件内部获取Store实例的方式是通过
provide
和inject
实现的。app.provide(injectKey || storeKey, this)
是为了配合useStore
的 - 同时将
store
实例挂载到全局的Vue上(感觉这个是为了兼容vuex 3的使用习惯) - 由于使用了vue的
provide
和inject
,就不用再一层一层地传递store了,因此也就不用使用vue.use(Vuex)
,以及将store作为根组件的属性。