温馨提示:本文以vue3+vite+ts举例,vite配置和ts语法侧重较少,比较适合有vuex或者vue基础的小伙伴们儿查阅。

安装pinia

  • yarn
yarn add pinia
  • npm
npm install pinia
  • pnpm
pnpm add pinia

1-开始

方式一:在main.ts中直接引入pinia

src/main.ts 中引入pinia(根存储),并传递给应用程序。

import { createApp } from 'vue'
import './style.css'
import App from './App.vue' // 1-创建一个 pinia(根存储)
import { createPinia } from 'pinia' const app = createApp(App) // 2-告诉应用程序,我们将使用pinia
const pinia = createPinia();
// 以插件形式传递给app
app.use(pinia); app.mount('#app');

方式二(推荐):单独开个.ts文件引入pinia

在根目录下新建文件夹,这里我命名为store,再在文件夹下新建一个index.ts文件(src/store/index.ts),用以配置和引入pinia。

// 1-创建一个 pinia(根存储)
import { createPinia } from 'pinia' // 2-定义pinia实例
const pinia = createPinia(); // 3-暴露pinia实例
export default pinia;

然后在src/main.ts 中使用。

......
import pinia from '@/store/index.ts'
app.use(pinia);
......

其实和方式一没啥区别,只是为了保持main.ts文件整洁,并且方便配置pinia。

2-创建仓库

pinia与vuex差不多,相比于vuex,少了mutationmodules

pinia创建仓库,有选项式写法组合式写法

选项式Options API写法

在根目录下创建一个文件夹store (src/store),在store文件夹中可以创建你的仓库,比如下面我创建了一个名为user的仓库 (src/store/user.ts)。

// 选项式写法
// 1-引入api
import { defineStore } from "pinia"; // 2-定义仓库
const store = defineStore('user', {
// 3-设置组件共享的状态,相当于组件的data
state: () => ({
userInfo: {
name: '老刘',
sex: '男',
age: 17,
isStudent: false
},
token: '5201314',
password: '123456',
}),
// 3-设置状态计算值,相当于组件的computed
getters: {
name: (state) => state.userInfo.name,
sex: (state) => state.userInfo.sex,
},
// 3-设置组件共享的方法,相当于组件的methods
actions: {
addAge() {
this.userInfo.age++;
}
}
}); // 最后别忘了把仓库暴露出去哦
export default store;

组合式Composition API写法(推荐)

上面的仓库 (src/store/user.ts)组合式写法如下:

// 组合式写法
// 1-引入pinia的api
import { defineStore } from "pinia";
// 2-引入vue3相关api
import { ref, reactive, computed } from 'vue'; // 3-定义仓库,注意第二个参数需要传入一个函数,函数需要返回一个对象!
const store = defineStore('user', () => {
// 4-在这里面可以像在组件中一样,使用vue3的API,定义响应式数据
const userInfo = reactive({
name: '老刘',
sex: '男',
age: 17,
isStudent: false
});
const token = ref('5201314');
const password = ref('123456'); // 这里computed的作用相当于getters
const name = computed(() => userInfo.name);
const sex = computed(() => userInfo.sex); // 4-还可以定义方法
function addAge() {
userInfo.age++;
} // 5-然后把需要共享的数据或方法,装进一个对象,return出去
return {
userInfo,
token,
password,
name,
sex,
addAge
}
}); // 最后别忘了把仓库暴露出去哦
export default store;

TIP

还可以在仓库中使用watchwatchEffect等vue3的API喔~。

import { ref, reactive, computed, watch } from 'vue';
const store = defineStore('user', () => {
const userInfo = reactive({
age: 17,
});
// 使用vue3的watch()函数,可以对仓库状态进行监听
watch(() => userInfo.age, (val) => {
console.log(val);
});
});

3-使用pinia

完成了上面的工作后,我们就可以在组件中愉快地使用pinia啦!

下面以src/App.vue作为示例。

(1)引入仓库

<template>

</template>

<script setup lang="ts">
// 1-引入刚刚自定义的仓库,模块名store 可以自定义
import store from '@/store/user.ts'; // 2-使用仓库,仓库实例名userStore 可以自定义
const userStore = store();
</script>

(2)在组件中访问仓库stategetters

在模板和script中,state和getters可以看作仓库实例(如userStore)的属性,直接加.访问即可。

<template>
<div>
<!-- 1-访问仓库的计算属性getters -->
<span>姓名:{{ userStore.name }}</span>
<span>性别:{{ userStore.sex }}</span>
<!-- 1-访问仓库的状态state -->
<span>年龄:{{ userStore.userInfo.age }}</span>
<span>是否学生:{{ userStore.userInfo.isStudent ? '是' : '否' }}</span>
</div>
</template> <script setup lang="ts">
import store from '@/store/user.ts';
const userStore = store(); // 1-访问state和getters,类似于reactive响应式数据的访问
console.log('userStore', userStore);
console.log('姓名:', userStore.name);
console.log('性别:', userStore.sex);
console.log('年龄:', userStore.userInfo.age);
console.log('是否学生:', userStore.userInfo.isStudent ? '是' : '否');
</script>
输出
    userStore Proxy(Object) {$id: 'user', $onAction: ƒ, $patch: ƒ, $reset: ƒ, $subscribe: ƒ, …}[[Handler]]: Object[[Target]]: Object[[IsRevoked]]: false

    姓名: 老刘
性别: 男
年龄: 17
是否学生: 否

(3)在组件中使用仓库actions

使用仓库方法与访问仓库state类似,仓库实例后直接加.调用即可。

<template>
<div>
<!-- 按钮点击,年龄+1 -->
<button @click="onAddAge">年龄+1</button>
<span>年龄:{{ userStore.userInfo.age }}</span>
</div>
</template> <script setup lang="ts">
import store from '@/store/user.ts';
const userStore = store(); // 按钮点击触发
function onAddAge() {
// 1-使用仓库的actions
userStore.addAge();
}
</script>

(4)修改state

直接修改

与vuex不同,pinia支持在组件中直接修改state

<template>
<div>
<!-- 按钮点击,年龄+1 -->
<button @click="onAddAge">年龄+1</button>
<span>年龄:{{ userStore.userInfo.age }}</span>
</div>
</template> <script setup lang="ts">
import store from '@/store/user.ts';
const userStore = store(); // 按钮点击触发
function onAddAge() {
// 1-直接修改state
userStore.userInfo.age++;
}
</script>

利用仓库actions进行修改

src/store/user.ts

......

const store = defineStore('user', () => {
...... // 1-定义方法
function addAge() {
userInfo.age++;
} // 2-return出去
return {
addAge
}
}); // 3-导出仓库
export default store;

src/App.vue

<template>
<div>
<!-- 按钮点击,年龄+1 -->
<button @click="onAddAge">年龄+1</button>
<span>年龄:{{ userStore.userInfo.age }}</span>
</div>
</template> <script setup lang="ts">
import store from '@/store/user.ts';
const userStore = store(); // 按钮点击触发
function onAddAge() {
// 4-使用仓库的方法
userStore.addAge();
}
</script>

批量变更

通过仓库实例(如userStore)的 $patch 方法,可以对state同时应用多个更改。

<script setup lang="ts">
...... function changeState() {
console.log(userStore.token); // '5201314'
console.log(userStore.password); // '123456' // $patch()接收一个对象,对象内的属性是 需要变更的state
// 注意是变更,新增state是无效的!
userStore.$patch({
token: '1024',
password: '654321'
}); console.log(userStore.token); // '1024'
console.log(userStore.password); // '654321'
}
</script>

上面的方法每次进行批量修改都需要传入一个新对象,有时候使用起来并不方便。下面是另一种写法,$patch接受一个函数来批量修改集合内部分对象。(推荐)

<script setup lang="ts">
...... function changeState() {
// 这里的any是typescript的类型标注,可以不用理会
// 回调函数的参数state就是 仓库目前的state
userStore.$patch((state: any) => {
state.token = '1024';
state.password = '654321';
});
}
</script>

整体替换(不推荐

通过仓库实例(如userStore)的 $state 属性,来为新对象替换仓库的整个状态。

pinia官网提到整体替换state的方法,但并未说明是否保留数据响应式。经笔者实践,这种方法会丢失数据的响应式,所以不推荐使用

<script setup lang="ts">
...... function updateStore() {
userStore.$state = {
userInfo: {
name: '老王',
sex: '男',
age: 66,
isStudent: false
}
};
}
</script>

(5)重置state

通过调用仓库实例上的 $reset() 方法将状态重置到其初始值。

<script setup lang="ts">
...... function resetStore() {
userStore.$reset();
}
</script>

$reset() 的坑

细心的你会发现,仓库state并没有重置,然后你打开你的的控制台,你会惊讶地发现它报了这么一个错误:

这时候请你不要慌,先冷静地看一下报错信息。

这里翻译一下:Store "user"是使用setup语法构建的,不实现$reset()。(猜测是pinia的缺陷)

所以,根据报错信息,这里提供下面两种解决方案。

使用选项式Options API

使用选项式Options API编写pinia仓库,并且在组件中不能用script setup语法,要使用setup函数进行开发。

src/store/user.ts

......

// 使用选项式Options API编写仓库
const store = defineStore('user', {
// 3-设置组件共享的状态,相当于组件的data
state: () => ({
userInfo: {
name: '老刘',
sex: '男',
age: 17,
isStudent: false
},
token: '5201314',
password: '123456',
}),
}); export default store;

src/App.vue

<!-- 这里不能用script setup,否则依然报错 -->
<script lang="ts">
import store from '@/store/user.ts'; export default {
setup() {
const userStore = store(); function resetStore() {
// 重置state
userStore.$reset();
} // 把响应式数据或方法return出去
return {
userStore,
resetStore
}
},
}
</script>

重写一个$reset()方法(推荐)

原理:自定义pinia插件(Plugins),利用$patch() 重置整个state

在之前创建的pinia配置文件中修改(src/store/index.ts)。

import { createPinia } from 'pinia';
const pinia = createPinia(); // 1-使用pinia自定义插件
pinia.use(({ store }) => {
// 2-获取最开始的State
const initialState = JSON.parse(JSON.stringify(store.$state));
// 3-重写$reset()方法
store.$reset = () => {
// 4-利用$patch()批量变更state,达到重置state的目的
store.$patch(initialState);
}
}); export default pinia;

推荐使用这种方法,这样就可以在script setup中愉快地使用pinia啦!

完整示例

仓库配置

src/store/index.ts

import { createPinia } from 'pinia';
const pinia = createPinia(); pinia.use(({ store }) => {
const initialState = JSON.parse(JSON.stringify(store.$state));
store.$reset = () => {
store.$patch(initialState);
}
}); export default pinia;

定义仓库

src/store/user.ts

// 组合式写法
import { defineStore } from "pinia";
import { ref, reactive, computed, watch } from 'vue'; const store = defineStore('user', () => {
const userInfo = reactive({
name: '老刘',
sex: '男',
age: 17,
isStudent: false
});
const token = ref('5201314');
const password = ref('123456'); const name = computed(() => userInfo.name);
const sex = computed(() => userInfo.sex); watch(() => userInfo.age, (val) => {
console.log(val);
}); function addAge() {
userInfo.age++;
} return {
userInfo,
token,
password,
name,
sex,
addAge
}
}); export default store;

父组件

src/App.vue

<template>
<div>
<button @click="resetStore">重置store</button>
<h1>我是父组件</h1>
<button @click="userStore.userInfo.age++">年龄+1</button>
<span class="marginLeft60">姓名:{{ userStore.name }}</span>
<span class="marginLeft60">性别:{{ userStore.sex }}</span>
<span class="marginLeft60 red">年龄:{{ userStore.userInfo.age }}</span>
<span class="marginLeft60 red">是否学生:{{ userStore.userInfo.isStudent ? '是' : '否' }}</span>
<hr> <!-- 使用子组件A -->
<son-a />
<hr> <!-- 使用子组件B -->
<son-b />
<hr>
</div>
</template> <script setup lang="ts">
// 引入子组件
import SonA from './components/sonA.vue';
import SonB from './components/sonB.vue'; // 1-引入仓库
import store from '@/store/user.ts'; // 2-使用仓库
const userStore = store(); // 重置store
function resetStore() {
userStore.$reset();
}
</script> <style>
.marginLeft60 {
margin-left: 60px;
} .red {
color: red;
}
</style>

子组件A

src/components/sonA.vue

<template>
<div>
<h2>我是子组件A</h2>
<button @click="userStore.userInfo.isStudent = true">入学</button>
<span class="marginLeft60">姓名:{{ userStore.name }}</span>
<span class="marginLeft60">性别:{{ userStore.sex }}</span>
<span class="marginLeft60 red">年龄:{{ userStore.userInfo.age }}</span>
<span class="marginLeft60 red">是否学生:{{ userStore.userInfo.isStudent ? '是' : '否' }}</span>
<grandson />
</div>
</template> <script setup lang="ts">
import Grandson from './grandson.vue';
import store from '@/store/user.ts'; const userStore = store();
</script>

子组件B

src/components/sonB.vue

<template>
<div>
<h2>我是子组件B</h2>
<button @click="userStore.userInfo.isStudent = false">毕业</button>
<span class="marginLeft60">姓名:{{ userStore.name }}</span>
<span class="marginLeft60">性别:{{ userStore.sex }}</span>
<span class="marginLeft60 red">年龄:{{ userStore.userInfo.age }}</span>
<span class="marginLeft60 red">是否学生:{{ userStore.userInfo.isStudent ? '是' : '否' }}</span>
</div>
</template> <script setup lang="ts">
import store from '@/store/user.ts';
const userStore = store();
</script>

孙组件

src/components/grandson.vue

<template>
<div>
<h3>我是孙组件</h3>
<span class="marginLeft60">姓名:{{ userStore.name }}</span>
<span class="marginLeft60">性别:{{ userStore.sex }}</span>
<span class="marginLeft60 red">年龄:{{ userStore.userInfo.age }}</span>
<span class="marginLeft60 red">是否学生:{{ userStore.userInfo.isStudent ? '是' : '否' }}</span>
</div>
</template> <script setup lang="ts">
import store from '@/store/user.ts';
const userStore = store();
</script>

效果图

vue3探索——5分钟快速上手大菠萝pinia的更多相关文章

  1. 三分钟快速上手TensorFlow 2.0 (上)——前置基础、模型建立与可视化

    本文学习笔记参照来源:https://tf.wiki/zh/basic/basic.html 学习笔记类似提纲,具体细节参照上文链接 一些前置的基础 随机数 tf.random uniform(sha ...

  2. 三分钟快速上手TensorFlow 2.0 (下)——模型的部署 、大规模训练、加速

    前文:三分钟快速上手TensorFlow 2.0 (中)——常用模块和模型的部署 TensorFlow 模型导出 使用 SavedModel 完整导出模型 不仅包含参数的权值,还包含计算的流程(即计算 ...

  3. 三分钟快速上手TensorFlow 2.0 (中)——常用模块和模型的部署

    本文学习笔记参照来源:https://tf.wiki/zh/basic/basic.html 前文:三分钟快速上手TensorFlow 2.0 (上)——前置基础.模型建立与可视化 tf.train. ...

  4. 【Microsoft Azure 的1024种玩法】一.一分钟快速上手搭建宝塔管理面板

    简介 宝塔Linux面板是提升运维效率的服务器管理软件,其支持一键LAMP/LNMP/集群/监控/网站/FTP/数据库/JAVA等100多项服务器管理功能.今天带大家一起学习的内容为一分钟快速上手搭建 ...

  5. 十分钟快速上手NutUI

    本文将会从 NutUI 初学者的使用入手,对 NutUI 做了一个快速的概述,希望能帮助新人在项目中快速上手. 文章包括以下主要内容 安装引入 NutUI NutUI 组件的使用 NutUI 主题和样 ...

  6. 【PyTorch v1.1.0文档研习】60分钟快速上手

    阅读文档:使用 PyTorch 进行深度学习:60分钟快速入门. 本教程的目标是: 总体上理解 PyTorch 的张量库和神经网络 训练一个小的神经网络来进行图像分类 PyTorch 是个啥? 这是基 ...

  7. 推荐一款全能测试开发神器:Mockoon!1分钟快速上手!

    1. 说一下背景 在日常开发或者测试工作中,经常会因为下游服务不可用或者不稳定时,通过工具或者技术手段去模拟一个HTTP Server,或者模拟所需要的接口数据. 这个时候,很多人脑海里,都会想到可以 ...

  8. Python+chatGPT编程5分钟快速上手,强烈推荐!!!

    最近一段时间chatGPT火爆出圈!无论是在互联网行业,还是其他各行业都赚足了话题. 俗话说:"外行看笑话,内行看门道",今天从chatGPT个人体验感受以及如何用的角度来分享一下 ...

  9. 三分钟快速上手TensorFlow 2.0 (后续)——扩展和附录

    TensorFlow Hub 模型复用 TF Hub 网站 打开主页 https://tfhub.dev/ ,在左侧有 Text.Image.Video 和 Publishers 等选项,可以选取关注 ...

  10. 基于Asp.net core + EF + Sqlite 5分钟快速上手一个小项目

    虽然该方法不会用在实际开发中,但该过程对于初学者还是非常友好的,真应了麻雀虽小,五脏俱全这句话了.好了不多废话了,直接开始!! 1.建立一个名为test的Asp.net core web应用程序 这一 ...

随机推荐

  1. OCR -- 文本识别 -- 实践篇

    OCR -- 文本识别 -- 理论篇 本章将详细介绍如何基于PaddleOCR完成CRNN文本识别模型的搭建.训练.评估和预测.数据集采用 icdar 2015,其中训练集有4468张,测试集有207 ...

  2. docker 对容器中的文件进行编辑

    用途 有一些情况下,例如docker安装的redis.nacos.mysql等等,在docker容器中的安装未进行文件的映射,当需要对其进行更改配置信息时,就会遇到这种情况,需要去容器中进行编辑配置文 ...

  3. Pinot2的无人机传感器和摄像头

    目录 1. 引言 2. 技术原理及概念 2.1 基本概念解释 2.2 技术原理介绍 2.3 相关技术比较 无人机传感器和摄像头在Pinot 2中得到广泛应用,其目的是为Pinot 2提供全面的传感器和 ...

  4. asp.net程序通过Microsoft Azure中SAML协议实现单点登录

    1. 新建应用程序 登录Azure门户,进入左侧菜单"企业应用程序--所有应用程序",点"新建应用程序", 继续点"创建你自己的应用程序", ...

  5. Vue 先初始化子组件再初始化父组件的方法(自定义父子组件mounted执行顺序)

    写在前面: 本篇内容内容主要讲述了,在使用 Konva 进行开发过程中遇到的一些问题.(既然是组件加载顺序,主要牵扯到的就是,父子组件的关系,父子组件的生命周期) 众所周知,Vue中父子组件生命周期的 ...

  6. 跟着 GPT-4 从0到1学习 Golang 并发机制(一)

    目录 一.前言 二.开聊 2.1 Golang 里的并发机制介绍 2.2 Goroutine 与线程 2.3 Goroutine 与线程的调度开销 2.4 用户态和内核态 2.5 Golang 并发编 ...

  7. Centos安装ELK

    目录 安装Elastic Search 安装 Java 安装Elastic Search 修改配置 开放端口 访问地址 相关命令 安装elasticsearch-head插件 安装Git 安装node ...

  8. 基于GPT搭建私有知识库聊天机器人(三)向量数据训练

    在前面的文章中,我们介绍了实现原理和基本环境安装.本文将重点介绍数据训练的流程,以及如何加载.切割.训练数据,并使用向量数据库Milvus进行数据存储. 1. 数据训练依赖于向量数据库 在本文中,我们 ...

  9. 包管理工具npm和Yarn的区别,我们该如何选择?

    好家伙,学习新工具    1.为什么我们需要包管理器? 关于npm我们已经知道了,这是我们项目的包管理器, 我们现在用的无比顺手的工具,都是在无数的竞争中杀出来的,他们淘汰了无数的产品   首先,倘若 ...

  10. MySQL到ClickHouse数据同步方案

    MySQL 同步到 ClickHouse的方案可以看下面的说明,选择合适最近的同步方法. 1. 对比结果概述 整体上,NineData(官网:www.ninedata.cloud )的数据复制功能在功 ...