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 相关文章推荐
简单实用的js调试logger组件实现代码
Nov 20 Javascript
js点击事件链接的问题解决
Apr 25 Javascript
将form表单中的元素转换成对象的方法适用表单提交
May 02 Javascript
JavaScript实现鼠标滑过图片变换效果的方法
Apr 16 Javascript
js注册时输入合法性验证方法
Oct 21 Javascript
javascript实现电脑和手机版样式切换
Nov 10 Javascript
Three.js 再探 - 写一个微信跳一跳极简版游戏
Jan 04 Javascript
React Native中NavigatorIOS组件的简单使用详解
Jan 27 Javascript
vuex中的 mapState,mapGetters,mapActions,mapMutations 的使用
Apr 13 Javascript
基于elementUI实现图片预览组件的示例代码
Mar 31 Javascript
如何使用Jquery动态生成二级选项列表
Feb 06 jQuery
在Vue中创建可重用的 Transition的方法
Jun 02 Javascript
微信小程序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 在文件指定行插入数据的代码
2010/05/08 PHP
php中数字、字符与对象判断函数用法实例
2014/11/26 PHP
WordPress中Gravatar头像缓存到本地及相关优化的技巧
2015/12/19 PHP
对php 判断http还是https,以及获得当前url的方法详解
2019/01/15 PHP
TP5(thinkPHP5框架)实现显示错误信息及行号功能的方法
2019/06/03 PHP
thinkphp5.1框架容器与依赖注入实例分析
2019/07/23 PHP
才发现的超链接js导致网页中GIF动画停止的解决方法
2007/11/02 Javascript
javascript 禁止复制网页
2009/06/11 Javascript
20个非常有用的PHP类库 加速php开发
2010/01/15 Javascript
Jquery中删除元素的实现代码
2011/12/29 Javascript
JS实现程序暂停与继续功能代码解读
2013/10/10 Javascript
javascript内存管理详细解析
2013/11/11 Javascript
node.js中的fs.futimesSync方法使用说明
2014/12/17 Javascript
JavaScript类型系统之正则表达式
2016/01/05 Javascript
jQuery动态添加及删除表单上传元素的方法(附demo源码下载)
2016/01/15 Javascript
类似于QQ的右滑删除效果的实现方法
2016/10/16 Javascript
Yarn的安装与使用详细介绍
2016/10/25 Javascript
JS实现微信弹出搜索框 多条件查询功能
2016/12/13 Javascript
理解nodejs的stream和pipe机制的原理和实现
2017/08/12 NodeJs
vue获取input输入值的问题解决办法
2017/10/17 Javascript
详解vue-router传参的两种方式
2018/09/10 Javascript
Vue中 v-if/v-show/插值表达式导致闪现的原因及解决办法
2018/10/12 Javascript
jQuery分组选择器简单用法示例
2019/04/04 jQuery
layui table 获取分页 limit的方法
2019/09/20 Javascript
iview实现动态表单和自定义验证时间段重叠
2021/01/10 Javascript
Python struct模块解析
2014/06/12 Python
Python求两个list的差集、交集与并集的方法
2014/11/01 Python
python基础知识(一)变量与简单数据类型详解
2019/04/17 Python
python实践项目之监控当前联网状态详情
2019/05/23 Python
法国珠宝店:CLEOR
2017/01/29 全球购物
学习十八大坚定理想信念心得体会
2014/03/11 职场文书
国际金融专业自荐信
2014/07/05 职场文书
学校施工安全责任书
2015/01/29 职场文书
开学第一周总结
2015/07/16 职场文书
HTML5+CSS+JavaScript实现捉虫小游戏设计和实现
2021/10/16 HTML / CSS
apache ftpserver搭建ftp服务器
2022/05/20 Servers