优势与使用场景
- Vuex的状态存储是响应式的,可跟踪每一个状态变化,一旦它改变,所有关联组件都会自动更新相对应的数据。
- 共享数据,解决了非父子组件的消息传递(将数据存放在state中)。
统一状态管理,减少了请求次数,有些情景可以直接从内存中的state获取数据。
什么时候需要用vuex?
当一个组件需要多次派发事件时。例如购物车数量加减。
- 跨组件共享数据、跨页面共享数据。例如订单状态更新。
- 需要持久化的数据。例如登录后用户的信息。
- 当您需要开发中大型应用,适合复杂的多模块多页面的数据交互,考虑如何更好地在组件外部管理状态时。
Vuex与全局变量区别
| vuex | 全局变量 | | —- | —- | | 不能直接改变store里面的变量,由统一的方法修改数据 | 可以任意修改 | | 每个组件可以根据自己vuex的变量名引用不受影响 | 全局变量可能操作命名污染 | | 解决了多组件之间通信的问题 | 跨页面数据共享 | | 适用于多模块、业务关系复杂的中大型项目 | 适用于demo或者小型项目 |
项目结构
使用 Vuex 需要遵守的规则:
- 应用层级的状态应该集中到单个
store对象中。 - 提交
mutation是更改状态的唯一方法,并且这个过程是同步的。 - 异步逻辑都应该封装到
action里面。
只要你遵守以上规则,如何组织代码随你便。如果你的 store 文件太大,只需将 action 、mutation 和 getter 分割到单独的文件。
对于大型应用,我们会希望把 Vuex 相关代码分割到模块中。下面是项目结构示例:
├── pages├── static└── store├── index.js # 我们组装模块并导出 store 的地方├── actions.js # 根级别的 action├── mutations.js # 根级别的 mutation└── modules # 模块文件夹├── cart.js # 购物车模块 模块moduleA└── products.js # 产品模块 模块moduleA├── App.vue├── main.js├── manifest.json├── pages.json└── uni.scss
核心概念
每一个 Vuex 应用的核心就是 store(仓库),它包含着你的应用中大部分的状态 (state)。
状态管理有5个核心:state、getter、mutation、action、module。
State
单一状态树,定义应用状态的默认初始值,页面显示所需的数据从该对象中进行读取。
Vuex使用单一状态树,用一个对象就包含了全部的应用层级状态。它便作为一个“唯一数据源”而存在。这也意味着,每个应用将仅仅包含一个 store 实例。- 单一状态树让我们能够直接地定位任一特定的状态片段,在调试的过程中也能轻易地取得整个当前应用状态的快照。
- 不可直接对
state进行更改,需要通过Mutation方法来更改。
由于 Vuex 的状态存储是响应式的,从 store 实例中读取状态最简单的方法就是在计算属性中返回某个状态:
// 创建一个 Counter 组件const Counter = {computed: {count () {return store.state.count}}}
每当 store.state.count 变化的时候, 都会重新求取计算属性,并且触发更新相关联的 DOM。
然而,这种模式导致组件依赖全局状态单例。在模块化的构建系统中,在每个需要使用 state 的组件中需要频繁地导入,并且在测试组件时需要模拟状态。
使用步骤
Vuex 通过 store 选项,提供了一种机制将状态从根组件“注入”到每一个子组件中(需调用 Vue.use(Vuex)):
- 1.在
uni-app项目根目录下,新建store目录,在此目录下新建index.js文件。在index.js文件配置如下 - .在
main.js中导入文件。

import Vue from 'vue'import Vuex from 'vuex'Vue.use(Vuex); //vue的插件机制//Vuex.Store 构造器选项const store = new Vuex.Store({state: { //存放状态"userName": "Motitol","age": 29,"login":false,"token":"token789455231","avatarUrl":"https://www.baidu.com"},mutations: {login(status, provider) {console.log(state)console.log(provider)state.login = true;state.token = provider.token;state.userName = provider.userName;state.avatarUrl = provider.avatarUrl;},logout(state) {state.login = false;state.token = '';state.userName = '';state.avatarUrl = '';}}})export default store
import Vue from 'vue'import App from './App'import store from './store'Vue.prototype.$store = storeVue.config.productionTip = falseApp.mpType = 'app'// 把 store 对象提供给 “store” 选项,这可以把 store 的实例注入所有的子组件const app = new Vue({store,...App})app.$mount()
获取state
- 1.通过属性访问,需要在根节点注入
store。 - 2.在组件中使用,通过
this.$store访问到state里的数据。 - 3.通过
mapState辅助函数获取。 ```<text>用户名:{{username}}</text> <text>年龄:{{age}}</text>
```<script>import store from '@/store/index.js'; //需要引入storeimport { mapState } from 'vuex'; //引入mapStateexport default {data() {return {temp: '安'};},computed: {// ...mapState({// token: function (state) {// return this.temp + ' ' + state.token// },// avatarUrl: state => state.avatarUrl,// })...mapState({token: state => state.token,avatarUrl: state => state.avatarUrl})}// computed: mapState([// 'token', //映射 this.username 为 store.state.username// 'avatarUrl'// ])// computed: mapState({// // 从state中拿到数据 箭头函数可使代码更简练// username: state => state.username,// age: state => state.age,// })};</script>


Getter
可以认为是
store的计算属性,对state的加工,是派生出来的数据。
- 就像
computed计算属性一样,getter返回的值会根据它的依赖被缓存起来,且只有当它的依赖值发生改变才会被重新计算。- 可以在多组件中共享
getter函数,这样做还可以提高运行效率。
在 uni-app 项目根目录下,store 目录 index.js 文件下:
<!-- 页面路径:store/index.js -->import Vue from 'vue'import Vuex from 'vuex'Vue.use(Vuex);const store = new Vuex.Store({state: {todos: [{id: 1,text: '我是内容一',done: true},{id: 2,text: '我是内容二',done: false}]},getters: {doneTodos: state => {return state.todos.filter(todo => todo.done)}}})export default store
在 store 上注册 getter,getter 方法接受以下参数:
- state, 如果在模块中定义则为模块的局部状态
- getters, 等同于 store.getters ``` import Vue from ‘vue’ import Vuex from ‘vuex’
Vue.use(Vuex);
const store = new Vuex.Store({ state: { todos: [{ id: 1, text: ‘我是内容一’, done: true }, { id: 2, text: ‘我是内容二’, done: false } ] }, getters: { doneTodos: state => { return state.todos.filter(todo => todo.done) }, doneTodosCount: (state, getters) => { //state :可以访问数据 //getters:访问其他函数,等同于 store.getters return getters.doneTodos.length }, getTodoById: (state) => (id) => { return state.todos.find(todo => todo.id === id) } } })
export default store
<a name="Ym402"></a>#### 获取getters1.通过属性访问,Getter 会暴露为 store.getters 对象,你可以以属性的形式访问这些值。
你也可以通过让 `getter` 返回一个函数,来实现给 `getter` 传参。在你对 `store` 里的数组进行查询时非常有用。
注意,`getter` 在通过方法访问时,每次都会去进行调用,而不会缓存结果。 ```
`mapGetters` 辅助函数仅仅是将 `store` 中的 `getter` 映射到局部计算属性: ```
mutations: { add(state, payload) { state.count += payload.amount } }
- 通过 `mapMutations` 辅助函数提交<br />
<a name="action"></a>### [Action](https://uniapp.dcloud.io/vue-vuex?id=action)> `action` 类似于 `mutation` ,不同在于:> - action 提交的是 `mutation`,通过 `mutation` 来改变 `state` ,而不是直接变更状态。> - action 可以包含任意异步操作。<a name="oWZ3U"></a>#### 注册action
import Vue from ‘vue’ import Vuex from ‘vuex’
Vue.use(Vuex);
const store = new Vuex.Store({ state: { count: 1 }, mutations:{ add(state) { // 变更状态 state.count += 2 } }, //actions:{ // addCountAction (context) { // context.commit(‘add’) // } //} actions: { //参数解构 addCountAction ({commit}) { commit(‘add’) } } }) export default store ```
分发Action
actions通过store.dispatch方法触发。actions支持以载荷形式分发。actions支持以对象形式分发。- 通过
mapActions辅助函数分发。 - 组合action。
Module
- 在
store文件夹下新建modules文件夹,并在下面新建moduleA.js和moduleB.js文件用来存放vuex的modules模块。 - 在
main.js文件中引入store。 在项目根目录下,新建 store 文件夹,并在下面新建 index.js 文件,作为模块入口,引入各子模块。子模块 moduleA 页面内容。子模块 moduleB 页面内容。在页面中引用组件 myButton ,并通过 mapState 读取 state 中的初始数据。在组件 myButton中,通过 mutations 操作刷新当前时间。<br />
