应用场景
在一些大型应用中,有时我们会遇到单页面中包含着大量的组件及复杂的数据结构,而且可能各组件还会互相影响各自的状态,在这种情况下组件树中的事件流会很快变得非常复杂,也使调试变得异常困难。
为了解决这种情况,我们往往会引入状态管理这种设计模式,来降低这种情况下事件的复杂程度并且使调试变得可以追踪。而Vuex就是一个专门给为Vue.js设计的状态管理架构。
概述
Vuex是状态管理模式的一种实现库,主要以插件的形式和Vue.js进行配合使用,能够使我们在Vue.js中管理复杂的组件事件流。
由于Vue.js本身的事件流是依赖于DOM结构的,组件修改状态后需要经过一系列冒泡才能达到顶部的组件,而且如果需要修改兄弟组件的状态还需要共同的父组件再进行一次广播。这种方式无疑是低效而且不易维护的,我们也很难去追踪事件流的走向。
Vuex中的数据是响应式的,也就是说Vuex中的数据只要一变化,引用了Vuex中数据的组件都会自动更新。
Vuex是为了保存组件之间共享数据而诞生的。如果组件之间有要共享的数据,可以直接挂载到Vuex中,而不必通过父子组件之间传值了;如果组件的数据不需要共享,此时,这些不需要共享的私有数据,没有必要放到Vuex中,只要放到组件的data中即可。放到Vuex中的数据所有组件可以共享,这也是会降低性能的,而且操作起来烦琐。
核心概念
Store(仓库)、State(状态)、Mutations(变更)、Actions(动作)
安装
npm install --save vuex
目录结构,以实际项目为例
modules文件夹下的settings.js文件 可以根据业务需求,新建对应的js文件,配置如下。
const state = {
themeImg: 'blue',
botColor: 'white',
websiteName: "综合档案管理系统",//系统名称
copyright: "",
companyLogo: "",//管理平台logo
}
const mutations = {
CHANGE_SETTING: (state, { key, value }) => {
if (state.hasOwnProperty(key)) {
state[key] = value
}
}
}
const actions = {
changeSetting({ commit }, data) {
commit('CHANGE_SETTING', data)
}
}
export default {
// 为了解决不同模块命名冲突的问题,将不同模块的namespaced:true
namespaced: true,
state,
mutations,
actions
}
在vue组件中通过dispatch改变state的状态
async websiteSetting() {
try {
let params = {};
let { data } = await this.$api.getAction(
this.url.websiteSetting,
params
);
let array = ["copyright", "websiteName", "companyLogo", "indexTitle"];
array.forEach((element) => {
this.$store.dispatch("settings/changeSetting", {
key: element,
value: data[element],
});
});
} catch (error) {}
},
getters.js文件
import Vue from 'vue'
const getters = {
themeImg: state => state.setttings.themeImg,
}
export default getters
组件中调用state的数据
computed: {
themeImg() {
return this.$store.state.settings.themeImg
},
websiteName: {
get() {
return this.$store.state.settings.websiteName
},
set(val) {
this.$store.dispatch('settings/changeSetting', {
key: 'websiteName',
value: val
})
}
}
}
通过辅助函数mapGetters 调用
import { mapGetters } from "vuex";
computed: {
...mapGetters(["themeImg"]),
}
index.js文件
import Vue from 'vue'
import Vuex from 'vuex'
import createPersistedState from "vuex-persistedstate"
import getters from './getters'
Vue.use(Vuex)
const modulesFiles = require.context('./modules', true, /\.js$/)
const modules = modulesFiles.keys().reduce((modules, modulePath) => {
const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
const value = modulesFiles(modulePath)
modules[moduleName] = value.default
return modules
}, {})
const store = new Vuex.Store({
modules,
getters,
plugins: [createPersistedState({
// storage: window.sessionStorage,
storage: window.localStorage,
reducer(val) {
return {
// 只储存state中的user
settings: val.settings,
user: val.user,
}
}
})]
})
export default store
vuex刷新会重新更新状态,可以通过vuex-persistedstate实现vuex持久化。
可以设置存储方式,还可以只储存state中的指定数据。
//安装
npm i -S vuex-persistedstate
//配置
import persistedState from 'vuex-persistedstate'
export default new Vuex.Store({
// ...
plugins: [persistedState()]
})
最后,在main.js中引入,挂载到Vue实例下 main.js文件
import store from './store'
new Vue({
el: '#app',
router,
store,
render: h => h(App)
})
vuex原理
每个组件(也就是Vue实例)在beforeCreate的生命周期中都混入(Vue.mixin)同一个Store实例作为属性store,也就是为什么可以通过this.$store.dispatch等调用方法的原因。
vuex通过使用Vue的响应式系统,实例化一个vue对象,把state装载到data属性上面,并且把getters装载到computed属性上面,来实现数据的响应式化。
vuex中为什么把异步操作封装在actions,把同步操作放着mutations
vue.js作者尤雨溪在知乎上是这样回答的:
区分actions和mutations并不是为了解决竞态问题,而是为了能用devtools追踪状态变化。
事实上在vuex里面actions只是一个架构性的概念,并不是必须的,说到底只是一个函数,你在里面想干嘛都可以,只要最后触发mutation就行。异步竞态怎么处理那是用户自己的事情。vuex真正限制你的只有mutation必须是同步的这一点(在redux里面就好像reducer必须同步返回下一个状态一样)。
同步的意义在于这样每一个mutation执行完成后都可以对应到一个新的状态(和reducer一样),这样devtools就可以打个snapshot存下来,然后就可以随便time-travel了。
大感谢
这是一篇谈不上原创的文章,参考了大量的帖子,书籍和文档。凿壁取光,整理资料。但是,对于我自己来说,写完这篇文章,如同重新认识了老朋友。
正如同卓克在得到课程《科学思维课》开头所说到的,“知识这东西就得经常地核实和订正,尤其是那些从别人那里听来的知识。”
在这条寻访故友的道路上,我看到了很多同道中人留下的足迹,感谢各大社区的网友的分享。
“对现状不满足,对未来有期许,无论任何时代都不会辜负这样的人。”
参考资料
网站
Vuex官网: vuex.vuejs.org/zh/guide/
电子书籍:
Vue.js 2.x实践指南 -- 邹琼俊
Vue.js前端开发 快速入门与专业应用 -- 陈陆扬