pinia原理

pinia原理

创建项目

  • 新建项目vite-pinia
  • 创建vite项目
  • 安装pinia
mkdir vite-pinia
cd vite-pinia
yarn create vite
yarn add pinia
code .

使用pinia

  • main.js
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import { createPinia } from 'pinia'
const app = createApp(App)
app.use(createPinia()).mount('#app')

  • scr/store/index.js
import { defineStore } from "pinia";
export const useStore = defineStore({
  id: "main",
  state: () => {
    return {
      a: 1,
    };
  },
  getters: {
    double: (store) => {
      return store.a * 2;
    },
  },
  actions: {
    add(num) {
      this.a += 1;
    },
  },
});

  • app.vue
<script setup>
import { useStore } from './store/index'
const counter = useStore()

const counterAdd = (num)=>{
  counter.add(num)
}
</script>

<template>
  <div>
    {{counter.a}}
    {{counter.double}}
    <button @click="counter.a++">直接累加</button>
    <button @click="counterAdd(1)">点击</button>
    </div>

</template>

<style scoped>

</style>

使用自己的pinia

  • vite.config.js中添加@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  resolve:{
    //设置路径别名
    alias: {
      '@': path.resolve(__dirname, './src'),
      }
  }
})

  • mian.js修改为@/pinia
import { createApp } from 'vue'
// import './style.css'
import App from './App.vue'
import { createPinia } from '@/pinia'
const app = createApp(App)
app.use(createPinia()).mount('#app')
  • store/index.js
import { defineStore } from "@/pinia";
export const useStore = defineStore({
  id: "main",
  state: () => {
    return {
      a: 1,
    };
  },
  getters: {
    double: (store) => {
      return store.a * 2;
    },
  },
  actions: {
    add(num) {
      this.a += 1;
    },
  },
});

  • src下新增pinia文件夹,添加index.js

实现pinia的store

  • pinia/index.js
  • 导出两个方法,defineStore定义store,createPinia创建一个pinia
import { defineStore } from "./defineStore";

import { createPinia } from "./createPinia";

export {
    defineStore,
    createPinia
}
  • defineStore.js
import { effectScope, getCurrentInstance, inject, reactive } from "vue";
import { SymbolPinia } from "./rootStore";

export function defineStore(idOrOptions, setup) {
    // 第一个参数可能是id,或者对象
  let id;
  let options;

  if (typeof idOrOptions === "string") {
    id = idOrOptions;
    options = setup;
  } else {
    options = idOrOptions;
    id = idOrOptions.id;
  }

  function useStore() {
    // 获取当前vue运行的实例
    const currentInstance = getCurrentInstance();
    const pinia = currentInstance && inject(SymbolPinia);
    // 如果实例存在,用inject注册到当前实例上

    if (!pinia._s.has(id)) {
        // 判断当前store上面有没有已经注册过的id
      createOptionsStore(id, options, pinia);
    }
    const store = pinia._s.get(id);
    return store;
  }
  return useStore;
}

function createOptionsStore(id, options, pinia) {
  let { state, getters, actions } = options;

  let scope;

  const store = reactive({});

  function setup() {
    // pinia.state是一个ref 要用value接受值
    pinia.state.value[id] = state ? state() : {};
    const localState = pinia.state.value[id];

    return localState;
  }

  const setupStore = pinia._e.run(() => {
    scope = effectScope();
    return scope.run(() => setup());
  });
  

//   把当前的setupStore 合并到reactive里面,确保是一个响应式
  Object.assign(store, setupStore);

  pinia._s.set(id, store);
}
  • createPinia.js
import { effectScope, markRaw, ref } from "vue";
import { SymbolPinia } from "./rootStore";

export function createPinia() {
  const scope = effectScope(true);
  const state = scope.run(() => ref({}));
  const pinia = markRaw({
    install(app) {
      pinia._a = app;
      app.provide(SymbolPinia, pinia);
      app.config.globalProperties.$pinia = pinia;
    },
    _a: null,
    state,
    // 管理整个应用
    _e: scope,
    // 记录所有的store
    _s: new Map(),
  });

  return pinia
}
  • 测试页面是否能展示出来 a 的值

实现pinia的getters

  • setup方法
function setup() {
    // pinia.state是一个ref 要用value接受值
    pinia.state.value[id] = state ? state() : {};
    //  localState 的值要转换响应式,否则getter不能响应
    const localState = toRefs(pinia.state.value[id]);
    return Object.assign(
      localState,
      actions,
      Object.keys(getters || {}).reduce((computedCetters, name) => {
        computedCetters[name] = computed(() => {
            // 获取当前的store
           return getters[name].call(store,store)
        });
        return computedCetters
      },{})
    );
  }

实现pinia的actions

  • createOptionsStore方法
function createOptionsStore(id, options, pinia) {
  let { state, getters, actions } = options;

  let scope;

  const store = reactive({});

  function setup() {
    // pinia.state是一个ref 要用value接受值
    pinia.state.value[id] = state ? state() : {};
    const localState = pinia.state.value[id];

    return Object.assign(localState, actions);
  }

  const setupStore = pinia._e.run(() => {
    scope = effectScope();
    return scope.run(() => setup());
  });

  function warpAction(name, action) {
    return function () {
      // 触发action
      let ret = action.apply(store, arguments);
      return ret;
    };
  }

  for (let key in setupStore) {
    const prop = setupStore[key];
    if (typeof prop === "function") {
      // 解决 action中this的问题  解构赋值时候,this会改变
      // const counter = useStore()
      // const {add} = counter
      // const counterAdd = (num)=>{
      //   add(num)
      // }
      setupStore[key] = warpAction(key, prop);
    }
  }

  //   把当前的setupStore 合并到reactive里面,确保是一个响应式
  Object.assign(store, setupStore);

  pinia._s.set(id, store);
}

pinia的参数是函数的时候

  • 修改store/index.js
import { defineStore } from "@/pinia";
import { computed, reactive, toRefs } from "vue";
export const useStore = defineStore('mian',()=>{
  const state = reactive({a:1})
  const double = computed(()=>{
    return state.a*2
  })
  const add = (num)=> state.a +=num
  return { ...toRefs(state),double,add }
})
  • 修改defineStore.js
import {
  computed,
  effectScope,
  getCurrentInstance,
  inject,
  reactive,
  toRefs,
} from "vue";
import { SymbolPinia } from "./rootStore";

export function defineStore(idOrOptions, setup) {
  // 第一个参数可能是id,或者对象
  let id;
  let options;

  if (typeof idOrOptions === "string") {
    id = idOrOptions;
    options = setup;
  } else {
    options = idOrOptions;
    id = idOrOptions.id;
  }

  const isSetupStore = typeof setup === "function";

  function useStore() {
    // 获取当前vue运行的实例
    const currentInstance = getCurrentInstance();
    const pinia = currentInstance && inject(SymbolPinia);
    // 如果实例存在,用inject注册到当前实例上

    if (!pinia._s.has(id)) {
      // 判断当前store上面有没有已经注册过的id
      if (isSetupStore) {
        createSetupStore(id, setup, pinia);
      } else {
        createOptionsStore(id, options, pinia);
      }
    }
    const store = pinia._s.get(id);
    return store;
  }
  return useStore;
}

function createSetupStore(id,setup,pinia){
    const store = reactive({});
    let scope;
    const setupStore = pinia._e.run(() => {
        scope = effectScope();
        return scope.run(() => setup());
      });
      function warpAction(name, action) {
        return function () {
          // 触发action
          let ret = action.apply(store, arguments);
          return ret;
        };
      }
      for (let key in setupStore) {
        const prop = setupStore[key];
        if (typeof prop === "function") {
          // 解决 action中this的问题  解构赋值时候,this会改变
          // const counter = useStore()
          // const {add} = counter
          // const counterAdd = (num)=>{
          //   add(num)
          // }
          setupStore[key] = warpAction(key, prop);
        }
      }
      //   把当前的setupStore 合并到reactive里面,确保是一个响应式
      Object.assign(store, setupStore);
      pinia._s.set(id, store);
      return store
}

function createOptionsStore(id, options, pinia) {
  let { state, getters, actions } = options;
  function setup() {
    // pinia.state是一个ref 要用value接受值
    pinia.state.value[id] = state ? state() : {};
    const localState = toRefs(pinia.state.value[id]);
    return Object.assign(
      localState,
      actions,
      Object.keys(getters || {}).reduce((computedCetters, name) => {
        computedCetters[name] = computed(() => {
          // 获取当前的store
          return getters[name].call(store, store);
        });
        return computedCetters;
      }, {})
    );
  }
  const store = createSetupStore(id,setup,pinia)
  return store

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值