【vue回顾系列】13-状态管理vuex的简单理解与个人使用记录

简单介绍什么是vuex

先看看我们的单向数据流的过程(从vuex的官方文档嫖来的一个表示“单向数据流”理念的简单示意)

  • state:驱动应用的数据源
  • view:以声明方式将 state 映射到视图
  • actions:响应在 view 上的用户输入导致的状态变化

如果严格按照单向数据流的思想去做开发,当遇到同一个变量(状态)在多个组件中通过子父传递的方式去使用,并伴随增删改查时,就会显得很麻烦不易维护和开发。
并且该状态与组件间的粘性太强了,特别是组件树庞大的时候。

vuex就提供了一个系统的状态管理机制,将状态与组件之间独立开,且能够使用插件来追踪状态增删改查记录。

这里再白嫖官方文档的图片,vuex的精简机制图(个人加了点说明):

mutation的主要作用是执行同步操作,并且让插件捕捉到记录,方便开发者进行调试,组件视图层的修改可以直接执行mutation中的操作,不需要异步派发。

简单点说,vuex为状态管理,它集中存储管理应用的所有组件状态,可以理解为一个全局仓库。


使用

如何安装就不记录了,随便百度一下。

vuex内容怎么写

直接写在一个js文件中(不建议)

建议真正在写项目的时候不要这样简单粗暴的使用,不灵活也不易扩展。举例在main.js中使用:

import Vue from "vue";
import App from "./App.vue";
import Vuex from "vuex";

Vue.use(Vuex)
Vue.config.productionTip = false;

const store = new Vuex.Store({
  state: {},
  mutations: {},
  actions: {},
  getters: {}
})

new Vue({
  store,
  render: h => h(App)
}).$mount("#app");

或者自行建立一个index.js文件

import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex)

const store = new Vuex.Store({
  state: {},
  mutations: {},
  actions: {},
  getters: {}
})

export default store

关于 Vue.use(Vuex) 这一步的意义,有精力的建议去看一下源码,像我这样的懒人选择直接白嫖别人的结论哈哈。

  • 判断vue是否已经注册过vuex插件;
  • vuexInit函数混入到vue的beforeCreate生命周期中;
  • 实例化vue时,会在vue每个实例上添加 $store属性,并将vuex的实例绑定到 $store属性上。

写在一个文件里

安装vuex后会默认有个src/store的文件夹

单个小业务

单个小业务,如果逻辑不复杂,就只需在文件夹里把成员分开写。

多个类型的业务逻辑

需要分模块写,在index.js主文件有两种写法

第一种:模块都写在index.js中

// 先自定义模块对象
const A = {
	state: {},
    mutations: {},
    actions: {},
    getters: {}
}
// 然后挂在主模块里
const store = new Vuex.Store({
   state: {},
   ...
   modules: {
     a: A
   }
})

第二种(推荐):模块也分文件写

文件格式和index.js代码部分如下:

每个成员的js文件这样写:

export default {
	state: {},
	getters: {},
	mutations: {},
	actions: {}
}
//不同模块的数据和方法可以相互使用,具体查看百度

引入main.js:

当文件都写好了,就需要将文件全局引入

import store from '@/store/index'

new Vue({
  el: '#app',
  router,
  store,
  render: h=> h(App)
})

每个成员的写法

state

state 存放公共数据,或者说是提供响应式数据;

state: {
  xxx: [],
  bbb: {},
  ...
}
getters

getters 由state数据得来的公共计算属性,借助的是vue的计算属性computed来实现的缓存;

getters: {
  xxx(state) {
     return state.a * 2
  }
}

还可以通过返回一个函数来实现传参的效果:

getters: {
  xxx(state) => (id) => {
  	return state.obj[id]
  }
}
mutations

mutations 同步的方式处理state数据的方法,并存在state中(唯一的修改state的方式)

mutations: {
  fn (state, n) { // 第一个为state数据,第二个为形参
      
      this.commit('function') // 在mutations中还可以使用其他mutations方法
  } 
}

为什么mutations不能执行异步操作,因为当mutations执行异步操作后,开发者工具并不知道异步的回调什么时候会执行,导致不可追溯。

actions

action 主要写一些异步操作,异步的回调一般为mutations方法。比如定时器,axios异步请求后异步执行一个或者多个mutations方法(多个原子操作);

而且经过actions提交mutations方法处理的state的数据在视图和devtool(F12)中会同步更新,而直接在mutations中使用异步去更新state,devtool是跟踪不到的,就没有办法知道状态是何时更新的,无法很好的进行状态的追踪,给调试带来困难。

actions: {
  fn (context, xxx) {
    // context是一个内置对象,里面有很多方法,可以用context.commit提交一个mutation,或者通过context.state和context.getters 来获取state和getters
    context.commit('fn', xxx)
    // 再次调用一个action方法
    context.dispatch(fn2)
  },
  fn2 (context, xxx) {}
}

// 也可以采用解构赋值的方式快速拿到里面的方法
actions: {
  fn ({state, commit, dispatch}, n) { 
      commit("fn", state.xxx)
      // 还能再调用actions里的其他方法
      dispatch("Fn", xxx)
      dispatch("Fn", xxx).then(()=>{}) // 还能做执行成功的回调哦
  } 
}

至于为啥mutations和actions里的方法入参不一样,是因为底层调用是这样的:

class Store {
	constructor(options) {
		this.state = reactive(options. state)
		this.options = options
	}
	commit(type, payload) {
		// 传入上下文和参数1都是state对象
		this.options .mutations[type].call(this.state, this state, payload)
	}
	dispatch(type, payload) {
		// 传入上下文和参数1都是store本身
		this.options.actions[type].call(this, this, payload)
	}
}

在组件中如何使用

当store的内容写好后,在组件中有两种使用的方式

不使用辅助函数

使用state数据 $store.state

// 例子
computed: {
    aaa () {
      return this.$store.state.xxx
    }
}
// 在模板中
{{this.$store.state.xxx}}

使用的时候是可以直接修改变量内容的this.$store.state.info = 直接修改 ,但不建议这样修改,原因:

  1. 当添加了严格模式 strict: ture时,就会报错;
  2. 没有修改记录;

所以还是推荐使用mutation里的方法进行修改。

使用mutation里的方法 $store.commit( )

// 例子
methods: {
   xxx() {
   	this.$store.commit('fn', 形参)
   	this.$store.commit('文件名/fn', 形参) // namespaced开启写法
   	// 如果需要传入多个变量,可以以字面量的形式传入
   	this.$store.commit('fn', {
      n: 1,
      m: 2
    })
   }
}

使用getters里的计算属性 $store.getters

使用方法和state一样

向actions里添加异步操作 ¥store.dispatch

// 例子
methods: {
   xxx() {
    // 第一种方式:
   	this.$store.dispatch('fn', 形参)
   	this.$store.dispatch('文件名/fn', 形参) // namespaced开启写法
   	this.$store.dispatch('fn', {
      n: 1,
      m: 2
    })
    // 第二种方式:
    this.$store.dispatch({
      type: 'fn',
      n: 1
   })
   }
}

使用辅助函数

mapState

// 在组件文件中:
<script>
import {mapStata} from 'vuex' 

computed: {
  ...mapState(['num1','num2','num3'])
  // 也可以传对象
  ...mapState({ 
    //这里用了this就不能用箭头函数
    num: state => state.文件.num1, // 映射,相当于别名
    ...
  })
}

// 需要与state中的数据一致

</script>

mapGetters

// 在组件文件中:
<script>
import {mapGetters} from 'vuex' 

computed: {
  ...mapGetters(['info'])
  // 也可以传对象
  ...mapGetters('文件名',{  // 开启namespaced
    info: 'info',
    ...
  })
</script>

mapMutations

// 在组件文件中:
<script>
import {mapMutations} from 'vuex' 

methods: {
  ...mapMutations(['fn'])
  // 或者 改名
  ...mapMutations({
	add: 'Fn'
  })
</script>

mapActions

// 在组件文件中:
<script>
import {mapActions} from 'vuex' 

methods: {
  ...mapActions(['fn'])
  // 或者 改名
  ...mapActions({
	add: 'Fn'
  })
</script>

刷新数据丢失的解决方法

因为vuex的数据是存储在运行内存中的,当刷新页面后,整个实例都会初始化。

方案一:第三方库vuex-along,简单易用,使用自行百度。

方案二:自己手写本地缓存

created() {
    //在页面加载时读取sessionStorage里的状态信息
    if (sessionStorage.getItem("store")) {
      this.$store.replaceState(
        Object.assign(
          {},
          this.$store.state,
          JSON.parse(sessionStorage.getItem("store"))
        )
      );
    }
    //在页面刷新时将vuex里的信息保存到sessionStorage里
    window.addEventListener("beforeunload", () => {
      sessionStorage.setItem("store", JSON.stringify(this.$store.state));
    });
},

补充技巧

使用常量代替mutation事件类型

在大型多人开发的工程中,使用常量能避免一定的冲突,所以mutation可写为常量函数的形式

// 搞个mutation-type.js的文件
export const SOME_MUTATION = 'SOME_MUTATION'

// 然后在store.js文件中
import Vuex from 'vuex'
import { SOME_MUTATION } from './mutation-types'

const store = new Vuex.Store({
  state: { ... },
  mutation: {
     [SOME_MUTATION] (state) {}
  }
})

这个写法形式挺灵活的,建议多看别人怎么写的

在使用modules分割的技巧

$store打印

如果不知道怎么在组件使用分割好的模块内容,可以在组件中把$store打印出来看看;

根状态

可以在index.js导出的地方写根状态

...

export default new Vuex.Store({
  state: {
     xxx: {}
  },
  modules: {
    cart,
    products
  }
})

namespaced

在用modules分割模式写vuex内容的时候,可能会出现命名相同的问题,可以在导出成员的时候开启namespaced参数


export default {
  namespaced: true,
  state,
  getters,
  actions,
  mutations
}

在组件中使用的时候,就需要加上文件名称来区分成员,例如this.$store.dispatch('文件名/fn', 形参)

不同模块之间的访问

比如a模块获取b模块的state成员

xxx: (state, getters, rootState) => {
      这样获取rootState.b.变量
    })
},

用辅助函数

mapXXXs('命名空间名称',['属性名1','属性名2'])

mapXXXs('命名空间名称',{
  '组件中的新名称1':'Vuex中的原名称1',
  '组件中的新名称2':'Vuex中的原名称2',
})

原理

这里就简单的意会一下vuex的state和mutation实现原理,直接嫖别人写的代码哈哈

import Vue from 'vue'

const Store = function Store(options = {}){
	const {state = {}, mutations = {}} = options;
	this._vm = new Vue({
		data:{
		$$state:state
		}
	});
	this._mutations=mutations;
}

Store.prototype.commit=function(type,payload){
// 如果mutations[type]这个方法存在;则执行这个方法;		
if(this._mutations[type]){
	this._mutations[type](this.state,payload);
	}
}

// 向原型上定义属性;
Object.defineProperties(Store.prototype,{
state:{
	get:function(){
		return this._vm._data.?state;
	}
}
});	

export default {Store}

未来继续补充…

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值