自己实现一个简单版 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");
相关推荐
攀登的牵牛花1 天前
前端向架构突围系列 - 跨端技术 [11 - 1]:JSBridge 原理与 Hybrid设计
前端
用户5757303346241 天前
从 LocalStorage 待办清单到 CSS 核心机制:一次搞懂数据持久化、继承与盒模型陷阱
前端
codingWhat1 天前
前端组件库开发实践:从零到发布
前端·npm·vite
cxxcode1 天前
浏览器模块加载与 Webpack 打包原理
前端
兆子龙1 天前
React Compiler 来了:少写 useMemo,照样稳
前端·架构
用户5433081441941 天前
Manifest V3 实战:从补天网站逆向到 Chrome 扩展开发全记录
前端·后端
zhqiok1 天前
React中类似于Vue中Pinia的轻量级状态管理神器——Zustand
前端
Mintopia1 天前
促成高端技术方案形成的关键要素与实践路径
前端
摸鱼的春哥1 天前
春哥的Agent通关秘籍13:实现RAG查询
前端·javascript·后端
明月_清风1 天前
滚动锁定:用户向上翻看历史时,如何阻止 AI 新消息把它“顶”下去?
前端·javascript