Vuex从入门到精通(一)

时间:2022-04-27
本文章向大家介绍Vuex从入门到精通(一),主要内容包括状态管理、Vue(x) er 须知、Vuex 核心概念、最后、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。

状态管理

状态的初始化

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

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

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 以及其他常用组件从入门到实战。