Vue3 Vite3 狀態管理 pinia 基本使用、持久化、在路由守衛中的使用

来源:https://www.cnblogs.com/youyacoder/archive/2022/10/10/16775776.html
-Advertisement-
Play Games

在《基於 vite 創建 vue3 項目》一文中整合了 pinia,有不少伙伴不知道 pinia 是什麼,本文簡單介紹 pinia。主要包括三方面: pinia 的基本用法,在《基於 vite 創建 vue3 項目》中 demo 的基礎上簡單重構。 如何持久化 pinia 中的數據,保證瀏覽器刷新時 ...


《基於 vite 創建 vue3 項目》一文中整合了 pinia,有不少伙伴不知道 pinia 是什麼,本文簡單介紹 pinia。主要包括三方面:

  1. pinia 的基本用法,在《基於 vite 創建 vue3 項目》中 demo 的基礎上簡單重構。
  2. 如何持久化 pinia 中的數據,保證瀏覽器刷新時,pinia 中的數據不丟失;
  3. vue-router 路由守衛中如何使用 pinia

文中的 demo 仍然基於 vite

1 pinia 的使用

1.1 pinia 是什麼

在 vue 2.x 中,vuex 是官方的狀態管理庫,並且 vue 3 中也有對應的 vuex 版本。但 vue 作者尤大神看了 pinia 後,強勢推薦使用 pinia 作為狀態管理庫。下圖是 vue 官網 “生態系統”,pinia 是 vue 生態之一。

image-20221009142022897

1.2 pinia 的特點

  1. 支持 vue2 和 vue3,兩者都可以使用 pinia
  2. 語法簡潔,支持 vue3 中 setup 的寫法,不必像 vuex 那樣定義 statemutationsactionsgetters 等,可以按照 setup Composition API 的方式返回狀態和改變狀態的方法,實現代碼的扁平化;
  3. 支持 vuex 中 stateactionsgetters 形式的寫法,丟棄了 mutations,開發時候不用根據同步非同步來決定使用 mutationsactions,pinia 中只有 actions
  4. TypeScript 支持非常友好。

1.3 pinia 的使用

在《基於 vite 創建 vue3 項目》中已經整合了 pinia,現簡單回顧併進行一些調整。

  1. 安裝 pinia 依賴:
yarn add pinia
  1. 創建 pinia 實例(根存儲 root store):

之前咱是在 main.ts 中創建的,現將其抽取到獨立的文件中:

src/store/index.ts

import { createPinia } from 'pinia'

const pinia = createPinia()

export default pinia
  1. main.ts 中以插件的方式傳遞給 App 實例。
...
import pinia from '@/store'
...
app.use(pinia)
...
  1. store/ 目錄下創建 modules 目錄,存儲每個模塊的狀態,將之前的 demo.ts 移動到 store/modules/ 中。這裡使用最新的 Composition API setup 的方式來定義狀態。

src/store/modules/demo.ts

import { defineStore } from 'pinia'
import { ref } from 'vue'

const useDemoStore = defineStore('demo', () => {
  const counter = ref(0)

  const increment = () => {
    counter.value++
  }

  return {
    counter,
    increment
  }
})

export default useDemoStore
  1. 在組件 about.vue 中使用 demo 中的狀態 counter 和改變狀態的函數 increment。代碼和之前一樣。

先引入 demo.ts 中定義的 useDemoStore 函數,通過該函數創建 demoStore 實例。然後就可以調用 demoStore 的狀態 counterincrement 函數了。這裡需要註意,無論是 pinia 還是 vuex,通過解構的方式獲取狀態,會導致狀態失去響應性。如:

const { counter } = demoStore

此時的 counter 會丟失響應性,當其值改變時,其他組件不會監聽到。所以 pinia 提供了 storeToRefs 函數,使其解構出來的狀態仍然具備響應性。

const { counter } = storeToRefs(demoStore)

src/views/about.vue 完整代碼如下:

<template>
  <div class="about">
    <h1>This is an about page</h1>
    <h3>counter: {{counter}}</h3>
    <el-button @click="add">
      <el-icon-plus></el-icon-plus>
    </el-button>

<div>
  <svg-icon icon="http://www.yygnb.com/demo/car.svg"></svg-icon>
  <svg-icon icon="car"></svg-icon>

  <svg-icon class-name="icon" icon="http://www.yygnb.com/demo/car.svg"></svg-icon>
  <svg-icon class-name="icon" icon="car"></svg-icon>
</div>

  </div>
</template>

<script lang="ts" setup>
import useDemoStore from '@/store/modules/demo'
import { storeToRefs } from 'pinia'
import SvgIcon from '@/components/svg-icon/index.vue'

const demoStore = useDemoStore()
const { counter } = storeToRefs(demoStore)

const add = () => {
  demoStore.increment()
}

</script>
<style scoped>
.icon {
  color: cornflowerblue;
  font-size: 30px;
}
</style>

最後在瀏覽器中訪問 about 頁面,可以正常運行,點擊加號按鈕,計數器會加1。

image-20221009145753908

2 持久化 pinia 狀態

2.1 為什麼需要持久化 pinia 狀態

在上面的 demo 中,假設計數器加到 5,如果刷新瀏覽器,counter 的值又會被初始化為 0。這是因為狀態是存儲在瀏覽器記憶體中的,刷新瀏覽器後,重新載入頁面時會重新初始化 vuepinia,而 pinia 中狀態的值僅在記憶體中存在,而刷新導致瀏覽器存儲中的數據沒了,所以 counter 的值就被初始化為 0。

在實際開發中,瀏覽器刷新時,有些數據希望是保存下來的。如用戶登錄後,用戶信息會存儲在全局狀態中,如果不持久化狀態,那麼每次刷新用戶都需要重新登錄了。

要解決這個問題非常簡單,在狀態改變時將其同步到瀏覽器的存儲中,如 cookielocalStoragesessionStorage 。每次初始化狀態時從存儲中去獲取初始值即可。

說起來思路很簡單,可真正實現起來就各種問題了,所以咱們就使用 pinia 的插件 pinia-plugin-persistedstate 來實現。

2.2 pinia-plugin-persistedstate

接下來就使用 pinia-plugin-persistedstate 插件實現 pinia 狀態的持久化。

  1. 安裝依賴:
yarn add pinia-plugin-persistedstate
  1. 引入該插件,在創建 pinia 實例時傳入該插件

src/store/index.ts

import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'

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

export default pinia
  1. 在需要持久化狀態的模塊中設置 persist。咱假設 demo 模塊需要對狀態需要持久化,defineStore 第一個參數定義唯一的模塊名,第二個參數傳遞 setup,其實還有第三個參數 options,在 options 中便可開啟 persist

src/store/modules/demo.ts

...
const useDemoStore = defineStore('demo', () => {
	...
}, {
  persist: true
})

此時改變 counter 的值後,刷新瀏覽器,counter 不會被重置為 0,仍然停留在刷新前的狀態。

persist 支持多種類型的值,最簡單的就是傳遞 true,此時會將狀態緩存在 localStorage 中,該 localStorage 的 key 為模塊名(defineStore 的第一個參數),value 為該模塊的狀態對象,由於該模塊只有一個狀態 counter,故value為 {"counter":8}。如下圖:

image-20221009151822704

如果需要將其存儲在 sessionStorage 中,就需要設置 persist 的值為一個對象:

...
const useDemoStore = defineStore('demo', () => {
	...
}, {
  persist: {
    key: 'aaa',
    storage: sessionStorage
  }
})

此時狀態就會同步緩存到 sessionStorage 中,並且key 為咱們指定的 key

image-20221009152105536

persist 對象類型為 PersistedStateOptions,上面演示了 keystorage 屬性,該對象的其他屬性如下:

}
interface PersistedStateOptions {
    /**
     * Storage key to use.
     * @default $store.id
     */
    key?: string;
    /**
     * Where to store persisted state.
     * @default localStorage
     */
    storage?: StorageLike;
    /**
     * Dot-notation paths to partially save state. Saves everything if undefined.
     * @default undefined
     */
    paths?: Array<string>;
    /**
     * Customer serializer to serialize/deserialize state.
     */
    serializer?: Serializer;
    /**
     * Hook called before state is hydrated from storage.
     * @default null
     */
    beforeRestore?: (context: PiniaPluginContext) => void;
    /**
     * Hook called after state is hydrated from storage.
     * @default undefined
     */
    afterRestore?: (context: PiniaPluginContext) => void;
}

3 在路由守衛中使用狀態

前面演示了在組件中使用 pinia,在組件外如何使用呢?這裡演示在全局路由守衛中獲取狀態值。咱們創建一個路由守衛,在路由守衛中使用 nprogress 顯示頁面載入進度條。

3.1 創建全局路由守衛

  1. 安裝 nprogress
yarn add nprogress
yarn add @types/nprogress -D 
  1. 創建全局路由守衛

src/router/guard/index.ts

import router from '@/router'
import nProgress from 'nprogress'
import 'nprogress/nprogress.css'

nProgress.configure({
  showSpinner: false
})

// 全局前置守衛
router.beforeEach((to, from) => {
  nProgress.start()
  return true
})

// 全局後置鉤子
router.afterEach(() => {
  nProgress.done(true)
})
  1. main.ts 中引入全局路由守衛:
...
import '@/router/guard/index'
...

此時路由切換時,頁面頂部會出現載入進度條,路由切換完成時該進度條消失。如果效果不明顯,可在前置守衛中 setTimeout 查看效果(個人覺得沒這必要,畫蛇添足):

// 全局前置守衛
router.beforeEach((to, from) => {
  nProgress.start()
  return new Promise(resolve => {
    setTimeout(() => {
      resolve(true)
    }, 1000)
  })
})

3.2 全局守衛中使用全局狀態

實際開發中,路由切換時,可能需要從全局狀態中獲取 token 等信息,判斷是否能進入下一個頁面。這裡演示路由切換時獲取 demo 中的 counter 的值。

首先試試在鉤子函數外面使用全局狀態:

...
import useDemoStore from '@/store/modules/demo'
import { storeToRefs } from 'pinia'
...

const demoStore = useDemoStore()
const { counter } = storeToRefs(demoStore)

// 全局前置守衛
router.beforeEach((to, from) => {
  nProgress.start()
  // 從 store 中獲取其他值,再決定返回值
  // 這裡演示獲取 store 中 counter 的值
  console.log(`counter:${counter}`)
  return true
})
...

此時瀏覽器控制台會報如下錯誤,這是因為 pinia 還沒有掛載到 app 上。

image-20221009155414238

網上有些解決方案是直接實例化一個 pinia 實例,傳遞給 useDemoStore() 函數,如下:

...
import useDemoStore from '@/store/modules/demo'
import { storeToRefs } from 'pinia'
import pinia from '@/store'
...

const demoStore = useDemoStore(pinia)
const { counter } = storeToRefs(demoStore)

...

這樣做,瀏覽器控制台不報錯了,頁面也可以正常載入,路由切換時,控制台會輸出當前 counter 的值。

但是如果刷新瀏覽器,counter 的值又被初始化為 0,貌似前面設置的持久化插件 pinia-plugin-persistedstate 失效了。那應該怎麼處理呢?

3.3 正確的處理方式

上面這種傳遞 pinia 對象給 useDemoStore() 函數只是一種野路子,pinia 官網已經清楚寫明組件外應該如何使用 pinia:

image-20221009160056386

在鉤子函數外,pinia 還沒有被掛載,但是在前置守衛函數中,pinia 已經被掛載了,所以獲取全局狀態,需要在鉤子函數中進行,正確的實現如下:

import router from '@/router'
import nProgress from 'nprogress'
import 'nprogress/nprogress.css'
import useDemoStore from '@/store/modules/demo'
import { storeToRefs } from 'pinia'

nProgress.configure({
  showSpinner: false
})

// 全局前置守衛
router.beforeEach((to, from) => {
  nProgress.start()

  const demoStore = useDemoStore()
  const { counter } = storeToRefs(demoStore)
  // 從 store 中獲取其他值,再決定返回值
  // 這裡演示獲取 store 中 counter 的值
  console.log(`counter:${counter.value}`)
  return true
})

// 全局後置鉤子
router.afterEach(() => {
  nProgress.done(true)
})

文中 demo 在 github 上搜索 vue3-vite-archetypemain 分支可以直接 yarn dev 啟動運行; template 分支是 yyg-cli 執行 yyg create 創建項目時拉取的模板。你也可以先執行 npm install -g yyg-cli 安裝 yyg-cli 腳手架工具,然後通過 yyg create xxx 創建項目,創建後的項目包含了 vue3 vite 的全部demo。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 摘要:GaussDB(DWS) 中鎖等待可以設置等待超時相關參數,一旦等鎖的時間超過參數配置值會拋錯。 本文分享自華為雲社區《GaussDB(DWS) 鎖相關參數及視圖詳解》,作者: yd_220527686。 一、鎖相關參數 GaussDB(DWS) 中鎖等待可以設置等待超時相關參數,一旦等鎖的時 ...
  • 先說一下為什麼需要備份MySQL數據? 一句話總結就是:為了保證數據的安全性。 如果我們把數據只存儲在一個地方,如果物理機器損壞,會導致數據丟失,無法恢復。 還有就是我們每次手動修改線上數據之前,為了安全起見,都需要先備份數據。防止人為的誤操作,導致弄髒數據或弄丟數據。 ...
  • MatrixOne從入門到實踐——物聯網平臺架構升級 公司介紹 西安天能軟體科技有限責任公司,成立於2018年,公司自成立起集中力量精心打造物聯網平臺,擁有集自主研發、終端生產、銷售、服務一體的物聯網平臺及服務團隊,已為國內外300多家物聯網企業、千萬級物聯網設備提供合作支持。 公司在物聯網領域擁有 ...
  • 在應用開發的早期,數據量少,開發人員開發功能時更重視功能上的實現,隨著生產數據的增長,很多SQL語句開始暴露出性能問題,對生產的影響也越來越大,有時可能這些有問題的SQL就是整個系統性能的瓶頸。 ...
  • [Android開發學iOS系列] iOS寫UI的幾種方式 作為一個現代化的平臺, iOS的發展也經歷了好幾個時代. 本文講講iOS寫UI的幾種主要方式和各自的特點. iOS寫UI的方式 在iOS中寫UI有多種選擇, 大的分類: 使用UIKit還是SwiftUI. 在使用UIKit的情形下, 還根據 ...
  • 一、新建原生工程和Flutter Module 1、新建Android工程 搭建一個空的Android工程FlutterDemo_Android模擬已經存在的原有工程 Android項目配置: 2、新建iOS工程 搭建一個空的iOS工程FlutterDemo_iOS模擬已經存在的原有工程 Xcode ...
  • 鬥魚直播相信大家都聽說過,打開鬥魚官網就可以直接在瀏覽器中觀看直播。那麼鬥魚是如何實現瀏覽器視頻直播的呢?本篇文章就來解析鬥魚是如何實現直播的,以及它是如何節省 80% 的 CDN 流量,要知道視頻直播流量費並不便宜,鬥魚每個月光這些流量費都要支付幾個億,節省 CDN 流量就是省錢。 直播技術方案 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 前言 隨著業務的需求,項目需要支持H5、各類小程式以及IOS和Android,這就需要涉及到跨端技術,不然每一端都開發一套,人力成本和維護成本太高了。團隊的技術棧主要以Vue為主,最終的選型是以uni-app+uview2.0作為跨端技術 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...