接下来,将 src/main.js 中对于 pinia 的引用改成对自己实现的引用。
// src/main.js
// import { createPinia } from 'pinia'
import { createPinia } from '@/pinia'
新建 src/pinia/index.js 作为库的入口文件。前面的 TodoList 中用到了两个方法:createPinia 和 defineStore。
// src/pinia/index.js
import createPinia from './createPinia'
import defineStore from './defineStore'
export { createPinia, defineStore }
观察 app.use(createPinia()) 可知,createPinia 是一个方法,返回一个包含 install 方法的对象,可以作为 app.use 方法的参数。
// src/pinia/createPinia.js
export default () => {
function install(app) {}
return {
install
}
}
创建一个总的 Store,来存储所有通过 defineStore 方法创建的 store。
// src/pinia/createPinia.js
import { reactive } from 'vue'
export default () => {
const piniaStore = reactive({})
function install(app) {}
return {
install
}
}
要把 piniaStore 暴露出来供 defineStore 调用,可以使用 vue 提供的 provide 方法。
// src/pinia/createPinia.js
import { reactive } from 'vue'
export default () => {
const piniaStore = reactive({})
function install(app) {
app.provide('piniaStore', piniaStore)
}
return {
install
}
}
但是把整个 Store 暴露出来并不十分合理,可以改成把存入 store 的方法 setSubStore 暴露出来。
// src/pinia/createPinia.js
import { reactive } from 'vue'
export default () => {
const piniaStore = reactive({})
function setSubStore(name, store) {
if (!piniaStore[name]) {
piniaStore[name] = store
}
return piniaStore
}
function install(app) {
app.provide('setSubStore', setSubStore)
}
return {
install
}
}
一些公共的 api 比如 $patch 可以在这里传入。新建一个文件 src/pinia/apis.js。
// src/pinia/apis.js
export function patch({ value }) {
const store = this
for (let key in value) {
store[key] = value[key]
}
}
给 store 添加上公共的 api。
// src/pinia/createPinia.js
import { reactive } from 'vue'
import { patch } from './apis'
export default () => {
const piniaStore = reactive({})
function setSubStore(name, store) {
if (!piniaStore[name]) {
piniaStore[name] = store
piniaStore[name].$patch = patch
}
return piniaStore
}
function install(app) {
app.provide('setSubStore', setSubStore)
}
return {
install
}
}
观察 src/store/todoList.js 中对 defineStore 方法的调用可知,该方法第一个参数是 store 的名称,第二个参数是 options 对象。
// src/pinia/defineStore.js
export default (
name,
{
state, // function
getters,
actions
}
) => {
const store = {}
}
先来实现 state,并将 store 打印出来。
// src/pinia/defineStore.js
import { reactive, toRef } from 'vue'
export default (
name,
{
state, // function
getters,
actions
}
) => {
const store = {}
if (state && typeof state === 'function') {
const _state = state()
store.$state = reactive(_state)
for (let key in _state) {
store[key] = toRef(store.$state, key)
}
}
console.log(store)
}
state
然后来实现 actions。
// src/pinia/defineStore.js
// ...
if (actions && Object.keys(actions).length > 0) {
for (let method in actions) {
store[method] = actions[method]
}
}
// ...
actions
再来实现 getters。getters 里面的方法都:
- 接收一个
state参数; - 其中的
this指向state; - 可以通过形如
this.anotherGetter()这样的语句调用其他getter。
// src/pinia/defineStore.js
// ...
if (getters && Object.keys(getters).length > 0) {
for (let getter in getters) {
store[getter] = getters[getter].bind(store.$state, store.$state)
store.$state[getter] = store[getter]
}
}
// ...
优化一下代码,将 state、actions、getters 的处理拆分到单个文件中。
// src/pinia/options/state.js
import { reactive, toRef } from 'vue'
export function createStates(store, state) {
const _state = state()
store.$state = reactive(_state)
for (let key in _state) {
store[key] = toRef(store.$state, key)
}
}
// src/pinia/options/action.js
export function createActions(store, actions) {
for (let method in actions) {
store[method] = actions[method]
}
}
// src/pinia/options/getter.js
import { computed } from 'vue'
export function createGetters(store, getters) {
for (let getter in getters) {
store[getter] = computed(
getters[getter].bind(store.$state, store.$state)
)
store.$state[getter] = store[getter]
}
}
// src/pinia/options/index.js
import { createStates } from './state'
import { createActions } from './action'
import { createGetters } from './getter'
export { createStates, createActions, createGetters }
// src/pinia/defineStore.js
import { inject, reactive } from 'vue'
import { createStates, createActions, createGetters } from './options'
export default (
name,
{
state, // function
getters,
actions
}
) => {
const store = {}
state && typeof state === 'function' && createStates(store, state)
actions &&
Object.keys(actions).length > 0 &&
createActions(store, actions)
getters &&
Object.keys(getters).length > 0 &&
createGetters(store, getters)
return () => {
const setSubStore = inject('setSubStore')
const piniaStore = setSubStore(name, reactive(store))
return piniaStore[name]
}
}
TodoList 的功能完美实现,将 store 打印出来看,有一点小瑕疵,但总体上前面分析的都实现了。
store



![[译] 终于,JavaScript 有了安全的数组方法](https://img-1252058122.cos.ap-guangzhou.myqcloud.com/blog/article-cover/cmfkkq9a00001uhcg5k63cmt3.jpg)
