前言【pinia源码】系列文章主要分析pinia的实现原理。本系列文章源码参考piniav2.0.14。源码地址:https://github.com/vuejs/pinia官方文档:https://pinia.vuejs.org本文将分析createPinia的实现。使用createPinia创建一个供应用程序使用的pinia实例。import{createApp}from'vue'import{createPinia}from'pinia'importAppfrom'./App.vue'constpinia=createPinia()constapp=createApp(App)app.use(pinia).mount('#app')createPiniacreatePinia不接受任何参数,它会返回一个pinia实例。默认值:packages/pinia/src/createPinia.tsexportfunctioncreatePinia():Pinia{constscope=effectScope(true)conststate=scope.run[>>(()=>ref>({}))!让_p:菠萝['_p']=[]让toBeInstalled:PineapplePlugin[]=[]const菠萝:菠萝=markRaw({安装(应用程序:应用程序){setActivePineapple(菠萝)如果(!isVue2){菠萝._a=应用程序app.provide(piniaSymbol,pinia)app.config.globalProperties.$pinia=piniaif(__DEV__&&IS_CLIENT){registerPiniaDevtools(app,pinia)}toBeInstalled.forEach((plugin)=>_p.push(plugin))toBeInstalled=[]}},使用(plugin){if(!this._a&&!isVue2){toBeInstalled.push(plugin)}else{_p.push(plugin)}returnthis},_p,_a:null,_e:作用域,_s:newMap()。state,})if(__DEV__&&IS_CLIENT&&!__TEST__){pinia.use(devtoolsPlugin)}returnpinia}在createPinia中,会先创建一个effectscope对象(不知道effectScope的可以参考:RFC),使用ref创建一个响应式对象,然后声明两个数组_p,toBeInstalled,其中_p用于存储所有扩展store的插件,toBeInstalled用于存储安装前使用pinia.use()添加的插件。//创建效果范围constscope=effectScope(true)//创建响应对象conststate=scope.run][>>(()=>ref>>({}))!//存储扩展store的pluginlet_p:Pinia['_p']=[]//安装前,使用pinia.use()添加的pluginlettoBeInstalled:PiniaPlugin[]=[]然后声明一个pinia对象(这个pinia会用markRaw包裹起来,这样就不会转化为proxy),返回。constpinia:Pinia=markRaw({install(app:App){//...},use(plugin){//...},//扩展商店的插件_p,//app实例_a:null,//作用域对象_e:scope,//在这个pinia中注册的存储_s:newMap(),state,})if(__DEV__&&IS_CLIENT&&!__TEST__){pinia.use(devtoolsPlugin)}returnpinia这里重点介绍安装和使用方法。install使用app.use(pinia)时,触发pinia.install函数。安装时,首先执行setActivePinia(pinia),将pinia赋值给activePinia的一个全局变量。然后会判断是否是Vue2环境。如果不是Vue2,将app实例赋值给pinia._a,然后将pinia注入app实例,设置pinia为全局属性$pinia。如果此时toBeInstalled中有插件(安装前添加的插件),那么这些插件会被添加到pinia._p中,添加后toBeInstalled为空。安装(应用程序:App){setActivePinia(pinia)if(!isVue2){pinia._a=appapp.provide(piniaSymbol,pinia)app.config.globalProperties.$pinia=piniaif(__DEV__&&IS_CLIENT){registerPiniaDevtools(app,pinia)}toBeInstalled.forEach((plugin)=>_p.push(plugin))toBeInstalled=[]}}use使用use方法添加一个插件来扩展每个商店。它接受一个插件参数并返回当前的pinia。如果this._a为空且不在Vue2环境下,插件会暂存在toBeInstalled中,待安装时再安装。否则,直接添加到this._p。use(plugin){if(!this._a&&!isVue2){toBeInstalled.push(plugin)}else{_p.push(plugin)}returnthis}你可能会有疑问,Vue2是在安装和使用情况下判断的,pinia不处理Vue2的情况吗?其实不是,pinia提供了专门用来对付Vue2的PiniaVuePlugin。如果是Vue2,需要使用下面的方法:Vue.use(PiniaVuePlugin)constpinia=createPinia()newVue({el:'#app',pinia,})PiniaVuePlugin我们先来看看PiniaVuePlugin的实现.exportconstPiniaVuePlugin:Plugin=function(_Vue){//等效于//app.config.globalProperties.$pinia=pinia_Vue.mixin({beforeCreate(){constoptions=this.$optionsif(options.pinia){常量pinia=options.piniaasPiniaif(!(thisasany)._provided){constprovideCache={}Object.defineProperty(this,'_provided',{get:()=>provideCache,set:(v)=>Object.assign(provideCache,v),})};(thisasany)._provided[piniaSymbolasany]=pinia如果(!this.$pinia){this.$pinia=pinia}pinia._a=thisasanyif(IS_CLIENT){setActivePinia(pinia)if(__DEV__){registerPiniaDevtools(pinia._a,pinia)}}}elseif(!this.$pinia&&options.parent&&options.parent.$pinia){this.$pinia=options.parent.$pinia}},destroyed(){deletethis._pStores},})}PiniaVuePlugin通过全局混合对象来支持pinia。这个对象有两个生命周期函数:beforeCreate和destroy。在beforeCreate中设置this.$pinia,这样vue实例就可以通过this.$pinia获取pinia]