编写时间:2019-07-31
更新时间:2019-09-21作者:鬼小妞
目的:
vuex流程图
; (点击直达);state、getters、actions、mutations对比
; (点击直达);dispatch和commit
; (点击直达);简单实例
; (点击直达);vuex在electron项目里无效的解决方案
; (点击直达);
备注: 本文
编写
了与vuex状态管理相关的一些知识,仅供参考,描述不当的地方,请评论指正,在这篇文章中,我将先总结vuex常用属性,然后以简单的demo来一步步带你熟悉使用vuex流程,熟悉操作流程后再一步步详解个属性及属性的相关内容,特别介绍electron项目中遇到的vuex的问题状态:
待整理、待编写、待更新
2019-08-07
[TOC]
状态管理示意图
-
约定所有state都必续通过mutations来改变,目的:开发工具可以追踪、调试修改状态
-
electron项目中,请在render进程中调用dispatch或mapActions。不要使用commit,因为通过commit触发的操作不会在进程之间共享,所以electron-vue项目中,尽量不要在组件里直接commit提交修改请求到mutations。
-
state可以不通过mutations来改变,但开发工具无法追踪
-
组件中可以越过actions,直接提交修改请求到mutations
-
组件中使用commit提交请求到mutation时,vue可能会报错,这是vue希望你遵守约定,先把请求dispatch到actions,再由actions提交修改动作到mutations
-
actions存在的意义是:mutations中只能处理同步操作,actions可以处理同步和异步,actions异步操作完成后,异步状态变为同步,再把同步修改state的修改动作提交的mutations处理
-
使用commit和dispatch时,传递参数是通过该commit、dispatch函数传参数,例如:
- this.$store.commit("addIncrement",{age:18,n:5},{ root: true })
- this.$store.dispatch("addIncrement",{age:18,n:5},{ root: true })
-
使用辅助函数mapState()、mapGetters()等传递参数是通过触发方法传递的
例1 mapMutations
...mapMutations([
'increment' // 将this.increment() 映射为 this.$store.commit('increment', payload)
// 在事件或方法中直接调用 this.increment({amount: 10 })即可
]),
(1)在调用的标签方法里直接传递参数
<div @click="increment({amount: 10 })">Click me</div>
(2)在方法中直接传参this.increment({amount: 10 })
someFn(){
this.increment({amount: 10 })
}
state、getters、actions、mutations对比
属性对比
其中,在modules模块中,state可能是函数state (){},请看api文档查阅模块重用详情
属性 | 属性描述 | 辅助函数 | 实例对象 |
---|---|---|---|
state | 状态对象 | mapState() | state = {...} 或 state (){return{}} |
getters | 包含多个getter计算属性函数的对象 | mapGetters() | getters = {...} |
actions | 包含多个对应事件回调函数的对象 | mapActions() | actions = {...} |
mutations | 包含多个更新state函数的对象 | mapMutations() | mutations = {...} |
实例对象类型对比
待更新
- state={存放对象或函数}、 state(){return{存放对象或函数}}
对于官方API文档上列举出来的例如:
初学者不知道这个是什么意思,简单直接就是这些vuex属性{}
里存放的类型
- 特别要注意,state里其实是可以写函数的,这个我在后面的章节中会说,这里暂时不介绍
实例参数对比
此表格待更新!!!
- actions里的方法对象的参数只有2个
dispatch和commit
vue实例与vuex实例主要属性类比
vuex 实例 |
vue 实例 |
---|---|
state | data |
getters | computed |
actions | method |
mutations | method |
简单实例
为了清楚演示vuex流程,我将以一个demo项目来解释vuex的工作流程, 该demo项目是涉及到webpack的一些东西,如果你已经具备webpack知识,然后跳转直接看vuex流程
以下步骤均针对没有webpack基础的读者而进行的详细操作
demo项目准备工作
1. 新建空文件夹,用来存放工程文件
在任一磁盘建立空文件夹,命名为
demoproject
, 我的项目文件夹路径为E:\aproject\demoproject
2.根据模板创建vue webpack项目
(1)、请先全局安装vue-cli脚手架
npm install -g vue-cli
(2)、右键git base here调出命令窗口,输入以下命令并回车:
vue init webpack testproject
(3)、根据提示完成配置
以下是我的配置,标红的是手动输入n
回车的,其他项默认回车
3.安装依赖并安装vuex
(1)、cd到testproject
cd testproject
(2)、安装依赖
npm install
(3)、安装vuex
npm install --save vuex
4.启动项目
(1)、命令窗口输入命令
npm run dev
(2)、打开浏览器,新建标签,输入命令行窗口提示的链接,一般是
http://localhost:8080
在浏览器中我们将会看到这样子的页面:
5.引入vuex并配置
以下使用模块化方式 首先在VScode编辑器打开项目
(1)、在
src
文件夹下新建文件夹store
(2)、在
store
文件夹下新建modules
文件夹和index.js
- 在这个
index.js
里写如下代码:
import Vue from 'vue'
import Vuex from 'vuex'
import modules from './modules'
Vue.use(Vuex)
export default new Vuex.Store({
modules
})
(3)、在
modules
文件夹下新建文件index.js
、car.js
car.js:购物车模块store;car.js就是我们最终需要写的vuex流程
- 在
modules
文件夹下index.js
里写如下代码,目的为了不需要一个个引入模块里的store:
/**
* The file enables `@/store/index.js` to import all vuex modules
* in a one-shot manner. There should not be any reason to edit this file.
*/
const files = require.context('.', false, /\.js$/)
const modules = {}
files.keys().forEach(key => {
if (key === './index.js') return
modules[key.replace(/(\.\/|\.js)/g, '')] = files(key).default
})
export default modules
- 在
modules
文件夹下car.js
里写如下代码
const state = {}
const getters = {}
const mutations = {}
const actions = {}
export default {
state,
getters,
mutations,
actions
}
(4)、在入口文件引入store并挂载
在src目录下的main.js里,引入store,挂载到vue实例里,如下图
main.js的代码如下:
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
components: { App },
template: '<App/>'
})
6.简洁页面并添加按钮绑定点击事件
首先看一下src下新增的store文件夹的目录结构
(1)、简洁项目结构
为了直观看到store的state数据状态对象的变化,我们需要简洁结构,删除模板引导页路由和logo图标
- 进入在src目录下的
App.vue
- 在
App.vue
下删除如下代码
(2)、添加按钮,并给按钮添加点击事件
- 删除后,在符合语法情况下添加一些字段或标签
我是添加button:广西科技大学
- 在8080端口下看到的是这样子的显示页面
- 给button绑定点击事件show,并添加相应的method方法对象
我在show方法里打印vuex的store
- 查看控制台打印的store值
在浏览器8080端口,点击按钮,查看控制台打印的store的state对象
我们发现,现在可以查看得到store值了,而且state下有我们的car模块, 证明我们对store的引入和store模块modules操作,完全正确
7.开始编写vuex流程
-
假设: 购物车car里有一个共享的数据状态对象为
ProID商品id
-
现实现一个功能: 每点击一次按钮时,商品id增加1 , 根据流程,我们写一下实现逻辑:
- 1:点击按钮,触发按钮绑定的方法
- 2:在按钮绑定的方法里dispatch发送修改请求给actions中的相应方法
- 3:在actions的相应方法中触发修改动作给mutations中相应修改方法
- 4:在mutations中相应修改方法直接修改state里的商品id,实现商品id增加
根据实现逻辑,我们执行以下步骤:
(1)、打开store下的modules文件夹下的car.js,
如果不知道actions、dispatch等的参数是什么,建议你先看看上文我总结出来的参数表格
- 在state里,添加共享商品状态,商品id
proID:1, //商品id
- 在actions里,添加触发动作方法
touchchangeID({commit}){
commit("changeID"); //提交动作
}
- 在mutations里,添加直接修改状态方法
changeID(state){
state.proID ++; //直接修改state状态对象
}
- 看看最终的car.js文件是什么样的
const state = {
proID:1, //商品id
}
const getters = {}
const mutations = {
changeID(state){
state.proID ++; //直接修改state状态对象
}
}
const actions = {
touchchangeID({commit}){
commit("changeID"); //提交动作
}
}
export default {
state,
getters,
mutations,
actions
}
(2)、进入App.vue模板文件,修改按钮绑定方法,执行以下步骤
- 把按钮显示文字改为从store获取到的共享状态对象商品id
{{this.$store.state.car.proID}}
- 在按钮绑定方法里修改,并添加触发修改请求代码
this.$store.dispatch("touchchangeID");
console.log(this.$store.state.car.proID)
- 我们来看看App.vue修改后是怎么样的
<template>
<div id="app" >
<button @click="show">
{{ this.$store.state.car.proID }}
</button>
</div>
</template>
<script>
import { mapState,mapGetters,mapMutations,mapActions } from "vuex";
export default {
name: 'App',
methods:{
show(){
this.$store.dispatch("touchchangeID");
console.log(this.$store.state.car.proID)
}
}
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
(3)、在浏览器8080端口,测试结果
- 在项目运行的端口里,我们可以看到,页面是这样的:
- 点击按钮
state改变了,实现了我们想要的效果
如果你想确认store是否修改了,可以修改一下APP.vue里的修改一下show方法直接在控制台调试出来
回到控制台再点击按钮看输出信息并展开,查看proID值
恭喜你!!,完成了一个vuex流程的小实例
state
state 状态对象、单一状态树
const state = {...} 或 const state (){}
如何取值: 由于我们已经在入口文件Vue实例上挂载了store了
所以页面中,我们可以直接在vue模板中使用$store
这个全局对象
state 显示数据
在组件标签中显示state(无命名空间的state 获取):
(1)直接this.$store.state
(2)使用mapState扩展参数到computed计算属性中,再在页面使用该参数
在组件script标签中获取state:
和在组件标签中显示state是一样的操作,我们可以直接使用this.$store.state
来获取,或者使用mapState扩展参数到computed计算属性中,再在引用该参数
在外部js中获取state:
首先我们在项目src目录下新建一个js文件夹,在js文件夹下新建一个js叫settleCar.js
,写下如下代码
import store from '../store' //引入store
export default{
countCar : ()=>{
console.log("外部js的store : " + store.state.car.proID)
}
}
这样就可以了,如果想测试是否真的获取到了,我们可以再APP.vue组件中引入一下settleCar.js
,调用countCar看看控制台输出的值
当我们点击按钮,控制台就会输出如下:
证明我们在settleCar.js
中可以正常调用我们的store
在各命名空间里相互调用store
比如命名空间下的A模块想要调用B模块的state
1. 可以通过actions里的各个方法的参数context
B模块state = context.rootState + B模块.state名
context上下文里的参数:
const actions = {
touchUser(context,payload){
let Bstate = context.rootState.Bstate
console.log(Bstate)
}
}
2. 可以通过getters里的各个方法的参数
B模块state = rootState + B模块.state名
const getters = {
getUser(rootState){
let Bstate = rootState.Bstate
console.log(Bstate)
}
}
state扩展函数 mapState
在绑定了命名空间后,在组件中,引入import { mapState } from "vuex"
后,就可以在计算属性中,使用开展函数都可以这样子写:
computed: {
...mapState("car", {
proID: state => state.proID,
})
}
当计算属性名与state中的数据名一样(相同)时
可以简写成: computed: {
...mapState("car",["proID"])
}
特别要注意,state里其实是可以写函数的
例如:我在modules里有一个car对象(声明了命名空间),我们打印store来看一下:
而且我们可以用,getters
getters 包含多个getter计算属性函数的对象
const getters = {...}
getters 是对state的进一步封装,比如,当在很多地方
获取商品ID时,我希望每次都是能获取到的是,该商品的名称+id,以明白是哪个产品的id.这个时候,我们就需要getters来对我们的id和商品名称进行组合:
我们修改一下car.js
添加getters
const state = {
proID:1, //商品id
proName:"数媒",//商品名
}
const getters = {
proNameID(state){
let proNameID = state.proName + state.proID;
return proNameID
}
}
const mutations = {
changeID(state){
state.proID ++; //直接修改state状态对象
}
}
const actions = {
touchchangeID({commit}){
commit("changeID"); //提交动作
}
}
export default {
state,
getters,
mutations,
actions
}
然后在App.vue
中应用
然后我们可以点击按钮后看控制台输出
证明我们getters使用正确
这里值得注意的是:即使getters是定义在car模块里的,但是当这个模块没有声明命名空间时,Vuex会把它作为vuex的store里外部的
getters,actions和mutations也一样,都是在外部的,看官方的描述:
模块内部的 action、mutation 和 getter是注册在全局命名空间的——这样使得多个模块能够对同一 mutation 或 action 作出响应。
如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名
getters 获取
1. 无命名空间的getters 获取
1.1 组件标签中(无命名空间的getters 获取)
无命名空间的getters在组件标签中获取:
- (1) 直接
this.$store.getters + getters方法名
- (2)或者我们使用mapGetters映射到computed后再使用computed映射好的属性
{{this.proNameID}}也可以这样子写{{proNameID}}
1.2 组件script中(无命名空间的getters 获取)
以下两种方式都可以,推荐使用第二种
...mapGetters(['proNameID']) ,//1
...mapGetters({ //2
proNameID:'proNameID'
})
1.3 外部js中(无命名空间的getters 获取)
(1) 首先在外部组件中先引入store,
import store from '../store' //引入store
(2)然后在方法里直接通过
store.getters.getters方法
首先我们修改一下settleCar.js
import store from '../store' //引入store
export default{
countCar : ()=>{
console.log("外部js的getters : " + store.getters.proNameID)
}
}
然后在App.vue组件中,引入外部js后,在按钮绑定方法里调用外部js的方法
App.vue组件完整代码在图下方
App.vue组件完整代码
<template>
<div id="app" >
<button @click="show">
<!-- {{ proNameID }} --> <!-- 也可以这样写 -->
{{ this.proNameID }}
</button>
</div>
</template>
<script>
import { mapState,mapGetters,mapMutations,mapActions } from "vuex";
import settleCar from './js/settleCar'
export default {
name: 'App',
computed:{
// ...mapGetters(['proNameID']),//组件script中(无命名空间的getters 获取)
...mapGetters({ //组件script中(无命名空间的getters 获取)
proNameID:'proNameID'
})
},
methods:{
show(){
settleCar.countCar()
}
}
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
在浏览器项目运行窗口,当我们点击按钮后,观察控制台输出
证明我们在外部引用的js可以正确获取到getters2. 命名空间下的getters 获取
那么当我们使用了命名空间之后,如何取值呢
首先我们在模块car.js
里设置命名空间namespaced: true
,
2.1 组件标签中(命名空间下的getters 获取)
请确保模块car.js
里设置命名空间namespaced: true
,查看上文的设置
命名空间下的getters在组件标签中获取:
- (1)直接
this.$store.getters ['命名空间/getters方法名']
- (2)或者我们使用mapGetters映射到computed后再使用computed映射好的属性
以下两种扩展都可以:
2.2 组件script中(命名空间下的getters 获取)
请确保模块car.js
里设置命名空间namespaced: true
,查看上文的设置
以下两种方式都可以,推荐使用第二种
...mapGetters('car',['proNameID']) ,//1
...mapGetters({ //2
proNameID:'car/proNameID'
})
2.3 外部js中(命名空间下的getters 获取)
请确保模块car.js
里设置命名空间namespaced: true
,查看上文的设置
(1) 首先在外部组件中先引入store ,
import store from '../store' //引入store
(2)然后在方法里直接通过
store.getters['命名空间/该命名空间下的getters方法']
首先修改一下settleCar.js
import store from '../store' //引入store
export default{
countCar : ()=>{
console.log("外部js的getters : " + store.getters['car/proNameID'])
}
}
在App.vue组件中,引入外部js后,在按钮绑定方法里调用外部js的方法
App.vue组件完整代码在图下方
App.vue组件完整代码
<template>
<div id="app" >
<button @click="show">
<!-- {{ proNameID }} --> <!-- 也可以这样写 -->
{{ this.proNameID }}
</button>
</div>
</template>
<script>
import { mapState,mapGetters,mapMutations,mapActions } from "vuex";
import settleCar from './js/settleCar'
export default {
name: 'App',
computed:{
// ...mapGetters('car',['proNameID']),//组件script中(命名空间下的getters 获取)
...mapGetters({ //组件script中(命名空间下的getters 获取)
proNameID:'car/proNameID'
})
},
methods:{
show(){
settleCar.countCar()
}
}
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
在浏览器项目运行端口,点击按钮后查看控制台输出信息
证明我们正在外部js里正确地获取到getters了
getters传递参数
getters默认是不能传递参数的,如果希望传递参数,那么只能让getters本身返回另一个函数,比如我们希望根据id来对数据进一步操作,
const getters = {
nameID(state, getters){
// return function(id){
// return state.proName + id
// }
return id => state.proName + id
}
}
然后使用的时候就这样的,
例如标签中 <div> {{ $store.getters.car.nameID(8) }} </div>
而这个 参数 8 就是 传递过去的id
---------以下待编写-----------
actions
actions 包含多个对应事件回调函数的对象
const actions = {...}
mutations
mutations 包含多个更新state函数的对象
const mutations = {...}
vuex 在electron项目里的注意事项
vuex-electron官网
查看vuex-electron官网
在electron项目中,使用vuex,你可能会发现明明书写正确,但是却无法修改state或调用actions、getters、mutations无效。
影响因素是: 首先最主要影响vuex的store的是,在实例化store时,引用插件不正确。 比如vuex-electron里的插件
vuex-electron介绍
跳过介绍,直接查看vuex在electron项目里无效的解决方案
你可以先去官网 查看vuex-electron官网 介绍
- vuex-electron:在所有进程(包括主进程)之间共享Vuex存储的最简单方法
- 特性:
- 数据持久化 Persisted state
- 共享数据修改 Shared mutations
- 版本支持
vuex-electron使用
1、 安装vuex-electron
- ```yarn install vuex-electron 或 npm install vuex-electron```
2、 引入到vuex的store里
src\renderer\store\index.js
3、 主进程引入store
如果启用createsharedvariables()插件
,则需要在主进程中创建store实例。只需将这一行添加到主进程中
(如果不在主进程中引入store,会导致vuex的store修改无效)
import '../renderer/store'
4、注意
electron项目中如果你使用了vuex-electron的插件,请在
render渲染进程中调用dispatch或mapActions。
不要使用commit,因为通过commit触发的操作不会在进程之间共享
,
所以electron-vue项目中,尽量不要在组件里或其他渲染进程文件里直接commit提交修改请求到mutations。
5、 createPersistedState()的可用选项
createPersistedState({
whitelist: ["whitelistedMutation", "anotherWhitelistedMutation"],
// 或者
whitelist: (mutation) => {
return true
},
// 或者
blacklist: ["ignoredMutation", "anotherIgnoredMutation"],
// 或者
blacklist: (mutation) => {
return true
}
})
vuex在electron项目里无效的解决方案
在这之前,你需要了解这个vuex-electron
插件是做什么用的,然后根据你的项目需求来选择对应的方案 或者看上文的vuex-electron介绍
1、vuex在electron项目里无效的解决方案1(不推荐)
- 方案:注释createSharedMutations()插件
- 前提:不需要进程间(多窗口)共享VUEX的Mutations()
- 影响:去掉createSharedMutations()插件后应该会影响多窗口的传值
进入store文件夹目录下的index.js,注释createSharedMutations()
,
路径:src\renderer\store\index.js
这样就可以正常了
2、vuex在electron项目里无效的解决方案2(推荐)
- 方案:在主进程中引入store
- 前提:需要进程间(多窗口)共享VUEX的数据和数据修改,即:
- 需要数据持久化 Persisted state
- 需要共享数据修改 Shared mutations
- 影响:不能直接在渲染进程中里commit提交修改请求到mutations,只能先调用dispatch或mapActions后再由action里commit提交修改动作给mutations。
进入主进程src\main\index.js,添加store(根据你的store存放路径来引入)
一般的路径是../renderer/store
import '../renderer/store'
electron项目里尽量不要在渲染进程中直接commit提交
electron项目中如果你使用了vuex-electron的插件,请在
render渲染进程中调用dispatch或mapActions。
不要使用commit,因为通过commit触发的操作不会在进程之间共享
,
所以electron-vue项目中,尽量不要在组件里或其他渲染进程文件里直接commit提交修改请求到mutations。
Vue项目中5个经典Vuex插件
这一部分内容转载自:Vue.JS项目中5个经典Vuex插件
1. 状态持久化___vuex-persistedstate
vuex-persistedstate
使用浏览器的本地存储( local storage )对状态( state )进行持久化。这意味着刷新页面或关闭标签页都不会删除你的数据。
一个很好的例子就是购物车:如果用户不小心关闭了一个标签,他们可以重新打开并回到之前页面的状态。
2. 同步标签页、窗口___vuex-shared-mutations
vuex-shared-mutations
可在不同的标签页之间同步状态。它通过 mutation 将状态储存到本地存储(local storage)来实现。选项卡、窗口中的内容更新时触发储存事件,重新调用 mutation ,从而保持状态同步。
3. 语言本地化___vuex-i18n
vuex-i18n
允许你轻松地用多种语言存储内容。让你的应用切换语言时更容易。
一个很酷的功能是你可以存储带有标记的字符串,比如"Hello {name}, this is your Vue.js app."。所有的翻译版本都会在标记的地方使用相同的字符串。
4. 管理多个加载状态__vuex-loading
vuex-loading
有助于你管理应用中的多个加载状态。这个插件适用于状态变化频繁且复杂的实时应用程序。
5. 缓存操作__vuex-cache
vuex-cache
可以缓存 Vuex 的 action。例如,如果你从服务器检索数据,这个插件将在第一次调用该 action 时缓存结果,然后在之后的dispatch中,直接返回缓存的值。必要时清除缓存也很简单。