一、状态管理
<body>
<div id='app'></div>
<div></div>
<div></div>
</body>
- 3个组件App,box,son都渲染count数据.count写在哪个组件上
- 当count变化时,3个组件同时更新视图. => 状态管理 => 应该把数据放到3个组件的共同祖先组件上
- 状态管理
- 什么叫状态? => 状态就是响应式数据和呈现它的视图.
- 组件的状态是由它的数据和视图决定的.
- 状态管理 => 数据和视图管理.
- 状态管理 => 如何实现多个组件共用一个状态?
- 1:把数据和修改数据的逻辑都放在某个祖先组件上.子孙组件触发祖先组件的修改逻辑.
- 2:Vuex.(Vue的插件 => Vue的全局状态管理)
Vue代码。
const box = {
template: `
<div>
<h3>box组件---{{count}}</h3>
<button @click='$emit("setcount")'>box--count++</button>
</div>
`,
props: ['count']
}
const son = {
template: `
<div>
<h3>son组件---{{count}}</h3>
<button @click='$emit("setcount")'>son--count++</button>
</div>
`,
props: ['count']
}
const App = {
template: `
<div id='app'>
<h3>App组件{{count}}</h3>
<button @click='setCount'>App--count++</button>
<box :count='count' @setcount='setCount' />
<son :count='count' @setcount='setCount' />
</div>
`,
data() {
return {
count: 0
}
},
components: { box, son },
methods: {
setCount() {
this.count++;
}
}
}
new Vue({
el: '#app',
components: {App},
template: `<App />`
})
二、组件的data为什么必须是一个函数
<body>
<div id='app'></div>
<div></div>
<div></div>
</body>
同一个组件,复用多次,它的状态应该是独立。
组件的data写成函数,则组件每次复用,组件的data函数都会重新调用返回。
每次重新调用都返回一个新的的对象。每个新的对象内都有独立数据,独立数据对应独立的状态。
组件的data为什么必须是函数 => 因为组件的状态应该是相互独立。组件复用时,函数返回新的对象,新的对象对应新的状态。
const box = {
template: `
<div>
<h3>box组件---{{count}}</h3>
<button @click='count++'>box--count++</button>
</div>
`,
data() {
// return obj
return {
count: 0
}
}
}
const App = {
template: `
<div id='app'>
<h3>App组件</h3>
<box/>
<box/>
<box/>
</div>
`,
components: { box }
}
new Vue({
el: '#app',
components: {App},
template: `<App />`
})
三、$forceUpdate
<body>
<div id='app'></div>
<div></div>
<div></div>
</body>
<script src="js/vue.js"></script>
<script>
// 改了数据视图不变.
const App = {
template: `
<div id='app'>
<h3>App组件</h3>
{{arr[0]}}
<button @click='fn'>按钮</button>
</div>
`,
data(){
return {
arr:[111, 222, 333]
}
},
methods: {
fn() {
this.arr[0] = 88888;
// 强制更新当前组件.(应急性api,不推荐滥用).
this.$forceUpdate();
}
}
}
new Vue({
el: '#app',
components: {App},
template: `<App />`
})
</script>
四、$children和$parent
<body>
<div id='app'></div>
<div></div>
<div></div>
</body>
<script src="js/vue.js"></script>
<script>
// 改了数据视图不变.
const box = {
template: `
<div>
<h3>box组件{{count}}</h3>
<button @click='fn'>把count给父组件</button>
</div>
`,
data() {
return { count: 100 }
},
methods: {
fn() {
// 在子组件中修改父组件的数据
// $parent => 当前组件的父组件.(应急性api)
this.$parent.count = this.count;
}
}
}
const App = {
template: `
<div id='app'>
<h3>App组件--{{count}}</h3>
<button @click='fn'>按钮</button>
<box />
</div>
`,
data(){
return {
count: 200
}
},
methods: {
fn() {
// this.$children => 当前父组件的子组件.(是一个数组)(应急api)
// this.$children[0].count = this.count;
// 触发子组件的fn方法
this.$children[0].fn();
}
},
components: { box }
}
new Vue({
el: '#app',
components: {App},
template: `<App />`
})
</script>
五、$refs
<body>
<div id='app'></div>
<div></div>
<div></div>
</body>
<script src="js/vue.js"></script>
<script>
// refs => 用来获取视图标签的。
const box = {
template: `
<div>
<h3 ref='h22222'>box组件{{count}}</h3>
<button @click='fn'>把count给父组件</button>
</div>
`,
data() {
return { count: 100 }
},
methods: {
fn() {
const [oH3] = document.querySelectorAll('h3');
oH3.style.backgroundColor = 'red';
// 获取ref属性的值是h2222的那个标签.
console.log(this.$refs.h22222);
// DOM操作修改背景色.
this.$refs.h22222.style.backgroundColor = 'red';
}
}
}
const App = {
template: `
<div id='app'>
<h3>App组件--{{count}}</h3>
<button @click='fn'>按钮</button>
<box ref='box' />
</div>
`,
data(){
return {
count: 200
}
},
methods: {
fn() {
// 如果给组件添加了ref,则可以通过这个ref获取到指定的组件.
// console.log(this.$refs.box).
// 获取box组件的count数据.(子传父).
this.count = this.$refs.box.count;
}
},
components: { box }
}
new Vue({
el: '#app',
components: {App},
template: `<App />`
})
</script>
六、vuex实现状态管理
<body>
<div id='app'></div>
</body>
状态管理 => 如何实现多个组件共用一个状态.
1:多个组件共享一个数据.
2:这个数据变化,则多个组件需要同时更新视图.
状态管理
1:把数据和修改数据的逻辑放在父组件上.子组件通过props获取父组件数据,再通过自定义事件触发父组件修改数据的逻辑.
2:Vuex插件实现.(全局状态管理).
Vuex的插件4xx版本是为了配合Vue3xx版本.
Vue的版本如果是2xx,则Vuex的版本应该是用3xx
Vuex如何实现
01:如何实例化Vuex数据仓库.(用于存放数据和修改数据的逻辑)
02:组件如何获取Vuex的数据
03:组件如何修改Vuex的数据
04:如何把Vuex的数据和逻辑映射成组件的数据和逻辑
实例化一个数据仓库.实例化之后,还需要挂载到Vue实例上.
挂载到new Vue上,就是全局的状态管理。所有的组件,都可以访问到state内的数据。
如果挂载到指定的组件A上,则组件A以及组件A的所有子孙组件,都可以访问到state内的数据。
<script src="js/vue.js"></script>
<!-- vuex是vue的插件,需要在引入完成Vue后再引入Vuex -->
<script src="js/vuex.js"></script>
<script>
const store = new Vuex.Store({
// 多个组件共用的状态.(数据)
state: {
count: 0
}
})
const box = {
template: `
<div>
<h3>box组件---{{count}}</h3>
<button @click='$emit("setcount")'>box--count++</button>
</div>
`,
}
const son = {
template: `
<div>
<h3>son组件---{{count}}</h3>
<button @click='$emit("setcount")'>son--count++</button>
</div>
`,
}
const App = {
template: `
<div id='app'>
<h3>App组件{{count}}</h3>
<button @click='setCount'>App--count++</button>
<box />
<son />
</div>
`,
data() {
return {
count: 0
}
},
components: { box, son },
methods: {
setCount() {
this.count++;
}
}
}
new Vue({
el: '#app',
components: {App},
template: `<App />`,
// store: store
store
});
</script>
七、组件获取Vuex的数据
<body>
<div id='app'></div>
</body>
实例化一个数据仓库.实例化之后,还需要挂载到Vue实例上.
挂载到new Vue上,就是全局的状态管理。所有的组件,都可以访问到state内的数据。
如果挂载到指定的组件A上,则组件A以及组件A的所有子孙组件,都可以访问到state内的数据。
挂载完成后,就可以通过Vuex的实例store来访问state内的数据。
vue组件内可以通过this.$store来获取这个state的实例。
<script src="js/vue.js"></script>
<!-- vuex是vue的插件,需要在引入完成Vue后再引入Vuex -->
<script src="js/vuex.js"></script>
<script>
const store = new Vuex.Store({
// 多个组件共用的状态.(数据)
state: {
count: 10000
}
});
const box = {
template: `
<div>
<h3>box组件---{{count}}</h3>
<button>box--count++</button>
</div>
`,
computed: {
count() {
return this.$store.state.count
}
},
}
const son = {
template: `
<div>
<h3>son组件---{{$store.state.count}}</h3>
<button>son--count++</button>
</div>
`,
}
const App = {
template: `
<div id='app'>
<h3>App组件{{$store.state.count}}</h3>
<button>App--count++</button>
<box />
<son />
</div>
`,
components: { box, son },
}
new Vue({
el: '#app',
components: {App},
template: `<App />`,
// store: store
store
});
</script>
八、组件如何修改Vuex的数据
<body>
<div id='app'></div>
</body>
实例化一个数据仓库.实例化之后,还需要挂载到Vue实例上.
挂载到new Vue上,就是全局的状态管理。所有的组件,都可以访问到state内的数据。
如果挂载到指定的组件A上,则组件A以及组件A的所有子孙组件,都可以访问到state内的数据。
挂载完成后,就可以通过Vuex的实例store来访问state内的数据。
vue组件内可以通过this.$store来获取这个state的实例。
修改state数据的逻辑,需要通过mutations选项写在数据仓库中,可以写多个方法.
mutations内的方法的第一个参数是state对象,第二个参数是组件传递的参数.
组件触发mutations内的方法,需要同this.$store.commit来触发.
commit('mutation的方法名', 组件实参);
<script src="js/vue.js"></script>
<!-- vuex是vue的插件,需要在引入完成Vue后再引入Vuex -->
<script src="js/vuex.js"></script>
<script>
const store = new Vuex.Store({
// 多个组件共用的状态.(数据)
state: {
count: 10000
},
// 修改state内数据的逻辑.
mutations: {
// count+1
plus(state, num) {
state.count += num;
},
// count-1
reduce(state, num) {
state.count -= num
}
}
});
const box = {
template: `
<div>
<h3>box组件---{{count}}</h3>
<button @click='$store.commit("plus", 1)'>box--count++</button>
<button @click='$store.commit("reduce", 1)'>box--count--</button>
</div>
`,
computed: {
count() {
return this.$store.state.count
}
},
}
const son = {
template: `
<div>
<h3>son组件---{{count}}</h3>
<button @click='$store.commit("plus", 2)'>son--count++</button>
<button @click='$store.commit("reduce", 2)'>son--count--</button>
</div>
`,
computed: {
count() {
return this.$store.state.count
}
},
}
const App = {
template: `
<div id='app'>
<h3>App组件{{count}}</h3>
<button @click='$store.commit("plus", 3)'>App--count++</button>
<button @click='$store.commit("reduce", 3)'>App--count--</button>
<box />
<son />
</div>
`,
components: { box, son },
computed: {
count() {
return this.$store.state.count
}
},
}
new Vue({
el: '#app',
components: {App},
template: `<App />`,
// store: store
store
});
</script>
九、组件异步修改vuex数据
<body>
<div id='app'></div>
</body>
实例化一个数据仓库.实例化之后,还需要挂载到Vue实例上.
挂载到new Vue上,就是全局的状态管理。所有的组件,都可以访问到state内的数据。
如果挂载到指定的组件A上,则组件A以及组件A的所有子孙组件,都可以访问到state内的数据。
挂载完成后,就可以通过Vuex的实例store来访问state内的数据。
vue组件内可以通过this.$store来获取这个state的实例。
修改state数据的逻辑,需要通过mutations选项写在数据仓库中,可以写多个方法.
mutations内的方法的第一个参数是state对象,第二个参数是组件传递的参数.
组件触发mutations内的方法,需要同this.$store.commit来触发.
commit('mutation的方法名', 组件实参);
commit内是不能有异步操作的.
如果有异步操作的需求,需要通过actions来实现
actions方法内触发mutations方法.
actions的触发方式是dispatch.
dispatch('actions的方法名', 组件实参);
<script src="js/vue.js"></script>
<!-- vuex是vue的插件,需要在引入完成Vue后再引入Vuex -->
<script src="js/vuex.js"></script>
<script>
const store = new Vuex.Store({
// 严格模式.禁止在mutations之外修改state.
// 如果在mutations之外修改state,会报这个错[vuex] do not mutate vuex store state outside mutation handlers.
strict: true,
// 多个组件共用的状态.(数据)
state: {
count: 10000
},
// 修改state内数据的逻辑.
mutations: {
// count+1
plus(state, num) {
state.count += num
},
// count-1
reduce(state, num) {
state.count -= num
}
},
// 异步触发mutations
actions: {
asyncPlus(store, num) {
// 2秒后触发mutations的plus方法修改state。
setTimeout(() => {
store.commit("plus", num);
}, 2000);
}
}
});
const box = {
template: `
<div>
<h3>box组件---{{count}}</h3>
<button @click='$store.dispatch("asyncPlus", 1)'>box--count++</button>
<button @click='$store.commit("reduce", 1)'>box--count--</button>
</div>
`,
computed: {
count() {
return this.$store.state.count
}
},
}
const son = {
template: `
<div>
<h3>son组件---{{count}}</h3>
<button @click='$store.dispatch("asyncPlus", 2)'>son--count++</button>
<button @click='$store.commit("reduce", 2)'>son--count--</button>
</div>
`,
computed: {
count() {
return this.$store.state.count
}
},
}
const App = {
template: `
<div id='app'>
<h3>App组件{{count}}</h3>
<button @click='$store.dispatch("asyncPlus", 3)'>App--count++</button>
<button @click='$store.commit("reduce", 3)'>App--count--</button>
<box />
<son />
</div>
`,
components: { box, son },
computed: {
count() {
return this.$store.state.count
}
},
}
let vm = new Vue({
el: '#app',
components: {App},
template: `<App />`,
// store: store
store
});
</script>
十、Vuex的数据流
Vuex(状态管理)的原理
Vuex的数据流
Vuex是如何让多个组件同时更新的?
实例化Vuex的过程中,Vue会给state内的数据都添加数据劫持.
通过挂载store实例,就可以知道当state内的数据变化时,应该更新多少个组件.(收集依赖).
每当state内的数据变化,都会更新对应组件的视图.
组件按钮 => actions(dispatch) => mutations(commit) => state => 自动通知对应的视图更新.
十一、辅助函数mapState
<body>
<div id='app'></div>
</body>
以上这些方法,都是返回纯对象的.
mapState(['count', 'msg']) => { count() {} , msg() {}}
实例化一个数据仓库.实例化之后,还需要挂载到Vue实例上.
挂载到new Vue上,就是全局的状态管理。所有的组件,都可以访问到state内的数据。
如果挂载到指定的组件A上,则组件A以及组件A的所有子孙组件,都可以访问到state内的数据。
挂载完成后,就可以通过Vuex的实例store来访问state内的数据。
vue组件内可以通过this.$store来获取这个state的实例。
修改state数据的逻辑,需要通过mutations选项写在数据仓库中,可以写多个方法.
mutations内的方法的第一个参数是state对象,第二个参数是组件传递的参数.
组件触发mutations内的方法,需要同this.$store.commit来触发.
commit('mutation的方法名', 组件实参);
commit内是不能有异步操作的.
如果有异步操作的需求,需要通过actions来实现
actions方法内触发mutations方法.
actions的触发方式是dispatch.
dispatch('actions的方法名', 组件实参);
map映射:
1:把state的数据变成组件数据.
2:把mutations和actions的方法变成组件方法.
<script src="js/vue.js"></script>
<!-- vuex是vue的插件,需要在引入完成Vue后再引入Vuex -->
<script src="js/vuex.js"></script>
<script>
const { mapState, mapMutations, mapActions } = Vuex;
strict: true,
// 多个组件共用的状态.(数据)
state: {
count: 10000,
msg: '你好'
},
// 修改state内数据的逻辑.
mutations: {
// count+1
plus(state, num) {
state.count += num
},
// count-1
reduce(state, num) {
state.count -= num
}
},
// 异步触发mutations
actions: {
asyncPlus(store, num) {
// 2秒后触发mutations的plus方法修改state。
setTimeout(() => {
store.commit("plus", num);
}, 2000);
}
}
});
const box = {
template: `
<div>
<h3>box组件---{{num}}---{{str}}</h3>
<button @click='$store.dispatch("asyncPlus", 1)'>box--count++</button>
<button @click='$store.commit("reduce", 1)'>box--count--</button>
</div>
`,
computed: {
// count() {
// return this.$store.state.count
// }
// msg() {
// return this.$store.state.msg
// }
// ...mapState(['count', 'msg'])
...mapState({
num: 'count',
str: 'msg'
})
},
}
const son = {
template: `
<div>
<h3>son组件---{{count}}</h3>
<button @click='$store.dispatch("asyncPlus", 2)'>son--count++</button>
<button @click='$store.commit("reduce", 2)'>son--count--</button>
</div>
`,
computed: {
// count() {
// return this.$store.state.count
// }
...mapState(['count'])
},
}
const App = {
template: `
<div id='app'>
<h3>App组件{{count}}</h3>
<button @click='$store.dispatch("asyncPlus", 3)'>App--count++</button>
<button @click='$store.commit("reduce", 3)'>App--count--</button>
<box />
<son />
</div>
`,
components: { box, son },
computed: {
// count() {
// return this.$store.state.count
// }
...mapState(['count'])
},
}
let vm = new Vue({
el: '#app',
components: {App},
template: `<App />`,
// store: store
store
});
</script>
十二、mapMutations等
<body>
<div id='app'></div>
</body>
<script src="js/vue.js"></script>
<!-- vuex是vue的插件,需要在引入完成Vue后再引入Vuex -->
<script src="js/vuex.js"></script>
<script>
const store = new Vuex.Store({
// 严格模式.禁止在mutations之外修改state.
// 如果在mutations之外修改state,会报这个错[vuex] do not mutate vuex store state outside mutation handlers.
strict: true,
// 多个组件共用的状态.(数据)
state: {
count: 10000
},
// 修改state内数据的逻辑.
mutations: {
// count+1
plus(state, num) {
state.count += num
},
// count-1
reduce(state, num) {
state.count -= num
}
},
// 异步触发mutations
actions: {
asyncPlus(store, num) {
// 2秒后触发mutations的plus方法修改state。
setTimeout(() => {
store.commit("plus", num);
}, 2000);
}
}
});
const box = {
template: `
<div>
<h3>box组件---{{count}}</h3>
<button @click='$store.dispatch("asyncPlus", 1)'>box--count++</button>
<button @click='$store.commit("reduce", 1)'>box--count--</button>
</div>
`,
computed: {
count() {
return this.$store.state.count
}
},
}
const son = {
template: `
<div>
<h3>son组件---{{count}}</h3>
<button @click='$store.dispatch("asyncPlus", 2)'>son--count++</button>
<button @click='$store.commit("reduce", 2)'>son--count--</button>
</div>
`,
computed: {
count() {
return this.$store.state.count
}
},
}
const App = {
template: `
<div id='app'>
<h3>App组件{{count}}</h3>
<button @click='$store.dispatch("asyncPlus", 3)'>App--count++</button>
<button @click='$store.commit("reduce", 3)'>App--count--</button>
<box />
<son />
</div>
`,
components: { box, son },
computed: {
count() {
return this.$store.state.count
}
},
}
let vm = new Vue({
el: '#app',
components: {App},
template: `<App />`,
// store: store
store
});
</script>
十三、组件混合
<body>
<div id='app'></div>
</body>
<script src="js/vue.js"></script>
<!-- vuex是vue的插件,需要在引入完成Vue后再引入Vuex -->
<script src="js/vuex.js"></script>
<script>
const { mapState, mapMutations, mapActions } = Vuex;
const store = new Vuex.Store({
strict: true,
// 多个组件共用的状态.(数据)
state: {
count: 10000,
msg: '你好'
},
// 修改state内数据的逻辑.
mutations: {
// count+1
plus(state, num) {
state.count += num
},
// count-1
reduce(state, num) {
state.count -= num
}
},
// 异步触发mutations
actions: {
asyncPlus(store, num) {
// 2秒后触发mutations的plus方法修改state。
setTimeout(() => {
store.commit("plus", num);
}, 2000);
}
}
});
// 组件的公共选项部分
// 这部分需要通过组件的mixins选项引入.
// 如果组件本身就包含mx内的选项,则以组件的的选项为准.
const mx = {
computed: {
...mapState(['count'])
},
methods: {
...mapActions(['asyncPlus']),
...mapMutations(['reduce'])
}
}
const box = {
template: `
<div>
<h3>box组件---{{count}}</h3>
<button @click='asyncPlus(1)'>box--count++</button>
<button @click='reduce(1)'>box--count--</button>
</div>
`,
mixins: [mx],
}
const son = {
template: `
<div>
<h3>son组件---{{count}}</h3>
<button @click='asyncPlus(2)'>son--count++</button>
<button @click='reduce(2)'>son--count--</button>
</div>
`,
mixins: [mx],
}
const App = {
template: `
<div id='app'>
<h3>App组件{{count}}</h3>
<button @click='asyncPlus(3)'>App--count++</button>
<button @click='reduce(3)'>App--count--</button>
<box />
<son />
</div>
`,
components: { box, son },
mixins: [mx],
}
let vm = new Vue({
el: '#app',
components: {App},
template: `<App />`,
// store: store
store
});
</script>
十四、vuex的计算属性
<body>
<div id='app'></div>
</body>
<script src="js/vue.js"></script>
<script src="js/vuex.js"></script>
<script>
const { mapState, mapMutations, mapGetters } = Vuex;
const store = new Vuex.Store({
state: {
count: 1,
price: 10,
},
// Vuex的计算属性total
getters: {
total({count, price}) {
return count * price
}
},
mutations: {
setCount(state, val) {
state.count = val
},
setPrice(state, val) {
state.price = val
}
}
});
const mx = {
computed: {
...mapState(['count', 'price'])
},
methods: {
...mapMutations(['setCount', 'setPrice'])
}
}
const count = {
template: `<input type='text' placeholder='数量' :value='count' @input='setCount($event.target.value)' />`,
mixins: [mx]
}
const price = {
template: `<input type='text' placeholder='单价' :value='price' @input='setPrice($event.target.value)' />`,
mixins: [mx]
}
const total = {
template: `<div>总价:{{total}}</div>`,
computed: {
// total() {
// 通过$store来获取Vuex的计算属性.
// return this.$store.getters.total
// }
// 通过映射方法快速引入Vuex的计算属性total
...mapGetters(['total'])
}
}
const App = {
template: `
<div>
<count />
<price />
<total />
</div>
`,
components: {count, price, total},
store
}
new Vue({
el: '#app',
components: { App },
template: `<App />`
})
</script>