跳到主要内容

“再”学习不一样的Vue:Mock假数据

前言

学习Vue的过程中,看完 Vue.js 官网的基础知识,特别想要找一个项目练练手。看到了杨怡的教程《学习不一样的Vue》,github上 vue-todos 标星1.5k,动手实践的过程中,碰到了各种问题,故此记录下学习的过程。

源地址


使用Mock假数据

为了更好的测试我们的程序,需要造一些假数据。mockjs 是一个专门用于生产假数据的工具,使用也是很方便的。

// src/mock/data/todoList.js
import Mock from 'mockjs'; // 导入mockjs 模块
let Todos = []; // 定义我们需要的数据
const COUNT = [1, 2, 3, 4, 5]; // 定义我们需要数量
for (let i = 1; i <= COUNT.length; i++) {
Todos.push(Mock.mock({ // 根据数据模板生成模拟数据。
id: Mock.Random.guid(), // 随机id
title: Mock.Random.first(), // 随机标题
isDelete: false, // 是否删除
locked: Mock.Random.boolean(), // 随机锁定
record: COUNT.map(() => { // 代办单项列表的数据
return {
text: Mock.Random.cparagraph(2), // 随机内容
isDelete: false, // 是否删除
checked: Mock.Random.boolean() // 是否完成
};
})
}));
}
export { // 导出列表数据
Todos
};


更多假数据可以参考这里

修改 src/store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
import * as getters from './getters';
+ import { Todos } from '@/mock/data/todoList'
+ import Mock from 'mockjs'

Vue.use(Vuex); // 安装 Vuex 插件

export default new Vuex.Store({
// 严格模式下,无论何时发生了状态变更且不是由 mutation 函数引起的,将会抛出错误
strict: true,
// 创建初始应用全局状态变量
state: {
// 指我们的待办事项列表数据
- todoList: [
- { title: '星期一', count: 1, locked: true , id: 'a', isDelete: false },
- { title: '星期二', count: 2, locked: false, id: 'b', isDelete: false },
- { title: '星期三', count: 3, locked: true, id: 'c', isDelete: false },
- ],
+ todoList: Todos,

},
// 定义所需的 mutations, 用来操作 state 数据
mutations: {
EDITTODE(state, data) { // 定义名为 EDITTODE函数用作改变todoList的值
state.todoList = data;
},
ADDTODO(state) { // 添加新的 TODO 菜单
state.todoList.push(
- { title: '星期四', count: 3, locked: true, id: Math.floor(Math.random() * 1000 + 1), isDelete: false }
- )
+ {
+ id: Mock.Random.guid(),
+ title: 'newList',
+ isDelete: false,
+ locked: false,
+ record: []
+ });
},

UPDATETODE(state, todo) { // 更新TODO菜单
state.todoList.some((t, index) => {
if (t.id == todo.id) {
t.title = todo.title;
t.locked = todo.locked;
t.isDelete = todo.isDelete;
return true;
}
});
},
+ ADDRECORD(state, todo){ // 添加 TODO 记录
+ let { id, text } = todo;
+ state.todoList.some((t, index) => {
+ if (t.id === id) {
+ t.record.push({
+ text: text,
+ isDelete: false,
+ checked: false
+ });
+ return true;
+ }
+ });
+ },
},
getters: getters, // 获取数据
actions: {},
modules: {}
})

添加Item组件

修改 src/components/todo.vue

引入 item.vue

// src/components/todo.vue
<script>
+ import item from './item';
export default {
data() {
return {
todo: {},
- items: [ // 代办单项列表
- ],
+ items: [], // 代办单项列表

text: '', // 用户输入单项项绑定的输入
isUpdate: false // 新增修改状态
};
},
created() {
this.init();
},
watch: {
'$route.params.id'() {
// 监听$route.params.id的变化,如果这个id即代表用户点击了其他的待办项需要重新请求数据。
this.init();
}
},
+ components: {
+ item,
+ },

methods: {
init() { // 初始化
const ID = this.$route.params.id;
- let { id, title, count, isDelete, locked } = this.$store.getters.getTodoId(ID);
+ let { id, title, count, isDelete, locked, record } = this.$store.getters.getTodoId(ID);

this.todo = {
id: id,
title: title,
count: count,
locked: locked,
isDelete: isDelete
}
this.items = record;
},
updateTodo(){ // 更新 todo 菜单
this.$store.commit('UPDATETODE', this.todo);
},
- onAdd() {}, // 添加 TODO项
+ onAdd() { // 添加 TODO项
+ const ID = this.$route.params.id;
+ this.$store.commit('ADDRECORD', {text: this.text, id: ID });
+ this.text = '';
+ },

updateTitle() { // 更新 菜单名称
this.updateTodo();
this.isUpdate = false;
},
onDelete() { // 删除 TODO 菜单
this.todo.isDelete = true;
this.updateTodo();
},
onlock() { // 锁定 TODO 菜单
this.todo.locked = !this.todo.locked;
this.updateTodo();
}
}
};
</script>

添加 src/components/item.vue

<template>
<transition name="slide-fade">
<div class="list-item editingClass editing " :class="{checked: item.checked}" v-show="!item.isDelete">
<label class="checkbox">
<input type="checkbox" v-model="item.checked" name="checked" @change="onChange" :disabled="locked">
<span class="checkbox-custom"></span>
</label>
<input type="text" v-model="item.text" placeholder='写点什么。。。' :disabled=" item.checked || locked" @keyup.enter="onChange">
<a class="delete-item" v-if="item.checked && !locked" @click="item.isDelete = true;onChange()">
<span class="icon-trash"></span>
</a>
</div>
</transition>
</template>

<script>

// item 是todo的子组件,他接受一个父组件传入的item
export default {
props: {
item: { // 任务项详情
type: Object,
default: () => {
return {
checked: false,
text: '你好,世界',
isDelete: false,
};
}
},
'index': {}, // 任务项的数组索引
'id': {}, // 菜单ID
'init': {},
'locked': {}
},
methods: {
// 用户无论删除,修改,锁定都可以利用这个方法。
onChange() {
// this.$store.commit('UPDATERECORD', {id: this.id, index: this.index, record: this.item});
this.init(); // 更新菜单信息,比如更新个数等
}
}
};
</script>
<style lang="less">
@import '../common/style/list-items.less';
.slide-fade-enter-active {
transition: all .3s ease;
}
.slide-fade-leave-active {
transition: all .3s cubic-bezier(1.0, 0.5, 0.8, 1.0);
}
.slide-fade-enter, .slide-fade-leave-active {
transform: translateX(10px);
opacity: 0;
}
</style>

数据双向绑定的处理方法

先运行代码程序。如果上面那么多复制起来不方便?可以直接运行我分支上的代码。

首先 clone 我的仓库 master 分支

$ git clone https://github.com/PPsteven/vue-todos-exercise.git master

然后切换到对应的 commit

git reset --hard 4c25c1ef

如果不熟悉的话,建议使用 SourceTree 这个 git 图形化工具

$ npm run serve

发现能够正常运行,但是打开 console,发现一堆报错。 do not mutation vuex store outside mutation

很明显,这是因为我们不经意间,对 vuex 的数据进行了修改,经过排查发现,一共有三处地方。两个是 v-model 数据双向绑定,一个是直接对 item 进行修改,这样的操作都是不推荐的用法。

官网对于这种情况,给了一个处理方法:双向绑定的计算属性

// ...
computed: {
message: {
get () {
return this.$store.state.obj.message
},
set (value) {
this.$store.commit('updateMessage', value) // 对写入部分额外处理
}
}
}

修改 src/components/item.vue

<template>
<transition name="slide-fade">
<div class="list-item editingClass editing " :class="{checked: item.checked}" v-show="!item.isDelete">
<label class="checkbox">
<input type="checkbox" v-model="isChecked" name="checked" @change="onChange" :disabled="locked">
<span class="checkbox-custom"></span>
</label>
<input type="text" v-model="recordText" placeholder='写点什么。。。' :disabled=" item.checked || locked" @keyup.enter="onChange">
<a class="delete-item" v-if="item.checked && !locked" @click="isDelete = true;onChange()">
<span class="icon-trash"></span>
</a>
</div>
</transition>
</template>

<script>
// item 是todo的子组件,他接受一个父组件传入的item
export default {
props: {
item: { // 任务项详情
type: Object,
default: () => {
return {
checked: false,
text: '你好,世界',
isDelete: false,
};
}
},
'index': {}, // 任务项的数组索引
'id': {}, // 菜单ID
'init': {},
'locked': {}
},
computed: {
isChecked: {
get() {
return this.item.checked;
},
set(value) {
let record = {checked: value};
this.$store.commit('UPDATERECORD', {id: this.id, index: this.index, record: record});
},
},
recordText: {
get() {
return this.item.text;
},
set(value) {
let record = {text: value};
this.$store.commit('UPDATERECORD', {id: this.id, index: this.index, record: record});
}
},
isDelete: {
get() {
return this.item.isDelete;
},
set(value) {
let record = {isDelete: value };
this.$store.commit('UPDATERECORD', {id: this.id, index: this.index, record: record})
}
}
},
methods: {
// 用户无论删除,修改,锁定都可以利用这个方法。
onChange() {
// this.$store.commit('UPDATERECORD', {id: this.id, index: this.index, record: this.item});
this.init(); // 更新菜单信息,比如更新个数等
}
}
};
</script>

当然, store 部分也要写对应的方法

// src/store/index.js
export default new Vuex.Store({
// 严格模式下,无论何时发生了状态变更且不是由 mutation 函数引起的,将会抛出错误
strict: true,
// 创建初始应用全局状态变量
state: {
// 指我们的待办事项列表数据
todoList: Todos,
},
// 定义所需的 mutations, 用来操作 state 数据
mutations: {
EDITTODE(state, data) { // 定义名为 EDITTODE函数用作改变todoList的值
state.todoList = data;
},
ADDTODO(state) { // 添加新的 TODO 菜单
state.todoList.push(
{
id: Mock.Random.guid(),
title: 'newList',
isDelete: false,
locked: false,
record: []
});
},
UPDATETODE(state, todo) { // 更新TODO菜单
state.todoList.some((t, index) => {
if (t.id == todo.id) {
t.title = todo.title;
t.locked = todo.locked;
t.isDelete = todo.isDelete;
return true;
}
});
},
ADDRECORD(state, todo){ // 添加 TODO 记录
let { id, text } = todo;
state.todoList.some((t, index) => {
if (t.id === id) {
t.record.push({
text: text,
isDelete: false,
checked: false
});
return true;
}
});
},
UPDATERECORD(state, todo) { // 更新 TODO 记录
let {id, index, record } = todo;
state.todoList.some((t) => {
if (t.id === id){
// t.record[index] = record;
for (var key in record){
t.record[index][key] = record[key];
}
return true;
}
});
}
},
getters: getters, // 获取数据
actions: {},
modules: {}
})

我的仓库

这里,我将学习过程同步到 我的仓库

$ git add -A 
$ git commit -m "“再”学习不一样的Vue:Mock假数据"
# 强制同步
$ git push -f https://github.com/PPsteven/vue-todos-exercise.git master