詳解Vue3狀態管理庫Pinia的使用方法
什麼是pinia?怎麼使用?這篇文章就來帶大家了解Vue新一代的狀態管理庫--Pinia,希望對大家有幫助!
在2020 年9 月Vue 3 發布正式版本之後,2021 年2 月Vuex 也發布了適配Vue 3 的4.0 版本,但是在2021 年8 月底,由Vue 核心團隊成員Eduardo 主要貢獻的全新Vue 狀態共享庫發布2.0 版本,並在同年11 月,尤大正式指定Pinia 為Vue 的官方狀態庫(現在Vue官網也已經將Vuex 替換為了Pinia)。 (學習影片分享:vue影片教學)
什麼是Pinia
Pinia 與Vuex 一樣,是作為Vue 的「狀態儲存庫”,用來實現跨頁面/元件 形式的資料狀態共用。
在平常的開發過程中,Vue 元件之間可以透過Props 和Events 實作元件之間的訊息傳遞,對於跨層級的元件也可以透過EventBus 來實作通訊。但是在大型專案中,通常需要在瀏覽器保存多種資料和狀態,而使用Props/Events 或EventBus 是很難維護和擴展的。所以才有了 Vuex 和 Pinia。
Pinia 為何能取代Vuex
作為Vue 開發者都知道,Vuex 作為Vue 的老牌官方狀態庫,已經和Vue 一起存在了很長時間,為什麼現在會被Pinia 取代呢?
官方的說法主要是以下幾點:
取消 mutations。因為在大部分開發者眼中,mutations 只支援同步修改狀態資料,而actions 雖然支援非同步,卻還是要在內部呼叫mutations 去修改狀態,無疑是非常繁瑣和多餘的
所有的程式碼都是TypeScript 編寫的,並且所有介面都盡可能的利用了TypeScript 的類型推斷,而不像Vuex 一樣需要自訂TS 的包裝器來實現對TypeScript 的支援
不像Vuex 一樣需要在實例/Vue原型上註入狀態依賴,而是透過直接引入狀態模組、呼叫getter/actions 函數來完成狀態的更新獲取;並且因為自身對TypeScript 的良好支援和類型推斷,開發者可以享受很優秀的程式碼提示
-
不需要預先註冊狀態資料,預設都是根據程式碼邏輯自動處理的;並且可以在使用中隨時註冊新的狀態
沒有Vuex 的modules 巢狀結構,所有狀態都是扁平化管理的。也可以理解為pinia 註冊的狀態都類似vuex 的module,只是pinia 不需要統一的入口來註冊所有狀態模組
雖然是扁平化的結構,但是依然支持每個狀態之間的互引和嵌套
#不需要namespace 命名空間,得利於扁平化結構,每個狀態在註冊時即使沒有宣告狀態模組名稱,pinia 也會預設對它進行處理
總結一下就是:Pinia 在實作Vuex 全域狀態共享的功能前提下,改善了狀態儲存結構,優化了使用方式,簡化了API 設計與規範;並且基於TypeScript 的類型推斷,為開發者提供了良好的TypeScript 支援與程式碼提示。
如何使用
至於Pinia 在專案中的安裝,大家應該都知道,直接透過套件管理工具安裝即可。
1. 註冊Pinia 實例
以Vue 3 專案為例,只需要在入口檔案main.ts 中引入即可完成Pinia 的註冊。
import { createApp } from 'vue' import { createPinia } from 'pinia' const app = createApp(App) const pinia = createPinia() app.use(pinia)
當然,因為支援createApp 支援鍊式呼叫,所以也可以直接寫成
createApp(App).use(createPinia()).mount ('#app')
.
此時createPinia() 建立的是一個根實例,在app.use的時候會在app 中註入該實例,並且配置一個app.config.globalProperties.$pinia 也指向該實例。
2. 定義狀態Store
在註冊一個Pinia 狀態模組的時候,可以透過defineStore 方法建立一個狀態模組函數(之所以是函數,是因為後面呼叫的時候需要透過函數的形式來獲得裡面的狀態)。
deineStore 函數的 TypeScript 定義如下:
function defineStore<Id, S, G, A>(id, options): StoreDefinition<Id, S, G, A> function defineStore<Id, S, G, A>(options): StoreDefinition<Id, S, G, A> function defineStore<Id, SS>(id, storeSetup, options?): StoreDefinition<Id, _ExtractStateFromSetupStore<SS>, _ExtractGettersFromSetupStore<SS>, _ExtractActionsFromSetupStore<SS>> type Id = ID extends string type storeSetup = () => SS type options = Omit<DefineStoreOptions<Id, S, G, A>, "id"> | DefineStoreOptions<Id, S, G, A> | DefineSetupStoreOptions<Id, _ExtractStateFromSetupStore<SS>, _ExtractGettersFromSetupStore<SS>, _ExtractActionsFromSetupStore<SS>>
可以看到该函数最多接收 3个参数,但是我们最常用的一般都是第一种或者第二种方式。这里以 第一种方式 例,创建一个状态模块函数:
// 该部分节选字我的开源项目 vite-vue-bpmn-process import { defineStore } from 'pinia' import { defaultSettings } from '@/config' import { EditorSettings } from 'types/editor/settings' const state = { editorSettings: defaultSettings } export default defineStore('editor', { state: () => state, getters: { getProcessDef: (state) => ({ processName: state.editorSettings.processName, processId: state.editorSettings.processId }), getProcessEngine: (state) => state.editorSettings.processEngine, getEditorConfig: (state) => state.editorSettings }, actions: { updateConfiguration(conf: Partial<EditorSettings>) { this.editorSettings = { ...this.editorSettings, ...conf } } } })
其中的 options 配置项包含三个部分:
- state:状态的初始值,推荐使用的是一个 箭头函数,方便进行类型推断
- getters:状态的获取,是一个对象格式;推荐配置为每个 getters 的对象属性为 箭头函数,方便进行类型推断;在使用时等同于获取该函数处理后的 state 状态结果;并且与 Vue 的计算属性一样,该方法也是惰性的,具有缓存效果
- actions:类似 Vue 中的 methods 配置项,支持异步操作,主要作用是 处理业务逻辑并更新状态数据;另外,此时的 actions 是一个 函数集合对象,与 getters 不同的是 不建议使用箭头函数。并且函数内部的 this 就指向当前 store 的 state。
注意:getters 的函数定义中 第一个参数就是当前 store 的状态数据 state,而 actions 中的函数参数为 实际调用时传递的参数,可以传递多个,内部通过 this 上下文 直接访问 state 并进行更新。
3. 组件使用(配合 setup)
众所周知,vue 3 最大的亮点之一就是 组合式API(Composition API),所以我们先以组件配合 setup 使用。
import { defineComponent, ref, computed } from 'vue' import { storeToRefs } from 'pinia' import { EditorSettings } from 'types/editor/settings' import editorStore from '@/store/editor' export default defineComponent({ setup(props) { const editor = editorStore() // 直接获取 state 状态 const { editorSettings } = storeToRefs(editor) // 使用 computed const editorSettings = computed(() => editor.editorSettings) // getters const prefix = editor.getProcessEngine // 更新方式 1:调用 actions editorStore.updateConfiguration({}) // 更新方式 2:直接改变 state 的值 editorStore.editorSettings = {} // 更新方式 3:调用 $patch editorStore.$patch((state) => { state.editorSettings = {} }) return { editorStore } } })
这里对以上几种处理方式进行说明:
获取值:
可以通过 解构 获取 state 定义的数据,但是 解构会失去响应式,所以需要用 storeToRefs 重新对其进行响应式处理
通过 computed 计算属性,好处是 可以对 state 中的状态数据进行组合
通过定义的 getters 方法来获取值,这种方式获取的结果本身就是 响应式的,可以直接使用
更新值:
- 首先是可以 直接改变 state 的状态值,缺点是多次使用容易有重复代码,且不好维护;也会影响代码的可读性
- 通过定义的 actions 更新,也算是推荐方法之一;在后续迭代和扩展中,只需要维护好 store 中的代码即可
- $patch: 这个方式 可以接收一个对象或者函数,但是 推荐使用箭头函数(函数参数为状态数据 state);因为如果是对象,则需要根据新数据和当前状态 重建整个 state,增加了很多的性能损耗;而使用箭头函数,其实就与 actions 中的方式类似,可以 按代码逻辑修改指定的状态数据
4. 组件使用(没有 setup)
而在传统的 optionsAPI 模式的组件中(也没有配置 setup),Pinia 也提供了与 Vuex 一致的 API:mapState,mapGetters,mapActions,另外还增加了 mapStores 用来访问所有已注册的 store 数据,新增了 mapWritableState 用来 定义可更新状态;也因为 pinia 没有 mutations,所以也取消了 mapMutations 的支持。
mapGetters 也只是为了方便迁移 Vuex 的组件代码,后面依然建议 使用 mapState 替换 mapGetters
<template> <div> <p>{{ settings }}</p> <p>{{ processEngine }}</p> <button @click="updateConfiguration({})">调用 action</button> <button @click="update">调用 mapWritableState</button> </div> </template> <script> import { defineComponent, ref, storeToRefs } from 'vue' import { mapState, mapActions, mapWritableState } from 'pinia' import editorStore from '@/store/editor' export default defineComponent({ computed: { ...mapState(editorStore, { settings: 'editorSettings', processEngine: (state) => `This process engine is ${state.editorSettings.processEngine}` }), ...mapWritableState(editorStore, ['editorSettings']) }, methods: { ...mapActions(editorStore, ['updateConfiguration']), update() { this.editorSettings.processEngine = "xxx" } } }) </script>
mapStores 用来访问 所有已注册 store 状态。假设我们除了上文定义的 editor,还定义了一个 id 为 modeler 的 store,则可以这么使用:
import editor from '@/store/editor' import modeler from '@/store/modeler' export default defineComponent({ computed: { ...mapStores(editor, modeler) }, methods: { async updateAll() { if (this.editorStore.processEngine === 'camunda') { await this.modelerStore.update() } } } })登入後複製其中引用的所有 store,都可以通过 id + 'Store' 的形式在 Vue 实例中访问到。
5. 互相引用
因为 Pinia 本身是支持各个 store 模块互相引用的,所以在定义的时候可以直接引用其他 store 的数据进行操作。
例如我们这里根据 editor store 创建一个 modeler store
import { defineStore } from 'pinia' import editor from '@/store/editor' export default defineStore('editor', { state: () => ({ element: null, modeler: null }), actions: { updateElement(element) { const editorStore = editor() if (!editorStore.getProcessEngine) { editorStore.updateConfiguration({ processEngine: 'camunda' }) } this.element = element } } })
6. 脱离 store 模块和组件使用
因为 Pinia 的每个 store 模块都是依赖 vue 应用和 pinia 根实例的,在组件内部使用时因为 Vue 应用和 pinia 根实例肯定都已经是 注册完成处于活动状态中的,所以可以直接通过调用对应的 store 状态模块函数即可。
但是在脱离 store 模块与组件,直接在外部的纯函数中使用时,则需要注意 store 状态模块函数的调用时机。
以官方的示例来看:
import { createRouter } from 'vue-router' const router = createRouter({ // ... }) // ❌ 根据导入的顺序,这将失败 const store = useStore() router.beforeEach((to, from, next) => { // 我们想在这里使用 store if (store.isLoggedIn) next() else next('/login') }) router.beforeEach((to) => { // ✅ 这将起作用,因为路由器在之后开始导航 // 路由已安装,pinia 也将安装 const store = useStore() if (to.meta.requiresAuth && !store.isLoggedIn) return '/login' })
直接在js模块的执行中 直接调用是可能会报错的,因为此时可能在 import router 的时候 还没有调用 createApp 和 createPinia 创建对应的应用实例和 pinia 根实例,所以无法使用。
而在路由导航的拦截器中使用时,因为 路由拦截触发时,应用和 pinia 根实例肯定已经全部实例化完毕,才可以正常使用。
所以 如果是在外部的 hooks 函数或者 utils 工具函数等纯函数模块中使用 store 数据时,最好是定义一个函数方法导出,在组件或者 store 模块中调用该方法,保证此时能正确执行
最后
总的来说,Pinia 作为 Vue 官方推荐的状态库,配合 Vue 3 的组合式 API,可以更好的实现项目中各种数据状态的管理,而不是像以前使用 Vuex 一样通过 modules 的形式注册各种状态。Pinia 对于抽离逻辑进行复用(hooks),简化使用方式来说,比之前的 Vuex 好了很多倍;加上良好的类型支持与代码提示,让我们在开发过程中可以省去很多前置工作,也是对我们的开发效率的一种提升吧。
当然,、Vue DevTools 在更新之后,也实现了对 Pinia 的支持。
以上是詳解Vue3狀態管理庫Pinia的使用方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

tinymce是一個功能齊全的富文本編輯器插件,但在vue中引入tinymce並不像別的Vue富文本插件一樣那麼順利,tinymce本身並不適配Vue,還需要引入@tinymce/tinymce-vue,並且它是國外的富文本插件,沒有透過中文版本,需要在其官網下載翻譯包(可能需要翻牆)。 1.安裝相關依賴npminstalltinymce-Snpminstall@tinymce/tinymce-vue-S2、下載中文包3.引入皮膚和漢化包在項目public資料夾下新建tinymce資料夾,將下載的

vue3+vite:src使用require動態導入圖片報錯和解決方法vue3+vite動態的導入多張圖片vue3如果使用的是typescript開發,就會出現require引入圖片報錯,requireisnotdefined不能像使用vue2這樣imgUrl:require(' …/assets/test.png')導入,是因為typescript不支援require所以用import導入,下面介紹如何解決:使用awaitimport

想要實現頁面的局部刷新,我們只需要實現局部元件(dom)的重新渲染。在Vue中,想要實現這效果最簡單的方式方法就是使用v-if指令。在Vue2中我們除了使用v-if指令讓局部dom的重新渲染,也可以新建一個空白元件,需要刷新局部頁面時跳轉至這個空白元件頁面,然後在空白元件內的beforeRouteEnter守衛中又跳轉回原來的頁面。如下圖所示,如何在Vue3.X中實現點擊刷新按鈕實現紅框範圍內的dom重新加載,並展示對應的加載狀態。由於Vue3.X中scriptsetup語法中組件內守衛只有o

Vue實作部落格前端,需要實作markdown的解析,如果有程式碼則需要實作程式碼的高亮。 Vue的markdown解析函式庫很多,如markdown-it、vue-markdown-loader、marked、vue-markdown等。這些庫都大同小異。這裡選用的是marked,程式碼高亮的函式庫選用的是highlight.js。具體實現步驟如下:一、安裝依賴庫在vue專案下開啟命令窗口,並輸入以下命令npminstallmarked-save//marked用於將markdown轉換成htmlnpmins

vue3+ts+axios+pinia實作無感刷新1.先在專案中下載aiXos和pinianpmipinia--savenpminstallaxios--save2.封裝axios請求-----下載js-cookienpmiJS-cookie-s//引入aixosimporttype{AxiosRequestConfigig ,AxiosResponse}from"axios";importaxiosfrom'axios';import{ElMess

前言無論是vue還是react,當我們遇到多處重複程式碼的時候,我們都會想著如何重複使用這些程式碼,而不是一個檔案裡充斥著一堆冗餘程式碼。實際上,vue和react都可以透過抽組件的方式來達到復用,但如果遇到一些很小的程式碼片段,你又不想抽到另外一個檔案的情況下,相比而言,react可以在相同文件裡面宣告對應的小元件,或透過renderfunction來實現,如:constDemo:FC=({msg})=>{returndemomsgis{msg}}constApp:FC=()=>{return(

最終效果安裝VueCropper組件yarnaddvue-cropper@next上面的安裝值針對Vue3的,如果時Vue2或想使用其他的方式引用,請訪問它的npm官方地址:官方教程。在元件中引用使用時也很簡單,只需要引入對應的元件和它的樣式文件,我這裡沒有在全域引用,只在我的元件檔案中引入import{userInfoByRequest}from'../js/api' import{VueCropper}from'vue-cropper&

vue3專案打包發佈到伺服器後存取頁面顯示空白1、處理vue.config.js檔案中的publicPath處理如下:const{defineConfig}=require('@vue/cli-service')module.exports=defineConfig({publicPath :process.env.NODE_ENV==='production'?'./':'/&
