原理请参考:(20条消息) 遇见面试--vuex原理_凌晨四点半er的博客-CSDN博客_vuex原理面试
注意:在导入·vuex之前,必须先导入vue
vuex解决的问题:
vuex中的state相当于组件中的data,就是专门用于保存共享数据的
(1)访问vuex中共享数据的前提,在祖先组件中保存vuex对象
(2)访问state中的属性值
访问state中msg属性值,不能通过普通的插值方式{{}}访问
访问state中msg属性值的正确方式:{{this.$store.state.msg}}
(3)共享数据的步骤总结:
mutations:修改Vuex中state共享数据的唯一方法,用于保存 修改共享数据的方法。
(1)主要是为了解决下面问题:
注意:在Vuex中不推荐直接修改state中的共享数据
原因:如果多个组件都修改了共享的数据,那么后期数据发生了错误,我们如果需要去调试错误,就需要把每一个修改了共享数据的组件都检查一遍,这样非常低效,不利于我们去维护。
(2)在Vuex对象中添加mutations
在执行mutations中定义的方法时,系统会自动将Vuex中的state作为参数,传入所有的方法中。
(3)在组件中调用修改共享数据的方法
调用方式:this.$store.commit("方法名")。
通过this.$store.commit 获取Vuex中的mutations。
例如:执行mutation中的mAdd方法 this.$store.commit("mAdd")
也可在组件的methods或computed中,通过...mapMutations
来调用(将组件中的 methods 映射为 store.commit
调用)(需要在根节点注入 store
)
(4)Payload :传入mutations方法中的参数(不包含state),是一个对象。
例如:调用mutations中的setCurIdx方法,并传参{index,text}
起到通过派发指令给mutation修改state的数据状态的作用。
一般在action中进行数据请求。
Action 类似于 mutation,不同在于:
(1) 在Vuex中添加actions,并定义方法
例如:getData方法用来请求数据。
获取数据后,通过commit调用mutations中的方法,将获取的数据赋值给state中的共享变量。
(2)调用actions中的方法:this.$store.dispatch('调用的方法名',传的参数)
Action 通过 store.dispatch
方法触发
(3)也可以在组件的methods中通过...mapActions
辅助函数来调用actions中的方法
(1)向Vuex对象中添加getters
(2)调用getters中的方法:{{this.$store.getters.方法名}}
当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter。
然后在store/index.js文件中导入所有模块,添加到modules中
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
1、流程顺序
“相应视图—>修改State”拆分成两部分,视图触发Action,Action再触发Mutation,Mutation修改State。
2、角色定位
基于流程顺序,二者扮演不同的角色。
Mutation:专注于修改State,理论上是修改State的唯一途径。
Action:业务代码、异步请求。
3、限制
角色不同,二者有不同的限制。
Mutation:必须同步执行。
Action:可以异步,但不能直接操作State。
4、Vuex为什么不允许在actions中修改状态state
dispatch代码大致如下:
dispatch (_type, _payload) {
const {
type,
payload
} = unifyObjectStyle(_type, _payload)
const action = { type, payload }
const entry = this._actions[type]
// ...
const result = entry.length > 1
? Promise.all(entry.map(handler => handler(payload)))
: entry[0](payload)
// ...
}
若将vue创建 store 的时候传入 strict: true, 开启严格模式,那么任何修改state的操作,只要不经过 mutation的函数都会报错。
(1)不能直接修改state的原因:
如果多个组件都修改了共享的数据,那么后期数据发生了错误,我们如果需要去调试错误,就需要把每一个修改了共享数据的组件都检查一遍,这样非常低效,不利于我们去维护。
(2)不用action修改state的原因:
如果同时在多个action中修改了同一个state。很有可能每个action赋给state的新值都有所不同,并且不能保证最后一个有返回结果action是哪一个action,所以最后赋予state的值可能是错误的;
(3)用mutation修改state的原因:
通过commit 提交 mutation 的方式来修改 state 时,vue的调试工具能够记录每一次state的变化,这样方便调试。但是如果是直接修改state,则没有这个记录。
Vuex是通过全局注入store对象,来实现组件间的状态共享。
- 总之,假如你需要 数据 和 组件 分离,分别处理,那么使用 Vuex 是非常合适的。
- 相反,如果不需要分离处理,那么不使用 Vuex 也没关系。
比如某个数据只跟某组件打交道,是强耦合的。那么这个数据就应该存放在该组件的 data 属性中。
例如,在以下场景里,我们应当使用 Vuex:
1、组件会被销毁
我们可以假设这样一个场景:
解决办法:
但事实上,最好的还是存在 Vuex 里:
这样处理的优点是解耦,不跟其他组件打交道。
2、组件基于数据而创建
我们可以假设这样一个场景:
那么:
解决办法:
3、多对多事件——多处触发,影响多处
我们可以假设这样一个场景:
那么:
解决办法:
参考:详细分析Vuex 的应用场景_qq20004604的博客-CSDN博客_vuex使用场景
- 为什么出现:vue一般是单项数据流,当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏:多个视图依赖于同一状态、来自不同视图的行为需要变更同一状态。
- 作用:多个组件共享数据或者是跨组件传递数据。
1)什么是 vuex ?
Vuex采用MVC模式中的Model层,规定所有的数据必须通过action—>mutaion—>state这个流程进行来改变状态的。再结合Vue的数据视图双向绑定实现页面的更新。统一页面状态管理,可以让复杂的组件交互变的简单清晰。
虽然vue中提供了props(父传子)commit(子传父)兄弟间也可以用localstorage和sessionstorage。但是这种方式在项目开发中带来的问题比他解决的问题(难管理,难维护,代码复杂,安全性低)更多。
面对一个储存状态数据的工具时, 有几个方面的问题我们马上可以想到的
2)vuex的优点:
3)vuex的缺点:
解决:
// store.js
const moduleE = {
namespaced: true,
state: {
name: 'xiaoming',
age: 1
}
}
export default new Vuex.Store({ // 将模块挂载到根store
modules: {
moduleE, // 等同于moduleE : 上面模块的命名空间是moduleE
// eee: moduleE, // 下面模块的命名空间是 eee
}
});
// 带命名空间的绑定函数
computed: {
// ...mapState('命名空间名', ["name"]) 在辅助函数mapState的第一参数上,填写上模块的命名空间名
...mapState('moduleE', { // 命名空间名用法1
name: 'name'
})
...mapState('moduleE', ['name']) // 命名空间名用法2
① 如果是对象或数组,都去根实例的state上找(所有module.state都挂载在store.state上)
对象{ age: state => state.age }:执行函数并传入根state作为参数,让它返回对应value
数组[ ‘age’ ]:通过key找到根state上的对应的value
② 如果是字符串,说明是用命名空间来获取值,则通过第一个参数(命名空间名)去根实例store._modulesNamespaceMap上找到对应的module模块,再通过第二个参数(key)找到state上对应的value返回
总结:都是通过key值在state上找到value值,组装成对象返回,然后再解构赋值到computed上
1)vuex的核心api:
install函数:用来注册插件到vue里(说白了就是在vue中执行这个函数,并把vue当作参数传入此函数,使用vue的方法和绑定store到各个组件上)
store类:state、getters、mutations、actions、modules、plugins
辅助函数:mapState、mapActions、mapMutations
2)围绕这些问题实现
1. 给每个实例注入$store,使每个组件都能拿到$store。
let Vue
const install = (_Vue) => {
Vue = _Vue
// 使用vue的混入方法,在创建之前,给每个组件都增加$store属性
Vue.mixin({
// 创建之前会被执行
beforeCreate () {
// 根实例有store属性
if (this.$options && this.$options.store) {
this.$store = this.$options.store
} else {
// 根实例上没有的store属性,往父亲节点找
// new Vue({store}) 这里已经在根组件挂载有store属性
this.$store = this.$parent && this.$parent.$store
}
}
})
}
export default {
install // 给用户提供一个install方法,默认会被调用
}
2. 设置state响应数据,实现state数据响应式。
利用vue的响应式原理,让state的修改都可以更新回视图,而不是单纯获取state数据
class Store {
constructor (options) {
// this.vm = options.state 只是单纯获取state数据,但是数据修改不会更新界面
/** 借用Vue的双向绑定机制让Vuex中data变化实时更新界面 */
this.vm = new _Vue({
data: {
state: options.state
}
})
}
/* 类的属性访问器
访问state对象时候,就直接返回响应式的数据
Object.defineProperty get 同理
*/
get state () {
return this.vm.state
}
}
3、getters。getters从根本上就是computed,给你返回一些派生的状态(对数据进行过滤操作)
遍历用户传入的参数获取属性名,利用Object.defineProperty的get获取方法执行的结果,赋值到getters对象对应的属性名上,用户通过this.getters.myName就可以调用对应的值
参考:vuex实现原理_越思考越清晰-CSDN博客_vuex实现原理
- VUEX是吸收了Redux的经验,放弃了一些特性并做了一些优化,代价就是VUEX只能和VUE配合。
- Redux则是一个纯粹的状态管理系统,React利用React-Redux将它与React框架结合起来。
- React-Redux:简单来说,它提供了一些接口,用于Redux的状态和React的组件展示结合起来,以用于实现状态与视图的一一对应。
redux的流程:
(1)vuex 与 redux对比
Vuex | Redux | |
核心对象 | store | store |
数据存储 | state | state |
状态state更新提交接口 | commit(触发mutation中的方法) | store.dispatch(action) |
状态state更新提交参数 | 带type和payload的mutation | action{ type: '描述类型', value: '' } |
状态更新计算 | mutation handler | reducer(之前的state,action) |
特性 | 支持带缓存的getter,用于获取state经过某些计算后的值 | 支持中间件 |
(2)vuex 与 react-redux 对比
Vuex | React-Redux | |
状态注入组件 | Vue.use(Vuex)将Vuex应用为全局的plugin,再将store对象传入根VUE实例 | 通过 |
包含的内容 | state: 存储数据; mutations:更改数据; action:执行异步操作,一般进行数据请求; |
state:存储数据; action: 是把数据从应用传到store的载体。组件触发action,action通过store.dispatch(action)传到reducer。只是描述了有事情发生,没描述如何去更新state; reducer:相当于中转站,用来处理action,并把state发送给store()。描述了如何更新state; store:通过监听函数,监听reducer发过来的数据; |
特性 | VUEX提供mapState,mapGetter,mapMutation等方法,用于生成store内部属性对组件内部属性的映射 | connect支持mapStateToProps方法(将数据state作为props绑定到组件),mapDispatchToProps(将action作为props绑定到函数),用于自定义映射。 |
通过使用方式上的较大差异,也可以看出理念上的不同。
1)和组件结合方式的差异:
2)容器组件的差异:
React-Redux提倡容器组件和表现组件分离的最佳实践,而VUEX框架下不做区分,全都是表现(展示)组件。我觉得不分优劣,React-Redux的做法更清晰、更具有强制性和规范性,而VUEX的方式更加简化和易于理解。
总的来说,就是谁包谁,谁插谁的问题。Redux毕竟是独立于React的状态管理,它与React的结合则需要对React组件进行一下外包装。而VUEX就是为VUE定制,作为插件、以及使用插入的方式就可以生效,而且提供了很大的灵活性。
补充:
持久化插件plugins
作用:把state都存储在localStorage里面,刷新不会丢失数据
原理:发布订阅模式
实例store的时候,遍历plugins里面的函数,并执行 this.subscribe() 订阅到sote._subscribe数组上
当监测到mutation有变化的时候,依次执行所有的订阅