Vue.js 笔记10

发布于 2020-06-03  328 次阅读


关键词:Vuex

VueX可以帮助我们管理共享状态,并附带了更多的概念和框架,在大型单页面应用或特殊使用场景中,可以帮我们更好地将一些共用数据提取出来进行管理

基本用法

(不以webpack打包的项目结构演示,直接在页面demo中演示)

在本地环境下获取vuex.js,然后通过<script>标签进行引入

<script src="./js/vuex.js"></script>

新建Vuex对象:

const store = new Vuex.Store({
	//vuex的配置内容
})

仓库store 包含了应用的数据(状态)和操作过程,Vuex里的数据都是响应式的,任何组件使用同一 store 的数据时,只要 store 的数据变化,对应的组件也会立即更新

数据保存在 Vuex 选项的 state 字段内,例如实现一个计数器,定义一个数据 count,初始值为0 :

<body>
	<div id="app">
		{{$store.state.count}}
	</div>
	<script>
		const store = new Vuex.Store({
			state:{
				count:0
			}
		})
		var vm = new Vue({
			el:'#app',
			store
		})
	</script>
</body>

此时,计数0就显示出来了,但在组件内,来自store的数据只能读取,不能手动修改,改变 store 中数据的唯一途径就是显示地提交 mutations 。

mutations 是Vuex 的第二个选项,用来直接修改state里的数据,我们给上面的计数器增加两个mutations ,用来+1和-1,并将插值表达式的{{$store.state.count}}写到计算方法中:

<body>
	<div id="app">
		{{count}}
		<button @click="handleIncrement">+1</button>
		<button @click="handleDecrease">-1</button>
	</div>
	<script>
		const store = new Vuex.Store({
			state:{
				count:0
			},
			mutations:{
				increment(state){
					state.count++
				},
				decrease(state){
					state.count--
				}
			}
		})
		var vm = new Vue({
			el:'#app',
			computed:{
				count(){
					return this.$store.state.count
				}
			},
			methods:{
				handleIncrement(){
					this.$store.commit('increment')
				},
				handleDecrease(){
					this.$store.commit('decrease')
				}
			},
			store
		})
	</script>
</body>

在组件内部,通过 this.$store.commit 方法来执行 mutations,组件只负责提交一个事件名,Vuex 对应的mutaions 来完成业务逻辑。

提交 mutations 的另一种方法是直接使用包含 type 属性的对象,例如:

mutations:{
	increment(state,params){
		state.count += params.count
	}
}
this.$store.commit({
	type:'increment',
	count:1
})

高级用法

Vuex 还有其他3个选项可以使用: getters、actions、modules

假设有这样一个场景,Vuex 定义了某个数据 list,它是一个数组,比如:

const store = new Vuex.Store({
	state:{
		list:[1,5,8,10,30,50]
	}
})

如果只想得到小于10的数据,可以直接在组件内部的计算方法中利用

return this.$store.state.list.filter(item => item < 10)

的方式实现,但如果还有其他组件也需要用到过滤后的数据,就需要重写一份一模一样的代码,这样对它们的使用和维护都不方便。
getters就是起这个作用的,它能把 computed 的方法提取出来:

<div id="app">
	{{list}}
</div>
<script>
	const store = new Vuex.Store({
		state:{
			list:[1,5,8,10,30,50]
		},
		getters:{
			filteredList(state) {
				return state.list.filter(function(item){
					return item<10;
				})
			}
		}
	})
	var vm = new Vue({
		el:'#app',
		computed:{
			list(){
				return this.$store.getters.filteredList
			}
		},
		store
	})
</script>

这种写法与组件的计算属性十分相似,getter 也可以依赖其他 getter,把getter作为第二个参数,比如再写一个 getter,计算 list 过滤后的结果数量:

const store = new Vuex.Store({
	state:{
		list:[1,5,8,10,30,50]
	},
	getters:{
		filteredList(state) {
			return state.list.filter(function(item){
				return item<10;
			})
		},
		listCount(state,getters){
			return getters.filteredList.length
		}
	}
})
var vm = new Vue({
	el:'#app',
	computed:{
		list(){
			return this.$store.getters.filteredList
		},
		listCount(){
			return this.$store.getters.listCount
		}
	},
	store
})

action

mutation 里不应该异步操作数据,所以有了 action 选项,action 和 mutation 很像,不同的是 action 里面提交的是 mutaion,并可以异步操作业务逻辑

action 在组件内通过$store.dispatch 触发,例如使用 action 来加1:

<div id="app">
	{{count}}
	<button @click="handleActionIncrement">+1</button>
</div>
<script>
	const store = new Vuex.Store({
		state:{
			count:0
		},
		mutations:{
			increment(state,n=1){
				state.count += n
			}
		},
		actions:{
			increment(context){
				context.commit('increment')
			}
		}
	})
	var vm = new Vue({
		el:'#app',
		computed:{
			count(){
				return this.$store.state.count
			}
		},
		methods:{
			handleActionIncrement(){
				this.$store.dispatch('increment')
			}
		},
		store
	})
</script>

就上述示例看来有点多此一举,但它在异步中的作用就凸显出来了,例如用一个Promise 在1秒后提交 mutaion:

<div id="app">
	{{count}}
	<button @click="handleAsyncIncrement">async +1</button>
</div>
<script>
	const store = new Vuex.Store({
		state:{
			count:0
		},
		mutations:{
			increment(state,n=1){
				state.count += n
			}
		},
		actions:{
			asyncIncrement(context){
				return new Promise(resolve=>{
					setTimeout(()=>{
						context.commit('increment')
						resolve()
					},1000)
				})
			}
		}
	})
	var vm = new Vue({
		el:'#app',
		computed:{
			count(){
				return this.$store.state.count
			}
		},
		methods:{
			handleAsyncIncrement(){
				this.$store.dispatch('asyncIncrement').then(()=>{
					console.log(this.$store.state.count)
				})
			}
		},
		store
	})
</script>

mutations 、actions 看起来很相似,一般情况下,涉及改变数据的,就用 mutations ,存在业务逻辑的,就用 actions ,至于将业务逻辑放到 actions 里还是放到Vue组件里完成,就视情况而定了

moudules

最后一个选项是 modules,它用来将 store 分割到不同模块。当项目很庞大时,store 里的选项会非常多,使用 modules 可以将它们写入不同的文件中,每个 modules 拥有自己的 state、getters、mutations、actions,而且可以多层嵌套,例如:

const moduleA = {
	state:{},
	mutations:{},
	actions:{},
	getters:{}
}
const moduleB = {
	state:{},
	mutations:{},
	actions:{},
	getters:{}
}
const store = new Vuex.Store({
	modules:{
		a:moduleA,
		b:moduleB
	}
})

此时,在组件中使用 $store.state.a就可以获取moduleA的状态

module 的 mutation 和 getter 接收的第一个参数 state 是当前模块的状态,在actions 和 getters 中,还可以接收一个参数 rootState,来访问根节点的状态,例如:

const moduleA = {
	state:{
		count:0
	},
	getters:{
		sumCount(state,getters,rootState){
			return state.count + rootState.count
		}
	}
}


何为BS?B-big,S-shui。