DayNote(Vuex-Ant)

274 阅读1分钟

为什么要使用Vuex

传统的兄弟传值

16481629542043_.pic_hd.jpg

Vuex概述

一般情况下,只有组件之间共享的数据,才有必要存储到vuex中

截屏2021-08-21 下午6.35.35.png 优点:

  1. 能够在vuex中集中管理共享的数据,便于开发和后期进行维护

  2. 能够高效的实现组件之间的数据共享,提高开发效率

  3. 存储在vuex中的数据是响应式的,当数据发生改变时,页面中的数据也会同步更新

加1案例

src/store/index.js

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
    state: {
        count: 0
    },
    mutations: {
        add(state, step) {
            //第一个形参永远都是state也就是$state对象
            //第二个形参是调用add时传递的参数
            state.count += step
        },
        sub(state, step) {
            state.count -= step
        }
    },
    actions: {},
    modules: {}
})

src/components/Addition.vue

<template>
  <div>
    <h3>当前最新的count值为:{{$store.state.count}}</h3>
    <button @click="add">+1</button>
  </div>
</template>

<script>
export default {
  data() {
    return {}
  },
  methods: {
    add() {
      this.$store.commit('add', 1)
    },
  },
}
</script>

Vuex中的核心特性

State

State提供唯一的公共数据源,所有共享的数据都要统一放到Store中的State中存储,如上诉例子的count

访问方式一

this.$store.state.全局数据名称

// 举例
this.$store.state.count

访问方式二

#1. 先按需导入mapState函数: 
import { mapState } from 'vuex'
#2. 然后数据映射为计算属性: 
computed:{ ...mapState(['全局数据名称']) }

// 举例
computed:{ ...mapState(['count']) }

Mutation

Mutation用于修改变更$store中的数据

注意

  • 不要在mutations函数里执行异步操作
  • 不支持多个参数的传递,可以以对象的形式传递

使用方式一

store.js

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

Addition.vue

<button @click="Add">+1</button>

methods:{
  Add(){
    //使用commit函数调用mutations中的对应函数,
    //第一个参数就是我们要调用的mutations中的函数名
    //第二个参数就是传递给add函数的参数
    this.$store.commit('add',10)
  }
}

使用方式二

// 1. 按需导入mapMutations
import { mapState,mapMutations } from 'vuex'

export default {
  methods:{
      //获得mapMutations映射的sub函数和add函数
      ...mapMutations(['sub','add']),
      //当点击按钮时触发Sub函数
      Sub(){
          //调用sub函数完成对数据的操作
          this.sub(10);
      }
  },
  computed:{
      ...mapState(['count'])      
  }
}

Action

mutations不能编写异步的代码,会导致vue调试器的显示出错。 截屏2021-08-23 下午8.08.09.png 使用Action来执行异步操作 store.js

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

actions: {
  addAsync(context,step){
    setTimeout(()=>{
      // 触发mutations中的add函数,并传入参数step
      context.commit('add',step);
    },2000)
  }
}

使用方式一

通过this.$store.dispatch调用actions中的方法

Addition.vue

<button @click="AddAsync">...+1</button>

methods:{
  AddAsync(){
    this.$store.dispatch('addAsync',5)
  }
}

使用方式二

  1. 先导入 import { mapActions } from 'vuex'

  2. 再解构获得mapActions映射的addAsync函数...mapActions(['subAsync'])

  3. 通过this.subAsync(5)来调用actions中映射的函数

import { mapState,mapMutations,mapActions } from 'vuex'

export default {
  data() {
    return {}
  },
  methods:{
      //获得mapMutations映射的sub函数
      ...mapMutations(['sub']),
      //当点击按钮时触发Sub函数
      Sub(){
          //调用sub函数完成对数据的操作
          this.sub(10);
      },
      //获得mapActions映射的addAsync函数
      ...mapActions(['subAsync']),
      asyncSub(){
          this.subAsync(5);
      }
  },
  computed:{
      ...mapState(['count']) 
  }
}

Getter

Getter用于对Store中的数据进行加工处理形成新的数据

它只会包装Store中保存的数据,并不会修改Store中保存的数据,当Store中的数据发生变化时,Getter生成的内容也会随之变化

store.js文件,添加getters

  .......
  getters:{
    //添加了一个showNum的属性
    showNum : state =>{
      return '最新的count值为:'+state.count;
    }
  }

使用方式一

Addition.vue中,添加插值表达式使用getters--{{$store.getters.showNum}}

使用方式二

导入mapGetters,并将之映射为计算属性

import { mapGetters } from 'vuex' computed:{ ...mapGetters(['showNum']) }

vuex模块化-module

官网 image-20200904164007116.png

文件结构

// store 文件夹 
│  actions.js
│  getters.js
│  index.js
│  mutations.js
│  state.js
│
└─modules
    │  moduleB.js
    │
    └─ moduleA
            index.js
            mutation.js
            state.js

模块化的简单应用

定义两个模块 usersetting

user中管理用户的状态 token

setting中管理 应用的名称 name

const store  = new Vuex.Store({
  modules: {
    user: {
       state: {
         token: '12345'
       }
    },
    setting: {
      state: {
         name: 'Vuex实例'
      }
    }
  })

定义child-b组件,分别显示用户的token和应用名称name

获取方式1:$store.state.模块名称.属性名 来获取

<template>
  <div>
      <div>用户token {{ $store.state.user.token }}</div>
      <div>网站名称 {{ $store.state.setting.name }}</div>
  </div>
</template>

获取方式2: getters|mapGetters

注意:这个getters是根级别的getters

 getters: {
   token: state => state.user.token,
   name: state => state.setting.name
 } 
 
 **通过mapGetters引用**
  computed: {
       ...mapGetters(['token', 'name'])
 }

模块化中的命名空间namespaced

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

image-20200904164007116.png

如果我们想保证内部模块的高封闭性,我们可以采用namespaced来进行设置

  user: {
       namespaced: true,
       state: {
         token: '12345'
       },
       mutations: {
        //  这里的state表示的是user的state
         updateToken (state) {
            state.token = 678910
         }
       }
    }

1.直接调用-带上模块的属性名路径

test () {
   this.$store.commit('user/updateToken') // 直接调用方法
}

2.辅助函数-带上模块的属性名路径

 methods: {
       ...mapMutations(['user/updateToken']),
       test () {
           this['user/updateToken']()
       }
   }
  <button @click="test">修改token</button>

3.createNamespacedHelpers 创建基于某个命名空间辅助函数

import { mapGetters, createNamespacedHelpers } from 'vuex'
const { mapMutations } = createNamespacedHelpers('user')
<button @click="updateToken">修改token2</button>

methods:{
    ...mapMutations(['updateToken'])
}

4.辅助函数-绑定命名空间

将模块的空间名称字符串作为第一个参数传递给函数,这样所有绑定都会自动将该模块作为上下文

test () {
   this.$store.commit('user/updateToken',{root:true})
}

在带命名空间的模块内访问全局内容(Global Assets)

如果你希望使用全局 state 和 getter,rootState 和 rootGetters 会作为第三和第四参数传入 getter,也会通过 context 对象的属性传入 action。

若需要在全局命名空间内分发 action 或提交 mutation,将 { root: true } 作为第三参数传给 dispatch 或 commit 即可。

 methods: {
       ...mapMutations(['user/updateToken']),
       test () {
           this.$store.commit(
       }
   }
  <button @click="test">修改token</button>

vuex案例-- todolist

截屏2021-08-23 下午8.12.45.png public/list.json

[{
        "id": 0,
        "info": "Racing car sprays burning fuel into crowd.",
        "done": false
    },
    {
        "id": 1,
        "info": "Japanese princess to wed commoner.",
        "done": false
    },
    {
        "id": 2,
        "info": "Australian walks 100km after outback crash.",
        "done": false
    },
    {
        "id": 3,
        "info": "Man charged over missing wedding girl.",
        "done": false
    },
    {
        "id": 4,
        "info": "Los Angeles battles huge wildfires.",
        "done": false
    }
]

src/main.js

import Vue from 'vue'
import App from './App.vue'
// import router from './router'
import store from './store'

// 1. 导入 ant-design-vue 组件库
import Antd from 'ant-design-vue'
// 2. 导入组件库的样式表
import 'ant-design-vue/dist/antd.css'

Vue.config.productionTip = false

// 3. 安装组件库
Vue.use(Antd)

new Vue({
  store,
  render: h => h(App)
}).$mount('#app')

src/store/index.js

import axios from 'axios'
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    //所有任务列表
    list: [],
    //文本输入框中的值
    inputValue: '',
    tag: 'all' // 筛选项的默认值
  },
  mutations: {
    // 初始化 列表数据
    initList(state, list) {
      state.list = list
    },
    // 修改文本框中的值
    setInputValue(state, value) {
      state.inputValue = value
    },
    // 添加项
    addItem(state) {
      const obj = {
        id:
          state.list.length == 0 ? 1 : state.list[state.list.length - 1].id + 1,
        info: state.inputValue.trim(),
        done: false
      }
      // 添加到list中
      state.list.push(obj)
      // 并清空输入框
      state.inputValue = ''
      // 将 tag 设为默认值 all
      state.tag = 'all'
    },
    // 删除项
    delItem(state, id) {
      state.list = state.list.filter(item => item.id != id)
    },
    // 修改状态
    changeStatus(state, params) {
      state.list.forEach(item => {
        if (item.id == params.id) {
          return (item.done = params.done)
        }
      })
    },
    //清除已完成项目
    clearDone(state) {
      state.list = state.list.filter(item => !item.done)
    },
    // 修改 筛选列表tag值
    changeTag(state, tag) {
      state.tag = tag
    }
  },
  actions: {
    async getList(context) {
      // 让本地的开发服务器 去请求,以/开头,默认回去请求public文件夹下的 静态资源
      const { data } = await axios('/list.json')
      context.commit('initList', data)
    }
  },
  getters: {
    // 统计 未完成的数量
    undoneCount(state) {
      return state.list.filter(item => !item.done).length
    },

    // 根据 tag和list的变化 来更新 过滤列表
    filterList(state) {
      switch (state.tag) {
        case 'undone':
          return state.list.filter(item => !item.done)
          break

        case 'done':
          return state.list.filter(item => item.done)
          break

        default:
          return state.list
      }
    }
  }
  // modules: {}
})

src/App.vue

<template>
  <div id="app">
    <a-input placeholder="请输入任务" class="my_ipt" @change="handleInputChange" :value="inputValue" />
    <a-button type="primary" @click="addItemToList">添加事项</a-button>

    <a-list bordered :dataSource="filterList" class="dt_list">
      <a-list-item slot="renderItem" slot-scope="item">
        <!-- 复选框 -->
        <a-checkbox :checked="item.done" @change="stateChangePre(item.id,$event)">{{item.info}}</a-checkbox>
        <!-- 删除链接 -->
        <a slot="actions" @click="removeItemById(item.id)">删除</a>
      </a-list-item>

      <!-- footer区域 -->
      <div slot="footer" class="footer">
        <!-- 未完成的任务个数 -->
        <span>{{undoneCount}}条剩余</span>
        <!-- 操作按钮 -->
        <a-button-group>
          <a-button :type="filterTag=='all'?'primary':''" @click="filterTag='all'">全部</a-button>
          <a-button :type="filterTag=='undone'?'primary':''" @click="filterTag='undone'">未完成</a-button>
          <a-button :type="filterTag=='done'?'primary':''" @click="filterTag='done'">已完成</a-button>
        </a-button-group>
        <!-- 把已经完成的任务清空 -->
        <a @click="clearDoneAll">清除已完成</a>
      </div>
    </a-list>
  </div>
</template>

<script>
import { mapState, mapMutations, mapGetters } from 'vuex'
export default {
  name: 'app',
  data() {
    return {
      filterTag: 'all',
    }
  },
  created() {
    // 去store里拿到 list
    this.$store.dispatch('getList')
  },
  methods: {
    ...mapMutations([
      'addItem',
      'setInputValue',
      'delItem',
      'changeStatus',
      'clearDone',
      'changeTag',
    ]),
    // 实时获取 input框的值,并在store中进行修改
    handleInputChange(e) {
      // e.target.value---文本框中输入的值
      // 不能直接修改,要传给store进行修改
      this.setInputValue(e.target.value)
    },
    // 添加项
    addItemToList() {
      if (this.inputValue.trim().length <= 0) {
        return this.$message.warning('文本框内容不能为空')
      }
      this.addItem()
    },
    // 根据id删除项目
    removeItemById(id) {
      this.delItem(id)
    },
    // 修改每一项的 状态
    stateChangePre(id, e) {
      const params = {
        id: id,
        done: e.target.checked,
      }
      this.changeStatus(params)
    },
    // 清除已完成的项
    clearDoneAll() {
      this.clearDone()
    },
  },
  computed: {
    ...mapState(['list', 'inputValue', 'tag']),
    ...mapGetters(['undoneCount', 'filterList']),
  },
  watch: {
    // 侦听 filterTag的变化,去修改store中的tag值
    filterTag(val) {
      this.changeTag(val)
    },
  },
}
</script>

<style scoped>
#app {
  padding: 10px;
}

.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>