概述
vuex https://vuex.vuejs.org/zh/ 是一个专为 vue.js 应用程序开发的状态管理模式. 也就是说 Vuex 用于单页面应用组件之间的数据共享, 在组件嵌套很多层的情况下, Vue 中父子组件的通信过程就变得很麻烦, 此时使用 Vuex 方便了组件间的通信.
vuex 官网上说是一个 vue 的状态管理工具. 其实我们可以简单地把状态理解成为 vue 的 data 里面的变量. 当组件之间的 data 变量关系复杂一点的时候, 就把其中的变量抽离出来管理.
Vuex 提供了一个数据仓库, 存放着各种数据 data. 谁要用谁去请求 num 的值, 谁想改就改该多好是吧, vuex 就是干这个的, 有点全局变量的意思. 任何组件需要拿, 改东西, 都可以找他.
Vuex 主要涉及到 state,getters,mutations,actions.
state : 是驱动应用的数据源, 是惟一的数据载体, 跟仓库一样.
mutations : 更改 state 的唯一方法, 意思是修改, 增加等处理 state 的方法,
getters : 从 state 中派生出的一些状态, 如获取数据的数组的长度, 方便其他组件获取使用. 简单来说, 就是过滤, 计算, 组合!
actions : 用来提交 mutations, 通过 commit 再去触发对应的 mutations, 而不是直接变更 state 状态.
稍微简单点的 vuex 管理就使用 state 和 mutations 这两个就行. 复杂的 vuex 管理还会涉及到 modules 等辅助方法.
应用实例: Todo-list
本文旨在通过一个简单的 todo list 例子, 熟悉 vuex 常见的方法, 了解组件间数据共享机制.
本文示例中, 分为父组件 Main.vue, 两个子组件 List.vue 和 Add.vue, 父组件包含两个子组件, 实时显示列表长度, 子组件 Add.vue 负责添加列表项, List.vue 负责显示列表, 以及删除列表项. 在子组件添加和删除列表项时, 相应的组件会联动: 父组件会实时计算列表长度, List 组件会增减列表项. 大家可以先观看效果: Demo.
1. 准备
本实例采用 Vue2 https://cn.vuejs.org/v2/guide/ + Vue Router https://router.vuejs.org/zh/ + Vuex https://vuex.vuejs.org/zh/ + vue-ydui http://vue.ydui.org/docs/#/quickstart 实现. 我们事先已经建立了 Vue webpack 模板, 并安装相关组件.
- NPM install --save vuex
- NPM install --save vue-router
- NPM install --save vue-ydui
2. 建立数据仓库
在 src 目录下建立文件夹 store/, 并在 store / 目录下新建 index.JS 文件.
- import Vue from 'vue'
- import Vuex from "vuex"
- Vue.use(Vuex)
- const store = new Vuex.Store({
- state: {
- message: '',
- todoList: [{id: 0, value: 'default'}]
- },
- getters: {
- // 计算 list 长度
- listCount(state) {
- return state.todoList.length;
- }
- },
- mutations: {
- // 新增
- addTodo(state, item) {
- state.todoList.push(item);
- },
- // 删除
- delTodo(state, index) {
- state.todoList.splice(index, 1);
- },
- // 设置错误提示信息
- showError(state, msg) {
- state.message = msg;
- }
- },
- actions: {
- // 提交 addTodo
- addTodo(context, item) {
- if (item.value == '') {
- context.commit('showError', '请输入内容');
- } else {
- context.commit('addTodo', item);
- context.commit('showError', '');
- }
- },
- // 提交 delTodo
- delTodo({commit}, index) {
- commit('delTodo', index);
- }
- },
- modules: {}
- });
- export default store;
如上代码和注释, 我们在 store/index.JS 中设置了数据以及修改这些数据的方法.
3. main.JS
在 main.JS 中将 store.JS 加进来. 示例中我们还用到了 UI 库: vue-ydui, 也一起加进来.
- import Vue from 'vue'
- import App from './App'
- import router from './router'
- import store from './store'
- import YDUI from 'vue-ydui';
- import 'vue-ydui/dist/ydui.rem.CSS';
- Vue.use(YDUI);
- Vue.config.productionTip = false
- new Vue({
- el: '#app',
- router,
- store,
- components: { App },
- template: '<app />'
- })
4. 设置路由
在 router/index.JS 加入路由设置, 让页面直接访问父组件 Main.vue.
- import Vue from 'vue'
- import Router from 'vue-router'
- import Main from '@/components/Main'
- Vue.use(Router)
- export default new Router({
- routes: [
- {
- path: '/',
- name: 'Main',
- component: Main
- }
- ]
- })
5. Main.vue
现在我们来看父组件:
- <template>
- <yd-layout>
- <p > 这是一个 Todo-List 示例 </p>
- <hw-add></hw-add>
- <hw-list></hw-list>
- <p>todoList 总数:{{listCount}}</p>
- <p>{{msg}}</p>
- </yd-layout>
- </template>
- <script>
- import hwAdd from "./Add.vue";
- import hwList from "./List.vue";
- export default {
- components: {
- hwAdd,
- hwList
- },
- data(){
- return {
- }
- },
- computed: {
- listCount() {
- return this.$store.getters.listCount;
- },
- msg() {
- let message = this.$store.state.message;
- if (message !== '') {
- this.$dialog.toast({
- mes: message,
- timeout: 1500
- });
- }
- }
- }
- }
- </script>
Main.vue 用来展示列表和添加列表项等子组件, 已经显示列表长度和错误信息提示. 我们看到在 computed 中获取到数据仓库中的数据, 并显示在页面上.
6. Add.vue
Add.vue 用来添加列表项.
<template> <yd-layout> <yd-cell-group> <yd-cell-item> <yd-icon slot="icon" name="discount" size=".35rem"></yd-icon> <input type="text" slot="right" placeholder="请输入内容" v-model="value" @keyup.enter="addItem"> <yd-button slot="right" type="warning" @click.native="addItem"> 新增 </yd-button> </yd-cell-item> </yd-cell-group> </yd-layout> </template> <script> export default { data() { return { value: '', id: 0 } }, methods: { addItem() { let item = { value: this.value, id: ++this.id } this.value = ''; this.$store.dispatch('addTodo', item); } } } </script>
在添加列表项的 addItem 方法中, 我们使用 this.$store.dispatch('addTodo', item); 告诉 vuex 的 addTodo , 我们要往 todo list 中添加新的列表项.
7. List.vue
List.vue 用来展示列表项, 并且提供删除列表项功能.
<template> <yd-layout> <yd-cell-group title="Todo 列表"> <yd-cell-item v-for="(item,index) in todoList" :key="index" class="list"> <span slot="left">{{index+1}}.{{item.value}}</span> <span slot="right"> <yd-icon slot="icon" name="delete" size=".36rem" @click.native="del(index)"></yd-icon> </span> </yd-cell-item> </yd-cell-group> </yd-layout> </template> <script> import {mapState} from 'vuex'; export default { data() { return { } }, computed: { ...mapState([ 'todoList' ]) }, methods: { del(index) { this.$store.dispatch('delTodo', index); } } } </script>
上述代码中的
computed: { ...mapState([ 'todoList' ]) },
其实相当于:
computed: { todoList() { return this.$store.state.todoList; } },
小结
通过示例我们可以知道, 使用 Vuex 来管理数据共享, 各组件无需关注组件间的数据通信传输, 一切数据的读取和更新都是各组件与 Vuex 数据仓库间的操作, 避免了复杂项目中数据管理混乱的情况发生.
Vuex 的使用还有很多优化的写法, 比如 mapState,mapGetters,mapActions , 本站后面会有文章讲解.
当然, 如果是小型的项目, 我们直接用 $emit, props 等就能解决组件间的数据通信问题, 不必使用 vuex 这个伤脑筋的工具.
注意: Vuex 是将数据存储在了内存中, 每一次刷新页面, 之前存在 Vuex 中的数据就会重新初始化.
来源: http://www.tuicool.com/articles/aaIzqyF