ピニアとは何ですか?使い方?この記事では、Vue の新世代状態管理ライブラリである Pinia について説明します。
Vue 3 の正式版が 2020 年 9 月にリリースされた後、Vuex は 2021 年 2 月に Vue 3 に適応した 4.0 バージョンもリリースしましたが、 2021 年 8 月末、Vue コア チーム メンバーの Eduardo が主に貢献した新しい Vue 状態共有ライブラリがバージョン 2.0 をリリースし、同年 11 月に Youda が Pinia を Vue の公式状態ライブラリとして正式に指定しました (現在は Vue 公式 Web サイトでも Vuex が Pinia に置き換えられています)。 (学習ビデオ共有: vue ビデオ チュートリアル )
Pinia は Vuex と同じで、「」です。 Vue リポジトリの状態 " は、クロスページ/コンポーネント形式のデータ状態共有を実現するために使用されます。
通常の開発プロセスでは、Vue コンポーネントは Props および Events を通じてコンポーネント間でメッセージを渡すことができます。クロスレベル コンポーネントの場合は、EventBus を使用することもできます。 コミュニケーションを実現します。ただし、大規模なプロジェクトでは、通常、さまざまなデータやステータス をブラウザに保存する必要があり、Props/Events や EventBus を使用すると、維持および拡張が困難になります。 . .したがって、Vuex と Pinia です。
mutations。ほとんどの開発者の目には、ミューテーションは状態データの 同期変更 のみをサポートしており、アクション は 非同期 をサポートしていますが、変更するにはミューテーションを内部で呼び出す必要があるためです。状態。これは間違いなく非常に面倒で冗長です。
型推論 を利用します。 TypeScript をサポートするにはカスタム TS ラッパーが必要な Vuex とは異なります。
インスタンス/Vue プロトタイプに状態依存関係を注入する必要がある Vuex とは異なり、代わりにステータス モジュールを直接導入して呼び出します。ステータス更新と get を完了するための getter/actions 関数、および TypeScript と型推論の優れたサポートにより、開発者は優れたコード プロンプトを楽しむことができます
いいえステータスデータを事前に登録する必要があります、デフォルトではコードロジックに従って自動的に処理され、使用中にいつでも新しいステータスを登録できます
Vuex にはネストされたモジュール構造がなく、すべての状態がフラットに管理されます。また、pinia によって登録されるステータスは、pinia がすべてのステータス モジュールを登録するための統一された入口を必要としないことを除いて、vuex のモジュールに似ていることも理解できます。
各状態間の相互参照とネスト
Pinia は、Vuex グローバル ステートの機能を実現することを前提として、ステートの保存構造を改善しました。使用法を最適化し、API の設計と仕様を簡素化し、TypeScript の型推論に基づいて、開発者に優れた TypeScript サポートとコード ヒントを提供します。使い方
1. Pinia インスタンスの登録
ピニアへの登録が完了します。 import { createApp } from 'vue'
import { createPinia } from 'pinia'
const app = createApp(App)
const pinia = createPinia()
app.use(pinia)
chain 呼び出しcreatePinia()をサポートしているため、createApp(App).use(createPinia()) として直接記述することもできます。 mount ('#app')
この時点で、.
は app.use## にルート インスタンスを作成します。 # インスタンスはアプリに挿入され、app.config.globalProperties.$pinia もインスタンスを指すように構成されます。 2. ステータス ストアの定義
ステータス モジュール関数を作成できます。メソッド (関数である理由は、後で呼び出すときに内部の状態を関数の形式で取得する必要があるためです)。 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 配置项包含三个部分:
注意:getters 的函数定义中 第一个参数就是当前 store 的状态数据 state,而 actions 中的函数参数为 实际调用时传递的参数,可以传递多个,内部通过 this 上下文 直接访问 state 并进行更新。
众所周知,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 方法来获取值,这种方式获取的结果本身就是 响应式的,可以直接使用
更新值:
而在传统的 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 实例中访问到。
因为 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 } } })
因为 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 中国語 Web サイトの他の関連記事を参照してください。