Vuex模块化应用实践示例


Posted in Javascript onFebruary 03, 2020

Vuex作为Vue全家桶的成员之一,重要性肯定不用多说,正在做Vue项目的同学,随着项目需求、功能逐渐增加,用到Vuex也是早晚的事儿,作为一个前端,只能面对现实:学不动也得学!

这篇文章主要介绍Vuex在大型项目中的模块化及持久化应用实践,下面正文开始

Vuex的应用场景

  • 多个组件视图共享同一状态时(如登录状态等)
  • 多个组件需要改变同一个状态时
  • 多个组件需要互相传递参数且关系较为复杂,正常传参方式变得难以维护时
  • 持久化存储某些数据

所以我们把组件共享的状态抽离出来,不管组件间的关系如何,都通过Vuex来处理

组织store目录

我们先按模块化的方式组织store目录,并在Vue根实例中注册store,Vuex 通过 store 选项,提供了一种机制将状态从根组件“注入”到每一个子组件中

src
├── ...
├── main.js
├── App.vue
└── store
  ├── index.js     # 我们组装模块并导出 store 的地方
  └── modules
    ├── product.js    # 产品模块
    ├── windowInfo.js  # 窗口信息模块
    └── user.js     # 登录模块

src/store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
import user from './modules/user'
import product from './modules/product'
import windowInfo from './modules/windowInfo'

Vue.use(Vuex)

export default new Vuex.Store({
 modules: {
  // 注册modules中的模块
  user,
  product,
  windowInfo
 }
})

src/main.js

import ...
import store from './store' // 添加这行

new Vue({
 el: '#app',
 router,
 store, // 注入到根实例
 template: '<App/>',
 components: { App }
})

store的属性

state(状态对象)
state中存放多页面共享的状态字段

getters
相当于当前模块state的计算属性

mutations
如果想更新state中的字段,提交mutations中定义的事件是唯一的方式(key为事件名,value是一个函数),但是这个事件函数必须是同步执行的

actions
可以定义异步函数,并在回调中提交mutation,就相当于异步更新了state中的字段

Vuex模块化应用实践示例

vuex数据传递规则

使用方法

把窗口的高度和宽度存到Vuex中,并且每当窗口被resize,state中的高度和宽度自动更新

src/store/modules/windowInfo.js

import { MU_WIN_RESIZE } from '../../common/constants'
const windowInfo = {
 state: {
  // 初始化
  winHeight: 0,
  winWidth: 0
 },
 mutations: {
  // 这里把事件名统一抽离到constants.js统一管理,方便维护,避免重复。
  // 当然,你也可以不这么写。。
  // mutation事件接受的第一个参数是当前模块的state对象
  // 第二个参数是提交事件时传递的附加参数
  [MU_WIN_RESIZE] (state, payload) {
   const { winWidth, winHeight } = payload
   state.winWidth = winWidth
   state.winHeight = winHeight
  }
 },
 actions: {},
 getters: {}
}

export default windowInfo

src/common/constants.js

export const MU_WIN_RESIZE = 'MU_WIN_RESIZE' // 更新窗口尺寸

下面打开项目的根组件添加监听resize事件和提交mutation事件逻辑

src/App.vue

<!--上面的template我就不往这儿放了-->
<script>
 import { _on, _off, getClientWidth, getClientHeight } from './common/dom'
 import { MU_WIN_RESIZE } from './common/constants'
 import { mapMutations } from 'vuex'

 export default {
  name: 'app',
  data () {
   return {}
  },
  mounted () {
   this.handleResize()
   // 这里对addEventListener方法做了IE兼容处理,就不贴出来了,反正事件监听你们都会
   _on(window, 'resize', this.handleResize)
  },
  beforeDestroy () {
   _off(window, 'resize', this.handleResize)
  },
  methods: {
   // 对象展开运算符,不熟悉的同学该学学ES6了
   ...mapMutations({
    // 映射 this.winResize 为 this.$store.commit(MU_WIN_RESIZE)
    winResize: MU_WIN_RESIZE
   }),
   handleResize () {
    const winWidth = getClientWidth()
    const winHeight = getClientHeight()
    this.winResize({ winWidth, winHeight })
   }
  }
 }
</script>

到这一步,在拖动窗口触发‘resize'事件的时候,就会触发‘MU_WIN_RESIZE'这个mutation事件并把窗口宽高写入vuex,下面我们随便找个页面看能不能获取到我们写入的值

<template>
 <div class="row">窗口高:{{winHeight}} 窗口宽:{{winWidth}}</div>
</template>
<script>
 import { mapState } from 'vuex'
 export default {
  name: 'test',
  data () {
   return {}
  },
  computed: {
   // 把state写入计算属性
   // 如果使用mapGetters也是写入计算属性
   ...mapState({
    winHeight: state => state.windowInfo.winHeight,
    winWidth: state => state.windowInfo.winWidth
   })
  },
 }
</script>

有的时候我们会从后端获取一些下拉框选项的静态常量,而且很多页面都能用到,这个时候用Vuex是比较好的选择,涉及到后端获取,就要用到可以使用异步的actions了

src/store/modules/product.js

import {MU_PRODUCT_UPDATE_CONSTANTS} from '../../common/constants'

const product = {
 state: {
  productConstants: []
 },
 mutations: {
  [MU_PRODUCT_UPDATE_CONSTANTS] (state, payload) {
   state.productConstants = payload
  }
 },
 actions: {
  // action函数第一个参数接受一个与 store 实例具有相同方法和属性的 context 对象,
  // 因此你可以调用 context.commit 提交一个 mutation,
  // 或者通过 context.state 和 context.getters 来获取 state 和 getters
  // 这里虽然能获取到state,但是不建议直接修改state中的字段
  async getProductConstants ({ commit }, payload) {
   try {
    // 请求接口,如果需要参数可以通过payload传递
    const res = await this.$api.product.getConstants()
    commit(MU_PRODUCT_UPDATE_CONSTANTS, res)
   } catch (e) {
    console.error(e)
   }
  }
 },
 getters: {}
}

export default product

下面触发这个getProductConstants事件,触发这个action事件的位置需要注意一下,假设你有5个组件需要使用这个state,那就应该在这5个组件共同的父组件中调用一次action(找不到就在根实例中调用),然后在各个子组件中通过mapState或mapGetters获取state,千万不要每个组件使用前都调用一次action方法!

src/App.vue

<!--为了更直观的展示action,把之前的代码删掉了-->
<script>
 import { mapActions } from 'vuex' // 注意是mapActions

 export default {
  name: 'app',
  data () {
   return {}
  },
  created () {
   // 触发请求
   this.getProductConstants()
  }
  methods: {
   ...mapActions([
    // 映射 this.getProductConstants 为 this.$store.dispatch('getProductConstants')
    'getProductConstants'
   ])
  }
 }
</script>

mapGetters, mapMutations, mapActions,这几个函数可以接受对象也可以接受数组作为参数,如果你需要在组件中以别的名字调用该事件(像上面的mapMutations)就可以传入对象,key为新命名,value是store中定义的名字;否则的话传数组就好了。

那么问题来了,既然是异步操作,我想在操作结束后干点儿别的怎么做呢?
很简单,调用action中的异步函数(this.$store.dispatch)返回的是一个Promise,如果你跟我一样用的是async await:

<!--为了更直观的展示action,把之前的代码删掉了-->
<script>
 import { mapActions } from 'vuex' // 注意是mapActions

 export default {
  name: 'app',
  data () {
   return {}
  },
  async created () {
   // 触发请求
   await this.getProductConstants()
   // 接下来执行的操作会等待上面函数完成才会执行
  }
  methods: {
   ...mapActions([
    // 映射 this.getProductConstants 为 this.$store.dispatch('getProductConstants')
    'getProductConstants'
   ])
  }
 }
</script>

如果你用的不是async await那就麻烦一点,在actions中定义事件的时候return一个new Promise,官方文档中有一个例子

表单处理

当你把从state中获取的字段填在v-model中时,如果用户修改表单数据,v-model会尝试直接修改store中的数据,这样做会有两个问题:

  1. 破坏了vuex的数据传递规则,如果想修改state中的数据只能通过提交一个mutation
  2. 控制台报错:计算属性没有setter

官方提供了两种解决方法,我更倾向于下面这种,给计算属性添加setter,并在setter中提交mutation修改state:

<template>
 <input v-model="message">
</template>
<script>
  export default {
  name: 'app',
  data () {
   return {}
  },
  computed: {
   message: {
    get () {
     return this.$store.state.test.message
    },
    set (value) {
     this.$store.commit('updateMessage', value)
    }
   }
  }
  methods: {}
 }
</script>

Vuex持久化

推荐插件vuex-persist

安装插件:

npm install --save vuex-persist

引入、配置、加载插件:
src/store/persist.js

import VuexPersistence from 'vuex-persist'

const persist = new VuexPersistence({
 // 其他参数看文档
 storage: window.sessionStorage
})
export default persist.plugin

src/store/index.js

import ...
import persist from './persist'

Vue.use(Vuex)

export default new Vuex.Store({
 modules: {
  user,
  product,
  windowInfo
 },
 plugins: [persist]
})

现在刷新浏览器数据也不会重置了!

总结

以上就是vuex比较常规的操作了,第一次看官方文档的我是懵逼的、无助的,但是用了一段时间vuex再重新看文档的时候会有很多收获。希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
网页和浏览器兼容性问题汇总(draft1)
Jun 01 Javascript
jquery parent和parents的区别分析
Oct 02 Javascript
在页面加载完成后通过jquery给多个span赋值
May 21 Javascript
Jquery 全选反选实例代码
Nov 19 Javascript
延时加载JavaScript代码提高速度
Dec 27 Javascript
JavaScript仿支付宝密码输入框
Dec 29 Javascript
Javascript中的对象和原型(二)
Aug 12 Javascript
jquery心形点赞关注效果的简单实现
Nov 14 Javascript
Vue2.0结合webuploader实现文件分片上传功能
Mar 09 Javascript
详解如何在vue-cli中使用vuex
Aug 07 Javascript
vue 源码解析之虚拟Dom-render
Aug 26 Javascript
vue项目实现分页效果
Mar 24 Vue.js
微信小程序8种数据通信的方式小结
Feb 03 #Javascript
使用js实现单链解决前端队列问题的方法
Feb 03 #Javascript
javscript 数组扁平化的实现
Feb 03 #Javascript
Vue强制组件重新渲染的方法讨论
Feb 03 #Javascript
JavaScript中的类型检查
Feb 03 #Javascript
Vue的Eslint配置文件eslintrc.js说明与规则介绍
Feb 03 #Javascript
压缩Vue.js打包后的体积方法总结(Vue.js打包后体积过大问题)
Feb 03 #Javascript
You might like
理解php原理的opcodes(操作码)
2010/10/26 PHP
Windows7下的php环境配置教程
2015/02/28 PHP
php给图片添加文字水印方法汇总
2015/08/27 PHP
php array_pop 删除数组最后一个元素实例
2016/11/02 PHP
php源码之将图片转化为data/base64数据流实例详解
2016/11/27 PHP
PHP基于关联数组20行代码搞定约瑟夫问题示例
2017/11/07 PHP
js验证电话号码与手机支持+86的正则表达式
2014/01/23 Javascript
js实现飞入星星特效代码
2014/10/17 Javascript
javascript实现的固定位置悬浮窗口实例
2015/04/30 Javascript
js兼容火狐获取图片宽和高的方法
2015/05/21 Javascript
bootstrap-wysiwyg结合ajax实现图片上传实时刷新功能
2016/05/27 Javascript
AngularJS基础 ng-class-odd 指令示例
2016/08/01 Javascript
jQuery实现遮罩层登录对话框
2016/12/29 Javascript
prototype与__proto__区别详细介绍
2017/01/09 Javascript
Vue+axios 实现http拦截及路由拦截实例
2017/04/25 Javascript
JavaScript表格隔行变色和Tab标签页特效示例【附jQuery版】
2019/07/11 jQuery
layui 上传插件 带预览 非自动上传功能的实例(非常实用)
2019/09/23 Javascript
深入理解 TypeScript Reflect Metadata
2019/12/12 Javascript
vue-resource 拦截器interceptors使用详解
2021/01/18 Vue.js
Python生成器(Generator)详解
2015/04/13 Python
python中redis的安装和使用
2016/12/04 Python
WINDOWS 同时安装 python2 python3 后 pip 错误的解决方法
2017/03/16 Python
python+mongodb数据抓取详细介绍
2017/10/25 Python
Python批量合并有合并单元格的Excel文件详解
2018/04/05 Python
python的依赖管理的实现
2019/05/14 Python
Python Pandas对缺失值的处理方法
2019/09/27 Python
新加坡时尚网上购物:Zalora新加坡
2016/07/26 全球购物
纽约服装和生活方式品牌:Saturdays NYC
2017/08/13 全球购物
编辑个人求职信范文
2013/09/21 职场文书
感恩父母的演讲稿
2014/05/06 职场文书
文明美德伴我成长演讲稿
2014/05/12 职场文书
会计学自荐信
2014/06/03 职场文书
小石潭记导游词
2015/02/03 职场文书
色戒观后感
2015/06/12 职场文书
html+css 实现简易导航栏功能
2021/04/07 HTML / CSS
戴尔Win11系统no bootable devices found解决教程
2022/09/23 数码科技