今日目标

1.Vuex概述
2.Vuex基本使用
3.使用Vuex完成todo案例

1.Vuex概述

Vuex是实现组件全局状态(数据)管理的一种机制,可以方便的实现组件之间的数据共享
使用Vuex管理数据的好处:
A.能够在vuex中集中管理共享的数据,便于开发和后期进行维护
B.能够高效的实现组件之间的数据共享,提高开发效率
C.存储在vuex中的数据是响应式的,当数据发生改变时,页面中的数据也会同步更新

2.Vuex的基本使用

创建带有vuex的vue项目,打开终端,输入命令:vue ui
当项目仪表盘打开之后,我们点击页面左上角的项目管理下拉列表,再点击Vue项目管理器
点击创建项目,如下图所示
第一步,设置项目名称和包管理器
day14 - 图1
第二步,设置手动配置项目
day14 - 图2
第三步,设置功能项
day14 - 图3
day14 - 图4
第四步,创建项目
day14 - 图5

3.使用Vuex完成计数器案例

打开刚刚创建的vuex项目,找到src目录中的App.vue组件,将代码重新编写如下:

  1. <template>
  2. <div>
  3. <my-addition></my-addition>
  4. <p>----------------------------------------</p>
  5. <my-subtraction></my-subtraction>
  6. </div>
  7. </template>
  8. <script>
  9. import Addition from './components/Addition.vue'
  10. import Subtraction from './components/Subtraction.vue'
  11. export default {
  12. data() {
  13. return {}
  14. },
  15. components: {
  16. 'my-subtraction': Subtraction,
  17. 'my-addition': Addition
  18. }
  19. }
  20. </script>
  21. <style>
  22. </style>

在components文件夹中创建Addition.vue组件,代码如下:

  1. <template>
  2. <div>
  3. <h3>当前最新的count值为:</h3>
  4. <button>+1</button>
  5. </div>
  6. </template>
  7. <script>
  8. export default {
  9. data() {
  10. return {}
  11. }
  12. }
  13. </script>
  14. <style>
  15. </style>

在components文件夹中创建Subtraction.vue组件,代码如下:

  1. <template>
  2. <div>
  3. <h3>当前最新的count值为:</h3>
  4. <button>-1</button>
  5. </div>
  6. </template>
  7. <script>
  8. export default {
  9. data() {
  10. return {}
  11. }
  12. }
  13. </script>
  14. <style>
  15. </style>

最后在项目根目录(与src平级)中创建 .prettierrc 文件,编写代码如下:

  1. {
  2. "semi":false,
  3. "singleQuote":true
  4. }

4.Vuex中的核心特性

A.State

  1. State提供唯一的公共数据源,所有共享的数据都要统一放到Store中的State中存储
  2. 例如,打开项目中的store.js文件,在State对象中可以添加我们要共享的数据,如:count:0
  3. 在组件中访问State的方式:
  4. 1).this.$store.state.全局数据名称 如:this.$store.state.count
  5. 2).先按需导入mapState函数: import { mapState } from 'vuex'
  6. 然后数据映射为计算属性: computed:{ ...mapState(['全局数据名称']) }

B.Mutation

Mutation用于修改变更$store中的数据
使用方式:
打开store.js文件,在mutations中添加代码如下

  1. mutations: {
  2. add(state,step){
  3. //第一个形参永远都是state也就是$state对象
  4. //第二个形参是调用add时传递的参数
  5. state.count+=step;
  6. }
  7. }

然后在Addition.vue中给按钮添加事件代码如下:

  1. <button @click="Add">+1</button>
  2. methods:{
  3. Add(){
  4. //使用commit函数调用mutations中的对应函数,
  5. //第一个参数就是我们要调用的mutations中的函数名
  6. //第二个参数就是传递给add函数的参数
  7. this.$store.commit('add',10)
  8. }
  9. }

使用mutations的第二种方式:
import { mapMutations } from ‘vuex’
methods:{
…mapMutations([‘add’])
}
如下:

  1. import { mapState,mapMutations } from 'vuex'
  2. export default {
  3. data() {
  4. return {}
  5. },
  6. methods:{
  7. //获得mapMutations映射的sub函数
  8. ...mapMutations(['sub']),
  9. //当点击按钮时触发Sub函数
  10. Sub(){
  11. //调用sub函数完成对数据的操作
  12. this.sub(10);
  13. }
  14. },
  15. computed:{
  16. ...mapState(['count'])
  17. }
  18. }

C.Action

在mutations中不能编写异步的代码,会导致vue调试器的显示出错。
在vuex中我们可以使用Action来执行异步操作。
操作步骤如下:
打开store.js文件,修改Action,如下:

  1. actions: {
  2. addAsync(context,step){
  3. setTimeout(()=>{
  4. context.commit('add',step);
  5. },2000)
  6. }
  7. }

然后在Addition.vue中给按钮添加事件代码如下:

  1. <button @click="AddAsync">...+1</button>
  2. methods:{
  3. AddAsync(){
  4. this.$store.dispatch('addAsync',5)
  5. }
  6. }

第二种方式:
import { mapActions } from ‘vuex’
methods:{
…mapMutations([‘subAsync’])
}
如下:

  1. import { mapState,mapMutations,mapActions } from 'vuex'
  2. export default {
  3. data() {
  4. return {}
  5. },
  6. methods:{
  7. //获得mapMutations映射的sub函数
  8. ...mapMutations(['sub']),
  9. //当点击按钮时触发Sub函数
  10. Sub(){
  11. //调用sub函数完成对数据的操作
  12. this.sub(10);
  13. },
  14. //获得mapActions映射的addAsync函数
  15. ...mapActions(['subAsync']),
  16. asyncSub(){
  17. this.subAsync(5);
  18. }
  19. },
  20. computed:{
  21. ...mapState(['count'])
  22. }
  23. }

D.Getter

Getter用于对Store中的数据进行加工处理形成新的数据
它只会包装Store中保存的数据,并不会修改Store中保存的数据,当Store中的数据发生变化时,Getter生成的内容也会随之变化
打开store.js文件,添加getters,如下:

  1. export default new Vuex.Store({
  2. .......
  3. getters:{
  4. //添加了一个showNum的属性
  5. showNum : state =>{
  6. return '最新的count值为:'+state.count;
  7. }
  8. }
  9. })

然后打开Addition.vue中,添加插值表达式使用getters

{{$store.getters.showNum}}

或者也可以在Addition.vue中,导入mapGetters,并将之映射为计算属性
import { mapGetters } from ‘vuex’
computed:{
…mapGetters([‘showNum’])
}

5.vuex案例

A.初始化案例

首先使用vue ui初始化一个使用vuex的案例
然后打开public文件夹,创建一个list.json文件,文件代码如下:

  1. [
  2. {
  3. "id": 0,
  4. "info": "Racing car sprays burning fuel into crowd.",
  5. "done": false
  6. },
  7. {
  8. "id": 1,
  9. "info": "Japanese princess to wed commoner.",
  10. "done": false
  11. },
  12. {
  13. "id": 2,
  14. "info": "Australian walks 100km after outback crash.",
  15. "done": false
  16. },
  17. {
  18. "id": 3,
  19. "info": "Man charged over missing wedding girl.",
  20. "done": false
  21. },
  22. {
  23. "id": 4,
  24. "info": "Los Angeles battles huge wildfires.",
  25. "done": false
  26. }
  27. ]

再接着,打开main.js,添加store.js的引入,如下:

  1. import Vue from 'vue'
  2. import App from './App.vue'
  3. import store from './store.js'
  4. // 1. 导入 ant-design-vue 组件库
  5. import Antd from 'ant-design-vue'
  6. // 2. 导入组件库的样式表
  7. import 'ant-design-vue/dist/antd.css'
  8. Vue.config.productionTip = false
  9. // 3. 安装组件库
  10. Vue.use(Antd)
  11. new Vue({
  12. store,
  13. render: h => h(App)
  14. }).$mount('#app')

再接着打开store.js,添加axios请求json文件获取数据的代码,如下:

  1. import Vue from 'vue'
  2. import Vuex from 'vuex'
  3. import axios from 'axios'
  4. Vue.use(Vuex)
  5. export default new Vuex.Store({
  6. state: {
  7. //所有任务列表
  8. list: [],
  9. //文本输入框中的值
  10. inputValue: 'AAA'
  11. },
  12. mutations: {
  13. initList(state, list) {
  14. state.list = list
  15. },
  16. setInputValue(state,value){
  17. state.inputValue = value
  18. }
  19. },
  20. actions: {
  21. getList(context) {
  22. axios.get('/list.json').then(({ data }) => {
  23. console.log(data);
  24. context.commit('initList', data)
  25. })
  26. }
  27. }
  28. })

最后,代开App.vue文件,将store中的数据获取并展示:

  1. <template>
  2. <div id="app">
  3. <a-input placeholder="请输入任务" class="my_ipt" :value="inputValue" @change="handleInputChange" />
  4. <a-button type="primary">添加事项</a-button>
  5. <a-list bordered :dataSource="list" class="dt_list">
  6. <a-list-item slot="renderItem" slot-scope="item">
  7. <!-- 复选框 -->
  8. <a-checkbox :checked="item.done">{{item.info}}</a-checkbox>
  9. <!-- 删除链接 -->
  10. <a slot="actions">删除</a>
  11. </a-list-item>
  12. <!-- footer区域 -->
  13. <div slot="footer" class="footer">
  14. <!-- 未完成的任务个数 -->
  15. <span>0条剩余</span>
  16. <!-- 操作按钮 -->
  17. <a-button-group>
  18. <a-button type="primary">全部</a-button>
  19. <a-button>未完成</a-button>
  20. <a-button>已完成</a-button>
  21. </a-button-group>
  22. <!-- 把已经完成的任务清空 -->
  23. <a>清除已完成</a>
  24. </div>
  25. </a-list>
  26. </div>
  27. </template>
  28. <script>
  29. import { mapState } from 'vuex'
  30. export default {
  31. name: 'app',
  32. data() {
  33. return {
  34. // list:[]
  35. }
  36. },
  37. created(){
  38. // console.log(this.$store);
  39. this.$store.dispatch('getList')
  40. },
  41. methods:{
  42. handleInputChange(e){
  43. // console.log(e.target.value)
  44. this.$store.commit('setInputValue',e.target.value)
  45. }
  46. },
  47. computed:{
  48. ...mapState(['list','inputValue'])
  49. }
  50. }
  51. </script>
  52. <style scoped>
  53. #app {
  54. padding: 10px;
  55. }
  56. .my_ipt {
  57. width: 500px;
  58. margin-right: 10px;
  59. }
  60. .dt_list {
  61. width: 500px;
  62. margin-top: 10px;
  63. }
  64. .footer {
  65. display: flex;
  66. justify-content: space-between;
  67. align-items: center;
  68. }
  69. </style>

B.完成添加事项

首先,打开App.vue文件,给“添加事项”按钮绑定点击事件,编写处理函数

  1. //绑定事件
  2. <a-button type="primary" @click="addItemToList">添加事项</a-button>
  3. //编写事件处理函数
  4. methods:{
  5. ......
  6. addItemToList(){
  7. //向列表中新增事项
  8. if(this.inputValue.trim().length <= 0){
  9. return this.$message.warning('文本框内容不能为空')
  10. }
  11. this.$store.commit('addItem')
  12. }
  13. }

然后打开store.js编写addItem

  1. export default new Vuex.Store({
  2. state: {
  3. //所有任务列表
  4. list: [],
  5. //文本输入框中的值
  6. inputValue: 'AAA',
  7. //下一个id
  8. nextId:5
  9. },
  10. mutations: {
  11. ........
  12. //添加列表项
  13. addItem(state){
  14. const obj = {
  15. id :state.nextId,
  16. info: state.inputValue.trim(),
  17. done:false
  18. }
  19. //将创建好的事项添加到数组list中
  20. state.list.push(obj)
  21. //将nextId值自增
  22. state.nextId++
  23. state.inputValue = ''
  24. }
  25. }
  26. ......
  27. })

C.完成删除事项

首先,打开App.vue文件,给“删除”按钮绑定点击事件,编写处理函数

  1. //绑定事件
  2. <a slot="actions" @click="removeItemById(item.id)">删除</a>
  3. //编写事件处理函数
  4. methods:{
  5. ......
  6. removeItemById(id){
  7. //根据id删除事项
  8. this.$store.commit('removeItem',id)
  9. }
  10. }

然后打开store.js编写addItem

  1. export default new Vuex.Store({
  2. ......
  3. mutations: {
  4. ........
  5. removeItem(state,id){
  6. //根据id删除事项数据
  7. const index = state.list.findIndex( x => x.id === id )
  8. // console.log(index);
  9. if(index != -1) state.list.splice(index,1);
  10. }
  11. }
  12. ......
  13. })

D.完成选中状态的改变

首先,打开App.vue文件,给“复选”按钮绑定点击事件,编写处理函数

  1. //绑定事件
  2. <a-checkbox :checked="item.done" @change="cbStateChanged(item.id,$event)">{{item.info}}</a-checkbox>
  3. //编写事件处理函数
  4. methods:{
  5. ......
  6. cbStateChanged(id,e){
  7. //复选框状态改变时触发
  8. const param = {
  9. id:id,
  10. status:e.target.checked
  11. }
  12. //根据id更改事项状态
  13. this.$store.commit('changeStatus',param)
  14. }
  15. }

然后打开store.js编写addItem

  1. export default new Vuex.Store({
  2. ......
  3. mutations: {
  4. ........
  5. changeStatus(state,param){
  6. //根据id改变对应事项的状态
  7. const index = state.list.findIndex( x => x.id === param.id )
  8. if(index != -1) state.list[index].done = param.status
  9. }
  10. }
  11. ......
  12. })

E.剩余项统计

打开store.js,添加getters完成剩余项统计

  1. getters:{
  2. unDoneLength(state){
  3. const temp = state.list.filter( x => x.done === false )
  4. console.log(temp)
  5. return temp.length
  6. }
  7. }

打开App.vue,使用getters展示剩余项

  1. //使用映射好的计算属性展示剩余项
  2. <!-- 未完成的任务个数 -->
  3. <span>{{unDoneLength}}条剩余</span>
  4. //导入getters
  5. import { mapState,mapGetters } from 'vuex'
  6. //映射
  7. computed:{
  8. ...mapState(['list','inputValue']),
  9. ...mapGetters(['unDoneLength'])
  10. }

F.清除完成事项

首先,打开App.vue文件,给“清除已完成”按钮绑定点击事件,编写处理函数

  1. <!-- 把已经完成的任务清空 -->
  2. <a @click="clean">清除已完成</a>
  3. //编写事件处理函数
  4. methods:{
  5. ......
  6. clean(){
  7. //清除已经完成的事项
  8. this.$store.commit('cleanDone')
  9. }
  10. }

然后打开store.js编写addItem

  1. export default new Vuex.Store({
  2. ......
  3. mutations: {
  4. ........
  5. cleanDone(state){
  6. state.list = state.list.filter( x => x.done === false )
  7. }
  8. }
  9. ......
  10. })

G.点击选项卡切换事项

打开App.vue,给“全部”,“未完成”,“已完成”三个选项卡绑定点击事件,编写处理函数
并将列表数据来源更改为一个getters。

  1. <a-list bordered :dataSource="infoList" class="dt_list">
  2. ......
  3. <!-- 操作按钮 -->
  4. <a-button-group>
  5. <a-button :type="viewKey ==='all'?'primary':'default'" @click="changeList('all')">全部</a-button>
  6. <a-button :type="viewKey ==='undone'?'primary':'default'" @click="changeList('undone')">未完成</a-button>
  7. <a-button :type="viewKey ==='done'?'primary':'default'" @click="changeList('done')">已完成</a-button>
  8. </a-button-group>
  9. ......
  10. </a-list>
  11. //编写事件处理函数以及映射计算属性
  12. methods:{
  13. ......
  14. changeList( key ){
  15. //点击“全部”,“已完成”,“未完成”时触发
  16. this.$store.commit('changeKey',key)
  17. }
  18. },
  19. computed:{
  20. ...mapState(['list','inputValue','viewKey']),
  21. ...mapGetters(['unDoneLength','infoList'])
  22. }

打开store.js,添加getters,mutations,state

  1. export default new Vuex.Store({
  2. state: {
  3. ......
  4. //保存默认的选项卡值
  5. viewKey:'all'
  6. },
  7. mutations: {
  8. ......
  9. changeKey(state,key){
  10. //当用户点击“全部”,“已完成”,“未完成”选项卡时触发
  11. state.viewKey = key
  12. }
  13. },
  14. ......
  15. getters:{
  16. .......
  17. infoList(state){
  18. if(state.viewKey === 'all'){
  19. return state.list
  20. }
  21. if(state.viewKey === 'undone'){
  22. return state.list.filter( x => x.done === false )
  23. }
  24. if(state.viewKey === 'done'){
  25. return state.list.filter( x => x.done === true )
  26. }
  27. }
  28. }
  29. })