1.定义
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化,并提供devtools extenions 插件工具,支持时间漫游调试,状态快照,导入导出等。
- state,驱动应用的数据源;
- view,以声明方式将 state 映射到视图;
- actions,响应在 view 上的用户输入导致的状态变化。
2.安装
vue add vuex
3.使用
直接使用matution操作state
<script>
//1.1直接定义
//src/store/index.js
export default new Vuex.Store({
state: { isLogin: false },
mutations: {
login(state) {
state.isLogin = true;
},
logout(state) {
state.isLogin = false;
},
},
})
//1.2模块化定义
//src/store/user.js
export default {
state: { isLogin: false },
mutations: {
login(state) {
state.isLogin = true;
},
logout(state) {
state.isLogin = false;
},
},
}
//src/store/index.js
import user from './user'
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
user
},
})
//2.使用
//main.js
import store from './store'
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
//login.vue调用
<template>
<button @click="login" v-if="!$store.state.isLogin">登录</button>
<button @click="logout" v-else>登出</button>
</template>
this.$store.commit('login')
this.$store.commit('logout')
4.常用对象
4.1 State
const app = new Vue({
el: '#app',
// 把 store 对象提供给 “store” 选项,这可以把 store 的实例注入所有的子组件
store,
components: { Counter },
template: `
<div class="app">
<counter></counter>
</div>
`
})
//子组件可以直接this访问
this.$store.state.count
4.2 mapState
优点:可以简化定义了命名空间的长访问,如this.$store.user.xxx 定义:默认返回键值堆的方式
data () {
return {
username: '张三'
}
},
//方法1 使用mapState
//缺点占用了整个computed标签,不得不修改原来的computed 外层
computed: mapState({
count: 'count', // 第一种写法
sex: (state) => state.count, // 第二种写法
from: function (state) { // 用普通函数this指向vue实例,要注意
return this.username + ':' + state.count
},
}),
//方法2 使用...mapState //解构使得computed:保留原来外层解构
//使用之前
computed:{
fn1(){ return ...},
fn2(){ return ...},
fn3(){ return ...}
}
//使用之后,外层结构依然不变
computed:{
//原来的继续保留
fn1(){ return ...},
fn2(){ return ...},
fn3(){ return ...}
//再维护vuex
...mapState({ //通过扩展符 混入到当前computed的对象
count:'count'
})
}
4.3Mutation
修改state只能通过mutation, mutation必须是同步函数
//定义
mutations: {
login(state) {
state.isLogin = true;
},
logout(state) {
state.isLogin = false;
},
},
//调用
this.$store.commit('login')
this.$store.commit('logout')
4.4Getter
可以使用getters从store的state中派生出一些状态,如果state发生变化会同步更新派生的状态,效果与computed相同
export default {
state: {
isLogin: false,
username: '' // 用户名
},
getters: { //该属性的获取跟 state获取方式一样
welcome: state => { return state.username + ',欢迎回来'; }
},
4.5Action
Action 类似于 mutation,不同在于:
- 提交的是 mutation,而不是直接变更状态。
- 可以包含任意异步操作。
export default {
state: {
isLogin: false,
username: ''
},
actions: {
login({ commit }, username) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (username === "admin") {
commit("login"); //通过comimt提交mutation
resolve();
} else {
reject();
}
}, 1000);
});
},
}
//通过dispatch派发action
this.$store.dispatch("login", "admin")
.then(() => {
this.$router.push(this.$route.query.redirect);
})
.catch(() => {
alert("用户名或密码错误");
});
4.6mapMutation()/mapAction()
自动映射$store方法,避免对$store直接访问
//mapMutations 使用方法一
...mapMutations([
'login', // 将 `this.login()` 映射为 `this.$store.commit('login')`
]),
//mapMutations 使用方法二
...mapMutations({
login: 'login'// 将 `this.login()` 映射为 `this.$store.commit('login')`
})
//action相关修改
import { mapActions } from 'vuex'
//1.带有命名空间,缺点和computed重名就区分不了
methods: {
login() {
this['login']('admin').then(...)
},
...mapActions('user',['login']) //解构方式注入当前methods对象
},
//1.直接命名,使用方法要使用this['xxx/xx']的方式
methods: {
login() {
this['user/login']('admin').then(...)
},
...mapActions(['user/login'])
},
4.6严格模式
严格模式下,无论何时发生了状态变更且不是由 mutation 函数引起的,将会抛出错误。这能保证所有
的状态变更都能被调试工具跟踪到。开启严格模式 strict: true
export default new Vuex.Store({
modules: {
user
},
strict: true,
})
4.7 定义命名空间
//src/store/user.js
export default {
namespaced: true, // 设置独立命名空间,避免命名冲突 使用的时候要加上user/xxxx
...
//定义
mutations: {
login(state) {
state.isLogin = true;
},
logout(state) {
state.isLogin = false;
},
},
}
//引入
import user from './user'
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
user
},
})
//调用
this.$store.state.user.isLogin
this.$store.commit('user/login')
this.$store.commit('user/logout')
4.8插件
Vuex 的 store 接受 plugins 选项,这个选项暴露出每次 mutation 的钩子。Vuex 插件就是一个函 数,它接收 store 作为唯一参数: 需求:每次页面刷新,之前登陆的用户信息都能正常读取并设置到当前的$store信息里
const myPlugin = store => {
// 当 store 初始化后调用
}
注册插件:
const store = new Vuex.Store({
// ...
plugins: [myPlugin],
});
//实现登录状态持久化,store/plugins/persist.js
export default (store) => {
// 初始化时从localStorage获取数据
if (localStorage) {
const user = JSON.parse(localStorage.getItem("user"));
if (user) {
store.commit("user/login");
store.commit("user/setUsername", user.username);
}
}
// 用户状态发生变化时缓存之
store.subscribe((mutation, state) => {
if (mutation.type.startsWith("user/")) {
localStorage.setItem("user", JSON.stringify(state.user));
} else if (mutation.type === "user/logout") {
localStorage.removeItem("user");
}
});
};
}
总结
- vuex是一个vue专用的状态管理库。以全局的方式集中管理应用状态,并且可以保证数据变更的可预测性。
- vuex主要解决多个组件状态共享问题。虽然利用组件通讯方式如 emit chlid 等也能实现状态共享,但是容易出错,也使得程序变得复杂。vuex可以把所有组件的状态都抽离出来,统一在一个全局store里单例管理。在任何的组件都可以用一致的方法获取或修改内容。配合响应式数据,保证了单向数据流的流动。代码也便于维护。
- 当我们只是开发小应用可以不使用vuex,(使用根实例定义一个全局的变量管理都可以,或者使用eventbus也是可以),只有当项目大到一定规模,需要大量的全局状态管理才需要引入。
- vuex的使用过程:将全局状态放到state对象,使用stote访问他。使用mutation实现同步的修改,配合commit提交使用。使用action实现异步的修改,配合dispatch使用。action里面最终还是执行mutation方法。通过modules选择组织不同的子模块,访问的时候需要加上对应的namespace。
- vuex实现单向数据流需要做到数据响应式。在源码中利用的是vue的数据响应式特征实现。利用Vue将state作为data进行响应化处理,从而使得数据变化能够使组件重新渲染。通过约定mutation方法才能修改数据,实现可预测性,实现后期数据的拦截与时间漫游等等扩展功能。