前言Pinia,读作/pi?nj?/,来自西班牙语pi?a。它的意思是菠萝,意思是它是由许多像菠萝一样的小块组成的。在Pinia中,各个Store独立存在,共同进行状态管理。Pinia最初是由Vue.js团队成员开发的,目的是探索Vuex的下一次迭代可能是什么样子。在此过程中,Pinia实现了大部分Vuex5提案,因此将其取代。与Vuex相比,Pinia提供了更简单的API,更少的规范,以及Composition-API风格的API。此外,使用TypeScript具有可靠的类型推理支持。Pinia与Vuex3.x/4.x的不同突变不再存在。只有状态、吸气剂、动作。动作支持同步和异步方法来修改状态。使用具有可靠类型推理支持的TypeScript。不再有模块的嵌套,只有Store的概念,Stores可以相互调用。支持插件扩展,非常方便实现本地存储等功能。更轻巧,压缩后体积只有2kb左右。既然荸荠这么香,还等什么,一起来用吧!基本用法安装:npminstallpinia在main.js中引入Pinia://src/main.jsimport{createPinia}from'pinia'constpinia=createPinia()app.use(pinia)在src/stores目录下定义一个Store创建一个counter.js文件并使用defineStore()来定义一个商店。defineStore()的第一个参数是storeId,第二个参数是一个选项对象://src/stores/counter.jsimport{defineStore}from'pinia'exportconstuseCounterStore=defineStore('counter',{state:()({count:0}),getters:{doubleCount:(state)state.count*2},actions:{increment(){this.count++}}})我们还可以使用更高级的方法,将第二个参数传入定义Store的函数://src/stores/counter.jsimport{ref,computed}from'vue'import{defineStore}from'pinia'exportconstuseCounterStore=defineStore('counter',(){constcount=ref(0)constdoubleCount=computed(()count.value*2)functionincrement(){count.value++}return{count,doubleCount,increment}})在组件中使用导入刚刚在组件中定义的函数,并执行这个functiontogetthestore:{{counterStore.doubleCount}}
这是基本的用法,接下来介绍一下各个选项的作用和插件的使用在方法State解构storestore是用reactive包裹的对象,如果直接解构,会失去响应能力。我们可以使用storeToRefs()来解构它:
{{count}}
{{doubleCount}}
除了修改store,还可以直接使用store.count++修改store,我们也可以调用$patch方法进行修改。$patch性能更高,可以同时修改多个状态。但是,这种修改集合的方法(例如从数组中添加、删除和插入元素)需要创建一个新的集合,代价太大。所以$patch方法也接受一个函数来批量修改:store我们可以通过$subscribe()方法监听store状态的变化,类似于Vuex的subscribe方法。与watch()相比,使用$subscribe()的好处是store多次状态变化后,回调函数只会执行一次。也可以监听pinia实例上所有store的变化。//src/main.jsimport{watch}from'vue'import{createPinia}from'pinia'constpinia=createPinia()watch(pinia.state,(state)=>{//每当状态改变时,所有状态持久化到本地存储localStorage.setItem('piniaState',JSON.stringify(state))},{deep:true})Getters访问存储实例在大多数情况下,getter只会依赖于状态state。但是有时候,它会使用其他的getter,这时候我们就可以通过this来访问当前的store实例。//src/stores/counter.jsimport{defineStore}from'pinia'exportconstuseCounterStore=defineStore('counter',{state:()({count:0}),getters:{doubleCount(state){返回状态。count*2},doublePlusOne(){returnthis.doubleCount+1}}})访问其他Stores的getter要使用其他Stores的getter,可以直接在getter内部使用://src/stores/counter.jsimport{defineStore}from'pinia'import{useOtherStore}from'./otherStore'exportconstuseCounterStore=defineStore('counter',{state:()({count:1}),getters:{composeGetter(state){constotherStore=useOtherStore()returnstate.count+otherStore.count}}})传递参数给gettergetter本质上是一个计算器,不能传递任何参数给它。但是,我们可以让它返回一个函数来接受参数://src/stores/user.jsimport{defineStore}from'pinia'exportconstuseUserStore=defineStore('user',{state:()({users:[{id:1,name:'Tom'},{id:2,name:'Jack'}]}),getters:{getUserById:(state)=>{return(userId)=>state.users.find((user)=>user.id===userId)}}})在组件中使用:const{getUserById}=storeToRefs(userStore)
User:{{getUserById(2)}}
注意:如果这样使用,getter将不会缓存,它只会作为一个普通函数使用。一般不推荐这种用法,因为在组件中定义一个函数就可以实现同样的功能。Actions访问store实例和getters一样,actions可以通过this访问store实例。不同之处在于动作可以是异步的。//src/stores/user.jsimport{defineStore}from'pinia'exportconstuseUserStore=defineStore('user',{state:()({userData:null}),actions:{asyncregisterUser(login,password){try{this.userData=awaitapi.post({login,password})}catch(error){returnerror}}}})访问其他Storeaction要使用其他Storeaction,也可以直接在action内部使用://src/stores/setting.jsimport{defineStore}from'pinia'import{useAuthStore}from'./authStore'exportconstuseSettingStore=defineStore('setting',{state:()({preferences:null}),actions:{asyncfetchUserPreferences(preferences){constauthStore=useAuthStore()if(authStore.isAuthenticated()){this.preferences=awaitfetchPreferences()}else{thrownewError('Usermustbeauthenticated!')}}}})以上就是Pinia的详细用法,是不是比Vuex简单多了?此外,插件也是Pinia的一大亮点。我个人认为非常实用。下面重点说说吧。由于插件是低级API,因此PaniaStore完全支持扩展。以下是可以扩展的功能列表:-向Store添加新状态-在定义Store时添加新选项-为Store添加新方法-包装现有方法-更改甚至取消操作-实现像本地存储这样的副作用-仅适用于具体的Store使用方法Pinia插件是一个函数,它接受一个可选的参数上下文,它包含四个属性:app实例、pinia实例、当前商店和选项对象。函数也可以返回一个对象,对象的属性和方法会分别添加到state和action中。exportfunctionmyPiniaPlugin(context){context.app//使用createApp()创建的应用程序实例(仅限Vue3)context.pinia//使用createPinia()创建的piniacontext.store//存储插件正在扩展的上下文。options//传入defineStore()的options对象(第二个参数)//...return{hello:'world',//给state添加一个hellostatechangeHello(){//给actions添加一个changeHello方法this.hello='pinia'}}}然后使用pinia.use()将此函数传递给pinia://src/main.jsimport{createPinia}from'pinia'constpinia=createPinia()pinia.use(myPiniaPlugin)向Store添加新状态可以简单地通过返回一个对象来为每个商店添加状态来完成:pinia.use(()({hello:'world'}))您也可以直接在商店上设置属性以添加状态,为了让它在devtools中可用,你还需要设置store.$state:import{ref,toRef}from'vue'pinia.use(({store}){consthello=ref('word')store.$state.hello=hellostore.hello=toRef(store.$state,'hello')})您还可以在use方法之外定义一个状态,共享全局引用或计算:import{ref}from'vue'constglobalSecret=ref('secret')pinia.use(({store})=>{//`secret`sharedstore.$s在所有商店之间tate.secret=globalSecretstore.secret=globalSecret})在定义Store时添加新选项您可以在定义Store时添加新选项以在插件中使用它们例如,可以添加去抖动选项以允许对所有操作进行去抖动://src/stores/search.jsimport{defineStore}from'pinia'exportconstuseSearchStore=defineStore('search',{actions:{searchContacts(){//...},searchContent(){//...}},debounce:{//操作searchContacts并去抖300mssearchContacts:300,//操作searchContent并去抖500mssearchContent:500}})Then使用插件读取Takethatoption,wrapandreplacetheoriginalop://src/main.jsimport{createPinia}from'pinia'import{debounce}from'lodash'constpinia=createPinia()pinia.use(({options,store})=>{if(options.debounce){//我们用新动作覆盖旧动作returnObject.keys(options.debounce).reduce((debouncedActions,action)=>{debouncedActions[action]=debounce(store[action],options.debounce[action])returndebouncedActions},{})}})这样组件中使用action的方法就可以去抖了,是不是很方便!实现本地存储。相信大家在使用Vuex的时候都有这样的困惑。F5刷新,数据没了。在我们眼里,这是正常的,但在考生眼里,这是一个bug。在Vuex中实现本地存储比较麻烦。需要将状态一一存储在本地,在取数据的时候处理。使用Pinia,一个插件即可完成所有工作。这次我们就不自己写了,而是直接安装开源插件。npmipinia-plugin-persist然后导入插件并将此插件传递给pinia://src/main.js.use(piniaPluginPersist)然后在定义存储时启用持久化://src/stores/counter.jsimport{defineStore}from'pinia'exportconstuseCounterStore=defineStore('counter',{state:()({count:1}),//启用数据缓存persist:{enabled:true}})这样无论你用什么姿势刷新,数据都不会丢失!默认情况下,storeId会作为键值,state中的所有状态都会存储在sessionStorage中。我们也可以通过strategies来修改://启用数据缓存persist:{enabled:true,strategies:[{key:'myCounter',//存储的key值,默认是storeIdstorage:localStorage,//存储的位置,defaultissessionStoragepaths:['name','age'],//需要存储的状态,默认存储所有状态}]}ok,今天的分享就到这里。不知道大家对皮尼亚有没有更好的了解,欢迎在评论区留言讨论。总结Pinia通常比Vuex更简单、更轻便,但功能更强大。也许这就是它取代Vuex的原因。另外Pinia还可以结合Vue2中的地图功能使用,有兴趣的同学可以研究一下。