🎑 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 📌

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,029评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,238评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,576评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,214评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,324评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,392评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,416评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,196评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,631评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,919评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,090评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,767评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,410评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,090评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,328评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,952评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,979评论 2 351