1. 状态管理器核心实现(ts写法)
typescript
复制代码
import { reactive, effectScope, computed, App } from 'vue'
type StoreState = Record<string, any>
type StoreActions = Record<string, Function>
type StoreGetters = Record<string, Function>
interface StoreOptions {
id: string
state?: () => StoreState
actions?: StoreActions
getters?: StoreGetters
}
interface StoreInstance {
state: StoreState
actions: StoreActions
getters: Record<string, any>
$reset: () => void
$dispose: () => void
}
export function createPinia() {
const stores: Record<string, StoreInstance> = {}
const plugins: Array<(context: { store: StoreInstance }) => void> = []
function install(app: App) {
app.provide('pinia', this)
}
function use(plugin: (context: { store: StoreInstance }) => void) {
plugins.push(plugin)
}
function defineStore(options: StoreOptions): StoreInstance {
const state = reactive(options.state?.() || {})
const scope = effectScope(true)
const actions: StoreActions = {}
const getters: Record<string, any> = {}
// Process actions
if (options.actions) {
for (const [name, fn] of Object.entries(options.actions)) {
actions[name] = function(...args: any[]) {
return scope.run(() => fn.call(store, ...args))
}
}
}
// Process getters
if (options.getters) {
scope.run(() => {
for (const [name, fn] of Object.entries(options.getters)) {
getters[name] = computed(() => fn.call(store, state))
}
})
}
const store: StoreInstance = {
state,
actions,
getters,
$reset() {
Object.assign(state, options.state?.() || {})
},
$dispose() {
scope.stop()
delete stores[options.id]
}
}
// Apply plugins
plugins.forEach(plugin => plugin({ store }))
stores[options.id] = store
return store
}
return { install, use, defineStore, _stores: stores }
}
export type Pinia = ReturnType<typeof createPinia>
JavaScript 版本
scss
复制代码
import { reactive, effectScope, computed } from 'vue'
export function createPinia() {
const stores = {}
const plugins = []
function install(app) {
app.provide('pinia', this)
}
function use(plugin) {
plugins.push(plugin)
}
function defineStore(options) {
const state = reactive(options.state?.() || {})
const scope = effectScope(true)
const actions = {}
const getters = {}
// Process actions
if (options.actions) {
for (const [name, fn] of Object.entries(options.actions)) {
actions[name] = function(...args) {
return scope.run(() => fn.call(store, ...args))
}
}
}
// Process getters
if (options.getters) {
scope.run(() => {
for (const [name, fn] of Object.entries(options.getters)) {
getters[name] = computed(() => fn.call(store, state))
}
})
}
const store = {
state,
actions,
getters,
$reset() {
Object.assign(state, options.state?.() || {})
},
$dispose() {
scope.stop()
delete stores[options.id]
}
}
// Apply plugins
plugins.forEach(plugin => plugin({ store }))
stores[options.id] = store
return store
}
return { install, use, defineStore, _stores: stores }
}
2. 项目中使用
2.1 初始化 Pinia
javascript
复制代码
import { createApp } from 'vue'
import { createPinia } from './store-core'
import App from './App.vue'
const pinia = createPinia()
const app = createApp(App)
app.use(pinia)
app.mount('#app')
2.2 创建 Store
typescript
复制代码
import { defineStore } from '../store-core'
export const useCounterStore = defineStore({
id: 'counter',
state: () => ({
count: 0,
todos: [] as string[]
}),
actions: {
increment() {
this.count++
},
addTodo(todo: string) {
this.todos.push(todo)
},
async fetchTodo() {
const response = await fetch('https://jsonplaceholder.typicode.com/todos/1')
const todo = await response.json()
this.addTodo(todo.title)
}
},
getters: {
doubleCount(state) {
return state.count * 2
},
todoCount(state) {
return state.todos.length
}
}
})
2.3 在组件中使用
xml
复制代码
<script setup lang="ts">
import { useCounterStore } from '../stores/counter'
const counter = useCounterStore()
</script>
<template>
<div>
<h2>Counter Store</h2>
<p>Count: {{ counter.state.count }}</p>
<p>Double Count: {{ counter.getters.doubleCount.value }}</p>
<p>Todo Count: {{ counter.getters.todoCount.value }}</p>
<button @click="counter.actions.increment()">Increment</button>
<button @click="counter.actions.addTodo('New Todo')">Add Todo</button>
<button @click="counter.actions.fetchTodo()">Fetch Todo</button>
<ul>
<li v-for="(todo, index) in counter.state.todos" :key="index">
{{ todo }}
</li>
</ul>
</div>
</template>
3. 高级用法 - 插件系统
3.1 创建一个持久化插件
typescript
复制代码
interface PersistOptions {
key?: string
storage?: Storage
}
export function createPersistPlugin(options: PersistOptions = {}) {
const { key = 'pinia-state', storage = localStorage } = options
return ({ store }) => {
// 从存储中恢复状态
const savedState = storage.getItem(`${key}:${store._id}`)
if (savedState) {
Object.assign(store.state, JSON.parse(savedState))
}
// 监听状态变化并保存
store._scope.run(() => {
watch(
() => store.state,
(state) => {
storage.setItem(`${key}:${store._id}`, JSON.stringify(state))
},
{ deep: true }
)
})
}
}
3.2 使用插件
javascript
复制代码
import { createApp } from 'vue'
import { createPinia } from './store-core'
import { createPersistPlugin } from './plugins/persist'
import App from './App.vue'
const pinia = createPinia()
pinia.use(createPersistPlugin({
key: 'my-app-state'
}))
const app = createApp(App)
app.use(pinia)
app.mount('#app')