这是个啥
使用做一个VUE 3的todo-list。一边看油管,一遍学vue。
Ref: https://youtu.be/Wy9q22isx3U
0. 准备工作
JavaScript
- ES 6 Module
-
forEach
,map
,filter
- Arrow Functions
- Fetch API & Promises
安装
-
国内建议使用淘宝镜像
-
设置淘宝镜像
npm config set registry [http://registry.npm.taobao.org/](http://registry.npm.taobao.org/)
-
换成原来的
npm config set registry https://registry.npmjs.org/
-
-
vue-cli
npm install -g @vue/cli
I. 建立项目
一种方法比较传统,使用cli命令来建立。
我一个文科生,将使用傻瓜式的UI界面哈哈🤗🤗😎😎清新脱俗,美丽大方~~~
vue ui
然后会抛出一个网页的UI界面,就很方便的管理和建立project了~
Details Presets接下来就是美好的等待~~~~~~~~~
等个一会儿!!!就这么建好了!!!!
在tasks > serve > run test,然后稍等一会~~~
点击open app~~~~
建好了~~所以建好的东西,里头各种文件发生了什么?
送便当的故事
HelloWorld.vue是一个模板,然后App.vue将HellowWorld放到index.html中。结构大概是这样👇
送便当的故事最关键的就是两个,不知道比喻成送外卖合适不合适。
- index.html是宅男家。
- App.vue是店家。
- main.js是外卖小哥。
店家负责做便当,然后外卖小哥取好便当后,送到宅男家。接着,就是宅男选择要不要吃咯~
所以就是,App.vue负责做便当(就是HelloWorld.vue),然后main.js把便当送给index.html使用。
便当是啥
便当,也就是component folder里头的vue文件,主要有三个东西,
<template>...</template>
<script>...</script>
<style scoped>....</style>
template顾名思义就是模板;script是需要渲染的部分,可以根据不同的需要,搭配不同内容。
style基本上就是CSS,scoped
的意思就是,这个style仅限于这个vue component,是一个encapsulated的概念。
就这么建完了~~~~~~~~~~~~~~
教程结束~~~~~~~~~~~~~~~~~
谢谢大家~~~~~~~~~~~~~~~~~
好吧...还没开始做Todo list呢...
II. 使用v-bind来渲染todo list
0. 开始之前
在建立todo list之前,先把代码清理一下,方便学习。👇
首先,在component folder下,新建一个Todos.vue:
<template>
<div>
<h1>Todos</h1>
</div>
</template>
<script>
export default {
name: "Todos"
}
</script>
<style scoped>
</style>
其次,把App.vue清理一下,引入Todos这个组件:
<template>
<div id="app">
<Todos/>
</div>
</template>
<script>
import Todos from './components/Todos'
export default {
name: 'app',
components: {
HelloWorld
Todos
},
data(){ ... }
}
1. 什么是v-bind
其实就是<div>的一个属性。
- 用在class上
<div v-bind:class="{ active: isActive }"></div>
- 用在数组上
<div v-bind:class="[activeClass, errorClass]"></div>
2. 使用v-bind来渲染数组
- 👇 假设我有一组todo list,像这样:
todos: [
{
id: 1,
title: "Todo One",
completed: false
},
{
id: 2,
title: "Todo Two",
completed: true
},
{
id: 3,
title: "Todo Three",
completed: false
}
]
本来我需要做的是,分别做三个元素。但是,现在只要一个component,然后用一个for循环,来渲染。
- 👇 修改Todos.vue,像这样:
<template>
<div>
<div v-bind:key="todo.id" v-for="todo in todos">
<h3>{{ todo.title }}</h3>
</div>
</div>
</template>
<script>
export default {
name: "Todos",
props: ["todos"]
}
</script>
name
是这个component的名字~
props
的作用,是父组件通过props
和子组件沟通,就是方便把data传入。
🤞注意喔,需要加一个v-bind:key,其实是一个eslint的问题。它希望你写出来的div tag,是有id的。于是,可以用v-bind:key 把id绑定上去。
那么问题来了,怎么把data传入呢?
- 让App.vue来,他是餐馆老大,买食材、定食谱,都是他喔!👇
<template>
<div id="app">
<Todos v-bind:todos="todos"/>
</div>
</template>
<script>
import Todos from './components/Todos'
export default {
name: 'app',
components: {
Todos
},
data(){
return {
todos: [
{
id: 1,
title: "Todo One",
completed: false
}
]
}
}
}
</script>
看看这里老大说了啥。
- “老弟,我有一组数据,叫todos”
- “老弟,这组数据,按照Todos.vue那个样式来展示”
建好了,长这样👇
3. 使用v-bind来渲染class
但是,有两个问题:
- 能不能把task的css写好看点。
- task 2是做完的,所以,希望它被划掉,eg:
Todo Two
这个时候,就可以使用v-bind来渲染。
首先在component folder下新建一个TodoItem.vue,然后写好3个css,分别来渲染3个class。
- todo-item,是第一个问题
- is-completed,是第二个问题
- del,先放着....
<style scoped>
.todo-item {
background: #f4f4f4;
padding: 10px;
border-bottom: 1px #ccc dotted;
}
.is-completed {
text-decoration: line-through;
}
.del {
background: #ff0000;
color: #fff;
border: none;
padding: 5px 9px;
border-radius: 50%;
cursor: pointer;
float: right;
}
</style>
Step.1
把之前Todos.vue中,改成这样👇
<template>
<div>
<h1>Todos</h1>
<div v-bind:key="todo.id" v-for="todo in todos">
<TodoItem v-bind:todo="todo"/>
</div>
</div>
</template>
主要就是多了一行<TodoItem v-bind:todo="todo"/>
,目的是,把原来在todos中,用for循环渲染的文字,传入一个新的组件,就是TodoItem.vue。
Step.2
接下来,修改TodoItem.vue,让每个task使用todo-item的css。
<template>
<div class="todo-item">
<p>{{todo.title}}</p>
</div>
</template>
<script>
export default {
name: "TodoItem",
props:["todo"]
}
</script>
Step.3
加一个v-bind。如果todo.completed为true,则使用is-completed这个class。
同样,继续修改TodoItem.vue👇
<template>
<div class="todo-item" v-bind:class="{'is-completed': todo.completed}">
<p>{{todo.title}}</p>
</div>
</template>
这样就弄好咯~~~~~✌✌✌✌✌
III.使用v-on来监听事件
前面做的都是静态的数据渲染,接下来要做的是,使用v-on来监听事件。
1. 什么是v-on
可以用 v-on 指令监听 DOM 事件,并在触发时运行一些 JavaScript 代码。
👆这段引用vue官网教程,也就说,v-on会出发js代码。
2. 使用v-on:change触发方法
首先,加一个check box进TodoItem👇
<template>
<div class="todo-item" v-bind:class="{'is-completed': todo.completed}">
<input type="checkbox">
{{ todo.title }}
</div>
</template>
接着,使用v-on绑定到checkbox上,关联到一个新建的method,我把它起名叫markCompleted
。
<input type="checkbox" v-on:change="markCompleted">
最后,在TodoItem的script中定义markCompleted这个method。
<script>
export default {
name: "TodoItem",
props:["todo"],
methods: {
markCompleted(){
this.todo.completed = ! this.todo.completed
}
}
}
</script>
于是,点一下checkbox,就可以画一条线咯~像这样👇
3. 使用v-on和$emit()
$emit()
的作用:子组件通过$emit()
和父组件沟通。
官方文档写emit是这样的。
这个是官方API的文档,而监听器就是v-on👇
vm.$emit( eventName, […args] )
-
参数:
{string} eventName
[...args]
触发当前实例上的事件。附加参数都会传给监听器回调。
接下来开始写代码,实现,如何删除task。因为task是存在App.vue上的,所以要从从子组件一层一层往上通报,所以是这个顺序:TodoItem.vue → Todo.vue → App.vue
-
TodoItem.vue
这个时候,前面那个del的class就用上了。
<template>
<div class="todo-item" v-bind:class="{'is-completed': todo.completed}">
<p>
<input type="checkbox" v-on:change="markCompleted">
{{ todo.title }}
<button v-on:click="$emit('del-todo', todo.id)" class="del"></button>
</p>
</div>
</template>
-
Todo.vue
继续往父组件传达
<TodoItem v-bind:todo="todo" v-on:del-todo="$emit('del-todo', todo.id)"/>
-
App.vue
这时候,在script中建一个deleteTodo的methods:在data中过滤掉子组件通报上来的id。
<template>
<div id="app">
<Todos v-bind:todos="todos" v-on:del-todo="deleteTodo"/>
</div>
</template>
<script>
import Todos from './components/Todos'
export default {
name: 'app',
components: {
Todos
},
data(){ ... },
methods: {
deleteTodo(id){
this.todos = this.todos.filter(todo => todo.id != id)
}
}
}
- 于是,我就可以删除掉task啦~~~画出来结构大概是这样👇
🤞但是,因为这个是前端,没有实质变动数据库。所以,刷新一遍网页后,又会回去了。
IIII. 加一些零件
1. Header
目前为止,界面上缺个header,所以利用vue,我来建个小header🤗🤗🤗👌👌👌
首先,在component > layout> Header.vue
Header.vue里头长这样:
<template>
<header class="header">
<h1>Todo List</h1>
</header>
</template>
<script>
export default {
name: "Header"
}
</script>
<style scoped>
.header {
background: #333;
color: #fff;
text-align: center;
padding: 10px;
}
.header a{
color: #fff;
padding-right: 5px;
}
</style>
2. 加一个新增task的栏
能够提交表单啦~
同理,新增一个component > AddTodo.vue👇
<template>
<form>
<input type="text" name="title" placeholder="Add new task ...">
<input type="submit" value="Add" class="btn">
</form>
</template>
<script>
export default {
name: "AddTodo"
}
</script>
<style scoped>
form {
display: flex;
}
input[type="text"] {
flex: 10;
padding: 5px;
}
input[type="submit"]{
flex: 2;
}
.btn {
display: inline-block;
border: none;
background: #555;
color: #fff;
padding: 7px 20px;
cursor: pointer;
}
.btn:hover {
background: #666;
}
</style>
然后再App.vue中增加这个header和新增按钮。
<template>
<div id="app">
<Header />
<AddTodo />
<Todos v-bind:todos="todos" v-on:del-todo="deleteTodo"/>
</div>
</template>
效果大概这样👆
唔,增加了add功能,可是里头的功能还没写。
今天先这样吧...明天继续完善。🤦♂️
Ronn Liu 🙋♂️
2019/01/21 📌
网友评论