外观
createPinia
TIP
通过createPinia创建一个pinia实例,供应用程序使用。
源码位置:packages/pinia/src/createPinia.ts
createPinia不接受任何参数,它会返回一个pinia实例。
在createPinia中首先会创建一个effect作用域对象(如果你不了解effectScope,可参考:RFC),使用ref创建一个响应式对象。紧接着声明了两个数组_p、toBeInstalled,其中_p用来存储扩展store的所有插件,toBeInstalled用来存储那些未install之前使用pinia.use()添加的的plugin。
ts
// 创建effect作用域
const scope = effectScope(true)
// 创建响应式对象
const state = scope.run<Ref<Record<string, StateTree>>>(() =>
ref<Record<string, StateTree>>({})
)!
// 存储扩展store的plugin
let _p: Pinia['_p'] = []
// install之前,使用pinia.use()添加的plugin
let toBeInstalled: PiniaPlugin[] = []
然后声明一个pinia对象(这个pinia会使用markRaw进行包装,使其不会转为proxy),将其return。
ts
const pinia: Pinia = markRaw({
install(app: App) {
// ...
},
use(plugin) {
// ...
},
// 扩展store的plugins
_p,
// app实例
_a: null,
// effecet作用域对象
_e: scope,
// 在这个pinia中注册的stores
_s: new Map<string, StoreGeneric>(),
state,
})
if (__DEV__ && IS_CLIENT && !__TEST__) {
pinia.use(devtoolsPlugin)
}
return pinia
这里重点看下install和use方法。
install
当使用app.use(pinia)时,触发pinia.install函数。在install中首先执行了setActivePinia(pinia),它会将pinia赋给一个activePinia的全局变量。
然后会判断是不是Vue2环境。如果不是Vue2,将app实例赋给pinia._a,然后将pinia注入到app实例,并将pinia设置为全局属性$pinia。如果此时toBeInstalled中有plugins(在install之前添加的plugins),那么会把这些plugins添加到pinia._p中,添加完之后,置空toBeInstalled。
ts
install(app: App) {
setActivePinia(pinia)
if (!isVue2) {
pinia._a = app
app.provide(piniaSymbol, pinia)
app.config.globalProperties.$pinia = pinia
if (__DEV__ && IS_CLIENT) {
registerPiniaDevtools(app, pinia)
}
toBeInstalled.forEach((plugin) => _p.push(plugin))
toBeInstalled = []
}
}
use
使用use方法可添加一个plugin以扩展每个store。它接收一个plugin参数,返回当前pinia。
如果this._a是空的,并且不是Vue2环境,会将plugin中暂存到toBeInstalled中,等待install时进行安装。否则,直接添加到this._p中。
ts
use(plugin) {
if (!this._a && !isVue2) {
toBeInstalled.push(plugin)
} else {
_p.push(plugin)
}
return this
}
你可能有疑问,在install、use中都判断了Vue2的情况,难道pinia没有处理Vue2的情况吗?其实并不是,pinia提供了PiniaVuePlugin专门用来处理Vue2的情况。
如果是Vue2需要使用如下方式:
ts
Vue.use(PiniaVuePlugin)
const pinia = createPinia()
new Vue({
el: '#app',
pinia,
})
PiniaVuePlugin
我们来看下PiniaVuePlugin的实现方式。
ts
export const PiniaVuePlugin: Plugin = function (_Vue) {
// Equivalent of
// app.config.globalProperties.$pinia = pinia
_Vue.mixin({
beforeCreate() {
const options = this.$options
if (options.pinia) {
const pinia = options.pinia as Pinia
if (!(this as any)._provided) {
const provideCache = {}
Object.defineProperty(this, '_provided', {
get: () => provideCache,
set: (v) => Object.assign(provideCache, v),
})
}
;(this as any)._provided[piniaSymbol as any] = pinia
if (!this.$pinia) {
this.$pinia = pinia
}
pinia._a = this as any
if (IS_CLIENT) {
setActivePinia(pinia)
if (__DEV__) {
registerPiniaDevtools(pinia._a, pinia)
}
}
} else if (!this.$pinia && options.parent && options.parent.$pinia) {
this.$pinia = options.parent.$pinia
}
},
destroyed() {
delete this._pStores
},
})
}
PiniaVuePlugin通过向全局混入一个对象来支持pinia。这个对象有两个生命周期函数:beforeCreate、destory。
在beforeCreate中设置this.$pinia,以使vue实例可以通过this.$pinia的方式来获取pinia