1、为什么要用 Vuex 进行状态管理?定义一个全局对象,再去上层封装了一些数据存取的接口不也可以么?
- Vuex 的状态存储是响应式的,当 Store 中状态发生改变时,对应的组件的视图能够得到高效的更新。
- 你不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。可以方便地使用工具(vue-devtools)跟踪每一个状态的变化。
2、为什么mutation可以修改状态,还要存在一个action?mutation 和 action 有什么区别?
Vuex中的状态修改必须使用 commit(``mutation``)
修改状态,mutation 必须是同步函数,如果在 mutation 存在的异步函数中修改状态,会导致工具(vue-devtools)无法捕捉到前一状态和后一状态的快照,导致状态的改变无法追踪,用官网一句话叫: “实质上任何在(异步)回调函数中进行的状态的改变都是不可追踪的”
Action 可以包含任意异步操作,Action 通过 store.dispatch
方法触发。dispatch 触发 action 去 commit mutation 进而修改 state
3、有哪些常读常新的体会?
**
原来:
computed: mapState({
// ...
})
// 或
computed: {
...mapState({
// ...
})
}
**
现在:
computed: {
localComputed () { /* ... */ },
// 使用对象展开运算符将此对象混入到外部对象中
...mapState({
// ...
})
}
可以将 …mapState 结构出来的 state 解构到一个对象中。
原来:
在 store 中写 getter 时只会傻傻的这么写
store.getters.doneTodos
现在:
可以在 getter 中定义一些逻辑,这样就避免了多个组件都需要处理某个状态都需要写一遍这个逻辑
getters: {
// ...
doneTodosCount: (state, getters) => {
return getters.doneTodos.length
}
}
getter 还可以通过方法访问,但是,getter 在通过方法访问时,每次都会去进行调用,而不会缓存结果。
看看这个代码函数里面 return 函数,就是高阶函数了
getters: {
// ...
getTodoById: (state) => (id) => {
return state.todos.find(todo => todo.id === id)
}
}
store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false }
语法糖层面的书写:
computed: mapState({
// 箭头函数可使代码更简练
count: state => state.count,
// 传字符串参数 'count' 等同于 `state => state.count`
countAlias: 'count',
// 为了能够使用 `this` 获取局部状态,必须使用常规函数
countPlusLocalState (state) {
return state.count + this.localCount
}
})
computed: {
// 使用对象展开运算符将 getter 混入 computed 对象中
...mapGetters([
'doneTodosCount',
'anotherGetter',
// ...
])
}
mapGetters({
// 把 `this.doneCount` 映射为 `this.$store.getters.doneTodosCount`
doneCount: 'doneTodosCount'
})
载荷提交
mutations: {
increment (state, n) {
state.count += n
}
}
还可以使用对象形式的提交
store.commit({
type: 'increment',
amount: 10
})
Vuex 的 store 中的状态是响应式的,意味着 Vuex 中的 mutation 也需要与使用 Vue 一样遵守一些注意事项:
- 最好提前在你的 store 中初始化好所有所需属性。
- 当需要在对象上添加新属性时,你应该
- 使用
Vue.set(obj, 'newProp', 123)
, 或者 - 以新对象替换老对象。例如,利用对象展开运算符我们可以这样写:原理就是替换对象在堆内存的指针地址
state.obj = { ...state.obj, newProp: 123 }
Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit
提交一个 mutation,或者通过 context.state
和 context.getters
来获取 state 和 getters
actions: {
increment ({ commit, state, getter }) {
commit('increment')
}
}
模块化 Store 时,像这种并不是命名空间,而是将状态分模块挂在全局上,命名空间需要添加 namespaced: true
// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'
Vue.use(Vuex)
const modulesFiles = require.context('./modules', true, /\.js$/)
const modules = modulesFiles.keys().reduce((modules, modulePath) => {
// set './app.js' => 'app'
const moduleName = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
const value = modulesFiles(modulePath)
modules[moduleName] = value.default
return modules
}, {})
// store/module/app.js
const state = {
sidebar: true
}
const mutations = {
CHANGE_SIDEBAR_STATE: (state) => {
state.sidebar = !state.sidebar
}
}
const actions = {
changeSidebar: ({commit}) => {
commit('CHANGE_SIDEBAR_STATE')
}
}
export default {
state,
mutations,
actions
}