溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Vue新的狀態(tài)管理Pinia怎么使用

發(fā)布時間:2023-04-28 10:44:50 來源:億速云 閱讀:122 作者:iii 欄目:開發(fā)技術

今天小編給大家分享一下Vue新的狀態(tài)管理Pinia怎么使用的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

    為什么有 Vuex 了還要再開發(fā)一個 Pinia ?

    先來一張圖,看下當時對于 Vuex5 的提案,就是下一代 Vuex5 應該是什么樣子的

    Vue新的狀態(tài)管理Pinia怎么使用

    Pinia 就是完整的符合了他當時 Vuex5 提案所提到的功能點,所以可以說 Pinia 就是 Vuex5 也不為過,因為它的作者就是官方的開發(fā)人員,并且已經被官方接管了,只是目前 Vuex 和 Pinia 還是兩個獨立的倉庫,以后可能會合并,也可能獨立發(fā)展,只是官方肯定推薦的是 Pinia

    因為在 Vue3 中使用 Vuex 的話需要使用 Vuex4,還只能作為一個過渡的選擇,存在很大缺陷,所以在 Componsition API 誕生之后,也就設計了全新的狀態(tài)管理 Pinia

    Pinia 和 Vuex

    VuexState、Gettes、Mutations(同步)、Actions(異步)

    PiniaState、Gettes、Actions(同步異步都支持)

    Vuex 當前最新版是 4.x

    • Vuex4 用于 Vue3

    • Vuex3 用于 Vue2

    Pinia 當前最新版是 2.x

    • 即支持 Vue2 也支持 Vue3

    就目前而言 Pinia 比 Vuex 好太多了,解決了 Vuex 的很多問題,所以筆者也非常建議直接使用 Pinia,尤其是 TypeScript 的項目

    Pinia 核心特性

    1.Pinia 沒有 Mutations

    2.Actions支持同步和異步

    3.沒有模塊的嵌套結構

    Pinia 通過設計提供扁平結構,就是說每個 store 都是互相獨立的,誰也不屬于誰,也就是扁平化了,更好的代碼分割且沒有命名空間。當然你也可以通過在一個模塊中導入另一個模塊來隱式嵌套 store,甚至可以擁有 store 的循環(huán)依賴關系

    4.更好的 TypeScript 支持

    不需要再創(chuàng)建自定義的復雜包裝器來支持 TypeScript 所有內容都類型化,并且 API 的設計方式也盡可能的使用 TS 類型推斷

    5.不需要注入、導入函數、調用它們,享受自動補全,讓我們開發(fā)更加方便

    6.無需手動添加 store,它的模塊默認情況下創(chuàng)建就自動注冊的

    7.Vue2 和 Vue3 都支持

    除了初始化安裝和SSR配置之外,兩者使用上的API都是相同的

    8.支持 Vue DevTools

    • 跟蹤 actions, mutations 的時間線

    • 在使用了模塊的組件中就可以觀察到模塊本身

    • 支持 time-travel 更容易調試

    • 在 Vue2 中 Pinia 會使用 Vuex 的所有接口,所以它倆不能一起使用

    • 但是針對 Vue3 的調試工具支持還不夠完美,比如還沒有 time-travel 功能

    9.模塊熱更新

    • 無需重新加載頁面就可以修改模塊

    • 熱更新的時候會保持任何現(xiàn)有狀態(tài)

    10.支持使用插件擴展 Pinia 功能

    11.支持服務端渲染

    Pinia 使用

    Vue3 + TypeScript 為例

    安裝

    npm install pinia

    main.ts 初始化配置

    import { createPinia } from 'pinia'
    createApp(App).use(createPinia()).mount('#app')

    在 store 目錄下創(chuàng)建一個 user.ts 為例,我們先定義并導出一個名為 user 的模塊

    import { defineStore } from 'pinia'
    export const userStore = defineStore('user', {
        state: () => {
            return { 
                count: 1,
                arr: []
            }
        },
        getters: { ... },
        actions: { ... }
    })

    defineStore 接收兩個參數

    第一個參數就是模塊的名稱,必須是唯一的,多個模塊不能重名,Pinia 會把所有的模塊都掛載到根容器上

    第二個參數是一個對象,里面的選項和 Vuex 差不多

    • 其中 state 用來存儲全局狀態(tài),它必須是箭頭函數,為了在服務端渲染的時候避免交叉請求導致的數據狀態(tài)污染所以只能是函數,而必須用箭頭函數則為了更好的 TS 類型推導

    • getters 就是用來封裝計算屬性,它有緩存的功能

    • actions 就是用來封裝業(yè)務邏輯,修改 state

    訪問 state

    比如我們要在頁面中訪問 state 里的屬性 count

    由于 defineStore 會返回一個函數,所以要先調用拿到數據對象,然后就可以在模板中直接使用了

    <template>
        <div>{{ user_store.count }}</div>
    </template>
    <script lang="ts" setup>
    import { userStore } from '../store'
    const user_store = userStore()
    // 解構
    // const { count } = userStore()
    </script>

    比如像注釋中的解構出來使用,是完全沒有問題的,只是注意了,這樣拿到的數據不是響應式的,如果要解構還保持響應式就要用到一個方法 storeToRefs(),示例如下

    <template>
        <div>{{ count }}</div>
    </template>
    <script lang="ts" setup>
    import { storeToRefs } from 'pinia'
    import { userStore } from '../store'
    const { count } = storeToRefs(userStore())
    </script>

    原因就是 Pinia 其實是把 state 數據都做了 reactive 處理,和 Vue3 的 reactive 同理,解構出來的也不是響應式,所以需要再做 ref 響應式代理

    getters

    這個和 Vuex 的 getters 一樣,也有緩存功能。如下在頁面中多次使用,第一次會調用 getters,數據沒有改變的情況下之后會讀取緩存

    <template>
        <div>{{ myCount }}</div>
        <div>{{ myCount }}</div>
        <div>{{ myCount }}</div>
    </template>

    注意兩種方法的區(qū)別,寫在注釋里了

    getters: {
        // 方法一,接收一個可選參數 state
        myCount(state){
            console.log('調用了') // 頁面中使用了三次,這里只會執(zhí)行一次,然后緩存起來了
            return state.count + 1
        },
        // 方法二,不傳參數,使用 this
        // 但是必須指定函數返回值的類型,否則類型推導不出來
        myCount(): number{
            return this.count + 1
        }
    }

    更新和 actions

    更新 state 里的數據有四種方法,我們先看三種簡單的更新,說明都寫在注釋里了

    <template>
        <div>{{ user_store.count }}</div>
        <button @click="handleClick">按鈕</button>
    </template>
    <script lang="ts" setup>
    import { userStore } from '../store'
    const user_store = userStore()
    const handleClick = () => {
        // 方法一
        user_store.count++
        
        // 方法二,需要修改多個數據,建議用 $patch 批量更新,傳入一個對象
        user_store.$patch({
            count: user_store.count1++,
            // arr: user_store.arr.push(1) // 錯誤
            arr: [ ...user_store.arr, 1 ] // 可以,但是還得把整個數組都拿出來解構,就沒必要
        })
        
        // 使用 $patch 性能更優(yōu),因為多個數據更新只會更新一次視圖
        
        // 方法三,還是$patch,傳入函數,第一個參數就是 state
        user_store.$patch( state => {
            state.count++
            state.arr.push(1)
        })
    }
    </script>

    第四種方法就是當邏輯比較多或者請求的時候,我們就可以封裝到示例中 store/user.ts 里的 actions 里

    可以傳參數,也可以通過 this.xx 可以直接獲取到 state 里的數據,需要注意的是不能用箭頭函數定義 actions,不然就會綁定外部的 this 了

    actions: {
        changeState(num: number){ // 不能用箭頭函數
            this.count += num
        }
    }

    調用

    const handleClick = () => {
        user_store.changeState(1)
    }

    支持 VueDevtools

    打開開發(fā)者工具的 Vue Devtools 就會發(fā)現(xiàn) Pinia,而且可以手動修改數據調試,非常方便

    Vue新的狀態(tài)管理Pinia怎么使用

    模擬調用接口

    示例:

    我們先定義示例接口 api/user.ts

    // 接口數據類型
    export interface userListType{
        id: number
        name: string
        age: number
    }
    // 模擬請求接口返回的數據
    const userList = [
        { id: 1, name: '張三', age: 18 },
        { id: 2, name: '李四', age: 19 },
    ]
    // 封裝模擬異步效果的定時器
    async function wait(delay: number){
        return new Promise((resolve) => setTimeout(resolve, delay))
    }
    // 接口
    export const getUserList = async () => {
        await wait(100) // 延遲100毫秒返回
        return userList
    }

    然后在 store/user.ts 里的 actions 封裝調用接口

    import { defineStore } from 'pinia'
    import { getUserList, userListType } from '../api/user'
    export const userStore = defineStore('user', {
        state: () => {
            return {
                // 用戶列表
                list: [] as userListType // 類型轉換成 userListType
            }
        },
        actions: { 
            async loadUserList(){
                const list = await getUserList()
                this.list = list
            }
        }
    })

    頁面中調用 actions 發(fā)起請求

    <template>
        <ul>
            <li v-for="item in user_store.list"> ... </li>
        </ul>
    </template>
    <script lang="ts" setup>
    import { userStore } from '../store'
    const user_store = userStore()
    user_store.loadUserList() // 加載所有數據
    </script>

    跨模塊修改數據

    在一個模塊的 actions 里需要修改另一個模塊的 state 數據

    示例:比如在 chat 模塊里修改 user 模塊里某個用戶的名稱

    // chat.ts
    import { defineStore } from 'pinia'
    import { userStore } from './user'
    export const chatStore = defineStore('chat', {
        actions: { 
            someMethod(userItem){
                userItem.name = '新的名字'
                const user_store = userStore()
                user_store.updateUserName(userItem)
            }
        }
    })

    user 模塊里

    // user.ts
    import { defineStore } from 'pinia'
    export const userStore = defineStore('user', {
        state: () => {
            return {
                list: []
            }
        },
        actions: { 
            updateUserName(userItem){
                const user = this.list.find(item => item.id === userItem.id)
                if(user){
                    user.name = userItem.name
                }
            }
        }
    })

    以上就是“Vue新的狀態(tài)管理Pinia怎么使用”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業(yè)資訊頻道。

    向AI問一下細節(jié)

    免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI