Vuex
认识Vuex
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。
Vuex:
state: 数据
view : 视图;将state数据展示到视图上;
actions : 动态,更改或者是触发视图的动作
数据传递:把各个组件共享的状态抽离出来,并且每个组件都可以使用它,并且还可以更改它;既然是公共状态,当其中组件把这个公共状态更改以后,凡是依赖这个公共状态的组件都要更新;
- VUEX的数据存储是响应式的;vue组件中使用了vuex的数据时,当vuex中的数据更新时,那么使用数据的组件也会得到高效的更新
- 不能直接修改store中的状态;需要commit一个mutation,这样方便我们追踪每一个数据的变化;
Vuex : 适用中大型单页面应用
<body>
<div id="app">
{{count}}
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
// VUEX:
// state: 数据
// view : 视图;将state数据展示到视图上;
// actions : 动态,更改或者是触发视图的动作
// 数据传递:把各个组件共享的状态抽离出来,并且每个组件都可以使用它,并且还可以更改它;
既然是公共状态,当其中组件把这个公共状态更改以后,凡是依赖这个公共状态的组件都要更新;
let num =100;
// 1. VUEX的数据存储是响应式的;vue组件中使用了vuex的数据时,当vuex中的数据更新时,
那么使用数据的组件也会得到高效的更新
// 2. 不能直接修改store中的状态;需要commit一个mutation,
这样方便我们追踪每一个数据的变化;
// Vuex : 适用中大型单页面应用
let vm = new Vue({
el:"#app",
data:{
count:0
},
methods:{
addCount(){
this.count++;
}
},
components:{
}
})
</script>
</body>
表示“单向数据流”理念的简单示意
- 状态自管理应用包含以下几个部分:
- state,驱动应用的数据源;
- view,以声明方式将 state 映射到视图;
- actions,响应在 view 上的用户输入导致的状态变化。
Vuex多个组件共享状态
数据传递–>VueX(store)
组件: 组件就是抽离的公共的部分,这个公共的部分可以有自己独立的html,css,js
实现复用:可复用,可维护
数据传递: vue中数据传递有哪些方式?
Vue是单项数据流,当父组件的数据发生更改,如果子组件也使用了父组件的数据,那么子组件也会随着更新;如果改变子组件这个数据,那么父组件不能更改;
- 父传子(props) props props=[“a”] :a=“num”
- 子传父(自定义事件) @change=‘num’ this. e m i t ( " c h a n g e " , 1000 ) . s y n c 修 饰 符 : a . s y n c t h i s . emit("change",1000) .sync修饰符 :a.sync this. emit("change",1000).sync修饰符:a.syncthis.emit(“updata:a”,2000)
3.兄弟组件(eventBus) let eventBus=new Vue eventBus. o n ( 事 件 池 , 方 法 ) e v e n t B u s . on(事件池,方法) eventBus. on(事件池,方法)eventBus.emit(事件池)
4.VueX(store) 把每一个stroe注入到各个组件里 [state,mutations,actions,modules,getters]
Vuex的原理图
- let store = new Vuex.Store({})
Vuex中的使用-state
如果这个数据是好几个组件共享的,这个数据放到VUEX的state上;如果这个数据只有当前这一个组件使用,那么放到对应的组件的data中
store // 当前vue的实例就会新增一个 s t o r e 的 属 性 , 属 性 值 时 一 个 对 象 ; 同 时 也 会 给 当 前 实 例 每 一 个 子 孙 组 件 也 新 增 一 个 store的属性,属性值时一个对象;同时也会给当前实例每一个子孙组件也新增一个 store的属性,属性值时一个对象;同时也会给当前实例每一个子孙组件也新增一个store属性
vuex更改store中状态的唯一方法就是commit;
在vuex中更改公有的数据,必须通过commit一个mutation;
当共享的数据发生改变以后,那么依赖这个数据的组件都会高效的更新;
<body>
<div id="app">
<first></first>
<second></second>
<!-- <div>{{this.$store.state.count}}</div> -->
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script src="../node_modules/vuex/dist/vuex.min.js"></script>
<script>
// 如果这个数据是好几个组件共享的,这个数据放到VUEX的state上;
如果这个数据只有当前这一个组件使用,那么放到对应的组件的data中
// 在vuex中更改公有的数据,必须通过commit一个mutation;
// 当共享的数据发生改变以后,那么依赖这个数据的组件都会高效的更新;
let store = new Vuex.Store({
state: {
count: 1001, // count : 这就是公共的数据
shop: [{
name: "奔驰"
}, {
name: "奥迪"
}]
},
mutations: {
increment(state, a) { // 参数接收到当前state
state.count += a;
},
min() {
// mutation 这个对象中的函数的第一个参数的实参都是state;
}
}
});
let first = {
data() {
return {}
},
methods: {
addCount(val) {
// 提交的是mutation里面的函数名;用于修改store中的数据
this.$store.commit("increment", val);
}
},
// computed:{
// shop(){
// return this.$store.state.shop
// }
// },
// VUEx.mapState:将vuex的store中的state数据,映射到当前实例的计算属性上;
//computed:Vuex.mapState(["shop","count"]),
computed: {
tt() {
},
...Vuex.mapState(["shop", "count"])
},
template: "<div>{{count}}<button @click='addCount(2)'>++</button>
<li v-for='a in shop'>{{a.name}}</li></div>"
};
let second = {
data() {
return {}
},
template: "<div>{{this.$store.state.count}}</div>"
}
let vm = new Vue({
el: "#app",
data: {
},
components: {
first,
second
},
store // 当前vue的实例就会新增一个$store的属性,属性值时一个对象;
同时也会给当前实例每一个子孙组件也新增一个$store属性
});
console.log(vm);
</script>
</body>
Vuex-getters
getters : vue允许定义getter;在store中的计算属性;也会被缓存;当依赖的值发生变化,才会发生改变
Vuex的私有属性提供了mapState mapGetters MapMutations,可以将store中的state、getters、mutations都映射当前的实例的computed或methods;
<body>
<div id="app">
<first></first>
<second></second>
<!-- <div>{{this.$store.state.count}}</div> -->
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script src="../node_modules/vuex/dist/vuex.min.js"></script>
<script>
// getters : vue允许定义getter;在store中的计算属性;也会被缓存;当依赖的值发生变化,
才会发生改变
let state= {
count:1001,// count : 这就是公共的数据
shop:[{name:"奔驰"},{name:"奥迪"}]
}
let getters={
str(state){// state 就是store中的state
return state.count%2===0?"偶数":"奇数"
},
a(){
}
}
let store= new Vuex.Store({
state,
getters,
mutations:{
increment(state,a){// 参数接收到当前state
state.count+=a;
},
min(){
// mutation 这个对象中的函数的第一个参数的实参都是state;
}
}
});
let first = {
data(){
return {
}
},
methods:{
addCount(val){
// 提交的是mutation里面的函数名;用于修改store中的数据
this.$store.commit("increment",val);
}
},
// computed:{
// shop(){
// return this.$store.state.shop
// }
// },
// VUEx.mapState:将vuex的store中的state数据,映射到当前实例的计算属性上;
//computed:Vuex.mapState(["shop","count"]),
computed:{
tt(){
},
// Vuex的私有属性提供了mapState mapGetters MapMutations;
可以将store中的state、getters、mutations都映射当前的实例的computed或methods;
// 这几个函数都返回一个包装后的对象 {shop:12,count:22}
// ... : 展开运算符展开,放到了computed这个对象中
...Vuex.mapState(["shop","count"]),
...Vuex.mapGetters(["str"])
},
template:"<div>{{count}}<button @click='addCount(1)'>++</button>
<li v-for='a in shop'>{{a.name}}</li>{{str}}</div>"
};
let second = {
data(){
return {}
},
template:"<div>{{this.$store.state.count}}</div>"
}
let vm = new Vue({
el:"#app",
data:{
},
components:{
first,
second
},
store // 当前vue的实例就会新增一个$store的属性,属性值时一个对象;
同时也会给当前的子组件新增一个$store属性
});
console.log(vm);
</script>
</body>
Vuex-mutations :同步
在mutation的方法中,都是同步事务,不支持异步;
methods:{
//同步
// add(val){
// this.$store.commit(“add_count”,val);
// }
…Vuex.mapMutations([“add_count”]),
}
<body>
<div id="app">
<first></first>
<second></second>
<!-- <div>{{this.$store.state.count}}</div> -->
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script src="../node_modules/vuex/dist/vuex.min.js"></script>
<script>
let state= {
count:1001,// count : 这就是公共的数据
shop:[{name:"奔驰"},{name:"奥迪"}]
}
let getters={
str(state){// state 就是store中的state
return state.count%2===0?"偶数":"奇数"
},
a(){
}
}
let store= new Vuex.Store({
state,
getters,
mutations:{
increment(state,a){// vuex更改store中状态的唯一方法就是commit;
// state: 状态 payload: 载荷
//state.count+=a;//在mutation的方法中,都是同步事务,不支持异步;
setTimeout(()=>{
state.count+=a;
},2000)
},
min(){
}
}
});
let first = {
data(){
return {
}
},
methods:{
// addCount(val){
// this.$store.commit("increment",val);
// }
//...Vuex.mapMutations(["increment"])
...Vuex.mapMutations({
add:"increment"// 将this.add()映射成'this.$store.commit("increment")
})
},
computed:{
tt(){
},
...Vuex.mapState(["shop","count"]),
...Vuex.mapGetters(["str"])
},
template:"<div>{{count}}<button @click='add(1)'>++</button>
<li v-for='a in shop'>{{a.name}}</li>{{str}}</div>"
};
let second = {
data(){
return {}
},
template:"<div>{{this.$store.state.count}}</div>"
}
let vm = new Vue({
el:"#app",
data:{
},
components:{
first,
second
},
store
});
</script>
</body>
commit提交
Vuex-actions :异步
actions 支持异步的,这里面提交一个mutation的动作;
actions:{
// 支持异步
// 在项目中,经常从后端获取数据赋值更改store中的state的值,
但是如果这个请求是异步的,大家需要在actions中发送请求,异步地更改state;
add({commit},val){// 提交一个mutations
// commit===KaTeX parse error: Expected 'EOF', got '}' at position 279: … }̲ } <…store.dispatch(“add”,val);
}
// commit==>mutations dipatch===> actions 在actions中是提交的mutations
// …Vuex.mapActions([“add”])
},
<body>
<div id="app">
<first></first>
<second></second>
<!-- <div>{{this.$store.state.count}}</div> -->
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script src="../node_modules/vuex/dist/vuex.min.js"></script>
<script>
// 工作需求中,经常发送异步的请求,请求回来的数据会赋值给Store中的state;
let state= {
count:1001,// count : 这就是公共的数据
shop:[{name:"奔驰"},{name:"奥迪"}]
}
let getters={
str(state){// state 就是store中的state
return state.count%2===0?"偶数":"奇数"
},
a(){
}
}
let store= new Vuex.Store({
state,
getters,
mutations:{
increment(state,a){// vuex更改store中状态的唯一方法就是commit;
// state: 状态 payload: 载荷
//state.count+=a;//在mutation的方法中,都是同步事务,不支持异步;
state.count+=a;
},
min(){
}
},
actions:{
addNum({commit},a){
// commit : 就是store上的commit;提交
// actions 支持异步的,这里面提价一个mutation的动作;
setTimeout(function(){
commit("increment",a);
},2000);
}
}
});
let first = {
data(){
return {
}
},
methods:{
// addCount(val){
// this.$store.commit("increment",val);
// }
//...Vuex.mapMutations(["increment"])
// ...Vuex.mapMutations({
// add:"increment"// 将this.add()映射成'this.$store.commit("increment")
// }),
...Vuex.mapActions({
addadd:"addNum"
})
},
computed:{
tt(){
},
...Vuex.mapState(["shop","count"]),
...Vuex.mapGetters(["str"])
},
template:"<div>{{count}}<button @click='addadd(1)'>++</button><li v-for='a in shop'>{{a.name}}</li>{{str}}</div>"
};
let second = {
data(){
return {}
},
template:"<div>{{this.$store.state.count}}</div>"
}
let vm = new Vue({
el:"#app",
data:{
},
components:{
first,
second
},
store
});
</script>
</body>
Vuex-module模块
// module
let moduesA = {
state: {
a: 100
},
getters: {
str1() {
return 99
}
}
};
let store = new Vuex.Store({
state: {
count: 0
},
modules: {
// 这个modules会把各自模块的state,getters,mutations,actions最终都会注入到该Store中
first: moduesA,
second: moduesB,
}
});
使用: {{$store.state.first.a}}
<body>
<div id="app">
<first></first>
<second></second>
</div>
<!-- IMPORT JS -->
<script src="../node_modules/vuex/dist/vuex.min.js"></script>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
// module
let moduesA = {
state: {
a: 100
},
getters: {
str1() {
return 99
}
}
};
let moduesB = {
state: {
num: 88
}
};
let store = new Vuex.Store({
state: {
count: 0
},
getters: { //vuex 计算属性
str(state) {
return state.count % 2 === 0 ? "偶数" : "奇数"
}
},
mutations: {
add_count(state, payload) { //state,默认接受一个state
state.count += payload
},
cut_count(state, payload) { //state,默认接受一个state
state.count -= payload
}
},
actions: {
// 支持异步
cut({
commit
}, val) { //提交一个mutations
// commit===$store.commit
setTimeout(_ => {
commit("cut_count", val)
}, 1000)
}
},
modules: {
// 这个modules会把各自模块的state,getters,mutations,actions
最终都会注入到该Store中
first: moduesA,
second: moduesB,
}
});
let first = {
data() {
return {}
},
methods: {
// add(val) {
// this.$store.commit("add_count", val)
// }
...Vuex.mapMutations(["add_count"]),
// 1
// ...Vuex.mapActions(["cut"]),
// 2
cut(val) {
this.$store.dispatch("cut", val)
}
},
computed: {
...Vuex.mapGetters(["str","str1"])
},
template: "<div>{{$store.state.count}}<button @click='add_count(1)'>++
</button>{{str}}{{$store.state.first.a}}{{str1}}<button @click=' cut(1)'>++
</button></div>"
};
let second = {
data() {
return {}
},
template: "<div></div>"
};
let vm = new Vue({
el: '#app',
created() {
console.log(this.$store);
},
data: {
},
components: {
first,
second
},
store //给当前实例新增一个$store的属性,并且给当前实例每一个子孙组件也新增一个$store
});
</script>
</body>
Vuex核心源码封装
<body>
<div id="app">
{{$store.state.msg}}
{{$store.getters.str}}
<child></child>
</div>
<script src="../node_modules/vue/dist/vue.js"></script>
<script>
// vuex:只要改变了state,凡是依赖state的组件都会高效的更新;
// new Vue的data属性;
let Vuex=(function(){
class Store{
constructor(options){
// this==> 返回值store实例
// 初始化一个state;如果传递进来的state,会给其默认一个{}
// 为了能让数据能够监听到,当数据发生改变,依赖的视图也要更新的;
let vm = new Vue({
data:{
state:options.state||{}
}
});
//this.state=options.state||{};
// 将Vm的state的空间地址赋值给了this.state
this.state = vm.state;
// this==> $store
this.mutations={};
let mutations=options.mutations;//这个就是传递进来的那个mutations
// Object.keys : 把对象中的属性名挑出来放到一个数组中
// 就是在实例身上准备一个mutations对象,里面包含了options外界传递进来的方法,
那么方法中的this已经是指向了store这个实例;
//
Object.keys(options.mutations).forEach(key=>{
//this.mutations[key].bind(this,this.state)
this.mutations[key]=(payload)=>{
options.mutations[key].call(this,this.state,payload)
}
});
// 执行私有属性的方法时,调用原型上的方法;
let commit = this.commit;// 把原型的commit给了变量commit;
// 给当前实例新增一个commit属性,属性值是一个函数
this.commit=(type,option)=>{
commit.call(this,type,option)
}
// this.commit=function(type,option){
// options.mutations[type].call(this,option)
// }
// actions
this.actions = {};
let actions = options.actions||{};
Object.keys(actions).forEach(key=>{
this.actions[key]=(option)=>{
// 第一个this指向把函数中的this改成当前实例store
// 把store传给action的方法;
actions[key].call(this,this,option)
}
});
let dispatch = this.dispatch;
this.dispatch=(type,option)=>{
dispatch.call(this,type,option)
}
// getters
this.getters={};
let getters = options.getters||{};
// Object.keys : 将对象的属性名收集起来放到一个数组中
Object.keys(getters).forEach(key=>{
// 给getters中每一个属性新增一个get方法;
Object.defineProperty(this.getters,key,{
get:()=>{
// 会进行缓存,只有依赖的属性发生改变会执行;
return getters[key].call(this,this.state)
}
});
});
}
// 把commit 放到store的原型上
commit(type,payload){
console.log(this);// Store的实例
this.mutations[type](payload)
}
dispatch(type,option){
this.actions[type](option)
}
}
//...Vuex.mapState(['a',"b"]);
// 将store中的state放到当前的computed属性中
function mapState(ary){
let obj ={};
ary.forEach(key=>{
obj[key]=function(){
console.log(this);
// this 执行组件的实例
return this.$store.state[key]
}
})
return obj;
}
function mapGetters(ary){
let obj ={};
ary.forEach(key=>{
obj[key]=function(){
return this.$store.getters[key]
}
})
return obj;
}
function mapActions(ary){
let obj ={};
ary.forEach(key=>{
obj[key]=function(option){
return this.$store.dispatch(key,option)
}
})
return obj;
}
function mapMutations(ary){
let obj ={};
ary.forEach(key=>{
obj[key]=function(option){
return this.$store.commit(key,option)
}
})
return obj;
}
// ...Vuex.mapState(["count"])
function install(_Vue){
// _Vue和外面的Vue指向同一个空间地址
// _Vue : 就是Vue这个类函数;
// mixin : 将生成store通过mixin方法注入到每一个组件上
_Vue.mixin({
beforeCreate(){// 比组件内部的beforeCreate早执行
// 生成一个组件的实例,就会执行一次;并且在自己的beforecreate之前执行的;
//console.log(this);// 代表每一个组件实例;
// this --> Vue的实例vm
// 第二次执行 组件的实例
//this.$store=this.$options.store
//console.log(this);
// 这个会进行循环遍历,
if(this.$options&&this.$options.store){
// 如果该条件是成立的,说明是vm实例;
this.$store=this.$options.store;
}else{
// 如果不成立,说明是子孙组件
// 如果父组件存在,那么就把父组件的$store属性赋值给子组件的$store属性;
// $parent : 指的是当前组件的父组件
this.$store =this.$parent&&this.$parent.$store
}
}
})
}
return {
Store,
install,
mapState,
mapActions,
mapGetters,
mapMutations
}
})();
// Vue的插件必须使用Vue.use;只是vuex会默认检测到是vue的官方插件,看不到vue.use;vue.use
执行时,会默认调用里面的install;
Vue.use(Vuex);
// Vuex.Store
// Vuex.mapState
// store 是Store的一个实例;并且这个实例最后放到了vue的实例属性上;
let store = new Vuex.Store({
state:{
count:100,
msg:"李明帅"
},
mutations:{
add(state,val){
// this==> store
console.log(state);
state.count+=val;
}
},
actions:{
addNum({commit},val){
commit("add",val);
}
},
getters:{
str(state){
return state.count%2===0?"偶数":"奇数";
}
}
});
let child={
created(){
// 组件在使用时,才会触发其钩子函数
},
methods:{
fn(){
// this.$store===store==Store的实例
this.$store.commit("add",100);
// this.$store.dispatch("addNum",1)
}
},
computed:{
str(){
},
...Vuex.mapState(['count'])
},
template:"<div>{{$store.state.count}}{{count}}<button @click='fn'>增加
</button></div>"
}
// $store 会添加到每一个组件的实例上;
let vm = new Vue({
el:"#app",
store,// 会给当前实例以及当前实例的所有子孙组件都会新增一个$store属性;
//a:100,
beforeCreate(){
//console.log(this);
// debugger
},
// 把Store的一个实例给了这个store属性
// 组件在注册时,不会调用生命周期钩子函数,
components:{child}
});
//console.log(vm);// 目前vm身上没有$store
// $options:代表 :new的对象,会把对象中的键值对添加到当前实例的$options上
// 1.先准备vuex对象【Store,install】;
// 2. Vue.use执行调用了里面install,install执行调用了Vuex.mixin,对Vue这个类进行了修改
// 3.生成了一个store
// 4.new Vue;把store放到了实例的$options
// 5.随后vm的生命周期,执行了mixin中的beforeCreate=>把options的store直接赋值给了实例的
$store属性;
</script>
</body>