“再”学习不一样的Vue:Mock假数据
# 前言
学习Vue的过程中,看完 Vue.js (opens new window) 官网的基础知识,特别想要找一个项目练练手。看到了杨怡的教程《学习不一样的Vue》,github上 vue-todos (opens new window) 标星1.5k,动手实践的过程中,碰到了各种问题,故此记录下学习的过程。
# 源地址
项目源码: 源码 (opens new window)
项目预览: 预览 (opens new window)
我的博客: 我的博客 (opens new window)
# 使用Mock假数据
为了更好的测试我们的程序,需要造一些假数据。mockjs (opens new window) 是一个专门用于生产假数据的工具,使用也是很方便的。
// 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
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
更多假数据可以参考这里 (opens new window)。
# 修改 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: {}
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# 添加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>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# 添加 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>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# 数据双向绑定的处理方法
先运行代码程序。如果上面那么多复制起来不方便?可以直接运行我分支上的代码。
首先 clone 我的仓库 master 分支
$ git clone https://github.com/PPsteven/vue-todos-exercise.git master
1
然后切换到对应的 commit
。
git reset --hard 4c25c1ef
1
如果不熟悉的话,建议使用 SourceTree
这个 git 图形化工具
$ npm run serve
1
发现能够正常运行,但是打开 console,发现一堆报错。 do not mutation vuex store outside mutation
很明显,这是因为我们不经意间,对 vuex 的数据进行了修改,经过排查发现,一共有三处地方。两个是 v-model 数据双向绑定,一个是直接对 item 进行修改,这样的操作都是不推荐的用法。
官网对于这种情况,给了一个处理方法:双向绑定的计算属性 (opens new window)
// ...
computed: {
message: {
get () {
return this.$store.state.obj.message
},
set (value) {
this.$store.commit('updateMessage', value) // 对写入部分额外处理
}
}
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 修改 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>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
当然, 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: {}
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# 我的仓库
这里,我将学习过程同步到 我的仓库 (opens new window) 中
$ git add -A
$ git commit -m "“再”学习不一样的Vue:Mock假数据"
# 强制同步
$ git push -f https://github.com/PPsteven/vue-todos-exercise.git master
1
2
3
4
2
3
4
编辑 (opens new window)
上次更新: 2022/10/25, 02:40:54