文章目录
简单介绍什么是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 = 直接修改
,但不建议这样修改,原因:
- 当添加了严格模式 strict: ture时,就会报错;
- 没有修改记录;
所以还是推荐使用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}
未来继续补充…