Vuex——TodoList

463 阅读3分钟

Vuex_TodoList

1. 初始化项目

  • 创建文件夹

    vue create vuex_demo

  • 安装相关依赖

    vue add vuex axios

    npm i --save ant-design-vue

  • 模板搭建

    App.vue

    <template>
      <div id="app">
        <a-input placeholder="请输入任务" class="my_ipt"></a-input>
        <a-button type="primary">添加事项</a-button>
    
    
        <a-list bordered :dataSource="list" class="dt_list">
          <a-list-item slot="renderItem" slot-scope="item">
            <!-- 复选框 -->
            <a-checkbox>{{item.info}}</a-checkbox>
            <!-- 删除键 -->
            <a slot="actions">删除</a>
          </a-list-item>
    
          <!-- footer -->
          <div class="footer" slot="footer">
          <!-- 未完成的任务个数 -->
          <span>0条剩余</span>
          <!-- 操作按钮 -->
          <a-button-group>
              <a-button type="primary">全部</a-button>
              <a-button>未完成</a-button>
              <a-button>已完成</a-button>
          </a-button-group>
          <!-- 把已完成的任务清空 -->
          <a>清除已完成</a>
          </div>
    
        </a-list>
      </div>
    </template>
    
    <script>
    export default {
      data(){
        return{
          list:[{
            "id": 0,
            "info": "woshixxyi",
            "done": false
        },
        {
            "id": 1,
            "info": "woshixxyi",
            "done": false
        }, {
            "id": 2,
            "info": "woshixxyi",
            "done": false
        }
    ]
        }
      }
    }
    </script>
    
    <style scoped>
    #app{
      padding: 10px;
    }
    .my_ipt{
      width: 500px;
      margin-right:10px;
    }
    .dt_list{
      width:500px;
      margin-right: 10px;
    }
    .footer{
      display:flex;
      justify-content: space-between;
      align-items:center;
    }
    </style>
    
    

    main.js

    import Vue from 'vue'
    import './plugins/axios'
    import App from './App.vue'
    import store from './store'
    
    //导入ant-design-vue组件库
    import Antd from 'ant-design-vue';
    //导入组件库样式表
    import 'ant-design-vue/dist/antd.css';
    
    Vue.config.productionTip = false
    
    //安装组件库
    Vue.use(Antd);
    
    new Vue({
        store,
        render: h => h(App)
    }).$mount('#app')
    
    

2. 项目实现

1. 数据源改进

  1. 将APP.vue中list数据删除,在public目录下新建list.json文件,将原先的数据存入
  2. 在index.js(vuex文件)中引入axios
  3. 在actions中通过axios获取list.json中的数据,并在App.vue中定义生命周期函数created,通过dispatch触发actions函数。
  4. 在action函数中,通过commit触发mutations函数,将数据传入mutations定义的initList函数中,修改state中list的数据。
  5. 在App.vue中导入辅助函数mapState,使用computed属性监听数据的变化

list.json

[
    {
        "id": 0,
        "info": "woshixxyi",
        "done": false
    },
    {
        "id": 1,
        "info": "woshixxyi",
        "done": false
    }, 
    {
        "id": 2,
        "info": "woshixxyi",
        "done": false
    }
]

index.js

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

Vue.use(Vuex)

export default new Vuex.Store({
    state: {
        list: []
    },
    mutations: {
        initList(state, step) {
            state.list = step
        }
    },
    actions: {
        getList(context) {
            axios.get('/list.json').then(({ data }) => {
                context.commit('initList', data)
            })
        }
    },
    modules: {}
})

APP.vue

<script>
import {mapState} from 'vuex'

export default {
  data(){
    return{
     
    }
  },
  created(){
    this.$store.dispatch('getList')
  },
  computed:{
    ...mapState(['list'])
  }
}
</script>

2.实现文本框的内容与state中的内容双向数据绑定

  1. 首先在state中设置一个inputItem,值为"aaa",并将它映射到App.vue中,将它的值绑定到输入框中
  2. 定义change事件,监听输入框中值的变化,并将值传递给mutations中setInputValue函数。让其改变state中inputItem的值

index.js

    state: {
        list: [],
        inputItem: ""
    },
    mutations: {
        initList(state, step) {
            state.list = step
        },
        setInputValue(state, val) {
            state.inputItem = val
        }
    }

App.vue

<a-input placeholder="请输入任务" class="my_ipt" :value="inputItem" @change='getInputItem'></a-input>


<script>
import {mapState} from 'vuex'


export default {
  data(){
    return{}
  },
  computed:{
    ...mapState(['list','inputItem'])
  },
  methods:{
    getInputItem(e){
      this.$store.commit('setInputValue',e.target.value)
    }
  }
}
</script>

3.实现添加事项

  1. 在按钮中定义点击事件addInputToList,首先判断输入框中的内容是否为空,若不为空,则使用commit触发mutations来改变state中list中的内容
  2. 在mutations中定义函数addInputToList来实现state中list中数据的添加,因为原本存在三条数据,故在state中定义nextId为3

App.vue

    <a-button type="primary" @click="addInputToList">添加事项</a-button>

	// mothods
    addInputToList(){
      //trim()去除字符串的头尾空格
      if(this.inputItem.trim().length <= 0){
        return this.$message.warning('文本框内容不能为空')
      }

      this.$store.commit('addInputToList')
    
    }

index.js

state: {
        list: [],
        inputItem: "",
        nextId: 3
    },
mutations: {
        addInputToList(state) {
            let opt = {
                "id": state.nextId,
                "info": state.inputItem,
                "done": false
            }
            state.list.push(opt)
            state.nextId++
            state.inputItem = ""
        }
    },

4.删除功能实现

  1. 给删除按钮添加删除事件,通过id进行删除,点击按钮通过commit触发mutations修改state中的数据
  2. 在mutations中定义removeItemById函数来实现通过传递过来的id删除对应的数据

App.vue

 <a slot="actions" @click="removeItemById(item.id)">删除</a>
 
// mothods
 removeItemById(id){
      this.$store.commit("removeItemById",id)
    }

index.js

removeItemById(state, id) {
            let itemId = state.list.findIndex(x => x.id === id)
            if (itemId != -1) {
                state.list.splice(itemId, 1)
            }
        }

5.实现复选框状态的绑定

通过:checked来绑定复选框状态的值,在此之前,可以修改list.json中的done值为true来查看是否绑定成功

App.vue

<a-checkbox :checked="item.done">{{item.info}}</a-checkbox>

6.获取复选框状态并拿到响应的数据

  • 监听复选框改变事件
  1. 首先为复选框添加数据改变事件,并为事件添加箭头函数,当数据发生改变触发箭头函数,在箭头函数的函数体中添加cbStatusChange事件,并将箭头函数得到的形参e传递进去,同时将对应的id传入进去
  2. 在cbStatusChange函数中定义一个参数对象cd,接收对应的id和复选框的状态,然后通过commit触发mutations中的cbStatusChange方法来改变state中的数据

App.vue

 <a-checkbox :checked="item.done" @change="(e)=>{cbStatusChange(e,item.id)}">{{item.info}}</a-checkbox>
  
  // mothods
  cbStatusChange(e,id){
      let cb = {
        id:id,
        status:e.target.checked
      }
      this.$store.commit('cbStatusChange',cb)
    }
 

index.js

        cbStatusChange(state, cb) {
            let i = state.list.findIndex(x => x.id === cb.id)
            if (i != -1) {
                state.list[i].done = cb.status
            }
        }
  • 统计未选中复选框条数
  1. 在index.js(vuex文件)中的actions下新增一个getters(对state数据进行过滤操作),在getters下定义函数unDoneLength来获取未被选中的个数。使用filter 过滤获取done值为false的数据
  2. 在App.vue中挂载mapGetters,并在computed中映射,最后将数据呈现。

index.js

    getters: {
        unDoneLength(state) {
            return state.list.filter(x => x.done === false).length
        }
    }

App.vue

<span>{{unDoneLength}}条剩余</span>


export default {
import {mapState,mapGetters} from 'vuex'


  computed:{
    ...mapState(['list','inputItem']),
    ...mapGetters(['unDoneLength'])
  }
}
  • 清除复选框被选中的数据
  1. 首先为清除按钮添加点击事件clearDoneTrue,在点击事件中通过commit触发mutations中的clearDoneTrue函数。
  2. mutations中的clearDoneTrue函数通过filter过滤,得到done的值为false的数据,并将数据重新赋值给state.list

App.vue

<a @click="clearDoneTrue">清除已完成事项</a>

    clearDoneTrue(){
      this.$store.commit('clearDoneTrue')
    }

index.js

        clearDoneTrue(state) {
            state.list = state.list.filter(x => x.done === false)
        }
  • 按钮高亮设置
  1. 定义按钮点击事件btnPrimary(key),并让三个按钮传入三个不同的参数,在btnPrimary函数中,通过commit触发mutations,来改变state中定义的 btnView: 'all'中的值。

  2. 将btnView映射到App.vue中,通过三元运算符来判断btnView的值,从而为按钮设置tpye属性

App.vue

 <a-button-group>
          <a-button :type="btnView === 'all'?'primary':'defalut'" @click="btnPrimary('all')">全部</a-button>
          <a-button :type="btnView === 'undone'?'primary':'defalut'" @click="btnPrimary('undone')">未完成</a-button>
          <a-button :type="btnView === 'done'?'primary':'defalut'" @click="btnPrimary('done')">已完成</a-button>
 </a-button-group>
 

  computed:{
    ...mapState(['item','inputItem','nextId','btnView']),
    ...mapGetters(['unDoneLength'])
  },
 
 
     btnPrimary(key){
      this.$store.commit('btnPrimary',key)
    }

index.js

    state: {
        list: [],
        inputItem: "",
        nextId: 3,
        btnView: 'all'
    },
    
    
 btnPrimary(state, key) {
    state.btnView = key
 }
  • 通过按钮切换数据
  1. 在getters中定义infolist函数,通过判断btnView的值来返回对应的数据
  2. 将infolist映射到App.vue中。
  3. 因为原先的list是全部数据,现在需要改为动态数据infolist,首先删除 ...mapState([])的“list",在 ...mapGetters([])中添加infolist
  4. 最后将****中的数据源:dataSource绑定的的数据改为infolist

index.js

//getters
infolist(state){
      if(state.btnView==='all'){
        return state.list
      }else if(state.btnView==='undone'){
        return state.list.filter(x => x.done ===false)
      }else if(state.btnView==='done'){
        return state.list.filter(x => x.done ===true)
      }
    }

App.vue

<a-list bordered :dataSource="infolist" class="dt_list">
........
<a-list>

computed: {
    ...mapState([ "inputItem",'btnView']),
    ...mapGetters(['unDoneLength',"infolist"])
  },

总结

state

state用于数据的存放。

组件获取数据方式:

  • 方式一:

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

  • 方式二:

    1. 从vuex中按需导入mapState辅助函数

      import {mapState} from 'vuex'

    2. 将全局数据映射为当前组件的计算属性

      computed:{

      ...mapState(['数据名称'])

      }

mutations

用于变更Store中的数据。在vuex中只能通过mutation变更store数据,不可以直接操作store中的数据。优点:可以集中监控所有数据的变化;缺点:操作繁琐

触发mutations的方式:

方式一:

methods:{

​ 方法名(){

​ this.$store.commit('mutation中的方法名',可选参数)

​ }

}

方式二:

  1. 从vuex中按需导入mapMutations辅助函数

    import {mapMutations} from 'vuex'

  2. 将所需的mutations函数,映射为当前组件的methods方法

    methonds:{

    ​ ...mapMutations(['函数名'])

    }

接收参数:

mutations:{

​ 方法名(state,参数){}

}

actions

Action 类似于 mutation,不同在于:

  • Action 提交的是 mutation,而不是直接变更状态。
  • Action 可以包含任意异步操作。

Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象

actions: { increment (context) { context.commit('increment') } }

触发方式:

方式一:

this.$store.dispatch("函数名",参数)

方式二:

  1. 从vuex中按需导入mapActions 辅助函数

    import {mapActions } from 'vuex'

  2. 将所需的mutations函数,映射为当前组件的methods方法

    methods: { ...mapActions([ 'increment', // 将 this.increment() 映射为 this.$store.dispatch('increment')

    ​ // mapActions 也支持载荷: ​ 'incrementBy' // 将 this.incrementBy(amount) 映射为 this.$store.dispatch('incrementBy', amount) ]),

    ​ ...mapActions({ add: 'increment' // 将 this.add() 映射为 this.$store.dispatch('increment') ​ })

    }

getters

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

访问getters中的数据:

方法一:

通过属性/方法访问

this.$store.getters.属性/方法名

方式二:

  1. 从vuex中按需导入mapGetters 辅助函数

    import {mapGetters } from 'vuex'

  2. 将所需的mapGetters 函数,映射为当前组件的computed方法

    ...mapGetters([‘getters中的方法名’])