日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

Pinia基本使用与源码分析-手动实现部分主要功能

發(fā)布時(shí)間:2023/12/8 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Pinia基本使用与源码分析-手动实现部分主要功能 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

基本使用

  • 在main.js中注冊(cè)pinia
import { Vue,createApp } from 'vue' import App from './App' const app = createApp(App) // 引入pinia import { createPinia } from 'pinia' // 初始化pinia,并注冊(cè) const pinia = createPinia() app.use(pinia).mount('#app')
  • /store/counter.js 聲明store的配置
import { defineStore } from 'pinia' export const useCounterStore = defineStore('counter',{state(){return {count: 10,price: 100}},getters:{totalPrice(){return `${this.count * this.price}`;}},actions:{increment(num){this.count += num}} })
  • 在App.vue中使用store
<script setup> // 引用counter倉(cāng)庫(kù) import { useCounterStore } from '@/store/counter' // 初始化倉(cāng)庫(kù) const store = useCounterStore(); </script><template><button type="button" @click="handleChangeSum">count is: {{ countStore.count }}</button><button type="button">price is: {{ countStore.price }}</button><h1>總價(jià)格:{{ countStore.totalPrice }}</h1> </template>

一、修改屬性的四種方式

/*** 1. 直接修改* 因?yàn)閜inia中的state屬性都是響應(yīng)式的,pinia支持直接修改屬性*/const dispatchIncrement = ()=>{store.count+=100;}/*** 2. 使用$patch更改屬性* $patch支持兩種修改屬性的方法(對(duì)象形式或回調(diào)函數(shù)形式)*/const dispatchIncrement = ()=>{// $patch對(duì)象形式store.$patch({ count: store.count + 100})}/*** 3. 使用$patch更改屬性 (回調(diào)函數(shù)形式)*/const dispatchIncrement = ()=>{// $patch對(duì)象形式store.$patch((state)=>{ state.count+=100 })}/*** 4. 使用actions修改屬性*/const dispatchIncrement = ()=>{store.increment(100)}

二、state屬性解構(gòu)實(shí)現(xiàn)響應(yīng)式

import { useCounterStore } from '@/store/counter' import { storeToRefs } = 'pinia' // 初始化倉(cāng)庫(kù) const store = useCounterStore(); // 通過(guò)storeToRefs實(shí)現(xiàn)解構(gòu)后依然是響應(yīng)式狀態(tài) (內(nèi)部通過(guò)toRef實(shí)現(xiàn)) const { count,price,totalPrice } = storeToRefs(store)<template><button type="button" @click="handleChangeCount">count is: {{ count }}</button><button type="button">price is: {{ price }}</button><h1>總價(jià)格:{{ totalPrice }}</h1> </template>

三、actions

counter.ts

export const useCounterStore = defineStore('counter', {actions: {getRandomNum(delay: number): Promise<number> {return new Promise(resolve => {setTimeout(() => {resolve(Math.random());}, delay);});},}, });

App.vue

import { useCounterStore } from '@/store/counter' const store = useCounterStore()const getRandomNumClick = async () => {// 兩秒之后獲取一個(gè)隨機(jī)數(shù)const number = await store.getRandomNum(2000);console.log(number); }<template><button @click="getRandomNumClick">獲取隨機(jī)數(shù)</button> </template>

三、監(jiān)聽(tīng)store的變化

// 監(jiān)聽(tīng)store的變化 countStore.$subscribe((mutations, state) => {console.log(mutations, state); })

四、重置數(shù)據(jù)

// 調(diào)用$reset方法重置數(shù)據(jù) const reset = () => {countStore.$reset() }<button @click="reset">重置</button>

源碼分析與實(shí)現(xiàn)

一、createPinia

  • 該方法返回一個(gè)pinia對(duì)象,內(nèi)部提供install方法,方便注冊(cè)
  • _a 用于保存Vue的實(shí)例對(duì)象
  • _m 參數(shù)用于保存所有的模塊
  • _e 最外層的作用域scope
  • state 通過(guò)作用域創(chuàng)建的ref對(duì)象,初始值是一個(gè)空對(duì)象{}
import { markRaw,EffectScope } from 'vue' import type { App } from 'vue' interface Pinia {install:(app:App)=>void_e: EffectScope;_m: Map<any, any>;_a?:App;state:Ref<Record<string,any>> } export function craetePinia(){// 創(chuàng)建一個(gè)scope用于控制依賴(lài)收集const scope = effectScope(true);// 初始化一個(gè)state 用于保存store所有的狀態(tài)const state = scope.run(()=>ref({}))!// 聲明一個(gè)pinia倉(cāng)庫(kù)(不能被響應(yīng)式)const pinia = markRaw<Pinia>({install(app:App){// 保存Vue的實(shí)例對(duì)象pinia._a = app;// 將pinia注入組件app.provide(SymbolPinia,pinia);// 將pinia掛載到全局app.config.globalProperties.$pinia = pinia;}_e: scope, // pinia依賴(lài)收集的作用域_m: new Map, // 管理倉(cāng)庫(kù)的集合state // 存放所有的狀態(tài)})return pinia; }

二、defineStore

  • store對(duì)象
    • 每一個(gè)store都是一個(gè)reactive對(duì)象
    • 處理state,getters,actives,將三者中的屬性與store合并
    • 將合并好的store對(duì)象存到pinia._m的集合內(nèi),key為該倉(cāng)庫(kù)id,值為store
  • state
    • 從模塊的配置項(xiàng)中取出state并執(zhí)行
    • 通過(guò)toRefs將state中的屬性轉(zhuǎn)為響應(yīng)式
    • 將結(jié)果合并到store
  • getters
    • 每一個(gè)getter都是一個(gè)計(jì)算屬性的結(jié)果,具有緩存特性,getter中的this指向store
    • 重新為getter賦值,他的結(jié)果是computed的結(jié)果,并且在計(jì)算屬性?xún)?nèi)通過(guò)call調(diào)用原始getter函數(shù)
    • 將結(jié)果合并到store
  • actions
    • 重寫(xiě)action的方法,通過(guò)apply調(diào)用原始action,改變action函數(shù)的this指向
    • 將結(jié)果合并到store
  • import {computed,effectScope,getCurrentInstance,inject,reactive,toRefs,ComputedRef,UnwrapRef,isRef,isReactive, } from 'vue'; import { SymbolPinia } from './rootStore'; import { Pinia, StoreOptions, StoreOptionsId, StateTree } from './types'; import { isObject } from './utils';// defineStore第一個(gè)參數(shù)可以是id 或者是一個(gè)配置項(xiàng) export function defineStore(idorOptions: string, options: StoreOptions): () => void; export function defineStore(idorOptions: StoreOptionsId): () => void; export function defineStore(idorOptions: string | StoreOptionsId, optionsSetup?: StoreOptions) {let id: string;let options: StoreOptions | StoreOptionsId;// 用戶(hù)傳入的可能第一個(gè)值是字符串的idif (typeof idorOptions === 'string') {id = idorOptions;options = optionsSetup!;} else if (typeof idorOptions === 'object') { //傳入的第一個(gè)參數(shù)是一個(gè)包含id的配置項(xiàng)id = idorOptions.id;options = idorOptions;}// 創(chuàng)建這個(gè)store 并添加到pinia._m中function useStore() {// 獲取組件的實(shí)例const currentInstance = getCurrentInstance();// 使用inject獲取piniaconst pinia = currentInstance && inject<Pinia>(SymbolPinia);// 從pinia._m屬性中獲取倉(cāng)庫(kù)let store = pinia?._m.get(id);// 第一次獲取沒(méi)有這個(gè)倉(cāng)庫(kù) 則創(chuàng)建倉(cāng)庫(kù)if (!store) pinia?._m.set(id, (store = createOptionsStore(id, options, pinia)));return store;}return useStore; }function createOptionsStore(id: string, options: StoreOptions | StoreOptionsId, pinia: Pinia) {// 從配置中取出用于創(chuàng)建的state actions getters屬性let { state, actions, getters } = options;// 每一個(gè)倉(cāng)庫(kù)都是一個(gè)響應(yīng)式對(duì)象let store = reactive({});function setup() {/*** 處理state* 將state中的數(shù)據(jù)添加到pinia.state中* state中所有的值都應(yīng)該是響應(yīng)式的*/pinia.state.value[id] = state ? state() : {};const localState = toRefs(pinia.state.value[id]) as any;/*** 處理getters* 因?yàn)槊恳粋€(gè)getter都是一個(gè)具有緩存的計(jì)算屬性,直接使用computed處理即可*/let localGetters = Object.keys(getters || {}).reduce((computedGetters, name) => {computedGetters[name] = computed(() => {return getters?.[name].call(store, store);});return computedGetters;}, {} as Record<string, ComputedRef>);// 返回處理后的結(jié)果return Object.assign(localState, actions, localGetters);}// 往最外層的作用域內(nèi)添加依賴(lài)(最外層作用域的scope可以管理所有模塊的依賴(lài))const setupStore = pinia._e.run(() => {// 每一個(gè)store也有自己的作用域effectconst scope = effectScope();// 使用setup收集參數(shù)return scope.run(() => setup());});/*** 處理actions* 改變action函數(shù)的this執(zhí)行*/for (let key in setupStore) {let prop = setupStore[key];// 攔截action并改寫(xiě)action的方法if (typeof prop === 'function') {prop = wrapAction(key, prop);}}function wrapAction(key: string, actionValue: Function) {return function (...args: any[]) {let res = actionValue.apply(store, args);return res;};}// 返回一個(gè)響應(yīng)式的store對(duì)象return Object.assign(store, setupStore); }

    三、$patch()

  • 合并更新操作,參數(shù)可以是對(duì)象或函數(shù)
  • 新值與舊值嵌套對(duì)象的情況下,遞歸拷貝覆蓋
  • // $patch可以是對(duì)象或函數(shù) function $patch(stateMutation: (state: UnwrapRef<S>) => void): void; function $patch(partialState: _DeepPartial<UnwrapRef<S>>): void; function $patch(partialStateOrMutator: _DeepPartial<UnwrapRef<S>> | ((state: UnwrapRef<S>) => void) ) {if (typeof partialStateOrMutator === 'function') {// 函數(shù)直接執(zhí)行partialStateOrMutator(pinia._m.get(id));} else {// 對(duì)象選擇拷貝覆蓋mergeReactiveObjects(pinia._m.get(id), partialStateOrMutator);} }function mergeReactiveObjects<T extends StateTree>(target: T, patchToApply: _DeepPartial<T>): T {// 將數(shù)據(jù)合并到store中for (let key in patchToApply) {// 原型鏈上的屬性不做處理if (!patchToApply.hasOwnProperty(key)) continuelet subPatch = patchToApply[key]!; // 新的數(shù)據(jù)let targetValue = target[key]; // 舊的數(shù)據(jù)// 新數(shù)據(jù)和舊數(shù)據(jù)仍然是對(duì)象的話(huà) 需要遞歸處理 (ref和reactive對(duì)象不做處理)if (isObject(subPatch) &&isObject(targetValue) &&target.hasOwnProperty(key) &&!isRef(subPatch) &&!isReactive(subPatch)) {// 遞歸拷貝target[key] = mergeReactiveObjects(targetValue, subPatch);} else {// @ts-expect-errortarget[key] = subPatch;}}return target; }const partialStore = {$patch,};// 返回合并后的整個(gè)store對(duì)象Object.assign(store, partialStore, setupStore);return store;

    四、$reset()

  • $reset函數(shù)用于重置state為初始狀態(tài)
  • 重新調(diào)用state方法,使用$patch更新
  • Object.assign(store, partialStore, setupStore); /*** 重置state中的狀態(tài)*/ store.$reset = function () {// 重新獲取state的結(jié)果const newState = state ? state() : {};// 使用patch將原始結(jié)果更新this.$patch(($state: _DeepPartial<UnwrapRef<S>>) => {Object.assign($state, newState);}); }; return store;

    五、$subscript()

  • $subscript函數(shù)用于監(jiān)聽(tīng)state中屬性的變化
  • 內(nèi)部使用watch實(shí)現(xiàn)
  • const partialStore = {$patch,// 監(jiān)聽(tīng)屬性的變化$subscript(callback: Function, options = {}) {scope.run(() => {watch(pinia.state.value[id], $state => {callback({ id, type: 'direct' }, $state);});});},};Object.assign(store, partialStore, setupStore);return store;

    六、storeToRefs

    • 將store中的屬性通過(guò)toRef進(jìn)行轉(zhuǎn)為響應(yīng)式屬性
    import { isReactive, isRef, toRaw, toRef } from 'vue'; import { StateTree } from './types';export function storeToRefs(store: StateTree) {store = toRaw(store);const ref = {} as Partial<typeof store>;for (let key in store) {if (isRef(store[key]) || isReactive(store[key])) {ref[key] = toRef(store, key);}}return ref; }

    總結(jié)

    以上是生活随笔為你收集整理的Pinia基本使用与源码分析-手动实现部分主要功能的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。