关于vuex的一些总结

状态管理模式,采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化

vue提供了直接下载和CDN引用两种方式:

//CDN:
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vuex"></script>
!!应该先引用vue,再引用vuex

//NPM:
npm install vuex --save 

//Yarn
yarn add vuex

//在一个模块化的打包系统中,必须显示地通过Vue.use()来安装Vuex:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

Vuex依赖Promise,如果项目需要支持没有实现Promise的IE,需要使用一个polyfill的库,例如es6-promise,同样可以通过直接下载或CDN引用的方式:

//CDN引入之后window.Promise自动可用
<script src="https://cdn.jsdelivr.net/npm/es6-promise@4/dist/es6-promise.auto.js"></script>

//使用npm/yarn等方式下载
npm install es6-promise --save 
yarn add es6-promise 
//在使用vuex之前添加
import 'es6-promise/auto'

每一个Vuex应用的核心就是store(仓库),store基本上就是一个容器,包含着应用中大部分的状态,Vuex和单纯的全局对象有两点不同:
1.Vuex的状态存储是响应式的,当Vue组件从store中读取状态的时候,若store中的状态发生变化,那么相应的组件也会相应地得到高效更新
2.不能直接改变store的状态,改变store的状态的唯一途径就是显式地提交(commit)mutation,使得方便跟踪每一个状态的变化

安装Vuex之后,创建一个store,创建过程直截了当 – 仅需要提供一个初始state对象和一些mutation:

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
	state:{
		count:0
	},
	mutation:{
		increment(state){
			state.count++
		}
	}
})

//现在你可以通过state.count来获取状态对象,也可以通过store.commit方法触发状态变更:
store.commit('increment')
console.log(store.state.count)    //1

Vuex提供了一个从根组件向所有子组件,以store选项的方式注入该store的机制:

new Vue({
	el:'#app',
	store:store
})

//现在可以从组件的方法提交一个变更:
method:{
	increment(){
		this.$store.commit('increment')
		console.log(this.$store.state.count)
	}
}

在vue组件中获得Vuex状态:

//创建一个Count组件
const Counter = {
	template:`<div>{{ count }}</div>`,
	computed:{
		count(){
			return store.state.count
		}
	}
}

mapState辅助函数,当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余,为了解决这个问题,可以使用mapState辅助函数帮助我们生成计算属性:

import { mapState } from 'vuex'
export default{
	computed:mapState({
		count:state => state.count,
		// 传字符串参数‘count’等同于'state => state.count'
		countAlias:'count',
	})
}

当映射的计算属性的名称与state的子节点名称相同时,也可以给mapState传一个字符串数组

computed: mapState(['count'])

mapState返回的是一个对象,但是有时候我们的计算属性不只用来存储状态,还会定义一些计算属性,如何写在一起呢?可以利用对象展开运算符:

computed:{
	localComputed(){ /* ... */ },
	...mapState({
		//...
	})
}

Getters
有时候我们需要从store中的state中派生出一些状态,例如对列表进行过滤并计数:

computed:{
	doneTodosCount(){
		return this.$store.state.todos.filter(todo => todo.done).length
	}
}

如果有多个组件需要用到这个属性,我们一般会复制这个函数,或者抽取到一个共享函数然后在多处导入它,无论哪种方式都不是很理想,vuex允许我们在store中定义getter,可以认为是store的计算属性,就算计算属性一样,getter的返回值会根据它的依赖被缓存起来,且只有当它的依赖发生了改变才会被重新计算:

const store = new Vuex.Store({
	state:{
		todos:[
			{ id:1,text:'...',done:true },
			{ id:2,text:'...',done:false }
		]
	},
	getters:{
		//Getter接受state作为其第一个参数
		doneTodos:state => {
			return state.todos.filter(todo => todo.done)
		},
		//也可以接受其他getter作为第二个参数
		doneTodosCount:(state,getters)=>{
			return getters.doneTodos.length
		}
	}
})

//通过属性访问
store.getters.doneTodos  // => [{id:1,text:'...',done:true}]
store.getters.doneTodosCount   // 1

//也可以通过方法访问,通过让getter返回一个函数,来实现给getter传参,在对store里的数据进行查询时非常有用
getters:{
	getTodoById: state => id => {
		return state.todos.find(todo => todo.id === id)
	}
}
store.getters.getTodoById(2)  //{id:2,text:'...',todo:false}

//通过mapGetters辅助函数访问
import { mapGetters } from 'vuex'
export default{
	computed:{
		...mapGetters([
			'doneTodosCount',
			'anotherGetter',
			'doneCount':'doneTodosCount'
		])
	}
}

Mutation
更改Vuex的store中的状态的唯一方法是提交mutation,Vuex中的mutation非常类似于事件:

const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment (state) {
      // 变更状态
      state.count++
    }
  }
})

store.commit('increment')

//也可以向store.commit传入额外的参数,即mutation的载荷:
mutations:{
	increment(state,n){
		state.count += n
	}
}
store.commit('increment',10)

//大多数情况下,载荷应该是一个对象,这样可以包含多个字段并且记录的mutation会更易读:
mutations:{
	increment(state,payload){
		state.count += payload.amount
	}
}
store.commit('increment',{
	amount:10
})

//也可以采用对象风格的提交方式
store.commit({
	type:'increment',
	amount:10
})

当使用对象风格的提交方式,整个对象都作为载荷传给 mutation 函数,因此 handler 保持不变

Mutation需要遵守Vue的响应规则
1.最好提前在你的store中初始化所有所需属性
2.当需要在对象上添加新属性时,应该:

//使用Vue.set
Vue.set(obj,'newProp',123)

//或者以新对象替换老对象
state.obj = {...state.obj, newProp:123}

使用常量替代Mutation事件类型
使用常量替代 mutation 事件类型在各种 Flux 实现中是很常见的模式。这样可以使 linter 之类的工具发挥作用,同时把这些常量放在单独的文件中可以让你的代码合作者对整个 app 包含的 mutation 一目了然:

//mutation-types.js
export const SOME_MUTATION =SOME_MUTATION//store.js
import Vuex from 'vuex'
import { SOME_MUTATION } from './mutation-types'
const store = new Vuex.store({
	state:{...},
	mutation:{
		[SOME_MUTATION](state){
			//mutate state
		}
	}
})

mutation只能是同步函数
在组件中提交Mutation,可以在组件中使用this.$store,commit(‘xxx’)提交mutation,或者使用mapMutations辅助函数将组件中的methods映射为store.commit调用

import { mapMutations } from 'vuex'
export default{
	methods:{
		...mapMutations([
			'increment',
			//使用this.increment() 会映射为 this.$store.commit('increment')
			'incrementBy',
			//this.incrementBy(amount) 会映射为 this.$store.commit('crementBy',amount)
		]),
		...mapMutations({
			add:'increment'
			//this.add() 会映射为this.$store.commit('increment')
		})
	}
}

Action,类似于mutation,不同在于:
1.Action提交的是mutation,而不是直接变更状态
2.Action可以包含任意异步操作

const store = new Vuex.Store({
	state:{
		count:0
	},
	mutations:{
		increment(state){
			state.count++
		}
	},
	actions:{
		increment(context){
			context.commit('increment')
		}
	}
})

Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters。也可以利用参数解构的方式:

actions:{
	increment({coommit}){
		commit('increment')
	}
}

//Action通过store.dispatch方法触发
store.dispatch('increment')

你可能会问,为什么要这么麻烦呢,不直接commit一个mutation就好了,不是的,我们的mutation只能进行同步操作,但是有时候我们需要执行异步操作,Action就不受约束:

actions:{
	incrementAsync({commit}){
		setTimeout(()=>{
			commit('increment')
		},1000)
	}
}

//Actions支持同样的载荷方式和对象方式进行分发
//以载荷形式分发
store.dispatch('incrementAsync',{
	amount:10
})

//以对象形式分发

来看一个更加实际的购物车示例,涉及到调用异步API和分发多重mutation:

actions:{
	checkout({commit,state},products){
		//把当前购物车的物品备份起来
		const savedCartItems = [...state.cart.added]
		//发出结账请求,清空购物车
		commit(types.CHECKOUT_REQUEST)
		shop.buyProducts(
			products,
			//成功操作
			() => commit(types.CHECKOUT_SUCCESS),
			//失败操作
			() => commit(types.CHECKOUT_FAILURE,savedCartItems)
		)
	}
}

在组件中分发Action,在组件中使用this.$store.dispatch(‘xxx’)分发action,或者使用mapActions辅助函数将组件的methods映射为store.dispatch调用:

import { mapActions } from 'vuex'
export default{
	methods:{
		...mapActions([
			'increment',
			//this.increment 会映射为 this.$store.dispatch('increment')
			'incrementBy',
			//this.incrementBy(amount) 会映射为 this.$store.dispatch('incrementBy',amount)
		]),
		...mapActions({
			add:'increment'
			//this.add() 会映射为this.$store.dispatch('crement')
		})
	}
}

组合Action
Action通常是异步的,那么如何知道action什么时候结束呢,如何结合多个action,处理更加复杂的异步流程?
首先需要明白store.dispatch可以处理被触发的action的处理函数返回的Promise,并且store.dispatch仍旧返回Promise

actions:{
	actionA({commit}){
		return new Promise((resolve,reject)=>{
			setTimeout(()=>{
				commit('someMutation')
				resolve()
			},1000)
		})
	}
}

store.dispatch('actionA').then(()=>{
	//...
})

在另外一个action中也可以:

actions: {
  // ...
  actionB ({ dispatch, commit }) {
    return dispatch('actionA').then(() => {
      commit('someOtherMutation')
    })
  }
}

//如果我们利用 async / await (opens new window),我们可以如下组合 action:
actions: {
  async actionA ({ commit }) {
    commit('gotData', await getData())
  },
  async actionB ({ dispatch, commit }) {
    await dispatch('actionA') // 等待 actionA 完成
    commit('gotOtherData', await getOtherData())
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值