该篇为翻译文章:原文为:Intro to Vue.js: Vuex
该系列文章目录:
Vue.js #1-渲染、指令、事件(原文)Vue.js #4-Vuex (当前)
Vuex
上回我们知道了如何从父级组件向子组件传递数据,并且知道了各个兄弟组件之间的数据是不共享的。如果它们之间想要进行一些交互,那么则必须要将数据状态向上传递,然后再向下进行传递。这样做虽然可以达到你要的效果,但一旦你的应用变的复杂之后,就会觉得特别力不从心了。如果你用过Redux的话,那么下面说的一些东西你将会很容易的理解。Vuex就是Vue版本的Redux。当然,你可能知道,Redux也是可以和Vue一起用的,但Vuex是一个更好的选择。
首先,让我们使用npm install vuex
或者yarn add vuex
进行安装Vuex。
然后,我在/src
下面新建一个store
目录,里面有一个store.js
文件,文件初始化内容如下:
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export const store = new Vuex.Store({
state: {
key: value
}
});
key: value
是我们写的默认初始化数据。比如在另一个例子中,使用counter:0
来初始化一个计数器。
在main.js
当中,我们把创建的store
添加进来:
import Vue from 'vue';
import App from './App.vue';
import { store } from './store/store';
new Vue({
el: '#app',
store: store,
template: '<App/>',
components: { App }
});
现在,我们可以继续修改上回的那个例子,把data()
数据放到store
当中,但在这之前 ,我们需要了解以下三个东西:
- Getters:Getters只是用来读取数据,不能用来改变数据。
- Mutations:Mutations是用来改变数据状态的,这个改变操作是同步的。它是唯一一种可以改变`store`中数据的方法。
- Actions:Actions也是用来改变数据状态的,只不过它是异步的,其实它是利用一个已经存在的mutation来完成的。如果你想以一种特定的规则来立即执行一些不同的mutations,那么这个将变的非常有用。
如果你之前没有通过异步的方式来改变数据状态的话,那么你可能有时候对于为什么要使用异步的方式表示难以理解。所以,现在我们首先抽象的了解一下这一块。假如现在有一个满是Gif图片的页面,这些图片很长时间都还没加载完。所以你现在并不想把所有的图片都一起加载,而是一次只加载20张,当页面滚动到最下面的时候再加载另一页。
你可以使用一个mutation来获取下20张图片。但是这个时候你并没有下20张图片的数据。你也不知道什么时候会滚动到页面底部。所以你需要通过事件来监听页面的滚动位置来触发一个动作。
当这个动作触发之后,它会从数据库取到20张图片的信息返回给你。得到这些信息之后,你用mutation包装一下,然后更新视图上的数据即可。
Actions的本质就是创建一个异步请求来请求数据。
一个基本示例
下面的例子向大家展示了在store.js
当中应该有哪些基本的东西:
export const store = new Vuex.Store({
state: {
counter: 0
},
//showing things, not mutating state
getters: {
tripleCounter: state => {
return state.counter * 3;
}
},
//mutating the state
//mutations are always synchronous
mutations: {
//showing passed with payload, represented as num
increment: (state, num) => {
state.counter += num;
}
},
//commits the mutation, it's asynchronous
actions: {
// showing passed with payload, represented as asynchNum (an object)
asyncDecrement: ({ commit }, asyncNum) => {
setTimeout(() => {
//the asyncNum objects could also just be static amounts
commit('decrement', asyncNum.by);
}, asyncNum.duration);
}
}
});
上面的代码中可以看到,我们可以在mutations中返回一个数据对象,但这不是一定要返回的,我们只是在需要的时候返回即可。
在组件里面,我们将针对getters使用computed
来获取已经计算处理后的值,在methods
里面,我们使用dispatch
来连接mutations和actions。
app.vue
:
computed: {
value() {
return this.$store.getters.value;
}
},
methods: {
increment() {
this.$store.dispatch('increment', 2)
}
}
当有许多的mutations/actions时,你可以使用ES6当中的「展开运算符」:
export default {
// ...
methods: {
...mapActions([
'increment', // map this.increment() to this.$store.commit('increment')
'decrement',
'asyncIncrement'
])
}
}
一个真正的实例
再次来看一下我们的「天气预报」APP,我们在这个例子当中加入了Vuex store(点此获取源码)。
在线展示:http://codepen.io/sdras/pen/YNpaoJ
store.js
:
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export const store = new Vuex.Store({
state: {
showWeather: false,
template: 0
},
mutations: {
toggle: state => state.showWeather = !state.showWeather,
updateTemplate: (state) => {
state.showWeather = !state.showWeather;
state.template = (state.template + 1) % 4;
}
}
});
我们首先把showWeather
设置为false
,即在初始化的时候不显示天气动画。在mutations中,我们切换了showWeather
的值。
同时,我们将template
初始化为0,我们将使用一个循环的数字(0-3)来代表不同的天气状态,所以在mutations中,我们创建了一个updateTemplate
方法。该方法里即更新showWeather
的值,还更新template
的值(从0到3进行循环)。
App.vue
:
<template>
<div id="app">
...
<g id="phonebutton" @click="updateTemplate" v-if="!showWeather">
...
</g>
<transition
@leave="leaveDroparea"
:css="false">
<g v-if="showWeather">
<app-droparea v-if="template === 1"></app-droparea>
<app-windarea v-else-if="template === 2"></app-windarea>
<app-rainbowarea v-else-if="template === 3"></app-rainbowarea>
<app-tornadoarea v-else></app-tornadoarea>
</g>
</transition>
...
</div>
</template>
<script>
import Dialog from './components/Dialog.vue';
...
export default {
computed: {
showWeather() {
return this.$store.state.showWeather;
},
template() {
return this.$store.state.template;
}
},
methods: {
updateTemplate() {
this.$store.commit('updateTemplate');
}
},
...
components: {
appDialog: Dialog,
...
}
}
</script>
dialog.vue
:
<script>
export default {
computed: {
template() {
return this.$store.state.template;
}
},
methods: {
toggle() {
this.$store.commit('toggle');
}
},
mounted () {
//enter weather
const tl = new TimelineMax();
...
}
}
</script>
这是一个最基本的例子,麻雀虽小,五脏俱全。如果你想更深入的了解Vuex,那么可以查看它的官方文档。
另外,例子中用到了<transition>
组件以及一些动画。关于动画,我们下回再说!