【初级】基于Vue2.x和ts使用状态管理器的新起之秀Pinia

462 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第N天,点击查看活动详情

前情提要

  • Pinia出来有一段时间了,一直想要去学习,但苦于没有时间就一直搁置了。刚巧最近有出于试点目的的基于微前端重构公司的一个海外的项目,于是决定试点就试点到底,在状态管理器这块抛弃Vuex拥抱Pinia
    • 但不可盲目拥抱
  • 至于Pinia相比较于Vuex的优缺点在哪里?在这里就进行简单的讲述一下:
    • 体积上,Pinia只有1kb大小左右,比Vuex小了很多。
    • 完整的TypeScript支持。
    • 支持Vue devtoolsSSRwebpack代码拆分
    • 支持生成多个Store,具备模块化开发。
    • 写法上相比较于Vuex更加简洁少了mutations,只有actions,且actions同时具备处理同步和异步任务。
    • 但是不具备时间旅行和编辑。
      • 所谓的时间旅行就是会缓存该state值的每一次变化,并且可以进行回退操作。可能很少使用该功能。
    • 相比较于VuexPinia当下更适用于中小型项目,而Vuex属于重量级选手,更适合大型项目。所以前面说不可盲目拥抱,一定要考量好自己的项目体量和复杂程度。但是相信随着Vue团队对Pinia的优化和升级迭代,很快就能胜任大型的高复杂度的项目开发。

搭建Pinia的环境

  • main.js / main.ts

    // 做如下增加
    ...
    import { createPinia, PiniaVuePlugin } from 'pinia';
    Vue.use(PiniaVuePlugin);
    const pinia = createPinia();
    new Vue({
        ...,
        pinia,
        ...
    }).$mount("#app")
    
    • 如此,在项目中pinia就注册好了。
  • 注意:如果是使用的vue-cli脚手架创建的项目,那记得要卸载vuex和@vue/cli-plugin-vuex两个npm包哦。因为他们已经用不到了。

具体使用

创建store

  • 我们可以在src目录下创建一个store的文件夹。然后在里面创建一个ts文件作为store之一。

  • 比如我们创建一个index.tsstore

    import { defineStore } from 'pinia';
    export const userStore = defineStore({
        id: 'index', // 此处的id可以理解为命名空间的唯一标识。如果使用pinia提供的mapstate或者mapactions就需要用到它。
        state: () => ({
            msg: 'Hello World!'
        }),
        getters: {
            newMsg: (state) => `Lei say ${ state.msg }`;
        },
        actions: { // 具体使用过程中可以直接当成普通函数使用,而不需要像vuex那种还得commit
            setNewMsg(msg: string) {
                this.msg = msg;
            }
        }
    })
    

在组件中的使用

  • 使用state:

    • 通过创建单个store实例的方式结合计算属性

      <template>
          <div>
              <p>{{ msg }}</p>
          </div>
      </template>
      <script lang="ts">
      import { Vue, Component } from 'vue-property-decorator';
      import { indexStore } from '@/store/index';
      const indexStore = indexStore();
      @Component
      export class About extends Vue {
          get msg(): string {
              return indexStore.msg;
          }
      }
      </script>
      
    • 有说也可以直接在template里写indexStore.msg获取。但是具体使用的时候会报错。会报indexStore属性或者方法不存在

    • 使用解构:

      • 代码

        <script lang="ts">
        import { Vue, Component } from 'vue-property-decorator';
        import { indexStore } from '@/store/index';
        const { msg } = indexStore();
        </script>
        
      • 问题:解构会使state中的值变成非响应式的

      • 如果非要这样用,但也要响应式的话也不是不可以解决。这个时候需要用到storeToRefs

      • 代码

        import { storeToRefs } from 'pinia';
        import { indexStore } from '@/store/index';
        const { msg } = storeToRefs(indexStore);
        
  • 修改state: 在实际使用中我们修改state的方式可以直接通过: indexStore.msg = 'xxxx';之所以能这么用是因为Vue的响应式。但是非常不建议这么改。强烈建议还是通过actions去改。

    <script lang="ts">
        ...
        // 可以绑定在button上或者生命周期里去运行都可以。
        setNewMsg() {
            indexStore.setNewMsg('I am not happy!');
        }
    </script>
    
  • 对于getters的使用,在组件中如何使用的state就如何使用getters即可。

  • 对于actions的使用,前面咱们在演示修改state的时候已经设计到了一种方式,那就是同步的情况。

    • 我们知道在Vuexmutations主要是处理同步任务,而actions是处理异步任务。但是在Piniaactions可以兼顾同步和异步任务。使用方法,对于同步任务,正常写就可以了。对于异步任务,我们可以结合async/await使用。
    • 在实现具体业务逻辑的时候难免会遇到actions中的方法调用actions的方法,或者调用其他store种的actions中的方法。
      • 对于actions中调用actions的方法,我们可以直接通过this进行访问。

      • 对于调用其他storeactions的方法,我们可以通过像在组件中使用的方式一样实例化,然后直接使用:

        import { userStore } from './user'
        ...... // 此处省略与上面相同的代码
        ,
        actions: { // 具体使用过程中可以直接当成普通函数使用,而不需要像vuex那种还得commit
            setNewMsg(msg: string) {
                const userStore = userStore();
                this.msg = userStore.nickName;
            }
        }
        

写在后面

  • 对于状态处理器最大的诟病无法持久化存储的问题,也就是我们常见的一刷新页面就什么都没有被初始化的问题时长困扰着大家,甚至一度放弃使用。但是实际业务中可能存在确实需要依赖状态处理器的地方,那时我们又得各种写将其与localStorage或者sessionStorage结合使用实现真正的持久化。但是便写的过程也是一个烦心的过程,而Pinia借助插件帮我们解决了这个问题。具体使用请见下片文章...
  • Pinia的官方文档