目录
一. Vuex 概述
1. 除了 Vuex 之外,组件之间共享数据的方式
- 父向子传值:v-bind 属性绑定
- 子向父传值:v-on 事件绑定
- 兄弟组件之间共享数据: EventBus
- $on 接收数据的那个组件
- $emit 发送数据的那个组件
2. Vuex 概念及作用
- Vuex 概念:
- Vuex 是实现组件全局状态(数据)管理的一种机制,实现组件数据共享
Vuex 作用:
- 集中管理共享的数据,易于开发和后期维护
- 高效地实现组件之间的数据共享,提高开发效率
- 存储在 Vuex 中的数据都是 响应式的,实时保持数据与页面的同步
- 注意:只有组件共享的数据,才存储到 Vuex 中;组件的私有数据,依旧存储在组件 自身的 data 中
二. Vuex 安装及使用
- 安装 Vuex:npm install vuex --save
使用 Vuex:导入 Vuex依赖,创建 Store对象,将 store 对象挂载到 vue 实例中import Vuex from 'vuex' Vue.use(Vuex) // 创建 store 对象 const store = new Vuex.Store({ state: { count: 0 } // state 存放全局共享的数据 }) new Vue({ el: '#app', render: h => h(app), router, store // 将 store 对象挂载到 vue 实例中 })
三. Vuex 核心部分
- Vuex 中的核心概念如下:State Mutation Action Getter
1. State(存放组件公共数据)
- State 用于存放公共数据
访问 State 中数据的两种方式:
- this.$store.state.全局数据名称
- 从 Vuex 中,按需导入 mapState 函数,并在 computed 中使用
// 按需导入 mapState 函数 import { mapState } from 'vuex' // 将全局数据,映射为当前组件的计算属性 computed: { ...mapState(['count']) }
2. Mutation(触发同步任务,修改 State)
- Mutation 用于 触发同步任务,修改 State 数据
- 注意:
- 只能通过 Mutation 变更 Store 数据,不可以直接操作 Store 中的数据
- Mutation 处理同步任务,异步任务只能通过 Action 触发
触发 Mutation 的两种方式:
- this.$store.commit()
- 从 Vuex 中,按需导入 mapMutations 函数,并在 methods 中使用
// 在 store 中,定义 mutation const store = new Vuex.Store({ state: { count: 0 }, mutations: { // 变更 state 状态 add(state) { state.count++ }, // 传递参数的 变更 state 状态 addN(state, step) { state.count += step }, }, }) // 组件 methods 内,触发 mutation methods: { handle1() { // 不带参数,触发 mutation this.$store.commit('add') // 带参数,触发 mutation this.$store.commit('addN', 3) } }
从 Vuex 中按需导入 mapMutations 函数 示例如下:// 按需导入 mapMutations 函数 import { mapMutations } from 'vuex' // 将指定的 mutations 函数,映射为当前组件的 methods 函数 methods: { ...mapMutations(['add', 'addN']) }
3. Action(触发异步任务,不能修改 State)
- Action 用于 触发异步任务,不能修改 State 数据
- 注意:
- 只能通过 Mutation 变更 Store 数据,Action 不能变更 store 数据,但是可以在 Action中,触发 Mutation,间接变更数据
- 如果通过异步操作变更数据,必须通过 Action,不能使用 Mutation
触发 Action 的两种方式:
- this.$store.dispatch()
- 从 Vuex 中,按需导入 mapActions 函数,并在 methods 中使用
const store = new Vuex.Store({ // 同步任务 mutations: { }, // 异步任务 actions: { // 不带参数 addAsync(context) { setTimeout(() => { // 内部调用 mutations 间接修改 state数据 context.commit('add') }, 1000) }, // 带参数 addNAsync(context, step) { setTimeout(() => { context.commit('addN', step) }, 1000) } } }) // 在组件 methods 中触发 actions 事件 methods: { handle() { // 不带参数 this.$store.dispatch('addAsync') // 带参数 this.$store.dispatch('addNAsync', 5) } }
从 vuex 中按需导入 mapActions 函数 示例如下:import { mapActions } from 'vuex' methods: { ...mapActions(['addASync', 'addNASync']) }
4. Getter(Vuex 中的 computed)
- Getter 用于 加工 Store 中的数据,形成新结构数据,类似于 Vue 中的 computed
- 注意:
- Store 中数据发生变化,Getter 的数据也会跟着变化
触发 Getter 的两种方式:
- this.$store.getters.名称
- 从 Vuex 中,按需导入 mapGetters 函数,并在 computed 中使用
const store = new Vuex.Store({ state: { count: 0, }, getters: { showNum: (state) => { return "当前最新的数量是【" + state.count + "】"; }, }, });
import { mapGetters } from 'vuex' computed: { ...mapGetters(['showNum']) }
四. 通过 Vuex 实现 ToDoLists
- Github:https://github.com/Lyrelion/vuex_todos
定义 Vuex:import Vue from "vue"; import Vuex from "vuex"; import axios from "axios"; Vue.use(Vuex); export default new Vuex.Store({ // 静态数据 state: { // 任务列表 list: [], // 输入框内容 inputValue: "", // 下一个Id nextId: 5, // 视图关键字 viewKey: "all", }, // 修改 state 静态数据 mutations: { // 初始化任务列表 // 接受 action 中异步获取的表格数据,并更改 state initList(state, list) { state.list = list; }, // 更改输入框的值 setInputValue(state, val) { state.inputValue = val; }, // 添加列表项 addItem(state) { // 设置一个 类似于 list 中的数据结构对象 const obj = { id: state.nextId, // 下一个id info: state.inputValue.trim(), // 输入框中的值(列表项内容) done: false, // 是否完成,默认未完成 }; // 向列表中添加列表项 state.list.push(obj); // 新id + 1,防止冲突 state.nextId++; // 添加列表之后,记得清空输入框的值 state.inputValue = ""; }, // 根据Id,删除列表项 removeItem(state, id) { // 根据传入的id,查询列表中对应的列表项 const i = state.list.findIndex((item) => item.id === id); // 如果找到了该项,就根据索引,删除对应的列表项 if (i !== -1) { state.list.splice(i, 1); } }, // 修改列表项的选中状态 changeStatus(state, listInfo) { // 根据传入的列表项id,查询列表中对应的列表项 const i = state.list.findIndex((item) => item.id === listInfo.id); // 如果找到了该项,就根据索引,修改对应的列表项状态 if (i !== -1) { state.list[i].done = listInfo.status; } }, // 清除已完成的任务 cleanDone(state) { // 遍历列表项,将列表项的每一项的完成状态都写为 false state.list = state.list.filter((item) => item.done === false); }, // 修改视图的关键字 changeViewKey(state, key) { state.viewKey = key; }, }, // 触发异步任务,通过 mutations 间接修改 state 数据 actions: { // 异步获取 list 列表(这里通过 axios 模拟) getList(context) { axios.get("./list.json").then(({ data }) => { // console.log(data); // 在 ations 中更改 state 的数据,要借助 matutions context.commit("initList", data); }); }, }, // vuex 中的计算属性(computed) getters: { // 统计未完成的任务的条数 unDoneLength(state) { return state.list.filter((item) => item.done === false).length; }, // 按需展示列表 infolist(state) { // 点击全部/已做/未做按钮之后 viewKey 发生更改,并展示不同数据 if (state.viewKey === "all") { return state.list; } else if (state.viewKey === "undone") { return state.list.filter((item) => !item.done); } else if (state.viewKey === "done") { return state.list.filter((item) => item.done); } else { return state.list; } }, }, });
在 Vue 中使用 Vuex(此处用了 ant-design 组件库):<template> <div id="app"> <a-input placeholder="请输入任务" class="my_ipt" :value="inputValue" @change="handleInputChange" /> <a-button type="primary" @click="addItemToList">添加事项</a-button> <a-list bordered :dataSource="infolist" class="dt_list"> <a-list-item slot="renderItem" slot-scope="item"> <!-- 复选框 --> <a-checkbox :checked="item.done" @change="(e) => {cbStatusChanged(e, item.id)}" >{{ item.info }}</a-checkbox > <!-- 删除链接 --> <a slot="actions" @click="removeItemById(item.id)">删除</a> </a-list-item> <!-- footer区域 --> <div slot="footer" class="footer"> <!-- 未完成的任务个数 --> <span>{{ unDoneLength }}条剩余</span> <!-- 操作按钮 --> <a-button-group> <a-button :type="viewKey === 'all' ? 'primary' : 'default'" @click="changeList('all')" >全部</a-button > <a-button :type="viewKey === 'undone' ? 'primary' : 'default'" @click="changeList('undone')" >未完成</a-button > <a-button :type="viewKey === 'done' ? 'primary' : 'default'" @click="changeList('done')" >已完成</a-button > </a-button-group> <!-- 把已经完成的任务清空 --> <a @click="clean">清除已完成</a> </div> </a-list> </div> </template> <script> import { mapState, mapGetters } from "vuex"; export default { name: "app", data() { return {}; }, created() { // 获取列表数据 this.$store.dispatch("getList"); }, computed: { // ...mapState(['list']), ...mapState(["inputValue", "viewKey"]), ...mapGetters(["unDoneLength", "infolist"]), }, methods: { // 监听文本框内容变化 handleInputChange(e) { // e.target.value:当前文本框的输入值 this.$store.commit("setInputValue", e.target.value); }, // 向列表中新增 item 项 addItemToList() { if (this.inputValue.trim().length <= 0) { return this.$message.warning("文本框内容不能为空!"); } this.$store.commit("addItem"); }, // 很据Id,删除对应的任务事项 removeItemById(id) { // console.log(id) this.$store.commit("removeItem", id); }, // 监听复选框选中状态变化的事件 cbStatusChanged(e, id) { // 通过 e.target.checked 可以接受到最新的选中状态 // console.log(e.target.checked) // console.log(id) const param = { id: id, status: e.target.checked, }; this.$store.commit("changeStatus", param); }, // 清除已完成的任务 clean() { this.$store.commit("cleanDone"); }, // 修改页面上展示的列表数据 changeList(key) { // console.log(key) this.$store.commit("changeViewKey", key); }, }, }; </script> <style scoped> #app { padding: 10px; margin-left: 50%; transform: translateX(-50%); } .my_ipt { width: 500px; margin-right: 10px; } .dt_list { width: 500px; margin-top: 10px; } .footer { display: flex; justify-content: space-between; align-items: center; } </style>