首頁  >  文章  >  web前端  >  怎麼上手Vue全新狀態管理Pinia,看看這篇文章吧!

怎麼上手Vue全新狀態管理Pinia,看看這篇文章吧!

青灯夜游
青灯夜游轉載
2022-03-18 11:14:312409瀏覽

Pinia是Vue開發的全新狀態管理函式庫,那麼新手怎麼上手Pinia呢?以下這篇文章帶大家了解Pinia,介紹核心特性和簡單用法,希望對大家有幫助!

怎麼上手Vue全新狀態管理Pinia,看看這篇文章吧!

Vuex 作為一個老牌Vue 狀態管理庫,大家都很熟悉了

Pinia 是Vue.js 團隊成員專門為Vue 開發的一個全新的狀態管理函式庫,並且已經被納入官方github

為什麼有Vuex 了還要再開發一個Pinia ?

先來一張圖,看下當時對於 Vuex5 的提案,就是下一代 Vuex5 應該是什麼樣子的。 【相關推薦:vuejs影片教學

怎麼上手Vue全新狀態管理Pinia,看看這篇文章吧!

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

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

Pinia 和Vuex

Vuex: StateGettesMutations(同步)、 Actions(非同步)

Pinia: StateGettesActions(同步異步都支援)

Vuex 目前最新版本是4.x

  • Vuex4 用於Vue3
  • Vuex3 用於Vue2

Pinia 目前最新版是2.x

  • 即支援Vue2 也支援Vue3

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

Pinia 核心特性

  • Pinia 沒有Mutations
  • Actions 支援同步和非同步
  • 沒有模組的巢狀結構
    • Pinia 透過設計提供扁平結構,就是說每個store 都是互相獨立的,誰也不屬於誰,也就是扁平化了,更好的程式碼分割且沒有命名空間。當然你也可以透過在一個模組中匯入另一個模組來隱含巢狀store,甚至可以擁有store 的迴圈依賴關係
  • 更好的TypeScript#支援
    • 不需要再建立自訂的複雜包裝器來支援TypeScript 所有內容都類型化,而且API 的設計方式也盡可能的使用TS 類型推斷
  • 不需要注入、導入函數、調用它們,享受自動補全,讓我們開發更加方便
  • 無需手動添加store,它的模組默認情況下創建就自動註冊的
  • Vue2和Vue3 都支援
    • 除了初始化安裝和SSR配置之外,兩者使用上的API都是相同的
  • Vue DevTools
    • 追蹤actions, mutations 的時間軸
    • 在使用了模組的元件中就可以觀察到模組本身
    • 支援time-travel 更容易除錯
    • #在Vue2 中Pinia 會使用Vuex 的所有接口,所以它倆不能一起使用
    • #但是針對Vue3 的調試工具支持還不夠完美,比如還沒有time-travel 功能
  • 模組熱更新
    • 無需重新載入頁面就可以修改模組
    • 熱更新的時候會保持任何現有狀態
  • 支持使用外掛程式擴充Pinia 功能
  • 支援服務端渲染

Pinia 使用

Vue3 TypeScript 為例

安裝

npm install pinia

main.ts 初始化配置

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

在store 目錄下建立一個user.ts 為例,我們先定義並匯出一個名為user 的模組

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

defineStore 接收兩個參數

第一個參數就是模組的名稱,必須是唯一的,多個模組不能重名,Pinia 會把所有的模組都掛載到根容器上
第二個參數是一個對象,裡面的選項和Vuex 差不多

  • 其中 state 用来存储全局状态,它必须是箭头函数,为了在服务端渲染的时候避免交叉请求导致的数据状态污染所以只能是函数,而必须用箭头函数则为了更好的 TS 类型推导
  • getters 就是用来封装计算属性,它有缓存的功能
  • actions 就是用来封装业务逻辑,修改 state

访问 state

比如我们要在页面中访问 state 里的属性 count

由于 defineStore 会返回一个函数,所以要先调用拿到数据对象,然后就可以在模板中直接使用了

<template>
    <div>{{ user_store.count }}</div>
</template>
<script setup>
import { userStore } from &#39;../store&#39;
const user_store = userStore()
// 解构
// const { count } = userStore()
</script>

比如像注释中的解构出来使用,是完全没有问题的,只是注意了,这样拿到的数据不是响应式的,如果要解构还保持响应式就要用到一个方法 storeToRefs(),示例如下

<template>
    <div>{{ count }}</div>
</template>
<script setup>
import { storeToRefs } from &#39;pinia&#39;
import { userStore } from &#39;../store&#39;
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>

注意两种方法的区别,写在注释里了

getters: {
    // 方法一,接收一个可选参数 state
    myCount(state){
        console.log(&#39;调用了&#39;) // 页面中使用了三次,这里只会执行一次,然后缓存起来了
        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 setup>
import { userStore } from &#39;../store&#39;
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 性能更优,因为多个数据更新只会更新一次视图
    
    // 方法三,还是$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

打开开发者工具的 Vue Devtools 就会发现 Pinia,而且可以手动修改数据调试,非常方便

怎麼上手Vue全新狀態管理Pinia,看看這篇文章吧!

模拟调用接口

示例:

我们先定义示例接口 api/user.ts

// 接口数据类型
export interface userListType{
    id: number
    name: string
    age: number
}
// 模拟请求接口返回的数据
const userList = [
    { id: 1, name: &#39;张三&#39;, age: 18 },
    { id: 2, name: &#39;李四&#39;, 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 &#39;pinia&#39;
import { getUserList, userListType } from &#39;../api/user&#39;
export const userStore = defineStore(&#39;user&#39;, {
    state: () => {
        return {
            // 用户列表
            list: [] as userListType // 类型转换成 userListType
        }
    },
    actions: { 
        async loadUserList(){
            const list = await getUserList()
            this.list = list
        }
    }
})

页面中调用 actions 发起请求

<template>
    <ul>
        <li v-for="item in user_store.list"> ... </li>
    </ul>
</template>
<script setup>
import { userStore } from &#39;../store&#39;
const user_store = userStore()
user_store.loadUserList() // 加载所有数据
</script>

跨模块修改数据

在一个模块的 actions 里需要修改另一个模块的 state 数据

示例:比如在 chat 模块里修改 user 模块里某个用户的名称

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

user 模块里

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

(学习视频分享:vuejs教程web前端

以上是怎麼上手Vue全新狀態管理Pinia,看看這篇文章吧!的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:juejin.cn。如有侵權,請聯絡admin@php.cn刪除