(創作不易,感謝有你,你的支持,就是我前行的最大動力,如果看完對你有幫助,請留下您的足跡)
目錄
Vue3.3新特性
defineOptions
defineModel
pinia?
介紹
與 Vuex 3.x/4.x 的比較?
安裝
核心概念
定義 Store
Option Store?
Setup Store
storeToRefs?
State?
Getter?
Action
pinia-plugin-persistedstate
概述
安裝?
用法?
Vue3.3新特性
defineOptions
背景說明:有 <script setup> 之前,如果要定義 props, emits 可以輕而易舉地添加一個與 setup 平級的屬性。但是用了 <script setup> 后,就沒法這么干了 setup 屬性已經沒有了,自然無法添加與其平級的屬性。為了解決這一問題,引入了 defineProps 與 defineEmits 這兩個宏。但這只解決了 props 與 emits 這兩個屬性。如果我們要定義組件的 name 或其他自定義的屬性,還是得回到最原始的用法——再添加一個普通的 <script> 標簽。這樣就會存在兩個 <script> 標簽。讓人無法接受
所以在 Vue 3.3 中新引入了 defineOptions 宏。顧名思義,主要是用來定義 Options API 的選項。可以用 defineOptions 定義任意的選項, props, emits, expose, slots 除外(因為這些可以使用 defineXXX 來做到)
?
defineModel
在 Vue3 中,自定義組件上使用 v-model, 相當于傳遞一個 modelValue 屬性,同時觸發 update:modelValue 事件
我們需要先定義 props,再定義 emits 。其中有許多重復的代碼。如果需要修改此值,還需要手動調用 emit 函數。
?新的defineModel宏的使用。宏會自動注冊一個Props,并返回一個可以直接突變的引用:
?注意:直接使用 defineModel 宏會報錯,需要開啟。
pinia?
介紹
Pinia 是 Vue 的最新狀態管理工具,是vuex的替代品
與 Vuex 相比,Pinia 提供了一個更簡單的 API,具有更少的規范,提供了 Composition-API 風格的 API,最重要的是,在與 TypeScript 一起使用時具有可靠的類型推斷支持。
與 Vuex 3.x/4.x 的比較?
Pinia API 與 Vuex ≤4 有很大不同,即:
- mutations?不再存在。他們經常被認為是?非常?冗長。他們最初帶來了 devtools 集成,但這不再是問題。
- 無需創建自定義復雜包裝器來支持 TypeScript,所有內容都是類型化的,并且 API 的設計方式盡可能利用 TS 類型推斷。
- 不再需要注入、導入函數、調用函數、享受自動完成功能!
- 無需動態添加 Store,默認情況下它們都是動態的,您甚至都不會注意到。請注意,您仍然可以隨時手動使用 Store 進行注冊,但因為它是自動的,您無需擔心。
- 不再有?modules?的嵌套結構。您仍然可以通過在另一個 Store 中導入和?使用?來隱式嵌套 Store,但 Pinia 通過設計提供平面結構,同時仍然支持 Store 之間的交叉組合方式。?您甚至可以擁有 Store 的循環依賴關系。
- 沒有?命名空間模塊。鑒于 Store 的扁平架構,“命名空間” Store 是其定義方式所固有的,您可以說所有 Store 都是命名空間的。
安裝
1.使用vite創建一個空的vue3項目
npm create vue@latest
2.用你喜歡的包管理器安裝pinia:?
yarn add pinia
# 或者使用 npm
npm install pinia
?3.創建一個 pinia 實例 (根 store) 并將其傳遞給應用:
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'const pinia = createPinia()
const app = createApp(App)app.use(pinia)
app.mount('#app')
核心概念
定義 Store
在深入研究核心概念之前,我們得知道 Store 是用?
defineStore()
?定義的,它的第一個參數要求是一個獨一無二的名字:
import { defineStore } from 'pinia'// 你可以對 `defineStore()` 的返回值進行任意命名,但最好使用 store 的名字,同時以 `use` 開頭且以 `Store` 結尾。(比如 `useUserStore`,`useCartStore`,`useProductStore`)
// 第一個參數是你的應用中 Store 的唯一 ID。
export const useAlertsStore = defineStore('alerts', {// 其他配置...
})
這個名字?,也被用作?id?,是必須傳入的, Pinia 將用它來連接 store 和 devtools。為了養成習慣性的用法,將返回的函數命名為?use...?是一個符合組合式函數風格的約定。
defineStore()
?的第二個參數可接受兩類值:Setup 函數或 Option 對象。?
Option Store?
與 Vue 的選項式 API 類似,我們也可以傳入一個帶有?
state
、actions
?與?getters
?屬性的 Option 對象
export const useCounterStore = defineStore('counter', {state: () => ({ count: 0 }),getters: {double: (state) => state.count * 2,},actions: {increment() {this.count++},},
})
你可以認為?
state
?是 store 的數據 (data
),getters
?是 store 的計算屬性 (computed
),而?actions
?則是方法 (methods
)。
Setup Store
?與 Vue 組合式 API 的 setup 函數相似,我們可以傳入一個函數,該函數定義了一些響應式屬性和方法,并且返回一個帶有我們想暴露出去的屬性和方法的對象。
export const useCounterStore = defineStore('counter', () => {const count = ref(0)function increment() {count.value++}return { count, increment }
})
在?Setup Store?中:
ref()
?就是?state
?屬性computed()
?就是?getters
function()
?就是?actions
storeToRefs?
請注意,
store
?是一個用?reactive
?包裝的對象,這意味著不需要在 getters 后面寫?.value
,就像?setup
?中的?props
?一樣,如果你寫了,我們也不能解構它:?
<script setup>
const store = useCounterStore()
// ? 這將不起作用,因為它破壞了響應性
// 這就和直接解構 `props` 一樣
const { name, doubleCount } = store
name // 將始終是 "Eduardo"
doubleCount // 將始終是 0
setTimeout(() => {store.increment()
}, 1000)
// ? 這樣寫是響應式的
// 💡 當然你也可以直接使用 `store.doubleCount`
const doubleValue = computed(() => store.doubleCount)
</script>
?為了從 store 中提取屬性時保持其響應性,你需要使用?
storeToRefs()
。它將為每一個響應式屬性創建引用。當你只使用 store 的狀態而不調用任何 action 時,它會非常有用。請注意,你可以直接從 store 中解構 action,因為它們也被綁定到 store 上:
<script setup>
import { storeToRefs } from 'pinia'
const store = useCounterStore()
// `name` 和 `doubleCount` 是響應式的 ref
// 同時通過插件添加的屬性也會被提取為 ref
// 并且會跳過所有的 action 或非響應式 (不是 ref 或 reactive) 的屬性
const { name, doubleCount } = storeToRefs(store)
// 作為 action 的 increment 可以直接解構
const { increment } = store
</script>
State?
在大多數情況下,state 都是你的 store 的核心。人們通常會先定義能代表他們 APP 的 state。在 Pinia 中,state 被定義為一個返回初始狀態的函數。這使得 Pinia 可以同時支持服務端和客戶端。
import { defineStore } from 'pinia'const useStore = defineStore('storeId', {// 為了完整類型推理,推薦使用箭頭函數state: () => {return {// 所有這些屬性都將自動推斷出它們的類型count: 0,name: 'Eduardo',isAdmin: true,items: [],hasChanged: true,}},
})
默認情況下,你可以通過?
store
?實例訪問 state,直接對其進行讀寫。
const store = useStore()store.count++
Getter?
Getter 完全等同于 store 的 state 的計算值。可以通過?
defineStore()
?中的?getters
?屬性來定義它們。推薦使用箭頭函數,并且它將接收?state
?作為第一個參數:
export const useStore = defineStore('main', {state: () => ({count: 0,}),getters: {doubleCount: (state) => state.count * 2,},
})
?然后你可以直接訪問 store 實例上的 getter 了:
<script setup>
import { useStore } from './counterStore'
const store = useStore()
</script>
<template><p>Double count is {{ store.doubleCount }}</p>
</template>
Action
Action 相當于組件中的?method。它們可以通過?
defineStore()
?中的?actions
?屬性來定義,并且它們也是定義業務邏輯的完美選擇。
export const useCounterStore = defineStore('main', {state: () => ({count: 0,}),actions: {increment() {this.count++},randomizeCounter() {this.count = Math.round(100 * Math.random())},},
})
?類似?getter,action 也可通過?
this
?訪問整個 store 實例,并支持完整的類型標注(以及自動補全)。不同的是,action
?可以是異步的,你可以在它們里面?await
?調用任何 API,以及其他 action!下面是一個使用?Mande?的例子。請注意,你使用什么庫并不重要,只要你得到的是一個Promise
,你甚至可以 (在瀏覽器中) 使用原生?fetch
?函數:
import { mande } from 'mande'const api = mande('/api/users')export const useUsers = defineStore('users', {state: () => ({userData: null,// ...}),actions: {async registerUser(login, password) {try {this.userData = await api.post({ login, password })showTooltip(`Welcome back ${this.userData.name}!`)} catch (error) {showTooltip(error)// 讓表單組件顯示錯誤return error}},},
})
?Action 可以像函數或者通常意義上的方法一樣被調用:
<script setup>
const store = useCounterStore()
// 將 action 作為 store 的方法進行調用
store.randomizeCounter()
</script>
<template><!-- 即使在模板中也可以 --><button @click="store.randomizeCounter()">Randomize</button>
</template>
pinia-plugin-persistedstate
概述
本插件兼容?
pinia^2.0.0
,在使用之前請確保你已經?安裝 Pinia。?pinia-plugin-persistedstate
?豐富的功能可以使 Pinia Store 的持久化更易配置:
安裝?
1.用你喜歡的包管理器安裝依賴
- pnpm:
pnpm i pinia-plugin-persistedstate
- npm:
npm i pinia-plugin-persistedstate
- yarn:
yarn add pinia-plugin-persistedstate
?2.將插件添加到 pinia 實例上
import { createPinia } from 'pinia' import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'const pinia = createPinia() pinia.use(piniaPluginPersistedstate)
用法?
創建 Store 時,將?
persist
?選項設置為?true
。
使用組合式 Store 語法:?
import { defineStore } from 'pinia'export const useStore = defineStore('main',() => {const someState = ref('你好 pinia')return { someState }},{persist: true,}
)