1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        Pinia 前端狀態(tài)管理使用指南!(含插件使用)

        共 15004字,需瀏覽 31分鐘

         ·

        2022-11-15 01:17

        術(shù)  堅(jiān)  

        前言

        Pinia ,發(fā)音為 */pi?nj?/*,來源于西班牙語 pi?a 。意思為菠蘿,表示與菠蘿一樣,由很多小塊組成。在 Pinia 中,每個(gè) Store 都是單獨(dú)存在,一同進(jìn)行狀態(tài)管理。

        Pinia 是由 Vue.js 團(tuán)隊(duì)成員開發(fā),最初是為了探索 Vuex 下一次迭代會(huì)是什么樣子。過程中,Pinia 實(shí)現(xiàn)了 Vuex5 提案的大部分內(nèi)容,于是就取而代之了。

        與 Vuex 相比,Pinia 提供了更簡單的 API,更少的規(guī)范,以及 Composition-API 風(fēng)格的 API 。更重要的是,與 TypeScript 一起使用具有可靠的類型推斷支持。

        Pinia 與 Vuex 3.x/4.x 的不同

        • mutations 不復(fù)存在。只有 state 、getters 、actions。
        • actions 中支持同步和異步方法修改 state 狀態(tài)。
        • 與 TypeScript 一起使用具有可靠的類型推斷支持。
        • 不再有模塊嵌套,只有 Store 的概念,Store 之間可以相互調(diào)用。
        • 支持插件擴(kuò)展,可以非常方便實(shí)現(xiàn)本地存儲(chǔ)等功能。
        • 更加輕量,壓縮后體積只有 2kb 左右。

        既然 Pinia 這么香,那么還等什么,一起用起來吧!

        基本用法

        安裝

        npm install pinia

        在 main.js 中 引入 Pinia

        // src/main.js
        import { createPinia } from 'pinia'

        const pinia = createPinia()
        app.use(pinia)

        定義一個(gè) Store

        在 src/stores 目錄下創(chuàng)建 counter.js 文件,使用 defineStore() 定義一個(gè) Store 。defineStore() 第一個(gè)參數(shù)是 storeId ,第二個(gè)參數(shù)是一個(gè)選項(xiàng)對(duì)象:

        // src/stores/counter.js
        import { defineStore } from 'pinia'

        export const useCounterStore = defineStore('counter', {
          state() => ({ count0 }),
          getters: {
            doubleCount(state) => state.count * 2
          },
          actions: {
            increment() {
              this.count++
            }
          }
        })

        我們也可以使用更高級(jí)的方法,第二個(gè)參數(shù)傳入一個(gè)函數(shù)來定義 Store :

        // src/stores/counter.js
        import { ref, computed } from 'vue'
        import { defineStore } from 'pinia'

        export const useCounterStore = defineStore('counter', () => {
          const count = ref(0)
          const doubleCount = computed(() => count.value * 2)
          function increment({
            count.value++
          }

          return { count, doubleCount, increment }
        })

        在組件中使用

        在組件中導(dǎo)入剛才定義的函數(shù),并執(zhí)行一下這個(gè)函數(shù),就可以獲取到 store 了:

        <script setup>
        import { useCounterStore } from '@/stores/counter'

        const counterStore = useCounterStore()
        // 以下三種方式都會(huì)被 devtools 跟蹤
        counterStore.count++
        counterStore.$patch({ count: counterStore.count + 1 })
        counterStore.increment()
        </script>

        <template>
          <div>{{ counterStore.count }}</
        div>
          <div>{{ counterStore.doubleCount }}</div>
        </template>

        這就是基本用法,下面我們來介紹一下每個(gè)選項(xiàng)的功能,及插件的使用方法。

        State

        解構(gòu) store

        store 是一個(gè)用 reactive 包裹的對(duì)象,如果直接解構(gòu)會(huì)失去響應(yīng)性。我們可以使用 storeToRefs() 對(duì)其進(jìn)行解構(gòu):

        <script setup>
        import { storeToRefs } from 'pinia'
        import { useCounterStore } from '@/stores/counter'

        const counterStore = useCounterStore()
        const { count, doubleCount } = storeToRefs(counterStore)
        </script>

        <template>
          <div>{{ count }}</div>
          <div>{{ doubleCount }}</div>
        </template>

        修改 store

        除了可以直接用 store.count++ 來修改 store,我們還可以調(diào)用 $patch 方法進(jìn)行修改。$patch 性能更高,并且可以同時(shí)修改多個(gè)狀態(tài)。

        <script setup>
        import { useCounterStore } from '@/stores/counter'

        const counterStore = useCounterStore()
        counterStore.$patch({
          count: counterStore.count + 1,
          name: 'Abalam',
        })
        </script>

        但是,這種方法修改集合(比如從數(shù)組中添加、刪除、插入元素)都需要?jiǎng)?chuàng)建一個(gè)新的集合,代價(jià)太高。因此,$patch 方法也接受一個(gè)函數(shù)來批量修改:

        cartStore.$patch((state) => {
          state.items.push({ name: 'shoes', quantity: 1 })
          state.hasChanged = true
        })

        監(jiān)聽 store

        我們可以通過 $subscribe() 方法可以監(jiān)聽 store 狀態(tài)的變化,類似于 Vuex 的 subscribe 方法。與 watch() 相比,使用 $subscribe() 的優(yōu)點(diǎn)是,store 多個(gè)狀態(tài)發(fā)生變化之后,回調(diào)函數(shù)只會(huì)執(zhí)行一次。

        <script setup>
        import { useCounterStore } from '@/stores/counter'

        const counterStore = useCounterStore()
        counterStore.$subscribe((mutation, state) => {
          // 每當(dāng)狀態(tài)發(fā)生變化時(shí),將 state 持久化到本地存儲(chǔ)
          localStorage.setItem('counter'JSON.stringify(state))
        })
        </script>

        也可以監(jiān)聽 pinia 實(shí)例上所有 store 的變化

        // src/main.js
        import { watch } from 'vue'
        import { createPinia } from 'pinia'

        const pinia = createPinia()
        watch(
          pinia.state,
          (state) => {
            // 每當(dāng)狀態(tài)發(fā)生變化時(shí),將所有 state 持久化到本地存儲(chǔ)
            localStorage.setItem('piniaState'JSON.stringify(state))
          },
          { deeptrue }
        )

        Getters

        訪問 store 實(shí)例

        大多數(shù)情況下,getter 只會(huì)依賴 state 狀態(tài)。但有時(shí)候,它會(huì)使用到其他的 getter ,這時(shí)候我們可以通過 this 訪問到當(dāng)前 store 實(shí)例。

        // src/stores/counter.js
        import { defineStore } from 'pinia'

        export const useCounterStore = defineStore('counter', {
          state() => ({ count0 }),
          getters: {
            doubleCount(state) {
              return state.count * 2
            },
            doublePlusOne() {
              return this.doubleCount + 1
            }
          }
        })

        訪問其他 Store 的 getter

        要使用其他 Store 的 getter,可以直接在 getter 內(nèi)部使用:

        // src/stores/counter.js
        import { defineStore } from 'pinia'
        import { useOtherStore } from './otherStore'

        export const useCounterStore = defineStore('counter', {
          state() => ({
            count1
          }),
          getters: {
            composeGetter(state) {
              const otherStore = useOtherStore()
              return state.count + otherStore.count
            }
          }
        })

        將參數(shù)傳遞給 getter

        getter 本質(zhì)上是一個(gè) computed ,無法向它傳遞任何參數(shù)。但是,我們可以讓它返回一個(gè)函數(shù)以接受參數(shù):

        // src/stores/user.js
        import { defineStore } from 'pinia'

        export const useUserStore = defineStore('user', {
          state() => ({
            users: [{ id1name'Tom'}, {id2name'Jack'}]
          }),
          getters: {
            getUserById(state) => {
              return (userId) => state.users.find((user) => user.id === userId)
            }
          }
        })

        在組件中使用:

        <script setup>
        import { storeToRefs } from 'pinia'
        import { useUserStore } from '@/stores/user'

        const userStore = useUserStore()
        const { getUserById } = storeToRefs(userStore)
        </script>

        <template>
          <p>User: {{ getUserById(2) }}</p>
        </template>

        注意:如果這樣使用,getter 不會(huì)緩存,它只會(huì)當(dāng)作一個(gè)普通函數(shù)使用。一般不推薦這種用法,因?yàn)樵诮M件中定義一個(gè)函數(shù),可以實(shí)現(xiàn)同樣的功能。

        Actions

        訪問 store 實(shí)例

        與 getters 一樣,actions 可以通過 this 訪問當(dāng) store 的實(shí)例。不同的是,actions 可以是異步的。

        // src/stores/user.js
        import { defineStore } from 'pinia'

        export const useUserStore = defineStore('user', {
          state() => ({ userDatanull }),
          actions: {
            async registerUser(login, password) {
              try {
                this.userData = await api.post({ login, password })
              } catch (error) {
                return error
              }
            }
          }
        })

        訪問其他 Store 的 action

        要使用其他 Store 的 action,也可以直接在 action 內(nèi)部使用:

        // src/stores/setting.js
        import { defineStore } from 'pinia'
        import { useAuthStore } from './authStore'

        export const useSettingStore = defineStore('setting', {
          state() => ({ preferencesnull }),
          actions: {
            async fetchUserPreferences(preferences) {
              const authStore = useAuthStore()
              if (authStore.isAuthenticated()) {
                this.preferences = await fetchPreferences()
              } else {
                throw new Error('User must be authenticated!')
              }
            }
          }
        })

        以上就是 Pinia 的詳細(xì)用法,是不是比 Vuex 簡單多了。除此之外,插件也是 Pinia 的一個(gè)亮點(diǎn),個(gè)人覺得非常實(shí)用,下面我們就來重點(diǎn)介紹一下。

        Plugins

        由于是底層 API,Pania Store 完全支持?jǐn)U展。以下是可以擴(kuò)展的功能列表:

        • 向 Store 添加新狀態(tài)
        • 定義 Store 時(shí)添加新選項(xiàng)
        • 為 Store 添加新方法
        • 包裝現(xiàn)有方法
        • 更改甚至取消操作
        • 實(shí)現(xiàn)本地存儲(chǔ)等副作用
        • 僅適用于特定 Store

        使用方法

        Pinia 插件是一個(gè)函數(shù),接受一個(gè)可選參數(shù) context ,context 包含四個(gè)屬性:app 實(shí)例、pinia 實(shí)例、當(dāng)前 store 和選項(xiàng)對(duì)象。函數(shù)也可以返回一個(gè)對(duì)象,對(duì)象的屬性和方法會(huì)分別添加到 state 和 actions 中。

        export function myPiniaPlugin(context{
          context.app // 使用 createApp() 創(chuàng)建的 app 實(shí)例(僅限 Vue 3)
          context.pinia // 使用 createPinia() 創(chuàng)建的 pinia
          context.store // 插件正在擴(kuò)展的 store
          context.options // 傳入 defineStore() 的選項(xiàng)對(duì)象(第二個(gè)參數(shù))
          // ...
          return {
            hello'world'// 為 state 添加一個(gè) hello 狀態(tài)
            changeHello() { // 為 actions 添加一個(gè) changeHello 方法
              this.hello = 'pinia'
            }
          }
        }

        然后使用 pinia.use() 將此函數(shù)傳遞給 pinia 就可以了:

        // src/main.js
        import { createPinia } from 'pinia'

        const pinia = createPinia()
        pinia.use(myPiniaPlugin)

        向 Store 添加新狀態(tài)

        可以簡單地通過返回一個(gè)對(duì)象來為每個(gè) store 添加狀態(tài):

        pinia.use(() => ({ hello'world' }))

        也可以直接在 store 上設(shè)置屬性來添加狀態(tài),為了使它可以在 devtools 中使用,還需要對(duì) store.$state 進(jìn)行設(shè)置:

        import { ref, toRef } from 'vue'

        pinia.use(({ store }) => {
          const hello = ref('word')
          store.$state.hello = hello
          store.hello = toRef(store.$state, 'hello')
        })

        也可以在 use 方法外面定義一個(gè)狀態(tài),共享全局的 ref 或 computed :

        import { ref } from 'vue'

        const globalSecret = ref('secret')
        pinia.use(({ store }) => {
          // `secret` 在所有 store 之間共享
          store.$state.secret = globalSecret
          store.secret = globalSecret
        })

        定義 Store 時(shí)添加新選項(xiàng)

        可以在定義 store 時(shí)添加新的選項(xiàng),以便在插件中使用它們。例如,可以添加一個(gè) debounce 選項(xiàng),允許對(duì)所有操作進(jìn)行去抖動(dòng):

        // src/stores/search.js
        import { defineStore } from 'pinia'

        export const useSearchStore = defineStore('search', {
          actions: {
            searchContacts() {
              // ...
            },
            searchContent() {
              // ...
            }
          },

          debounce: {
            // 操作 searchContacts 防抖 300ms
            searchContacts300,
            // 操作 searchContent 防抖 500ms
            searchContent500
          }
        })

        然后使用插件讀取該選項(xiàng),包裝并替換原始操作:

        // src/main.js
        import { createPinia } from 'pinia'
        import { debounce } from 'lodash'

        const pinia = createPinia()
        pinia.use(({ options, store }) => {
          if (options.debounce) {
            // 我們正在用新的 action 覆蓋原有的 action
            return Object.keys(options.debounce).reduce((debouncedActions, action) => {
              debouncedActions[action] = debounce(
                store[action],
                options.debounce[action]
              )
              return debouncedActions
            }, {})
          }
        })

        這樣在組件中使用 actions 的方法就可以去抖動(dòng)了,是不是很方便!

        實(shí)現(xiàn)本地存儲(chǔ)

        相信大家使用 Vuex 都有這樣的困惑,F5 刷新一下數(shù)據(jù)全沒了。在我們眼里這很正常,但在測試同學(xué)眼里這就是一個(gè) bug 。Vuex 中實(shí)現(xiàn)本地存儲(chǔ)比較麻煩,需要把狀態(tài)一個(gè)一個(gè)存儲(chǔ)到本地,取數(shù)據(jù)時(shí)也要進(jìn)行處理。而使用 Pinia ,一個(gè)插件就可以搞定。

        這次我們就不自己寫了,直接安裝開源插件。

        npm i pinia-plugin-persist

        然后引入插件,并將此插件傳遞給 pinia :

        // src/main.js
        import { createPinia } from 'pinia'
        import piniaPluginPersist from 'pinia-plugin-persist'

        const pinia = createPinia()
        pinia.use(piniaPluginPersist)

        接著在定義 store 時(shí)開啟 persist 即可:

        // src/stores/counter.js
        import { defineStore } from 'pinia'

        export const useCounterStore = defineStore('counter', {
          state() => ({ count1 }),
          // 開啟數(shù)據(jù)緩存
          persist: {
            enabledtrue
          }
        })

        這樣,無論你用什么姿勢刷新,數(shù)據(jù)都不會(huì)丟失啦!

        默認(rèn)情況下,會(huì)以 storeId 作為 key 值,把 state 中的所有狀態(tài)存儲(chǔ)在 sessionStorage 中。我們也可以通過 strategies 進(jìn)行修改:

        // 開啟數(shù)據(jù)緩存
        persist: {
          enabledtrue,
          strategies: [
            {
              key'myCounter'// 存儲(chǔ)的 key 值,默認(rèn)為 storeId
              storage: localStorage, // 存儲(chǔ)的位置,默認(rèn)為 sessionStorage
              paths: ['name''age'], // 需要存儲(chǔ)的 state 狀態(tài),默認(rèn)存儲(chǔ)所有的狀態(tài)
            }
          ]
        }

        ok,今天的分享就是這些。不知道你對(duì) Pinia 是不是有了更進(jìn)一步的了解,歡迎評(píng)論區(qū)留言討論。

        小結(jié)

        Pinia 整體來說比 Vuex 更加簡單、輕量,但功能卻更加強(qiáng)大,也許這就是它取代 Vuex 的原因吧。此外,Pinia 還可以在 Vue2 中結(jié)合 map 函數(shù)使用,有興趣的同學(xué)可以研究一下。


        如果想學(xué)習(xí)更多H5游戲webpack,node,gulp,css3javascript,nodeJScanvas數(shù)據(jù)可視化等前端知識(shí)和實(shí)戰(zhàn),歡迎在《趣談前端》加入我們的技術(shù)群一起學(xué)習(xí)討論,共同探索前端的邊界。


        從零搭建全棧可視化大屏制作平臺(tái)V6.Dooring

        從零設(shè)計(jì)可視化大屏搭建引擎

        Dooring可視化搭建平臺(tái)數(shù)據(jù)源設(shè)計(jì)剖析

        可視化搭建的一些思考和實(shí)踐

        基于Koa + React + TS從零開發(fā)全棧文檔編輯器(進(jìn)階實(shí)戰(zhàn)




        點(diǎn)個(gè)在看你最好看


        瀏覽 149
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            啪啪的网站 | 久久国产日韩 | 国产又爽 又黄 在线 | 久久精品国产免费看久久精品 | 欧美操逼女 | 五月天av在线 | 男女爱爱网站 | 国产乱来╳╳AV | 北条麻妃内射在线直播 | 操逼视频网站免费观看 |