经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » JS/JS库/框架 » Vue.js » 查看文章
vue3探索——5分钟快速上手大菠萝pinia
来源:cnblogs  作者:前端cry  时间:2023/9/10 14:25:48  对本文有异议

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

安装pinia

  • yarn
  1. yarn add pinia
  • npm
  1. npm install pinia
  • pnpm
  1. pnpm add pinia

1-开始

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

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

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

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

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

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

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

  1. ......
  2. import pinia from '@/store/index.ts'
  3. app.use(pinia);
  4. ......

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

2-创建仓库

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

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

选项式Options API写法

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

  1. // 选项式写法
  2. // 1-引入api
  3. import { defineStore } from "pinia";
  4. // 2-定义仓库
  5. const store = defineStore('user', {
  6. // 3-设置组件共享的状态,相当于组件的data
  7. state: () => ({
  8. userInfo: {
  9. name: '老刘',
  10. sex: '男',
  11. age: 17,
  12. isStudent: false
  13. },
  14. token: '5201314',
  15. password: '123456',
  16. }),
  17. // 3-设置状态计算值,相当于组件的computed
  18. getters: {
  19. name: (state) => state.userInfo.name,
  20. sex: (state) => state.userInfo.sex,
  21. },
  22. // 3-设置组件共享的方法,相当于组件的methods
  23. actions: {
  24. addAge() {
  25. this.userInfo.age++;
  26. }
  27. }
  28. });
  29. // 最后别忘了把仓库暴露出去哦
  30. export default store;

组合式Composition API写法(推荐)

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

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

TIP

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

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

3-使用pinia

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

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

(1)引入仓库

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

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

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

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

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

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

  1. <template>
  2. <div>
  3. <!-- 按钮点击,年龄+1 -->
  4. <button @click="onAddAge">年龄+1</button>
  5. <span>年龄:{{ userStore.userInfo.age }}</span>
  6. </div>
  7. </template>
  8. <script setup lang="ts">
  9. import store from '@/store/user.ts';
  10. const userStore = store();
  11. // 按钮点击触发
  12. function onAddAge() {
  13. // 1-使用仓库的actions
  14. userStore.addAge();
  15. }
  16. </script>

(4)修改state

直接修改

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

  1. <template>
  2. <div>
  3. <!-- 按钮点击,年龄+1 -->
  4. <button @click="onAddAge">年龄+1</button>
  5. <span>年龄:{{ userStore.userInfo.age }}</span>
  6. </div>
  7. </template>
  8. <script setup lang="ts">
  9. import store from '@/store/user.ts';
  10. const userStore = store();
  11. // 按钮点击触发
  12. function onAddAge() {
  13. // 1-直接修改state
  14. userStore.userInfo.age++;
  15. }
  16. </script>

利用仓库actions进行修改

src/store/user.ts

  1. ......
  2. const store = defineStore('user', () => {
  3. ......
  4. // 1-定义方法
  5. function addAge() {
  6. userInfo.age++;
  7. }
  8. // 2-return出去
  9. return {
  10. addAge
  11. }
  12. });
  13. // 3-导出仓库
  14. export default store;

src/App.vue

  1. <template>
  2. <div>
  3. <!-- 按钮点击,年龄+1 -->
  4. <button @click="onAddAge">年龄+1</button>
  5. <span>年龄:{{ userStore.userInfo.age }}</span>
  6. </div>
  7. </template>
  8. <script setup lang="ts">
  9. import store from '@/store/user.ts';
  10. const userStore = store();
  11. // 按钮点击触发
  12. function onAddAge() {
  13. // 4-使用仓库的方法
  14. userStore.addAge();
  15. }
  16. </script>

批量变更

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

  1. <script setup lang="ts">
  2. ......
  3. function changeState() {
  4. console.log(userStore.token); // '5201314'
  5. console.log(userStore.password); // '123456'
  6. // $patch()接收一个对象,对象内的属性是 需要变更的state
  7. // 注意是变更,新增state是无效的!
  8. userStore.$patch({
  9. token: '1024',
  10. password: '654321'
  11. });
  12. console.log(userStore.token); // '1024'
  13. console.log(userStore.password); // '654321'
  14. }
  15. </script>

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

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

整体替换(不推荐

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

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

  1. <script setup lang="ts">
  2. ......
  3. function updateStore() {
  4. userStore.$state = {
  5. userInfo: {
  6. name: '老王',
  7. sex: '男',
  8. age: 66,
  9. isStudent: false
  10. }
  11. };
  12. }
  13. </script>

(5)重置state

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

  1. <script setup lang="ts">
  2. ......
  3. function resetStore() {
  4. userStore.$reset();
  5. }
  6. </script>

$reset() 的坑

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

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

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

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

使用选项式Options API

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

src/store/user.ts

  1. ......
  2. // 使用选项式Options API编写仓库
  3. const store = defineStore('user', {
  4. // 3-设置组件共享的状态,相当于组件的data
  5. state: () => ({
  6. userInfo: {
  7. name: '老刘',
  8. sex: '男',
  9. age: 17,
  10. isStudent: false
  11. },
  12. token: '5201314',
  13. password: '123456',
  14. }),
  15. });
  16. export default store;

src/App.vue

  1. <!-- 这里不能用script setup,否则依然报错 -->
  2. <script lang="ts">
  3. import store from '@/store/user.ts';
  4. export default {
  5. setup() {
  6. const userStore = store();
  7. function resetStore() {
  8. // 重置state
  9. userStore.$reset();
  10. }
  11. // 把响应式数据或方法return出去
  12. return {
  13. userStore,
  14. resetStore
  15. }
  16. },
  17. }
  18. </script>

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

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

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

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

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

完整示例

仓库配置

src/store/index.ts

  1. import { createPinia } from 'pinia';
  2. const pinia = createPinia();
  3. pinia.use(({ store }) => {
  4. const initialState = JSON.parse(JSON.stringify(store.$state));
  5. store.$reset = () => {
  6. store.$patch(initialState);
  7. }
  8. });
  9. export default pinia;

定义仓库

src/store/user.ts

  1. // 组合式写法
  2. import { defineStore } from "pinia";
  3. import { ref, reactive, computed, watch } from 'vue';
  4. const store = defineStore('user', () => {
  5. const userInfo = reactive({
  6. name: '老刘',
  7. sex: '男',
  8. age: 17,
  9. isStudent: false
  10. });
  11. const token = ref('5201314');
  12. const password = ref('123456');
  13. const name = computed(() => userInfo.name);
  14. const sex = computed(() => userInfo.sex);
  15. watch(() => userInfo.age, (val) => {
  16. console.log(val);
  17. });
  18. function addAge() {
  19. userInfo.age++;
  20. }
  21. return {
  22. userInfo,
  23. token,
  24. password,
  25. name,
  26. sex,
  27. addAge
  28. }
  29. });
  30. export default store;

父组件

src/App.vue

  1. <template>
  2. <div>
  3. <button @click="resetStore">重置store</button>
  4. <h1>我是父组件</h1>
  5. <button @click="userStore.userInfo.age++">年龄+1</button>
  6. <span class="marginLeft60">姓名:{{ userStore.name }}</span>
  7. <span class="marginLeft60">性别:{{ userStore.sex }}</span>
  8. <span class="marginLeft60 red">年龄:{{ userStore.userInfo.age }}</span>
  9. <span class="marginLeft60 red">是否学生:{{ userStore.userInfo.isStudent ? '是' : '否' }}</span>
  10. <hr>
  11. <!-- 使用子组件A -->
  12. <son-a />
  13. <hr>
  14. <!-- 使用子组件B -->
  15. <son-b />
  16. <hr>
  17. </div>
  18. </template>
  19. <script setup lang="ts">
  20. // 引入子组件
  21. import SonA from './components/sonA.vue';
  22. import SonB from './components/sonB.vue';
  23. // 1-引入仓库
  24. import store from '@/store/user.ts';
  25. // 2-使用仓库
  26. const userStore = store();
  27. // 重置store
  28. function resetStore() {
  29. userStore.$reset();
  30. }
  31. </script>
  32. <style>
  33. .marginLeft60 {
  34. margin-left: 60px;
  35. }
  36. .red {
  37. color: red;
  38. }
  39. </style>

子组件A

src/components/sonA.vue

  1. <template>
  2. <div>
  3. <h2>我是子组件A</h2>
  4. <button @click="userStore.userInfo.isStudent = true">入学</button>
  5. <span class="marginLeft60">姓名:{{ userStore.name }}</span>
  6. <span class="marginLeft60">性别:{{ userStore.sex }}</span>
  7. <span class="marginLeft60 red">年龄:{{ userStore.userInfo.age }}</span>
  8. <span class="marginLeft60 red">是否学生:{{ userStore.userInfo.isStudent ? '是' : '否' }}</span>
  9. <grandson />
  10. </div>
  11. </template>
  12. <script setup lang="ts">
  13. import Grandson from './grandson.vue';
  14. import store from '@/store/user.ts';
  15. const userStore = store();
  16. </script>

子组件B

src/components/sonB.vue

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

孙组件

src/components/grandson.vue

  1. <template>
  2. <div>
  3. <h3>我是孙组件</h3>
  4. <span class="marginLeft60">姓名:{{ userStore.name }}</span>
  5. <span class="marginLeft60">性别:{{ userStore.sex }}</span>
  6. <span class="marginLeft60 red">年龄:{{ userStore.userInfo.age }}</span>
  7. <span class="marginLeft60 red">是否学生:{{ userStore.userInfo.isStudent ? '是' : '否' }}</span>
  8. </div>
  9. </template>
  10. <script setup lang="ts">
  11. import store from '@/store/user.ts';
  12. const userStore = store();
  13. </script>

效果图

原文链接:https://www.cnblogs.com/cry0-0/p/17691177.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号