学习 Vue.js: Todo 小样组件版

上一篇 中写了一个 Todo 小样,但 Vue.js 提供的便利不只有这些,还有组件化。下面将之前的 Todo 小样重写成可重复利用的组件,结果在 codepen.io 上可看到。

注册组件

Vue.js 使用 Vue.component 注册全局组件,下面注册组件 todo-items

Vue.component('todo-items', {
    template: '#todo-items-template',
    props: ['initialTodos'],
    data() {
        return {
            todos: this.initialTodos ? [].concat(this.initialTodos) : [],
            newTodo: { title: '', completed: false }
        }
    },
    methods: {
        add() {
            if (! this.newTodo.title.trim()) { return ; }
            this.todos.push({
                title: this.newTodo.title,
                completed: this.newTodo.completed
            });
            this.newTodo.title = '';
        },
        destroy(index) {
            this.todos.splice(index, 1);
        },
        toggleDone(index) {
            this.todos[index].completed = !this.todos[index].completed;
        },
        up(index) { 
            if (index - 1 < 0) { return ; }
            var temp = this.todos[index];
            this.todos[index] = this.todos[index-1];
            this.todos[index-1] = temp;
            this.todos = [].concat(this.todos);
        },
        down(index) {
            if (index + 1 >= this.todos.length) { return ; }
            var temp = this.todos[index];
            this.todos[index] = this.todos[index+1];
            this.todos[index+1] = temp;
            this.todos = [].concat(this.todos);
        }
    }
});

组件中 data 数据是使用函数形式返回的对象,是为了每个组件实例都有独一无二 data 对象。

写模板代码

组件 todo-items 使用的 #todo-items-template 处的模板代码,就是之前的 Todos 的 HTML 代码,用 type="text/x-template 的 script 标签包围。

<script type="text/x-template" id="todo-items-template">
    <div class="panel panel-default">
        <div class="panel-heading text-center">
            计划要做的事情,共 {{ todos.length }} 件
        </div>
        <div class="panel-body">
            <div class="list-groups">
                <a class="list-group-item" v-bind:class="{ 'completed': todo.completed }" v-for="(todo, index) in todos">
                    {{ todo.title }}
                    <button class="btn btn-xs btn-danger pull-right" v-on:click='destroy(index)'title="删除">✘</button>
                    <button class="btn btn-xs btn-info pull-right" v-on:click='down(index)'title="下移">↓</button>
                    <button class="btn btn-xs btn-info pull-right" v-on:click='up(index)'title="上移">↑</button>
                    <button class="btn btn-xs pull-right" v-on:click='toggleDone(index)' v-bind:class="[todo.completed ? 'btn-success': '']" v-bind:title="[todo.completed ? '点击,标记为未完成': '点击,标记为已完成']">✔</button>
                </a>
            </div>
        </div>
        <div class="panel-footer">
            <form v-on:submit.prevent="add">
                <div class="form-group">
                    <input type="text" class="form-control text-center" v-model="newTodo.title">
                </div>
                <button class="btn btn-default btn-block" type="submit">添加</button>
            </form>
        </div>
    </div>
</script>

使用组件

<div id="app" class="container">
    <div class="row">
        <div class="col-md-6">
            <todo-items v-bind:initial-todos="initialTodos"></todo-items>
        </div>
        <div class="col-md-6">
            <todo-items v-bind:initial-todos="initialTodos"></todo-items>
        </div>
    </div>
</div>
new Vue({
    el: '#app',
    data: {
        initialTodos: [
            {title: '吃早饭', completed: false},
            {title: '吃午饭', completed: false},
            {title: '吃晚饭', completed: false}
        ]
    }
});

注意到,两个组件实例,都赋予了同一个 initialTodos 对象。为了保证每个组件里的 todos 对象是独一无二的,所以组件内部 todos 数据都使用了 [].concat(this.initialTodos) 返回的新数组;否则同一个 Vue 实例下的组件的 todos 数据,会因为引用了同一个对象,行为变一样了。

本作品采用《CC 协议》,转载必须注明作者和本文链接
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 2
Destiny

支持下,加油(๑•̀ㅂ•́)و✧加油

6年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!