Vuex从入门到实战(二)

魏晓巍

发布于 2020.04.17 16:25 阅读 411 评论 0

4.基于vuex的案例——todos任务记录工具

4.1我们重新初始化一个vue-cli项目,配置时别忘了选中Vuex配置。

4.2我们还要安装几个依赖:axios和ant-design-vue(ant-designUI组件库,也可以不使用或者使用其他擅长的组件库)

npm install axios ant-design-vue -S

 

4.3在mian.js中引入ant-design和ant-design的样式文件

import Vue from 'vue'
import App from './App.vue'
import store from './store'
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')

 

4.4实现基本布局

<template>
  <div id="app">
    <a-input placeholder="请输入任务" class="my_ipt" />
    <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 slot="footer" class="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 {
  name: 'app',
  data () {
    return {
      list: [
        {
          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 }
      ]
    }
  }
}
</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>

 

4.5在public路径下新增一个list.json文件进行数据存储,并且将我们在App.vue中list里面的数据放到这里。

[
  {
    "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 
  }
]

 

我们的目的是通过请求这个json文件,来控制我们这个任务列表的内容。

4.6通过axios请求获取到list.json中的数据

在store路径下的index.js文件中引入axios,并且进行异步请求,我们需要将请求到的数据挂载到state中的list数组中,但是由于挂载数据只能在mutation中进行,所以我们要在mutation中声明一个方法,将获取到的数据挂载到list中。在actions中的getList方法获取到数据后,调用mutation中的initList方法,将数据传递给list。

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, list) {
      state.list = list
    }
  },
  actions: {
    getList (context) {
      axios.get('/list.json').then(({ data }) => {
        context.commit('initList', data)
      })
    }
  },
  modules: {
  }
})

 

 

4.7在App.vue引入state中的list变量,我们通过引入mapState,在computed属性中展开获取。

import { mapState } from 'vuex'

computed: {
    ...mapState(['list'])
},

 

4.8输入值的双向绑定

在state中定义一个inputValue属性,默认值为空,存放文本框输入的信息。

并且在App.vue中获取到这个属性后,绑定在标签上。并且给文本框绑定一个change方法,监听input组件值的变化,将这个值传递给state中的inputValue。

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

 

在motation中定义一个接收inputValue的方法:

    setInputValue (state, value) {
      state.inputValue = value
    }

 

在App.vue中定义handlInputChange方法:

  methods: {
    handlInputChange (e) {
      this.$store.commit('setInputValue', e.target.value)
    }
  }

 

4.9 给各个组件绑定事件

4.9.1给添加事项按钮绑定点击事件

在vuex中的state数据中定义nextId属性,用于存放下一项数据的id(添加项的id)

在vuex中定义addItem方法,将id,info(输入内容,trim去除空格),done(是否完成的标识,默认为false未完成)添加到一个对象内;将这个对象push到state属性的list中,并且将nextId数值加一,并且清空inputValue(清空输入框)。

    addItem (state) {
      const obj = {
        id: state.nextId,
        info: state.inputValue.trim(),
        done: false
      }
      state.list.push(obj)
      state.nextId++
      state.inputValue = ''
    },

 

在App.vue中定义一个向列表中新增item项的方法,并且将这个方法绑定给“添加事项”按钮,方法首先判断输入内容是否为空,如果为空则进行提示,不为空则调用vuex中的addItem方法:

    addItemToList () {
      if (this.inputValue.trim().length <= 0) {
        return this.$notification.open({
          message: '文本框内容不能为空!',
          type: 'warning'
        })
      }
      this.$store.commit('addItem')
    },

 

4.9.2给删除按钮绑定点击事件

在vuex中定义removeItem方法,根据调用该方法时传入的id查找到对应id的索引值,然后使用splice方法将该索引的项删除。

    removeItem (state, id) {
      // 1.根据id查找对应项索引
      const index = state.list.findIndex(i => i.id === id)
      // 2.根据索引删除对应项
      if (index !== -1) {
        state.list.splice(index, 1)
      }
    },

 

在App.vue中定义一个向列表中删除item项的方法,并且将这个方法绑定给“删除”按钮,并且将checkbox的id传递给方法,removeItemById方法调用removeIitem方法,将id传入,删除选中项。

    removeItemById (id) {
      this.$store.commit('removeItem', id)
    },

 

4.9.3给checkbox绑定点击事件

checkbox的点击事件是改变list中选中项的done属性,和删除一样,传入id值后查找索引,如果索引值存在,则改变该项的done属性。

 

    changeDone (state, id) {
      const index = state.list.findIndex(i => i.id === id)
      if (index !== -1) {
        state.list[index].done = !state.list[index].done
      }
    },

 

在App.vue中定义changeDoneById方法,并且将这个方法绑定给checkbox,并且将checkbox的id传递给方法,changeDoneById方法调用changeDone方法,将id传入,改变选中项状态。

    changeDoneById (id) {
      this.$store.commit('changeDone', id)
    },

 

4.9.4给状态选择按钮绑定点击事件

项目底部的状态选择框在点击后需要切换样式,并且获取对应的item项,我们献给这些按钮绑定事件和样式。绑定事件时,将对应的状态传给方法,绑定type时进行判断,如果为当前选中的type,则以primary样式展示,否则以默认样式展示:

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

 

上面我们用了viewKey判断样式,所以我们需要在vuex的state中定义viewKey,并且定义ChangeList点击事件:

  state: {
    viewKey: 'undone'
  },  
  changeViewKey (state, key) {
      state.viewKey = key
    }
  },

 

并且在getters中判断viewKey,如果viewKey为all,返回list;如果viewKey为undone,返回done为false的list;如果viewKey为done,返回done值为true的list:

   infoList (state) {
      if (state.viewKey === 'all') {
        return state.list
      } else if (state.viewKey === 'undone') {
        return state.list.filter(x => x.done === false)
      } else if (state.viewKey === 'done') {
        return state.list.filter(x => x.done === true)
      } else {
        return state.list
      }
    }

 

同样,在App.vue中定义ChangeList方法,调用changeViewKey方法,并且返回key值即可

    ChangeList (key) {
      this.$store.commit('changeViewKey', key)
    }

 

4.9.5给清除已完成按钮绑定点击事件

清除已完成是将done属性为done的项删除,我们在vuex中定义cleanDone方法,将过滤后的list赋值给原来的list即可:

    cleanDone (state) {
      state.list = state.list.filter(x => x.done === false)
    },

 

在App.vue中定义clean方法调用该cleanDone方法,并且绑定给清除已完成按钮:

    clean () {
      this.$store.commit('cleanDone')
    },

 

-------------------------------------------------至此,vuex内容完结,附源代码---------------------------------------

github地址:https://github.com/weizhuren/VuexDemo.git

App.vue

<template>
  <div id="app">
    <a-input placeholder="请输入任务" class="my_ipt" :value="inputValue" @change="handlInputChange"/>
    <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' @click.native="changeDoneById(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 @click="ChangeList('all')" :type="viewKey === 'all' ? 'primary' : ''">全部</a-button>
          <a-button @click="ChangeList('undone')" :type="viewKey === 'undone' ? 'primary' : ''">未完成</a-button>
          <a-button @click="ChangeList('done')" :type="viewKey === 'done' ? 'primary' : ''">已完成</a-button>
        </a-button-group>
        <!-- 把已经完成的任务清空 -->
        <a @click="clean">清除已完成</a>
      </div>
    </a-list>
  </div>
</template>

<script>
import { mapState, mapGetters } from 'vuex'
export default {
  name: 'app',
  created () {
    this.$store.dispatch('getList')
  },
  computed: {
    ...mapState(['list', 'inputValue', 'viewKey']),
    ...mapGetters(['unDoneLength', 'infoList'])
  },
  data () {
    return {}
  },
  methods: {
    handlInputChange (e) {
      this.$store.commit('setInputValue', e.target.value)
    },
    // 向列表中新增item项
    addItemToList () {
      if (this.inputValue.trim().length <= 0) {
        return this.$notification.open({
          message: '文本框内容不能为空!',
          type: 'warning'
        })
      }
      this.$store.commit('addItem')
    },
    // 删除列表中的item
    removeItemById (id) {
      this.$store.commit('removeItem', id)
    },
    // 改变任务状态
    changeDoneById (id) {
      this.$store.commit('changeDone', id)
    },
    // 清除已完成的任务
    clean () {
      this.$store.commit('cleanDone')
    },
    // 修改页面上展示的列表数据
    ChangeList (key) {
      this.$store.commit('changeViewKey', key)
    }
  }
}
</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>

 

vuex(src/store/index.js)

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: 'undone'
  },
  mutations: {
    initList (state, list) {
      state.list = list
    },
    setInputValue (state, value) {
      state.inputValue = value
    },
    // 添加项目列表
    addItem (state) {
      const obj = {
        id: state.nextId,
        info: state.inputValue.trim(),
        done: false
      }
      state.list.push(obj)
      state.nextId++
      state.inputValue = ''
    },
    // 根据id删除对应任务事项
    removeItem (state, id) {
      // 1.根据id查找对应项索引
      const index = state.list.findIndex(i => i.id === id)
      // 2.根据索引删除对应项
      if (index !== -1) {
        state.list.splice(index, 1)
      }
    },
    changeDone (state, id) {
      const index = state.list.findIndex(i => i.id === id)
      if (index !== -1) {
        state.list[index].done = !state.list[index].done
      }
    },
    // 清除已完成的任务
    cleanDone (state) {
      state.list = state.list.filter(x => x.done === false)
    },
    // 修改页面上展示列表数据
    changeViewKey (state, key) {
      state.viewKey = key
    }
  },
  actions: {
    getList (context) {
      axios.get('/list.json').then(({ data }) => {
        context.commit('initList', data)
      })
    }
  },
  getters: {
    // 统计未完成的任务条数
    unDoneLength (state) {
      return state.list.filter(x => x.done === false).length
    },
    infoList (state) {
      if (state.viewKey === 'all') {
        return state.list
      } else if (state.viewKey === 'undone') {
        return state.list.filter(x => x.done === false)
      } else if (state.viewKey === 'done') {
        return state.list.filter(x => x.done === true)
      } else {
        return state.list
      }
    }
  }
})