美文网首页开眼看世界
Vuex从入门到实战(一)

Vuex从入门到实战(一)

作者: lonelydawn | 来源:发表于2018-02-14 09:38 被阅读0次

    由于多个状态分散的跨越在许多组件和交互间各个角落,大型应用复杂度也经常逐渐增长。为了解决这个问题,Vue 提供 vuex:我们有受到 Elm 启发的状态管理库。vuex 甚至集成到 vue-devtools,无需配置即可访问时光旅行。

    状态管理

    状态的初始化

    状态管理,我们应该并不陌生。

    举个例子,超市里新进了一批商品,管理员给这些商品分类,建立索引,然后按照顺序放入货架的过程就是最简单的状态管理。

    let goods1 = {
      category: 'fruit',
      name: 'apple',
      quantity: 5
    }
    
    let goods2 = {
      category: 'supplies',
      name: 'toothbrush',
      quantity: 5
    }
    
    let goods3 = {
      category: 'clothes',
      name: 'sweater',
      quantity: 5
    }
    

    简单归类后 :

    let shop = {
      goods: {
        fruit: [{ name: 'apple', quantity: 5 }],
        supplies: [{ name: 'toothbrush', quantity: 5 }],
        clothes: [{ name: 'sweater', quantity: 5 }]
      }
    }
    

    这样,当我们需要某一商品时,很容易根据类目检索到这个商品 :

    console.log(shop.goods.fruit.find(f => f.name === 'apple'))
    //-> { name: 'apple', quantity: 5 }
    

    状态的改变

    当有顾客前来购买商品时,我们需要类似的操作来减少被购买商品的数量 :

    shop.goods.fruit.find(f => f.name === 'apple').quantity --
    

    然而在成千上万的交易量背后,你不知道这些商品被购买的详细情况,你甚至不知道上周卖出了多少苹果,你也就无从得知下周该进多少。

    所以你需要一个账目来记录商品购买明细 :

    let account = {
      appleSold (value) {
        console.log("apple sold " + value)
        shop.goods.fruit.find(f => f.name === 'apple').quantity -= value
      }
    }
    

    当卖出苹果时,POS机“滴”一声,记录生成了 :

    account.appleSold (5)
    //-> apple sold 5
    

    最简单的store

    于是,我们得到了一个最简单的store :

    let shop = {
      goods: {
        fruit: [{ name: 'apple', quantity: 5 }],
        supplies: [{ name: 'toothbrush', quantity: 5 }],
        clothes: [{ name: 'sweater', quantity: 5 }]
      },
      account: {
        appleSold (value) {
          console.log("apple sold " + value)
          shop.goods.fruit.find(f => f.name === 'apple').quantity -= value
        },
        funcN () { }
      }
    }
    

    由此可知,状态管理可以帮助我们更友好的改变状态,同时,跟踪状态变化的轨迹。

    Vue(x) er 须知

    开始

    Vuex 官方文档:

    https://vuex.vuejs.org/zh-cn/getting-started.html

    Vuex最核心的概念 :

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

    下面对此拓展:

    对象引用

    下面这两段代码将输出什么? 先不要往下看, 自己写一下

    let store = {
      state: {
        msg: "welcome"
      }
    }, copy = store.state;
    store.state = {
      hello: "world"
    };
    console.log(Object.keys(copy));
    
    let store = {
      state: {
        msg: 'welcome'
      }
    }, copy = store.state;
    store.state.hello = "world";
    console.log(Object.keys(copy))
    

    结果如下(如果你都答对了,那么理解和上手Vuex将会很轻松) :

    //-> ["msg"]
    //-> ["msg", "hello"]
    

    提交和分发

    vuex 只是一个工具,或许过了这段时间,过了这个项目,你就不会再用它。

    我们要记住的是它留给我们的启示:

    不要直接更改状态, 而是通过提交(commit)和分发(dispatch)的方法通知管理者改变对象状态,这是大型项目和复杂状态管理的最佳实践。

    Vuex 核心概念

    一个完整的 Vuex Store

    /**
     * index.js
     */
    import axios from 'axios'
    
    const store = new Vuex.Store({
      state: {
        counter: 0
      },
      getters: {
        counter: state => state.counter
      },
      // 可处理异步请求, dispatch 触发
      actions: {
        askPermission ({commit}) {
          axios.get('/url').then((res) => {
            if(res.data.permission)
              commit('addCounter')
          }).catch((err) => {
            console.log('Error: in process "Ask permission".\n Detailed: ' + err)
          })
        }
      },
      // 同步, commit 触发
      mutations: {
        addCounter (state) {
          state.counter ++
        }
      }
    })
    

    PS: 仔细研究一下 dispatch & actions, commit & mutations, 是否有一种似曾相识的感觉?

    Look, 看这对 emit & on (事件机制),同样的事件类型,同样的回调函数。

    State

    单一状态树

    Vuex使用单一状态树,一个state对象包含全部应用层状态,使得一个应用只有唯一数据源(SSOT, Single Source of Truth)

    这对模块化并不造成影响

    state: {
      moduleA: {
    
      },
      moduleB: {
    
      }
    }
    

    Getter

    state: {
      prop: ''
    }
    

    你可以使用store.state.prop直接读取状态的值, 当然也可以使用Getter :

    getters: {
      prop = state => state.prop
    }
    

    使用Getter的好处在于,你可以从state中派生出一些状态 :

    getters: {
      prop = state => state.prop,
      fixedProp = state => state.prop || '暂无'
    }
    

    Mutation

    Vuex 中的 mutation 类似于事件,有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler),回调函数的接受state作为第一个参数,我们在这里修改状态(state)

    state: {
      counter: 0
    },
    mutations: {
      addCounter (state) {
        state.counter ++
      },
      addCounter (state, payload) {
        state.counter += payload.value
      }
    }
    

    通过 commit 通知状态变化

    store.commit('addCounter')
    store.commit('addCounter', {value: 1})
    

    Action

    类似于mutation,不同在于

    • 只能通过 commit mutation 通知状态变化
    • mutation 只能包含同步操作,而 action 可以包含异步操作(比如, 在这里可以执行ajax请求)
    actions: {
      askPermission ({commit}) {
        axios.get('/url').then((res) => {
          if(res.data.permission)
            commit('addCounter')
        }).catch((err) => {
          console.log('Error: in process "Ask permission".\n Detailed: ' + err)
        })
      },
      askPermission ({commit}, payload) {
        axios.get('/url', { params:payload }).then((res) => {
          if(res.data.permission)
            commit('addCounter')
        }).catch((err) => {
          console.log('Error: in process "Ask permission".\n Detailed: ' + err)
        })
      }
    }
    

    通过 dispatch 通知状态变化

    store.dispatch('askPermission')
    store.dispatch('askPermission', { author: "lonelydawn" })
    

    Module

    Vuex 允许我们将store分割成模块,每个模块拥有自己的state, mutation, action, getter, 甚至是嵌套子模块 :

    const store = new Vuex.Store({
      modules: {
        a: {
          state: {},
          mutations: {
            addCounter(state) {}
          },
          actions: {},
          getters: {}
        },
        b: {
          namespaced: true, // 建立命名空间
          state: {},
          mutations: {
            addCounter(state) {}
          },
          actions: {}
        }
      }
    })
    
    store.state.a
    store.state.b
    
    //  提交 给 模块 a 的 mutations
    store.commit('addCounter')
    //  提交 给 模块 b 的 mutations
    store.commit('b/addCounter')
    

    最后

    Vuex 的基本用法已经介绍完了。

    相关内容 :

    官方文档: https://vuex.vuejs.org/zh-cn/

    官方实例: https://github.com/vuejs/vuex/tree/dev/examples

    在下列内容中, 我将 演示如何使用 vue + vuex 以及其他常用组件从入门到实战。

    Vuex从入门到实战(二)

    Vuex从入门到实战(三)

    相关文章

      网友评论

        本文标题:Vuex从入门到实战(一)

        本文链接:https://www.haomeiwen.com/subject/rcxetftx.html