🎑 VUE小教程-建一个TODO list (上)

这是个啥

使用做一个VUE 3的todo-list。一边看油管,一遍学vue。
Ref: https://youtu.be/Wy9q22isx3U

0. 准备工作

JavaScript

  1. ES 6 Module
  2. forEach, map, filter
  3. Arrow Functions
  4. Fetch API & Promises

安装

  1. node

  2. 国内建议使用淘宝镜像

    • 设置淘宝镜像

        npm config set registry [http://registry.npm.taobao.org/](http://registry.npm.taobao.org/)
      
    • 换成原来的

        npm config set registry https://registry.npmjs.org/
      
  3. 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>

看看这里老大说了啥。

  1. “老弟,我有一组数据,叫todos”
  2. “老弟,这组数据,按照Todos.vue那个样式来展示”

建好了,长这样👇

3. 使用v-bind来渲染class

但是,有两个问题:

  1. 能不能把task的css写好看点。
  2. 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.vueTodo.vueApp.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 📌

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。