生活随笔
收集整理的這篇文章主要介紹了
Pinia基本使用与源码分析-手动实现部分主要功能
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
基本使用
import { Vue
,createApp
} from 'vue'
import App
from './App'
const app
= createApp(App
)
import { createPinia
} from 'pinia'
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
}}
})
<script setup
>
import { useCounterStore
} from '@/store/counter'
const store
= useCounterStore();
</script
><template
><button type
="button" @click
="handleChangeSum">count is
: {{ countStore
.count
}}</button
><button type
="button">price is
: {{ countStore
.price
}}</button
><h1
>總價格
:{{ countStore
.totalPrice
}}</h1
>
</template
>
一、修改屬性的四種方式
const dispatchIncrement = ()=>{store
.count
+=100;}const dispatchIncrement = ()=>{store
.$patch({ count: store
.count
+ 100})}const dispatchIncrement = ()=>{store
.$patch((state)=>{ state
.count
+=100 })}const dispatchIncrement = ()=>{store
.increment(100)}
二、state屬性解構實現響應式
import { useCounterStore
} from '@/store/counter'
import { storeToRefs
} = 'pinia'
const store
= useCounterStore();
const { count
,price
,totalPrice
} = storeToRefs(store
)<template
><button type
="button" @click
="handleChangeCount">count is
: {{ count
}}</button
><button type
="button">price is
: {{ price
}}</button
><h1
>總價格
:{{ 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 () => {const number = await store
.getRandomNum(2000);console.log(number);
}<template
><button
@click="getRandomNumClick">獲取隨機數
</button
>
</template
>
三、監聽store的變化
countStore
.$subscribe((mutations, state) => {console
.log(mutations
, state
);
})
四、重置數據
const reset = () => {countStore
.$reset()
}<button @click
="reset">重置
</button
>
源碼分析與實現
一、createPinia
- 該方法返回一個pinia對象,內部提供install方法,方便注冊
- _a 用于保存Vue的實例對象
- _m 參數用于保存所有的模塊
- _e 最外層的作用域scope
- state 通過作用域創建的ref對象,初始值是一個空對象{}
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(){const scope
= effectScope(true);const state
= scope
.run(()=>ref({}))!const pinia
= markRaw<Pinia>({install(app
:App
){pinia
._a
= app
;app
.provide(SymbolPinia
,pinia
);app
.config
.globalProperties
.$pinia
= pinia
;}_e
: scope
, _m
: new Map, state
})return pinia
;
}
二、defineStore
store對象 - 每一個store都是一個reactive對象
- 處理state,getters,actives,將三者中的屬性與store合并
- 將合并好的store對象存到pinia._m的集合內,key為該倉庫id,值為store
state - 從模塊的配置項中取出state并執行
- 通過toRefs將state中的屬性轉為響應式
- 將結果合并到store
getters - 每一個getter都是一個計算屬性的結果,具有緩存特性,getter中的this指向store
- 重新為getter賦值,他的結果是computed的結果,并且在計算屬性內通過call調用原始getter函數
- 將結果合并到store
actions - 重寫action的方法,通過apply調用原始action,改變action函數的this指向
- 將結果合并到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';
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
;if (typeof idorOptions
=== 'string') {id
= idorOptions
;options
= optionsSetup
!;} else if (typeof idorOptions
=== 'object') { id
= idorOptions
.id
;options
= idorOptions
;}function useStore() {const currentInstance
= getCurrentInstance();const pinia
= currentInstance
&& inject<Pinia>(SymbolPinia
);let store
= pinia
?._m
.get(id
);if (!store
) pinia
?._m
.set(id
, (store
= createOptionsStore(id
, options
, pinia
)));return store
;}return useStore
;
}function createOptionsStore(id
: string, options
: StoreOptions
| StoreOptionsId
, pinia
: Pinia
) {let { state
, actions
, getters
} = options
;let store
= reactive({});function setup() {pinia
.state
.value
[id
] = state
? state() : {};const localState
= toRefs(pinia
.state
.value
[id
]) as any;let localGetters
= Object
.keys(getters
|| {}).reduce((computedGetters
, name
) => {computedGetters
[name
] = computed(() => {return getters
?.[name
].call(store
, store
);});return computedGetters
;}, {} as Record
<string, ComputedRef
>);return Object
.assign(localState
, actions
, localGetters
);}const setupStore
= pinia
._e
.run(() => {const scope
= effectScope();return scope
.run(() => setup());});for (let key
in setupStore
) {let prop
= setupStore
[key
];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
;};}return Object
.assign(store
, setupStore
);
}
三、$patch()
合并更新操作,參數可以是對象或函數新值與舊值嵌套對象的情況下,遞歸拷貝覆蓋
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') {partialStateOrMutator(pinia
._m
.get(id
));} else {mergeReactiveObjects(pinia
._m
.get(id
), partialStateOrMutator
);}
}function mergeReactiveObjects<T extends StateTree>(target
: T, patchToApply
: _DeepPartial
<T>): T {for (let key
in patchToApply
) {if (!patchToApply
.hasOwnProperty(key
)) continuelet subPatch
= patchToApply
[key
]!; let targetValue
= target
[key
]; if (isObject(subPatch
) &&isObject(targetValue
) &&target
.hasOwnProperty(key
) &&!isRef(subPatch
) &&!isReactive(subPatch
)) {target
[key
] = mergeReactiveObjects(targetValue
, subPatch
);} else {target
[key
] = subPatch
;}}return target
;
}const partialStore
= {$patch
,};Object
.assign(store
, partialStore
, setupStore
);return store
;
四、$reset()
$reset函數用于重置state為初始狀態重新調用state方法,使用$patch更新
Object
.assign(store
, partialStore
, setupStore
);
store
.$reset = function () {const newState
= state
? state() : {};this.$patch(($state
: _DeepPartial
<UnwrapRef
<S>>) => {Object
.assign($state
, newState
);});
};
return store
;
五、$subscript()
$subscript函數用于監聽state中屬性的變化內部使用watch實現
const partialStore
= {$patch
,$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中的屬性通過toRef進行轉為響應式屬性
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
;
}
總結
以上是生活随笔為你收集整理的Pinia基本使用与源码分析-手动实现部分主要功能的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。