vue单向数据流原理
单向数据流是一个闭环,但是当多个组件共享一个状态时
- 多个视图依赖于同一状态。
- 来自不同视图的行为需要变更同一状态。
问题一,传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。
问题二,我们经常会采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码。
如何使用
1.state+mutations简单例子(同步调用)
store
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
export default new Vuex.Store({
state: {
count: 0,
},
mutations: {
addCount(state) {
state.count++;
},
},
actions: {},
modules: {},
});
app.vue
<template>
<div id="app">
<div>
app {{ count }}
<button @click="handleClick">点击</button>
</div>
<Item1 />
<Item2 />
</div>
</template>
<script>
import Item1 from "./views/item1";
import Item2 from "./views/item2";
export default {
components: {
Item1,
Item2,
},
data() {
return {};
},
computed: {
count() {
return this.$store.state.count;
},
},
methods: {
handleClick() {
this.$store.commit("addCount");
},
},
};
</script>
<style>
</style>
item.vue
<template>
<div class="item1">item1+{{ this.$store.state.count }}</div>
</template>
<script>
export default {};
</script>
<style>
</style>
解释:
- 安装vuex
npm i vuex --save - 引入store初始化,并且将全局使用这个store
vue.use(vuex)
new Vue({
router,
store,
render: (h) => h(App),
}).$mount("#app");
- 初始化一个store
<template>
<div class="item1">item1+{{ count }}</div>
</template>
<script>
export default {
computed: {
count() {
return this.$store.state.count;
},
},
};
</script>
<style>
</style>
- 调用state,直接
this.$store.state.count,注意在组件中使用coomputed来接收这个,能够实时更新计算。 - 同步修改state,记住前面说到的闭环图,修改store,必须使用mutations的方法,触发mutations的方法是this.$store.commit("addCount"),mutations默认接受的第一个参数是state,第二个参数是commit传过来的值。一个同步使用store的流程处理完成了。
2.state+mutations+action(异步调用)
commit设置的mutations时,可能mutations时里需要请求数据异步操作,那么异步状态无法追踪,dev-tool就无法实时获取到真实的快照,为了解决这个竞态。vuex采用了action中间状态,在action中处理异步,在action的异步状态触发commit设置的mutations。执行了一个异步操作mutations中,将不会打印出准确的快照,count还是初始值。
使用action作为中间态处理异步操作
app.vue
subtractClick() {
this.$store.dispatch("subtractClick");
},
store.js
mutations: {
subtractCount(state, newCount) {
state.count = newCount;
},
},
actions: {
subtractClick(context) {
setTimeout(() => {
context.commit("subtractCount", -1);
}, 3000);
},
},
context是默认接收的全局对象,理解为store。
3.使用mapstate映射app.vue的方法到store的action方法
app.vue
methods: {
...mapActions({
subtractClick: "subtractClick",
}),
},
store.js
actions: {
subtractClick(context) {
setTimeout(() => {
context.commit("subtractCount", -1);
}, 3000);
},
},
4.Getters(用于筛选出state中一个filter列表)
store.js
state: {
todos: [
{ id: 1, text: "...", done: true },
{ id: 2, text: "...", done: false },
],
},
getters: {
doneTodos: (state) => {
return state.todos.filter((todo) => todo.done);
},
},
app.vue
this.$store.getters.doneTodos调用
5.module(用于拆分主仓库为多个module的内容)
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 的状态
6.vuex实现原理
store2.js
import Vue from "vue";
export default function() {
const vuex = {};
//创建一个不绑定dom的vue对象
vuex._vm = new Vue({
data: {
message: "vuex",
},
});
vuex.state = vuex._vm;
vuex.mutations = {
editMessage(value) {
vuex.state.message = value;
},
};
//定义绑定的原理
function init() {
this.$store2 = vuex;
}
Vue.mixin({ beforeCreate: init });
}
main.js(注册使用)
import store2 from "./store/index2";
Vue.use(store2);
app.vue使用
<template>
<div id="app">
<div>{{ this.$store2.state.message }}</div>
<button @click="editMessage">修改message</button>
</div>
</template>
<script>
import { mapActions } from "vuex";
export default {
methods: {
editMessage() {
this.$store2.mutations.editMessage("修改");
},
},
};
</script>