Pinia的使用与原理

Pinia 与 Vuex 对比

  • vuex:

    • ts兼容性不好
    • 命名空间的缺陷(只能有一个store)
    • mutation和action有区别
  • pinia:

    • 更简洁的API
    • ts兼容性更好
    • 无命名空间的缺陷(可以创建多个store)
    • 删除了mutation,统一在action中开发

使用方法

引入

// main.ts
import { createApp } from "vue";
import App from "./App.vue";
import { createPinia } from "@/my-pinia";const app = createApp(App);
const pinia = createPinia()
console.log("createPinia", createPinia);
// 注册pinia
app.use(pinia);app.mount("#app");

创建store

// stores/count.ts// 方式1
import { reactive, computed, toRefs } from "vue";
import { defineStore } from "@/my-pinia";
// export const useCountStore = defineStore("counter", {
//   state: () => ({ count: 1 }),
//   getters: {
//     doubleCount: (store) => store.count * 2,
//   },
//   actions: {
//     // 同步异步都在actions中完成
//     addCount() {
//       this.count++;
//     },
//   },
// });// 方式2export const useCountStore = defineStore("count", () => {const state = reactive({count: 0,});const doubleCount = computed(() => state.count * 2);const addCount = () => state.count++;return {...toRefs(state),doubleCount,addCount,};
}); 

页面调用

<script setup>
import { useCountStore } from "./stores/count";const store = useCountStore();</script><template><div>{{ store.count }}<div>数量:{{ store.count }} getters:{{ store.doubleCount }}</div><button @click="store.addCount">增加</button></div>
</template>

核心实现

  • 通过在app中注入一个对象pinia.state,触达每一个子组件,在定义时给state追加store。
  • 在获取时通过inject将store传入组件中。

createPinia.js : 创建插件并注入

  • 管理state及其下的store:
    • 基于effectScope生成scope,scope管理state,state内部存放所有的store,用于批量管理其中的响应式数据(控制响应式数据是否刷新视图)。
    • 每个store内部也有一个scope,管理内部的属性是否刷新视图。
  • 注入pinia:通过provide将pinia注入到app上,各组件实例可以获取到该pinia
import { markRaw, effectScope, ref } from "vue";
import { symbolPinia } from "./rootStore";
export function createPinia() {/***  用法*      const pinia = createPinia();app.use(pinia);*  所以createPinia返回一个pinia插件**  pinia需要有的能力*      不被响应式[markRaw]*      能被挂载到全局上*/// 创建一个独立的scope,将所有的store都丢进去,后期可以统一管理storeconst scope = effectScope(true);const state = scope.run(() => ref({}));// 创建一个静态属性(markRaw),无法被响应式[表层]const pinia = markRaw({install(app) {// app.use(pinia)时,会调用pinia的install方法并传入app// 将pinia挂载到所有组件的属性上,组件可通过inject获取app.provide(symbolPinia, pinia);// vue2语法app.config.globalProperties.$pinia = pinia;pinia._a = app;},_a: null, //挂载app实例,后期可能用到_e: scope, //指向effectScope_s: new Map(),state,});return pinia;
}

defineStore.js : 创建store

  • 每一个store都是一个响应式对象reactive
import {effectScope,getCurrentInstance,inject,reactive,computed,toRefs,
} from "vue";
import { symbolPinia } from "@/my-pinia/rootStore.js";
export function defineStore(idOrOptions, setup) {// 整合参数 defineStore({id:'xx'}) defineStore('id',setup) defineStore('id',options)let id;let options;if (typeof idOrOptions == "string") {id = idOrOptions;options = setup;} else {id = idOrOptions.id;options = idOrOptions;}const isSetupStore = typeof setup === "function";function useStore() {// 保证useStore是在setup中执行的,只有在setup中才能通过组件实例注入父组件属性const currentInstant = getCurrentInstance();// 注入app中的pinia对象const pinia = currentInstant && inject(symbolPinia);// 判断pinia中是否有该storeif (!pinia._s.has(id)) {// 第一次创建storeif (isSetupStore) {createSetupStore(id, options, pinia);} else {createOptionStore(id, options, pinia);}}// 获取pinia._s中的store并返回const store = pinia._s.get(id);return store;}return useStore;
}

createSetupStore : 根据传入的函数返回store

// 函数式store(传入一个setup函数并返回对象)
function createSetupStore(id, setup, pinia) {// !!!这是最终挂载到state上的store,每个store都是一个响应式对象let store = reactive({});let scope;/*** 在根scope上再运行一次run,则此run也会被scope收集控制* setupScope => setup()*/const setupScope = pinia._e.run(() => {// 创建一个scope,让store本身也有停止本身收集依赖的能力scope = effectScope();// 内部的setup也会被pinia控制return scope.run(() => setup());});// 包装action,将其this指向storefunction wrapAction(name, action) {return function () {// 此处可以有额外逻辑,且返回值也可以经过处理return action.apply(store, arguments);};}for (let key in setupScope) {let prop = setupScope[key];if (typeof prop === "function") {/*** 切片编程:此处主要目的是修改其this指向为当前store*          也可以加若干逻辑在其中*/setupScope[key] = wrapAction(key, prop);}}// 覆盖store并挂载方法于store中。Object.assign(store, setupScope);// 挂载到pinia上pinia._s.set(id, store);return store;
}

createOptionStore(建议写法) :根据传入的配置返回store

  • 将选项配置整合一下再调用createSetupStore
// 普通的store(state、getters...):根据id和options,创建并挂载store至pinia中
function createOptionStore(id, options, pinia) {// 取出当前store的配置let { state, getters, actions } = options;let store = reactive({});// 提供一个setup函数function setup() {// 将state挂载到pinia上的state中pinia.state.value[id] = state ? state() : {};// pinia.state => markRaw({state})// state只被proxy,但是没有响应式,因此需要将其响应式// 将其state返回的值变为响应式的,便于computed收集依赖const localStore = toRefs(pinia.state.value[id]); return Object.assign(localStore,actions,// 将getters用computed缓存,并暴露到store上Object.keys(getters).reduce((computedGetters, name) => {//   getters: {//     doubleCount: (store) => store.count * 2,//   },computedGetters[name] = computed(() => {// 如果此处pinia.state.value[id]不拿toRefs包裹// 则返回的是一个具体值,computed无法收集到store中的数据变化return getters[name].call(store, store);}); // 改变其this,并把store传入,两种写法return computedGetters;}, {}));}store = createSetupStore(id, setup, pinia);return store;
}

主文件(lib/index.js)

// index.js
export { createPinia } from "./createPinia";
export { defineStore } from "./defineStore";// rootStore.js
export const symbolPinia = Symbol();

其余方法

  • $patch:批量更改store中的属性,本质上是合并对象(深拷贝)
  • $reset:重置store中的state为初始值(保存刚开始的值,调用时覆盖即可)
  • $subscribe:监听对应store中state所有的属性,当属性变化时触发回调watch(()=>store)
  • $onAction:监听store中的actions,当调用actions时触发回调(发布订阅,在wrapAction中发布)
  • $dispose:删除store(停止收集依赖,视图不根据数据更新了[effectScope.stop])

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/668532.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ChatGPT之制作短视频

引言 今天带来了如何使用 ChatGPT和剪映来制作简单的短视频教程&#xff0c;在这其中 ChatGPT的作用主要是帮我们生成文案&#xff0c;剪映的功能就是根据文案自动生成视频&#xff0c;并配上一些图片、动画、字幕和解说。 ChatGPT生成文案 首先&#xff0c;我们需要使用提示…

Anaconda的安装及其配置

一、简介 Anaconda是一个开源的包、环境管理器&#xff0c;主要具有以下功能和特点&#xff1a; 提供conda包管理工具&#xff1a;可以方便地创建、管理和分享Python环境&#xff0c;用户可以根据自己的需要创建不同的环境&#xff0c;每个环境都可以拥有自己的Python版本、库…

python爬虫抓取新闻并且植入自己的mysql远程数据库内

python爬虫抓取新闻并且植入自己的mysql远程数据库内&#xff01;这个代码是我自己写了很久才写好的&#xff0c;分享给大家。喜欢的点个赞。 # -*- coding: utf-8 -*- from xml.etree import ElementTree as ET import datetime import randomimport pymysql from selenium im…

常用排序算法(Java版本)

1 引言 常见的排序算法有八种&#xff1a;交换排序【冒泡排序、快速排序】、插入排序【直接插入排序、希尔排序】、选择排序【简单选择排序、堆排序】、归并排序、基数排序。 2 交换排序 所谓交换&#xff0c;就是序列中任意两个元素进行比较&#xff0c;根据比较结果来交换…

nginx slice模块的使用和源码分析

文章目录 1. 为什么需要ngx_http_slice_module2. 配置指令3. 加载模块4. 源码分析4.1 指令分析4.2 模块初始化4.3 slice模块的上下文4.2 $slice_range字段值获取4.3 http header过滤处理4.4 http body过滤处理5 测试和验证 1. 为什么需要ngx_http_slice_module 顾名思义&#…

程序员为什么不喜欢关电脑,这回答很霸道!

在大家的生活中&#xff0c;经常会发现这样一个现象&#xff1a;程序员经常不关电脑。 至于程序员不关电脑的原因&#xff0c;众说纷纭。 其中这样的一个程序员&#xff0c;他的回答很霸道&#xff1a; “因为我是程序员&#xff0c;我有权选择不关电脑。我需要在任何时候都能够…

C++一维数组

个人主页&#xff1a;PingdiGuo_guo 收录专栏&#xff1a;C干货专栏 铁汁们大家好呀&#xff0c;我是PingdiGuo_guo&#xff0c;今天我们来学习一下数组&#xff08;一维&#xff09;。 文章目录 1.数组的概念与思想 2.为什么要使用数组 3.数组的特性 4.数组的操作 1.定义…

0 代码自动化测试:RF 框架实现企业级 UI 自动化测试

前言 现在大家去找工作&#xff0c;反馈回来的基本上自动化测试都是刚需&#xff01;没有自动化测试技能&#xff0c;纯手工测试基本没有什么市场。 但是很多人怕代码&#xff0c;觉得自动化测试就需要代码&#xff01;代码学习起来很难&#xff01; 当然代码学习不难&#xf…

优思学院|精益生产-改变制造业的革命性理念

在今日这个变幻莫测、竞争如潮的市场环境中&#xff0c;企业如同海上的帆船&#xff0c;面临着狂风巨浪的考验。在这样的大背景之下&#xff0c;精益生产&#xff08;Lean Production&#xff09;这一理念&#xff0c;宛如一盏明灯&#xff0c;指引着无数企业穿越迷雾&#xff…

安科瑞消防设备电源监控系统在杭后旗医院项目的设计与应用

摘要&#xff1a;本文简述了消防设备电源的组成原理&#xff0c;分析了消防设备电源监控系统在应用中的设计依据和相关规范。通过安科瑞消防设备电源监控系统在杭后旗医院项目的实例介绍&#xff0c;阐述了消防设备电源功能的实现及其重要意义。 关键词&#xff1a;消防设备电…

OpenGL的着色器内存访问

着色器内存访问 Shader Memory Access 着色器在高度流水线化的系统中执行时&#xff0c;由于其读写操作的顺序在很大程度上未定义&#xff0c;可能会引发排序和同步问题。 着色器内存访问顺序&#xff1a; 对于顶点着色器和细分评估着色器&#xff0c;尽管对于应用程序指定的每…

【jenkins】主从机制及添加Slave节点操作

一、master-slave 日常构建Jenkins任务中&#xff0c;会经常出现下面的情况&#xff1a; 自动化测试需要消耗大量的 CPU 和内存资源&#xff0c;如果服务器上还有其他的服务&#xff0c;可能会造成卡顿或者宕机这样的情况&#xff1b; Jenkins 平台上除了这个项目&#xff0c…

【Linux】解决:为什么重复创建同一个【进程pid会变化,而ppid父进程id不变?】

前言 大家好吖&#xff0c;欢迎来到 YY 滴Linux 系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过Linux的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; YY的《C》专栏YY的《C11》专栏YY的…

单调队列优化DP,LeetCode1696. 跳跃游戏 VI

一、题目 1、题目描述 给你一个下标从 0 开始的整数数组 nums 和一个整数 k 。 一开始你在下标 0 处。每一步&#xff0c;你最多可以往前跳 k 步&#xff0c;但你不能跳出数组的边界。也就是说&#xff0c;你可以从下标 i 跳到 [i 1&#xff0c; min(n - 1, i k)] 包含 两个…

开源软件在技术革新和行业变革中的作用

引言&#xff1a; 在数字化浪潮推动下&#xff0c;开源软件以其独特的魅力重构了软件开发的生态系统&#xff0c;成为技术创新和行业变革的催化剂。它通过低成本、高协作性、极致透明度的特征&#xff0c;成为企业和个人的首选。本文将深度探讨开源软件的影响力&#xff0c;展…

【C++刷题】二叉树的深搜

二叉树的深搜 一、计算布尔二叉树的值1、题目描述2、代码3、解析 二、求根节点到叶节点数字之和1、题目描述2、代码3、解析 三、二叉树剪枝1、题目描述2、代码3、解析 四、验证二叉搜索树1、题目描述2、代码3、解析 五、二叉搜索树中第K小的元素1、题目描述2、代码3、解析 六、…

16.docker删除redis缓存数据、redis常用基本命令

1.进入redis容器内部 &#xff08;1&#xff09;筛选过滤出redis容器 docker ps | grep "redis"&#xff08;2&#xff09;进入redis容器 #说明&#xff1a;d24为redis容器iddocker exec -it d24 /bin/bash2.登陆redis (1) 进入redis命令行界面 redis-cli说明&a…

重写Sylar基于协程的服务器(7、TcpServer HttpServer的设计与实现)

重写Sylar基于协程的服务器&#xff08;7、TcpServer & HttpServer的设计与实现&#xff09; 重写Sylar基于协程的服务器系列&#xff1a; 重写Sylar基于协程的服务器&#xff08;0、搭建开发环境以及项目框架 || 下载编译简化版Sylar&#xff09; 重写Sylar基于协程的服务…

算法随想录第五十一天打卡|309.最佳买卖股票时机含冷冻期, 714.买卖股票的最佳时机含手续费 ,总结

309.最佳买卖股票时机含冷冻期 本题加了一个冷冻期&#xff0c;状态就多了&#xff0c;有点难度&#xff0c;大家要把各个状态分清&#xff0c;思路才能清晰 视频讲解&#xff1a;动态规划来决定最佳时机&#xff0c;这次有冷冻期&#xff01;| LeetCode&#xff1a;309.买卖…

CAN通信----(创芯科技)CAN分析仪----转CANTest使用

点击进入官方链接进行下载创芯科技 CAN分析仪资料包&#xff1a; 创芯科技的官网&#xff1a;https://m.zhcxgd.com/ 我使用的是至尊版红色带OBD转接头的&#xff1a; 所有下图是我选择…