目录
一、 为啥会出现vuex
二 、什么是vuex
三、 怎么使用vuex
四、vue和vuex比较
五、vuex的五个属性
5.1 state
5.2 mutations
5.3 getters
5.4 Actions
5.5 Modules
六、为啥state里的数据可以实现响应式
七、为啥每个组件都可以拿到state里的数据
若是vue内需要传递数据的组件不是父子组件关系或者两组件之间隔了很多其他组件,这时再通过上面的几种方式传递数据就会十分困难。于是有了vuex,我们可以把要共享的数据都存放到vuex中的state属性里,通过this.$store.state就能拿到里面的数据了!
可以把vuex理解为一个大仓库,整个vue组件内都可以拿到里面的资源
1、npm install vuex --save
补充:--save和--save -dev的区别
--save:将保存配置信息到package.json文件中的dependencies节点中,运行时依赖
--save-dev:将保存配置信息到package.json文件中的devdependencies节点中,开发时依赖
2、导入vuex
import Vuex from 'vuex'
3、安装插件
Vue.use(Vuex)
4、创建实例
const store = new Vuex.Store({
state,
mutations,
actions,
getters
})
5、挂载到vue实例上
export default store
import store from './store'
new Vue({
render: h => h(App),
router,
store
}).$mount('#app')
state相当于data,里面定义一些基本数据类型;
mutations相当于methods,里面定义一些方法来操作state里面的数据;
actions用来存放一些异步操作;
getters里存放一些基本数据类型的派生类型,相当于computed
modules内部仍然可以继续存放一些子store,内部再有五种属性,模块化vuex
分别为state,mutations,actions,getters,modules,一个一个介绍
vuex采用单一状态树,即用一个对象表示所有的状态数据,state里面存放的数据就是基本状态数据
1、怎样获取state里面的数据呢?
通过组件内部的computed属性获取
const store = new Vuex.Store({
state: {
count:0
}
})
const app = new Vue({
//..
store,
computed: {
count: function(){
return this.$store.state.count
}
},
//..
})
如果在组件中想要使用state内部的很多数据,按照上面这种写法只能一个一个定义,有啥方法可以快速批量的获取到state里的数据呢?可以使用这个方法:mapState辅助函数
2、怎么使用mapState辅助函数
上代码!
先在组件内导入
import {mapState} from 'vuex'
开始在compute内使用:
注意这里有三种方式导入:a. count: 'count'
b. count: (state) => {state.count}
c. count: function(state){ return this.data + state.count}
为啥要把箭头函数和普通函数分开,因为要是在computed里先拿到state里的数据,然后和本组件data内的数据进行一些操作,就得保证函数里的this指向
{{count}}
{{dataCount}}
{{sex}}
{{from}}
{{myCmpted}}
这时又有一个新问题可能产生,如果原先在computed里面有其他的函数,还能直接这样computed: mapState({ })吗?可以使用对象展开符,合并computed里的函数和mapState里定义的数据
3、对象展开符
先来说说...对象展开符
let arr = [1,2,3]
console.log(...arr) //1,2,3
mapState是一个函数,它会返回一个对象,用...展开符就会把返回的对象一一放到computed中,与原来computed里定义的函数放在一起,也可以叫做对象混合。
//之前的computed
computed:{
fn1(){ return ...},
fn2(){ return ...},
fn3(){ return ...}
........
}
//引入mapState辅助函数之后
computed:{
//原来的继续保留
fn1(){ return ...},
fn2(){ return ...},
fn3(){ return ...}
......
//再维护vuex
...mapState({ //这里的...不是省略号了,是对象扩展符
count:'count'
})
}
当映射的对象名称和state里面的变量名称相同时,也可以传一个数组
computed: mapState([
// 映射 this.count 为 store.state.count
'count'
])
4、向state中添加删除新属性
添加必须使用
方式一:使用Vue.set(obj,"新属性名称","对应的属性值")
方式二:用新对象对就对象重新赋值 state.obj = {...state.obj, newProp: 123 }
删除必须使用
使用“delete+某个属性”,满足不了响应式。
解决办法:Vue.delete(obj,“属性名”)
vuex中只能通过mutations唯一改变state里面的数据(若没有异步操作),mutations的第一个参数是state,第二个参数是传输过来的数据(payload),可省略。
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
//无提交荷载
increment(state) {
state.count++
}
//提交荷载
incrementN(state, obj) {
state.count += obj.n
}
}
})
组件内部
//无提交荷载
store.commit('increment')
//提交荷载
store.commit('incrementN', {
n: 100
})
通过this.$store.commit('事件类型’),这里的事件类型是mutations里的函数,也可以通过...mapMutations
import { mapMutations } from 'vuex'
export default {
//..
methods: {
...mapMutations([
'increment' // 映射 this.increment() 为 this.$store.commit('increment')
]),
...mapMutations({
add: 'increment' // 映射 this.add() 为 this.$store.commit('increment')
})
}
}
getters接收第一个参数为state,第二个参数为其他getters(可省略)
const store = new Vuex.Store({
state: {
count:0
},
getters: {
// 单个参数
countDouble: function(state){
return state.count * 2
},
// 两个参数
countDoubleAndDouble: function(state, getters) {
return getters.countDouble * 2
}
}
})
和state一样也可以在computed属性中获取到getters
const app = new Vue({
//..
store,
computed: {
count: function(){
return this.$store.state.count
},
countDouble: function(){
return this.$store.getters.countDouble
},
countDoubleAndDouble: function(){
return this.$store.getters.countDoubleAndDouble
}
},
//..
})
当然也可以使用...mapGetters
import { mapGetters } from 'vuex'
export default {
// ...
computed: {
// 使用对象展开运算符将 getters 混入 computed 对象中
...mapGetters([
'countDouble',
'CountDoubleAndDouble',
//..
])
}
}
Actions里面用于存放一些异步代码,在组件中只能先通过dispatch到actions,再在actions里commit到mutations,最后在mutations里更改state
注意:action接受的第一个参数是context
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
setInterval(function(){
context.commit('increment')
}, 1000)
}
}
})
store.dispatch('increment')
modules实现vuex模块化,每个模块都有自己的五种属性,甚至嵌套子模块
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
对于模块内部的 mutation
和 getter
,接收的第一个参数是模块的局部状态,对于模块内部的 getter,根节点状态会作为第三个参数:
const moduleA = {
state: { count: 0 },
mutations: {
increment (state) {
// state 模块的局部状态
state.count++
}
},
getters: {
doubleCount (state) {
return state.count * 2
},
sumWithRootCount (state, getters, rootState) {
return state.count + rootState.count
}
}
}
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
}
}
在data里注入state,使得可以响应式
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方法,默认会被调用
}
用vue.mixin混入,在每个组件的beforeCreate生命周期函数,使得每个组件初始化时都会给自身注入一个store属性