自己实现一个简单版 Pinia

我们做一个极简 Pinia,实现:

  • 创建 store
  • 响应式 state
  • 支持 actions
  • 支持在组件中使用

创建 store

createPinia() ------ 创建一个"全局容器"

js 复制代码
export function createPinia() {
  const stores = new Map();
  return {
    install(app) {
      app.provide(piniaSymbol, this);
    },
    _stores: stores,
    useStore(storeId, createStoreFn) {
      if (!this._stores.has(storeId)) {
        const store = createStoreFn();
        this._stores.set(storeId, store);
      }
      return this._stores.get(storeId);
    },
  };
}

相当于 Pinia 实例,持有所有的 store(用 Map 存储)

提供 useStore 方法,确保每个 store 是单例

app.use(pinia) 时通过 providepinia 实例挂进 Vue 的上下文中(用于 inject())

🔁 store 是单例的,怎么实现的?

js 复制代码
if (!this._stores.has(storeId)) {
  const store = createStoreFn();
  this._stores.set(storeId, store);
}

每个 storeId 对应的 store 只会初始化一次

后续所有 useStore() 都返回同一个对象

defineStore() ------ 定义 store 的注册逻辑

js 复制代码
export function defineStore(id, setupFn) {
  return function useStore() {
    const pinia = inject(piniaSymbol);
    if (!pinia) throw new Error("Pinia not installed");
    return pinia.useStore(id, () => setupFn());
  };
}

使用者写:defineStore('counter', () => { ... })

返回一个函数 useStore(),用于组件中调用

内部调用 pinia 的 useStore(),传入 storeIdsetup 逻辑

注入 pinia 实例,从 provide 中获取,确保 store 可以在全局访问

setupFn() ------ 创建响应式的 state 和 actions

js 复制代码
const state = reactive({ count: 0 });
function increment() {
  state.count++;
}

使用 Vue 3 的 reactive() 创建响应式数据

组合式 API 的方式将 state 和方法组织成一个 store

返回给组件使用

useStore() in 组件中 ------ 获取 store 实例

js 复制代码
const counter = useCounterStore();

整体代码

🔧 miniPinia.js

js 复制代码
// miniPinia.js
import { reactive, inject, provide } from "vue";

const piniaSymbol = Symbol("miniPinia");

export function createPinia() {
  const stores = new Map();
  return {
    install(app) {
      app.provide(piniaSymbol, this);
    },
    _stores: stores,
    useStore(storeId, createStoreFn) {
      if (!this._stores.has(storeId)) {
        const store = createStoreFn();
        this._stores.set(storeId, store);
      }
      return this._stores.get(storeId);
    },
  };
}

export function defineStore(id, setupFn) {
  return function useStore() {
    const pinia = inject(piniaSymbol);
    if (!pinia) throw new Error("Pinia not installed");
    return pinia.useStore(id, () => setupFn());
  };
}
js 复制代码
// counterStore.js
import { reactive } from "vue";
import { defineStore } from "./miniPinia";

export const useCounterStore = defineStore("counter", () => {
  const state = reactive({
    count: 0,
  });

  function increment() {
    state.count++;
  }

  return {
    state,
    increment,
  };
});
html 复制代码
<!-- App.vue -->
<template>
  <div>
    <h2>Count: {{ counter.state.count }}</h2>
    <button @click="counter.increment()">+1</button>
  </div>
</template>

<script setup>
  import { useCounterStore } from "./counterStore";

  const counter = useCounterStore();
</script>
js 复制代码
// main.js
import { createApp } from "vue";
import App from "./App.vue";
import { createPinia } from "./miniPinia";

const app = createApp(App);
const pinia = createPinia();
app.use(pinia);
app.mount("#app");
相关推荐
张可爱3 分钟前
ES6奶茶铺版通俗笔记 🍵✨
前端
用户877244753963 分钟前
Lubanno7UniverSheet:选择命令式,为了真正的跨框架通用
前端
paopaokaka_luck4 分钟前
基于SpringBoot+Vue的数码交流管理系统(AI问答、协同过滤算法、websocket实时聊天、Echarts图形化分析)
vue.js·人工智能·spring boot·websocket·echarts
Aoda9 分钟前
从痛点到落地:PawHaven 的 Monorepo 架构设计
前端·javascript
望获linux12 分钟前
【实时Linux实战系列】使用 u-trace 或 a-trace 进行用户态应用剖析
java·linux·前端·网络·数据库·elasticsearch·操作系统
zxg_神说要有光19 分钟前
我好像找到了最适合我的生活状态
前端·javascript
飞哥数智坊24 分钟前
今天,我的个人网站正式上线了!
前端
念念不忘 必有回响42 分钟前
前端自动化部署全流程(Jenkins + Nginx)
前端·自动化·jenkins
爱上妖精的尾巴1 小时前
5-22 WPS JS宏reduce数组的归并迭代应用(实例:提取最大最小值的记录)
服务器·前端·javascript·笔记·wps·js宏
IT_陈寒1 小时前
Java性能调优:这5个被你忽略的JVM参数让你的应用吞吐量提升50%!
前端·人工智能·后端