自己实现一个简单版 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 分钟前
网页应用化部署指南:基于 Edge 浏览器创建桌面快捷方式
前端·edge
IT_陈寒1 分钟前
Python的异步陷阱:我竟然被await坑了一整天
前端·人工智能·后端
光影少年2 分钟前
Android和iOS原生开发的基础知识对RN开发的重要性,RN打包发布时原生端需要做哪些配置?
android·前端·react native·react.js·ios
Fanfffff7205 分钟前
从 6s 到 3s:一次电商前端性能优化实践的系统性总结
前端·性能优化
cypking6 分钟前
npm 依赖包版本扫描提示插件Version Lens
前端·npm·node.js
还是大剑师兰特7 分钟前
Vue3 Mixin 与 Vue2 Mixin 核心区别
前端·javascript·vue.js
188号安全攻城狮10 分钟前
【前端基础知识】JavaScript 数组方法总结:从表格速查到分类详解
开发语言·前端·javascript·网络安全
qq_3813385011 分钟前
微前端架构深度实践:从 qiankun 到 Module Federation 的企业级方案
前端·架构
鱼干~14 分钟前
【全栈知识点】全栈开发知识点
前端·人工智能·c#
英俊潇洒美少年15 分钟前
迷你 React 调度器(带优先级+时间切片)手写实现
前端·javascript·react.js