Vuex入门

什么是Vuex?http://www.jianshu.com/p/490e7726fe67


Vuex 是一个专门为 Vue.js 应用所设计的集中式状态管理架构,它借鉴了 Flux 和 Redux的设计思想,但简化了概念,并且采用了一种为能更好发挥 Vue.js 数据响应机制而专门设计的实现。

我为什么需要它?


当你的应用还很简单的时候,你多半并不需要 Vuex。也不建议过早的使用 Vuex。但如果你正在构建一个中型以上规模的 SPA,你很有可能已经需要思考应该如何更好的归纳 Vue 之外,应用的其他组成部分。这就是 Vuex 要大显身手的时刻。

我们在单独使用 Vue.js 的时候,通常会把状态储存在组件的内部。也就是说,每一个组件都拥有应用状态的一部分,整个应用的状态是分散在各个角落的。然而,我们经常会需要把状态的一部分共享给多个组件。一个常见的解决策略为: 使用定制的事件系统,让一个组件把一些状态“发送”到其他组件中。这种模式的问题在于,大型组件树中的事件流会很快变得非常繁杂,并且调用时很难去找出究竟哪里出错。

为了更好的解决在大型应用中状态的共用问题,我们需要对组件的 组件本地状态(component local state) 和 应用层级状态(application level state) 进行区分。应用级的状态不属于任何特定的组件,但每一个组件仍然可以监视(Observe)其变化从而响应式地更新 DOM。通过汇总应用的状态管理于一处,我们就不必到处传递事件。因为任何牵扯到一个以上组件的逻辑,都应该写在这里。此外,这样做也能让我们更容易地记录并观察状态的变更(Mutation,原意为突变),甚至可以实现出华丽如时光旅行一般的调试效果。(译注:是时候安利一波 vue-devtools 了)

Vuex 也对如何管理分撒各地的状态增加了一些约束,但仍保留有足够面对真实使用场景的灵活性。

准备开始


每一个 Vuex 应用的核心就是 store(仓库)。"store"基本上就是一个容器,它包含着你应用里大部分的 状态(即 state).Vuex 和单纯的全局对象有以下两点不同:

  1. Vuex 的状态存储是响应式的,当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到更新。
  2. 你不能直接改变 store 中的状态,改变 store 中的状态的唯一途径就是显示地分发 状态变更事件(explicitly dispatching mutations)。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。

最简单的store

注意:我们将会在后续的文档中用 ES2015 语法进行案例展示。如果你还没能掌握 ES2015,你得抓紧了!本文同样假设你已经了解了 Vue 本体的官方文档中构建大型应用章节所涉及的概念.

创建 Vuex store 的过程相当直截了当 - 只要提供一个初始化的 state 对象,以及一些 mutations:

 
  1. import Vuex from 'vuex'

  2.  
  3. const state = {

  4. count: 0

  5. }

  6.  
  7. const mutations = {

  8. increment (state, amount) {

  9. state.count += amount

  10. }

  11. }

  12.  
  13. export default new Vuex.Store({

  14. state,

  15. mutations

  16. })

现在,你可以通过 store.state 来读取 state 对象,还可以通过 store.commit 某 mutation 的名字来触发这些状态变更:

 
  1. store.commit('increment')

  2. console.log(store.state.count) // -> 1

再次强调,我们通过分发 mutation 的方式,而非直接改变 store.state.count,是因为我们想要更明确地追踪到状态的变化。这个简单的约定能够让你的意图更加明显,这样你在阅读代码的时候能更容易地解读应用内部的状态改变。此外,这样也让我们有机会去实现一些能记录每次状态改变,保存状态快照的调试工具。有了它,我们甚至可以实现如时间穿梭般的调试体验。

以上只是一个用来展示 store 究竟是什么的一个极简例子。但是 Vuex 可不仅仅是状态存储。接下来,我们将会更深入地探讨一些核心概念:State(状态)Mutations(变更)和 Actions(动作)

简单教程


让我们通过一个简单的实际例子来理解怎样使用 Vuex。这个例子里,我们要实现一个按钮,每点击它一次,计数器加一。

image

我们会通过这个例子解释相应的概念,以及 Vuex 所要解决的问题:如何管理一个包含许多组件的大型应用。我们假定这个例子使用以下三个组件:

components/index.vue
index 组件,它包含了两个另外的子组件:

  • Display 显示当前计数器的值
  • Increment 使计数器加一的按钮
 
  1. <template>

  2. <div>

  3. <Display></Display>

  4. <Increment></Increment>

  5. </div>

  6. </template>

  7.  
  8. <script>

  9. import Display from './display'

  10. import Increment from './increment'

  11.  
  12. export default {

  13. components: {

  14. Display: Display,

  15. Increment: Increment

  16. }

  17. }

  18. </script>

  19.  
  20. <!-- Add "scoped" attribute to limit CSS to this component only -->

  21. <style scoped>

  22. h1, h2 {

  23. font-weight: normal;

  24. }

  25.  
  26. ul {

  27. list-style-type: none;

  28. padding: 0;

  29. }

  30.  
  31. li {

  32. display: inline-block;

  33. margin: 0 10px;

  34. }

  35.  
  36. a {

  37. color: #42b983;

  38. }

  39. </style>

components/display.vue

 
  1. <template>

  2. <div>

  3. <h4>Count is 0</h4>

  4. </div>

  5. </template>

  6.  
  7. <script>

  8. export default {}

  9. </script>

components/increment.vue

 
  1. <template>

  2. <div>

  3. <button>Increment +1</button>

  4. </div>

  5. </template>

  6.  
  7. <script>

  8. export default {

  9.  
  10. }

  11. </script>

在没有 Vuex 的日子里

  • Increment 与 display 彼此无法感知到彼此的存在,也无法相互传递消息。是怎样的孤独。
  • App 将必须通过事件(events) 与广播(broadcasts)与其他两个组件进行协调。
  • 而 App 作为两者之间的协调者,导致这些组件并没法被服用,被迫紧密耦合。调整应用的结构,则可能导致应用崩溃。

Vuex 的流程

我们需要依次执行这些步骤:

image


仅仅为了增加计数而采取这么多步骤显然很多余。但请注意,这些概念的引入是为了构建大型应用,提高可维护性,降低调试与长期维护的难度而设计的。(译注:换言之,这是屠龙刀,拿来杀鸡只是为了让我们好懂)好,那么接下来我们就用 vuex 来进行重构吧!

第一步:加入 store

store 存储应用所需的数据。所有组件都从 store 中读取数据。在我们开始之前,先用 npm 安装 vuex

$ npm install --save-dev vuex

建议一个新文件 vue/store.js

 
  1. import Vue from 'vue'

  2. import Vuex from 'vuex'

  3.  
  4. // 使用 vuex

  5. Vue.use(Vuex)

  6.  
  7. // 创建一个对象来保存应用启动时的初始状态

  8. const state = {

  9. // TODO 放置初始状态

  10. }

  11.  
  12. // 创建一个对象存储一系列我们接下来要写的 mutation 函数

  13. const mutations = {

  14. // TODO 放置我们的状态变更函数

  15. }

  16.  
  17. // 整合初始状态和变更函数,我们就得到了我们所需的 store

  18. // 至此,这个 store 就可以链接到我们的应用中

  19. export default new Vuex.Store({

  20. state, mutations

  21. })

我们需要修改 index 组件来让应用注意到 store 的存在位置。

修改 components/index.vue,注入 store。

 
  1. import Display from './Display.vue'

  2. import Increment from './IncrementButton.vue'

  3. import store from '../vuex/store' // import 我们刚刚创建的 store

  4.  
  5. export default {

  6. components: {

  7. Display: Display,

  8. Increment: Increment

  9. },

  10. store: store // 在根组件加入 store,让它的子组件和 store 连接

  11. }

提示: 如果使用 ES6 和 babel 你可以这样写:

 
  1. components: {

  2. Display,

  3. Increment,

  4. },

  5. store

第二步:创建 action

action 是一种专门用来被 component 调用的函数。action 函数能够通过分发相应的 mutation 函数,来触发对 store 的更新。action 也可以先从 HTTP 后端或 store 中读取其他数据之后再分发更新事件。

创建一个新文件 vuex/actions.js,然后写入一个函数 incrementCounter:

 
  1. // 既然我们只对事件的分发(dispatch 对象)感兴趣。(state 也可以作为可选项放入)

  2. // 我们可以利用 ES6 的解构(destructuring)功能来简化对参数的导入

  3. // action 会收到 store 作为它的第一个参数

  4. export const increment = function ({ commit, state }) {

  5. commit('increment', 1) //

  6. }

然后我们从 component/increment.vue 组件里调用 action 函数

 
  1. <template>

  2. <div>

  3. <button @click='increment'>Increment +1</button>

  4. </div>

  5. </template>

  6.  
  7. <script>

  8. import { mapActions } from 'vuex'

  9.  
  10. export default {

  11. methods: {

  12. ...mapActions([

  13. 'increment'

  14. ])

  15. }

  16. }

  17. </script>

回顾一下我们刚刚添加的内容背后所潜藏的一些有趣的点:

  1. 我们有了一个新对象 vuex.actions,包含着新的 action;
  2. 我们没有指定特定的 store,object,state 等等。Vuex 会自动把它们串联起来;
  3. 我们可以用 this.increment() 在任何方法中调用此 action;
  4. 我们也可以通过 @click 参数调用它,与使用其他普通的 Vue 组件方法并无二致;
  5. 我们给 action 起名叫 incrementCounter,但是在具体使用时,我们可以根据需要进行重新命名。

第三步:创建 state 和 mutation

在我们 vuex/actions.js 文件里我们 dispatch 了一个叫做 INCREMENT 的 mutation,但是我们还没有写它所对应的具体操作。我们现在就来做这个事情。

修改 vuex/store.js

 
  1. const state = {

  2. // 应用启动时, count置为0

  3. count: 0

  4. }

  5.  
  6. const mutation = {

  7. // mutation的第一个参数是当前的state

  8. // 你可以在函数里修改 state

  9. increment (state, amount) {

  10. state.count = state.count + amount

  11. }

  12.  
  13. }

第四步:在组件获取值

创建一个新的文件 vuex/getters.js

 
  1. // 这个 getter 函数会返回 count 的值

  2. // 在 ES6 里你可以这样写

  3. // export const getCount = state => state.count

  4. export function getCount (state) {

  5. return state.count

  6. }

这个函数返回了 state 对象里我们所需要的部分--count 的值。我们现在在组件里加入这个 getter 函数。

修改 component/display.vue:

 
  1. <template>

  2. <div>

  3. <h4>Count is {{ getCount }}</h4>

  4. </div>

  5. </template>

  6.  
  7. <script>

  8. import { mapGetters } from 'vuex'

  9.  
  10. export default {

  11. computed: {

  12. ...mapGetters([

  13. 'getCount'

  14. ])

  15. }

  16. }

  17. </script>

这里我们又加入了一个新的对象 vuex.getters。它将 counterValue 绑定到了 getCount 这个 getter 函数上。我们给它起了一个新名字来使得这个变量在你的组件里表意更明确。

你可能有点困惑——为什么我们需要用 getter 函数而不是直接从 state 里读取数据。这个概念更多的是一种最佳实践,在大型应用里更加适用。它有这么几种独特优势:

  1. 我们可能需要使用 getter 函数返回需经过计算的值(比如总数,平均值等);
  2. 在大型应用里,很多组件之间可以服用同一个 getter 函数;
  3. 如果这个值的位置改变了(比如从 store.count 变成了 store.counter.value),你只需要改一个 getter 方法,而不是一堆组件。

以上便是使用 getter 带来的好处。

第五步:接下来……

运行一下你的应用,它应该能正常工作了。

要更深入地理解 Vuex,你可以尝试以下挑战,对该应用进行少许修改,权当练习,嗯~

  • 加一个“减一”的按钮。
  • 安装 VueJS Devtools,尝试使用它提供的 Vuex 工具来观察 mutation 是如何生效的。
  • 添加一个名为 IncrementAmount 的文本框组件,让用户可以输入要增加的数值。这个可能会稍有难度,因为表单在 vuex 中与原生的表现稍有不同。可以读一下表单处理章节了解更多内容。

来源:https://blog.csdn.net/sinat_17775997/article/details/54999357

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值