Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >结合 Vuex 和 Pinia 做一个适合自己的状态管理 nf-state

结合 Vuex 和 Pinia 做一个适合自己的状态管理 nf-state

作者头像
用户1174620
发布于 2022-05-12 06:00:23
发布于 2022-05-12 06:00:23
96000
代码可运行
举报
运行总次数:0
代码可运行

一开始学习了一下 Vuex,感觉比较冗余,就自己做了一个轻量级的状态管理。 后来又学习了 Pinia,于是参考 Pinia 改进了一下自己的状态管理。

结合 Vuex 和 Pinia, 保留需要的功能,去掉不需要的功能,修改一下看着不习惯的使用方法,最后得到了一个满足自己需要的轻量级状态管理 —— nf - state

设计思路

还是喜欢 MVC设计模式,状态可以看做 M,组件是V,可以用 controller 做调度,需要访问后端的话,可以做一个 services。这样整体结构比较清晰明了。

当然简单的状态不需要 controller,直接使用 getters、actions 即可。整体结构如下:

源码

https://gitee.com/naturefw-code/nf-rollup-state

在线演示

https://naturefw-code.gitee.io/nf-rollup-state/

在线文档

https://nfpress.gitee.io/doc-nf-state

优点

  • 支持全局状态局部状态
  • 可以像 Vuex 那样,用 createStore 统一注册全局状态 ;
  • 也可以像 Pinia 那样,用 defineStore 分散定义全局状态和局部状态;
  • 根据不同的场景需求,选择适合的状态变更方式(安全等级);
  • 可以和 Vuex、Pinia 共存;
  • 数据部分和操作部分“分级”存放,便于遍历;
  • 状态采用 reactive 形式,可以直接使用 watch、toRefs 等;
  • 更轻、更小、更简洁;
  • 可以记录变化日志,也可以不记录;
  • 封装了对象、数组的一些方法,使用 reactive 的时候可以“直接”赋值。

缺点

  • 不支持 option API、vue2;
  • 暂时不支持 TypeScript
  • 暂时不支持 vue-devtool;
  • 不支持SSR;
  • 只有一个简单的状态变化记录(默认不记录)。

nf-state 的结构

  • state:支持对象、函数的形式。
  • getters:会变成 computed,不支持异步(其实也可以用异步)。
  • actions:变更状态,支持异步。
  • 内置函数: state:整体赋值。patch:修改部分属性,支持深层。

本来想只保留 state 即可,但是看看 Pinia,感觉加上 getter、action 也不是不行,另外也参考 Pinia 设置了几个内置函数。

内置函数

reactive 哪都好,就是不能直接赋值,否则就会失去响应性,虽然有办法解决,但是需要多写几行代码,所以我们可以封装一下。好吧,是看到 Pinia 的 state、patch 后想到的。

$state

可以直接整体赋值,支持 object 和 数组。直接赋值即可,这样用起来就方便多了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
this.dataList.$state = {xxx}

$patch

修改部分属性。我们可以直接改状态的属性值,但是如果一次改多个的话,就有一点点麻烦,用$patch可以整洁一点。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 依次设置属性值:
this.pagerInfo.count = list.allCount === 0 ? 1 : list.allCount
this.pagerInfo.pagerIndex = 1

// 使用 $patch 设置属性值:
this.pagerInfo.$patch({
  count: list.allCount === 0 ? 1 : list.allCount,
  pagerIndex: 1
})

支持深层属性。

全局状态的使用方式

全局状态有两种定义方式:

  • 像 Vuex 那样,在 main.js 里面统一注册;
  • 像 Pinia 那样,在组件里面定义。

在 main.js 里面统一注册全局状态

nf-state 的全局状态的使用方法和 Vuex 差不多,先创建一个 js文件,定义一个或者多个状态,然后在main.js里面挂载。

优点:可以统一注册、便于管理,一个项目里有哪些全局状态,可以一目了然。

  • /store/index.js
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 定义全局状态
import { createStore } from '@naturefw/nf-state'

/* 模拟异步操作 */
const testPromie = () => {
  return new Promise((resolve) => {
    setTimeout(() => {
      const re = {
        name: '异步的方式设置name'
      }
      resolve(re)
    }, 500)
  })
}

/**
 * 统一注册全局状态。key 相当于  defineStore 的第一个参数(id)
 */
export default createStore({
  // 定义状态,会变成 reactive 的形式。store 里面是各种状态
  store: {
    // 如果只有 state,那么可以简化为一个对象的方式。
    user: {
      isLogin: false,
      name: 'jyk', //
      age: 19,
      roles: []
    },
    // 有 getters、actions
    userCenter: {
      state: {
        name: '',
        age: 12,
        list: []
      },
      getters: {
        userName () {
          return this.name + '---- 测试 getter'
        }
      },
      actions: {
        async loadData(val, state) {
          const foo = await testPromie()
          state.name = foo.name
          this.name = foo.name
          this.$state = foo
          this.$patch(foo)
        }
      },
      options: {
        isLocal: false, // true:局部状态;false:全局状态(默认属性);
        isLog: true, // true:做记录;false:不用做记录(默认属性);
        /**
         * 1:宽松,可以各种方式改变属性,适合弹窗、抽屉、多tab切换等。
         * 2:一般,不能通过属性直接改状态,只能通过内置函数、action 改变状态
         * 3:严格,不能通过属性、内置函数改状态,只能通过 action 改变状态
         * 4:超严,只能在指定组件内改变状态,比如当前用户的状态,只能在登录组件改,其他组件完全只读!
        */
        level: 1
      }
    },
    // 数组的情况
    dataList: [123] 
  },
  // 状态初始化,可以给全局状态设置初始状态,支持异步。
  init (store) {
      // 可以从后端API、indexedDB、webSQL等,设置状态的初始值。
  }
})
  • main.js
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import { createApp } from 'vue'
import App from './App.vue'

import store from './store'

createApp(App)
  .use(store)
  .mount('#app')

在组件里获取统一注册的全局状态

使用方法和 Vuex 类似,直接获取全局状态:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  import { store } from '@naturefw/nf-state'

  const { user, userCenter } = store

在组件里注册全局状态

这种方式,借鉴了Pinia的方式,我们可以建立一个 js 文件,然后定义一个状态,可以用Symbol 作为标志,这样可以更方便的避免重名。(当然也可以用 string)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import { defineStore } from '@naturefw/nf-state'

const flag = Symbol('UserInfo')
// const flag = 'UserInfo'

const getUserInfo = () => defineStore(flag, {
  state: {
    name: '客户管理',
    info: {}
  },
  getters: {
  },
  actions: {
    updateName(val) {
      this.name = val
    }
  }
})

export {
  flag,
  getUserInfo
}

虽然使用 Symbol 可以方便的避免重名,但是获取状态的时候有点小麻烦。 ID(状态标识)支持 string 和 Symbol ,大家可以根据自己的情况选择适合的方式。

在组件里面引入 这个js文件,然后可以通过 getUserInfo 函数获取状态,可以用统一注册的全局状态的方式获取。

使用局部状态

基于 provide/inject 设置了局部状态。

有时候,一个状态并不是整个项目都需要访问,这时候可以采用局部状态,比如列表页面里的状态。

定义一个局部状态

我们可以建立一个js文件,定义状态:

  • state-list.js
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import { watch } from 'vue'

import { defineStore, useStore, store } from '@naturefw/nf-state'

const flag = Symbol('pager001')
// const flag = 'pager001'

/**
 * 注册局部状态,父组件使用 provide 
 * * 数据列表用
 * @returns
 */
const regListState = () => {
  // 定义 列表用的状态
  const state = defineStore(flag, {
    state: () => {
      return {
        moduleId: 0, // 模块ID
        dataList: [], // 数据列表
        findValue: {}, // 查询条件的精简形式
        findArray: [], // 查询条件的对象形式
        pagerInfo: { // 分页信息
          pagerSize: 5,
          count: 20, // 总数
          pagerIndex: 1 // 当前页号
        },
        selection: { // 列表里选择的记录
          dataId: '', // 单选ID number 、string
          row: {}, // 单选的数据对象 {}
          dataIds: [], // 多选ID []
          rows: [] // 多选的数据对象 []
        },
        query: {} // 查询条件
      }
    },
    actions: {
      /**
       * 加载数据,
       * @param {*} isReset true:需要设置总数,页号设置为1;false:仅翻页
       */
      async loadData (isReset = false) {
        // 获取列表数据
        const list = await xxx
        // 使用 $state 直接赋值
        this.dataList.$state = list.dataList
        if (isReset) {
          this.pagerInfo.$patch({
            count: list.allCount === 0 ? 1 : list.allCount,
            pagerIndex: 1
          })
        }
      }
    }
  },
  { isLocal: true } // 设置为局部状态,没有设置的话,就是全局状态了。
  )

  // 初始化
  state.loadData(true)

  // 监听页号,实现翻页功能
  watch(() => state.pagerInfo.pagerIndex, (index) => {
    state.loadData()
  })

  // 监听查询条件,实现查询功能。
  watch(state.findValue, () => {
    state.loadData(true)
  })

  return state
}

/**
 * 子组件用 inject 获取状态
 * @returns
 */
const getListState = () => {
  return useStore(flag)
}

export {
  getListState,
  regListState
}

是不是应该把 watch 也内置了?

在父组件引入局部状态

建立父组件,使用 getListState 引入局部状态:

  • data-list.vue
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  // 引入
  import { regListState } from './controller/state-list.js'

  // 注册状态
  const state = regListState()

调用 getListState() 会用 provide 设置一个状态。

在子组件里获取局部状态

建立子组件,获取局部状态:

  • pager.vue
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  // 局部状态
  import { getListState } from '../controller/state-list.js'

  // 获取父组件提供的局部状态
  const state = getListState()

调用 getListState(), 内部会用 inject (注入)获取父组件的局部状态。这样使用起来就比较明确,也比较简单。

子组件也可以调用 regListState ,这样可以注册一个子组件的状态,子子组件只能获取子组件的状态。 子子组件如果想获取父组件的状态,那么需要设置不同的ID。

安全等级

变更状态可以有四个安全级别:宽松、一般、严格、超严。

安全级别

state类型

直接改属性

内置函数

action

范围

举例

宽松

reactive

所有组件

弹窗、抽屉的状态

一般

readonly

所有组件

严格

readonly

所有组件

超严

readonly

特定组件才可更改

当前用户状态

  • 宽松:任何组件里都可以通过属性、内置函数和 action 来更改状态。 比如弹窗状态(是否打开)、抽屉状态(是否打开)、tab标签的切换等。 这些场景里,如果可以直接修改属性的话,那么可以让代码更简洁。
  • 一般和严格:二者主要区别是,内置函数是否可以使用的问题,其实一开始不想区分的,但是想想还是先分开的话,毕竟多提供了一个选择。
  • 超严:只能在特定的组件里改变状态,其他组件只能读取状态。 比如当前访问者的状态,只有在登录组件、退出组件里改变,其他组件不能更改。

这样可以更好的适应不同的场景需求。

和 Pinia 的区别

nf-state 看起来和 Pnina 挺像的,那么有哪些区别呢?

局部状态

Pinia 都是 全局状态,没有局部状态,或者说,局部状态比较简单,似乎不用特殊处理,只是,既然都封装了,那么就做全套吧,统一封装,统一使用风格。

状态的结构

虽然都是 reactive 的形式,但是内部结构的层次不一样。

pinia 的状态,数据部分和操作部分都在一个层级里面,感觉有点分布清楚,所以 pinia 提供了 来实现 toRefs 的功能。

我还是喜欢那种层次分明的形式,比如这样:

这样设计层次很清晰,可以直接使用 toRefs 实现解构,而不会解构出来“不需要”的方法。

支持的功能

官方提供的状态管理需要满足各种需求,所以要支持 option API、vue2、TypeScript等。

而我自己做的状态管理,满足自己的需求即可,所以可以更简洁,当然可能无法满足你的需求。

可以不重复制造轮子,但是要拥有制造轮子的能力。做一个状态管理,可以培养这种能力。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-05-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
Vue 框架学习系列五:Vue 3 与状态管理库 Pinia 的深度集成
在构建复杂的 Vue.js 应用时,状态管理是一个重要的考虑因素。Vuex 是 Vue.js 官方推荐的状态管理库,但随着 Vue 3 和 Composition API 的发布,一个新的状态管理库 Pinia 开始崭露头角。Pinia 提供了与 Vue 3 和 Composition API 更紧密的集成,同时保持了 Vuex 的核心概念,如 state、mutations、actions 和 getters。本篇文章将探讨如何在 Vue 3 应用中深度集成 Pinia。
china马斯克
2024/10/04
3190
pinia核心笔记
记录pinia核心源码阅读笔记,这里跳过hmr(热更新), mapHelpers(class 工具)等工具源码。 剔除的部分vue2.0兼容代码。 当前pinia版本2.0.13
copy_left
2022/05/13
1.1K0
pinia核心笔记
​轻松掌握vuex,让你对状态管理有一个更深的理解
Vuex 是一个专为 Vue.js 应用程序开发的 状态管理模式 。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化
前端老鸟
2019/07/29
3.4K0
制作一个轻量级的状态管理插件:Vue-data-state
Vuex 是针对 Vue2 来设计的,因为 option API 本身有很多缺点,所以 Vuex 只好做各种补丁弥补这些缺点,于是变得比较“复杂”。
用户1174620
2021/04/28
8800
制作一个轻量级的状态管理插件:Vue-data-state
【玩转全栈】---- Pinia 组件状态管理器
Pinia 是一个用于 Vue 3 的状态管理库,旨在提供更加简洁、直观且灵活的状态管理模式。它由 Vue 官方团队成员开发,以其轻量级和高性能著称。Pinia 支持 Vue 3 的组合式 API,并允许开发者以一种更自然的方式组织和共享应用状态。与 Vuex 不同,Pinia 去除了复杂的概念如模块嵌套,转而采用扁平化的 store 结构,使得代码更加清晰易懂。此外,Pinia 还支持 TypeScript,提供了优秀的类型推断能力,增强了开发体验。通过 Pinia,你可以轻松地创建多个独立的 store 模块,每个模块都可以拥有自己的 state(状态)、getters(计算属性)、actions(方法)等,极大地简化了复杂应用中的状态管理。其灵活性还体现在对插件系统的良好支持,可以方便地扩展功能。总的来说,Pinia 是现代 Vue 应用中进行状态管理的理想选择。
用户11404404
2025/03/12
1060
Vue3之状态管理:Vuex和Pinia,孰强孰弱?
在前端开发中,状态管理器是一种用于管理应用程序全局状态的工具。它通常用于大型应用程序,可以帮助开发者更好地组织和管理状态,并提供一些强大的工具来简化状态的变更和使用。
用户6297767
2023/11/21
2.6K0
Vue3之状态管理:Vuex和Pinia,孰强孰弱?
Vue项目进阶:再谈Pinia函数式(composition API)用法
Hello大家好,前段时间写了一篇关于Pinia的composition API用法的文章《Pinia进阶:优雅的setup(函数式)写法+封装到你的企业项目》,收到了不少朋友的反馈和建议。笔者也结合最近项目情况和网友们的建议做一次优化,也算是一个比较完整的版本了,这包括:
南山种子外卖跑手
2022/04/21
3.5K0
Vue项目进阶:再谈Pinia函数式(composition API)用法
Pinia 入门:零基础开启 Vue 状态管理之旅
在现代 Vue 应用开发中,高效的状态管理是构建强大、可维护应用的关键。Pinia 作为一种新兴的状态管理方案,为 Vue 开发者带来了全新的体验。本文将带你从零开始,深入了解 Pinia 的基础知识,开启 Vue 状态管理的精彩之旅。
程序媛夏天
2024/11/19
3900
Pinia.js - Vue新一代状态管理器
安装成功会把依赖加在 package.json 的 dependencies 中。
青年码农
2022/09/27
1.3K1
Pinia.js - Vue新一代状态管理器
感受 Vue 新的状态管理工具之美之 Pinia---学习篇
相信在Vue中提到状态管理,大家一定会想到 Vuex。的确 Vuex确实是比较强大的,但Vuex 多少有点重,而且对 Typescript的支持也不是那么的友好,同时门槛也比较高,学习成本比较大。
用户9078190
2022/10/28
2530
感受 Vue 新的状态管理工具之美之 Pinia---学习篇
学习 Vue 3 全家桶 - vuex
现代 Web 应用都是由三大件构成,分别是:组件、数据和路由。在一些数据组件之间需要共享的时候,应该如何实现?
Cellinlab
2023/05/17
3480
学习 Vue 3 全家桶 - vuex
vue3了,试试轻量化的Vuex -- Pinia?
Pinia 是 Vue.js 的轻量级状态管理库,最近很受欢迎。它使用 Vue 3 中的新反应系统来构建一个直观且完全类型化的状态管理库。
玖柒的小窝
2021/11/04
1.5K0
vue3了,试试轻量化的Vuex -- Pinia?
Pinia入门-实现简单的用户状态管理
在整个应用程序中访问的数据(且不需要被持久化),例如导航栏中显示的用户信息,以及需要通过页面保留的数据,例如一个非常复杂的多步骤表格。
luciozhang
2023/04/22
7640
测试一下Pinia,Vuex 要出局了?
自从我开始使用Vue 3和组合API以来,我也尝试使用 Pinea 作为状态管理库。如果是从是 vue2 和 vuex 过来的,就会觉得用起来差别还是很大的。
前端小智@大迁世界
2022/09/01
6530
测试一下Pinia,Vuex 要出局了?
告别Vuex,发挥compositionAPI的优势,打造Vue3专用的轻量级状态 定义main.jscontroller组件设置监听和钩子局部状态
Vuex 是基于 Vue2 的 option API 设计的,因为 optionAPI 的一些先天问题,所以导致 Vuex 不得不用各种方式来补救,于是就出现了 getter、mutations、action、module、mapXXX 这些绕圈圈的使用方式。想要使用 Vuex 就必须先把这些额外的函数给弄明白。
用户1174620
2021/10/20
1.1K0
一杯茶的时间入门Vue新的状态管理库Pinia
Pinia 是 Vue.js 官方推荐的新一代状态管理库,它提供了非常简洁和直观的 API,可以极大地提高我们管理应用状态的效率。本文将深入介绍 Pinia 的各种高级用法,内容涵盖:
linwu
2023/08/08
4440
一杯茶的时间入门Vue新的状态管理库Pinia
Vue.js 状态管理:Pinia 与 Vuex
Vuex 和 Pinia 是用于管理 Vue.js 应用程序状态的标准 Vue.js 库。让我们比较一下他们的代码、语言、功能和社区支持。
海拥
2023/02/27
2.7K0
Vue3中使用Pinia详解
Pinia是一个专门为Vue.js设计的状态管理库,它提供了一种简单和直观的方式来管理应用程序的状态。在使用Pinia时,可以轻松地创建定义状态的存储,然后将其与Vue组件绑定,使它们能够使用该状态。和上一个博客提到的Vuex相比,Pinia 更加简单易用,体积更小,同时具有更好的 TypeScript 支持和插件系统。
九仞山
2023/10/14
9960
使用 Vue 3 与 TypeScript 构建 Web 应用: Todo
引言 界面: Vue.js 3 JavaScript 超集: TypeScript 包管理器: pnpm 前端工程化/打包: Vite 路由: Vue Router 状态管理: Pinia CSS 预处理器: Less 代码格式化: Prettier 代码质量: ESLint 预览
yiyun
2023/07/17
1.2K0
使用 Vue 3 与 TypeScript 构建 Web 应用: Todo
Pinia状态管理器学习笔记,持续记录
官方文档:https://pinia.vuejs.org/ 中文文档:https://pinia.web3doc.top/
房东的狗丶
2023/02/17
1.7K0
相关推荐
Vue 框架学习系列五:Vue 3 与状态管理库 Pinia 的深度集成
更多 >
LV.0
这个人很懒,什么都没有留下~
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验