vue学习笔记-----vuex

1.什么是vuex?

官方给出的回答是

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式
每一个 Vuex 应用的核心是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。

可以理解为vuex主要进行状态管理,而且是共享状态管理,类似于全局对象的管理,但又不完全是。Vuex 和单纯的全局对象有以下两点不同:

  1. Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。

  2. 不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。

接下来可以通过一些简单的例子去学习vuex。

2.安装vuex

在已有的vue项目中通过以下命令完成安装:

npm install vuex --save

其他方式可参考官方安装方式
如果没有创建项目,通过以下命令创建:

vue init webpack vuex-demo

3.简单例子

将main.js 文件中的代码改为如下

import Vue from 'vue';
import Vuex from 'vuex';  // add code here
import App from './App';
import router from './router';

Vue.use(Vuex);  // add code here
Vue.config.productionTip = false;

// add code here(*first step)
const store = new Vuex.Store({
  state: {
    count: 0,
  },
  mutations: {
    increments(state) {
      // eslint-disable-next-line no-plusplus
      state.count++;
    },
  },
});

// add code here(*second and third step)
store.commit('increments');  
// eslint-disable-next-line no-console
console.log(store.state.count); // -> 1

/* eslint-disable no-new */
new Vue({
  el: '#app',
  store,  // add code here(*forth step)
  router,
  components: { App },
  template: '<App/>',
});

上述代码

  1. 通过 new Vuex.Store() 创建一个 store。
  2. 然后通过 store.commit 方法触发状态(state)改变。
  3. 最后通过 store.state 来获取状态对象。
  4. 为 Vue 实例提供创建好的 store。

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

由于 store 中的状态是响应式的,在组件中调用 store 中的状态简单到仅需要在计算属性中返回即可。触发变化也仅仅是在组件的
methods 中提交 mutation。

这是一个最基本的 Vuex 记数应用 (opens new window)示例。
但是可以发现,这个例子里面vuex和子组件没有任何的交互。
接下来看看vuex的容器store在子组件中如何使用。

1.State的使用

到这一步,我们已经把store挂载在根组件上了,子组件可以通过 t h i s . this. this.$ s t o r e store store去访问store对象。
也就是说在子组件中,我们可以通过 this.$store.state 去访问state。通常会搭配子组件的computed属性(methods也可以)去使用,子组件HelloWorld代码如下。

<template>
  <div>
    <h1>{{ count }}</h1>
    <button @click='addCount'>add count</button>
    <h1>{{ getCount() }}</h1>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  computed: {
    count() {
      return this.$store.state.count;  // 返回count
    },
  },
  data() {
    return {
      localCount: 2,
    };
  },
  methods: {
    addCount() {
      this.$store.commit('increments');  // 通过commit改变count
    },
    getCount() {
      return this.$store.state.count;
    },
  },
};
</script>

当在computed中需要使用state中多个属性时候,可以使用辅助函数mapState()。

mapState会返回一个对象。
mapState函数中可以直接使用state,
相对于写this.$store.state代码简洁一些,
代码如下。

<template>
  <div>
    <h1>{{ count }}</h1>
    <h1>{{ countAlias }}</h1>
    <h1>{{ countPlusLocalState }}</h1>
    <h1>{{ localComputed }}</h1>
    <button @click='addCount'>add count</button>
    <h1>{{ getCount() }}</h1>
  </div>
</template>

<script>
import { mapState } from 'vuex';  // 引入mapState

export default {
  name: 'HelloWorld',
  computed: {
    localComputed() {
      return this.localCount;
    },
    // 使用辅助函数mapState
    ...mapState({
      // 箭头函数可使代码更简练
      count: state => state.count,

      // 传字符串参数 'count' 等同于 `state => state.count`
      countAlias: 'count',

      // 为了能够使用 `this` 获取局部状态,必须使用常规函数
      countPlusLocalState(state) {
        return state.count + this.localCount;
      },
    }),
  },
  data() {
    return {
      localCount: 2,
    };
  },
  methods: {
    addCount() {
      this.$store.commit('increments');
    },
    getCount() {
      return this.$store.state.count;
    },
  },
};
</script>

2.Getters的使用

首先现在main.js文件中向state添加一个todos属性,代码如下。

const store = new Vuex.Store({
  state: {
    count: 0,
    // 添加todos属性
    todos: [
      {
        id: 1,
        text: '...',
        done: true,
      },
      {
        id: 2,
        text: '...',
        done: false,
      },
    ],
  },
  mutations: {
    increments(state) {
      // eslint-disable-next-line no-plusplus
      state.count++;
    },
  },
});

考虑以下代码的doneTodosCount方法不只在一个子组件中使用,而是很多个子组件中都需要获取已完成todo的数目。

computed:{
  doneTodosCount() {
    return this.$store.state.todos.filter(todo=>todo.done).length;
  }
}

这时应该怎么办呢?可以在每个子组件中的computed都重复写一遍该方法,但这不是最佳的方法,因为这么做会使得结构耦合度高。

vuex在store中提供getters,可向外暴露,
通过this.$store.getters访问getters。
代码如下。
在main.js中向store添加getters。

const store = new Vuex.Store({
  state: {
    count: 0,
    todos: [
      {
        id: 1,
        text: '...',
        done: true,
      },
      {
        id: 2,
        text: '...',
        done: false,
      },
    ],
  },
  // add getters
  getters: {
    doneTodosCount(state) {
      return state.todos.filter(todo => todo.done).length;
    },
  },
  mutations: {
    increments(state) {
      // eslint-disable-next-line no-plusplus
      state.count++;
    },
  },
});

HelloWorld子组件中引用该方法。

<template>
  <div>
    <h1>{{ count }}</h1>
    <h1>{{ countAlias }}</h1>
    <h1>{{ countPlusLocalState }}</h1>
    <h1>{{ localComputed }}</h1>
    <button @click='addCount'>add count</button>
    <h1>{{ getCount() }}</h1>
    <!-- add code here -->
    <h1>{{ this.$store.getters.doneTodosCount }}</h1>
  </div>
</template>

类似于State的mapState,Getters也有辅助函数mapGetters()。
用法如下。

<template>
  <div>
    <h1>{{ count }}</h1>
    <h1>{{ countAlias }}</h1>
    <h1>{{ countPlusLocalState }}</h1>
    <h1>{{ localComputed }}</h1>
    <button @click='addCount'>add count</button>
    <h1>{{ getCount() }}</h1>
    <!--修改方法名称 -->
    <h1>{{ doneCount }}</h1> 
  </div>
</template>

<script>
// 引入mapGetters
import { mapState, mapGetters } from 'vuex';

export default {
  name: 'HelloWorld',
  computed: {
    localComputed() {
      return this.localCount;
    },
    ...mapGetters({
      // 重新命名函数
      doneCount: 'doneTodosCount',
    }),
    ...mapState({
      // 箭头函数可使代码更简练
      count: state => state.count,

      // 传字符串参数 'count' 等同于 `state => state.count`
      countAlias: 'count',

      // 为了能够使用 `this` 获取局部状态,必须使用常规函数
      countPlusLocalState(state) {
        return state.count + this.localCount;
      },
    }),
  },
  data() {
    return {
      localCount: 2,
    };
  },
  methods: {
    addCount() {
      this.$store.commit('increments');
    },
    getCount() {
      return this.$store.state.count;
    },
  },
};
</script>

3.Mutations的使用

更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。
Vuex 中的 mutation 非常类似于事件:
每个mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数(handler)。
这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数:

也就是说,当我们想要改变State中属性的值时候,

const store = new Vuex.Store({
  state: {
    count: 1
  },
  mutations: {
    increment (state) {
      // 变更状态
      state.count++
    }
  }
})

需要通过一下代码完成。

this.$store.commit('increment')

store.commit 可以传入额外的参数:

// ...
mutations: {
  increment (state, n) {
    state.count += n
  }
}
store.commit('increment', 10)

在大多数情况下,载荷应该是一个对象,这样可以包含多个字段并且记录的 mutation 会更易读。
需要注意的是,Mutation 必须是同步函数

辅助函数mapMutations()
可以在组件中使用 this.$store.commit(‘xxx’) 提交 mutation,或者使用 mapMutations 辅助函数将组件中的 methods 映射为 store.commit 调用(需要在根节点注入 store)。
代码如下。

// 引入mapMutations
import { mapState, mapGetters, mapMutations } from 'vuex';

export default {
  name: 'HelloWorld',
  computed: {
    localComputed() {
      return this.localCount;
    },
    ...mapGetters({
      // 重新命名函数
      doneCount: 'doneTodosCount',
    }),
    ...mapState({
      // 箭头函数可使代码更简练
      count: state => state.count,

      // 传字符串参数 'count' 等同于 `state => state.count`
      countAlias: 'count',

      // 为了能够使用 `this` 获取局部状态,必须使用常规函数
      countPlusLocalState(state) {
        return state.count + this.localCount;
      },
    }),
  },
  data() {
    return {
      localCount: 2,
    };
  },
  methods: {
    // 使用mapMutations,将this.$store.commit('increments')映射到addCount方法上
    ...mapMutations({
      addCount: 'increments',
    }),
    getCount() {
      return this.$store.state.count;
    },
  },
};

4.Actions的使用

Actions与Mutations的使用基本是一样的,除了一下几点:

  1. mutaions只能进行同步操作,而actions同步异步操作都可以,主要用于处理异步操作。
  2. mutaions通过store.commit()触发,actions通过store.dispatch()触发。

5.Modules的使用

当store 对象变得复杂时,Vuex 允许将 store 分割成模块(module)
每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:

const moduleA = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: () => ({ ... }),
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态

默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的——这样使得多个模块能够对同一mutation 或 action 作出响应。

如果希望你的模块具有更高的封装度和复用性,你可以通过添加 namespaced: true
的方式使其成为带命名空间的模块。
当模块被注册后,它的所有 getter、action 及 mutation都会自动根据模块注册的路径调整命名。例如:

const store = new Vuex.Store({
  modules: {
    account: {
      namespaced: true,

      // 模块内容(module assets)
      state: () => ({ ... }), // 模块内的状态已经是嵌套的了,使用 `namespaced` 属性不会对其产生影响
      getters: {
        isAdmin () { ... } // -> getters['account/isAdmin']
      },
      actions: {
        login () { ... } // -> dispatch('account/login')
      },
      mutations: {
        login () { ... } // -> commit('account/login')
      },

      // 嵌套模块
      modules: {
        // 继承父模块的命名空间
        myPage: {
          state: () => ({ ... }),
          getters: {
            profile () { ... } // -> getters['account/profile']
          }
        },

        // 进一步嵌套命名空间
        posts: {
          namespaced: true,

          state: () => ({ ... }),
          getters: {
            popular () { ... } // -> getters['account/posts/popular']
          }
        }
      }
    }
  }
})

启用了命名空间的 getter 和 action 会收到局部化的 getter,dispatch 和 commit。换言之,你在使用模块内容(module assets)时不需要在同一模块内额外添加空间名前缀。更改 namespaced 属性后不需要修改模块内的代码。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值