Zonebit

个人的奋斗还是历史的进程?

View the Project on GitHub

2017-12-21-使用vue完成一个简单的TODO应用

本例全部是在一个HTML文件中完成的

1. 文档模板

<!doctype html>
<html>

<head>
    <title>TODO MVC</title>
    <meta charset="utf-8">
</head>

<body>
	<!--通过第三方CDN引入vue.js-->
    <script src="http://cdn.bootcss.com/vue/2.2.6/vue.js"></script>
</body>

</html>

2. model到view的绑定

<!doctype html>
<html>

<head>
    <title>TODO MVC</title>
    <meta charset="utf-8">
</head>

<body>
	<!--新增根节点,通过mustache模板语法展示newtodo的值-->
    <div id="app"></div>
    <script src="http://cdn.bootcss.com/vue/2.2.6/vue.js"></script>
    <script>
        var app = new Vue({
            //使用el属性传入css选择器绑定根节点
            el: '#app',
            //使用data属性传入对象,对象内自定义属性newtodo传入待办内容
            data: {
                newtodo: '我系渣渣辉'
            }
        });
    </script>
</body>

</html>

这个时候就可以通过修改data中newtodo的值改变页面的展现 注意:可能因为jeklly本身的缘故,div#app中的mustache模板语法没有正常显示,后同

3.view到model的绑定

<!doctype html>
<html>

<head>
    <title>TODO MVC</title>
    <meta charset="utf-8">
</head>

<body>
    <div id="app">
        <br/>
        <!--通过v-model属性绑定newtodo-->
        <input type="text" v-model="newtodo">
    </div>
    <!--通过第三方CDN引入vue.js-->
    <script src="http://cdn.bootcss.com/vue/2.2.6/vue.js"></script>
    <script>
        var app = new Vue({
            //使用el属性传入css选择器绑定element节点
            el: '#app',
            //使用data属性传入对象,对象内自定义属性newtodo传入待办内容
            data: {
                newtodo: '我系渣渣辉'
            }
        });
    </script>
</body>

</html>

这时候可以通过页面的input框修改newtodo值,data中的值和页面上引用的值都会同步变化(数据双向绑定)

4.列表循环

TODO应用需要一个列表展示所有的待办已办事项

<!doctype html>
<html>

<head>
    <title>TODO MVC</title>
    <meta charset="utf-8">
</head>

<body>
    <div id="app">
        <br/>
        <!--通过v-model属性绑定newtodo-->
        <input type="text" v-model="newtodo">
        <ul>
            <!--使用v-for属性循环展示todolist中的项目-->
            <li v-for="item in todolist"></li>
        </ul>
    </div>
    <!--通过第三方CDN引入vue.js-->
    <script src="http://cdn.bootcss.com/vue/2.2.6/vue.js"></script>
    <script>
        var app = new Vue({
            //使用el属性传入css选择器绑定element节点
            el: '#app',
            //使用data属性传入对象,对象内自定义属性newtodo传入待办内容
            data: {
                newtodo: '我系渣渣辉',
                //新增todolist数组用来保存待办项目
                todolist: ['渣渣辉', '陈秀春', '古天乐']
            }
        });
    </script>
</body>

</html>

5.添加待办事项

<!doctype html>
<html>

<head>
    <title>TODO MVC</title>
    <meta charset="utf-8">
</head>

<body>
    <div id="app">
        <br/>
        <!--给input标签新增v-on属性绑定键盘回车事件,回调函数名称为addTodo-->
        <input type="text" v-model="newtodo" v-on:keyup.enter="addTodo">
        <ul>
            <li v-for="item in todolist"></li>
        </ul>
    </div>
    <script src="http://cdn.bootcss.com/vue/2.2.6/vue.js"></script>
    <script>
        var app = new Vue({
            el: '#app',
            data: {
                newtodo: '我系渣渣辉',
                todolist: ['渣渣辉', '陈秀春', '古天乐']
            },
            //使用methods属性保存自定义的addTodo方法
            methods: {
                addTodo: function() {
                    //将newtodo插入todolist数组
                    this.todolist.push(this.newtodo);
                    //置空输入框
                    this.newtodo = '';
                }
            }
        });
    </script>
</body>

</html>

现在可以通过敲击回车将input框中的内容保存到todolist列表

6.删除事项

<!doctype html>
<html>

<head>
    <title>TODO MVC</title>
    <meta charset="utf-8">
</head>

<body>
    <div id="app">
        <br/>
        <input type="text" v-model="newtodo" v-on:keyup.enter="addTodo">
        <ul>
            <!--在列表项末尾增加删除按钮,使用click事件绑定delTodo方法,参数是item-->
            <li v-for="item in todolist"><button v-on:click="delTodo(item)">X</button></li>
        </ul>
    </div>
    <script src="http://cdn.bootcss.com/vue/2.2.6/vue.js"></script>
    <script>
        var app = new Vue({
            el: '#app',
            data: {
                newtodo: '我系渣渣辉',
                todolist: ['渣渣辉', '陈秀春', '古天乐']
            },
            methods: {
                addTodo: function() {
                    this.todolist.push(this.newtodo);
                    this.newtodo = '';
                },
                delTodo: function(item) {
                    //从todolist中删除item
                    this.todolist.splice(this.todolist.indexOf(item), 1);
                }
            }
        });
    </script>
</body>

</html>

现在可以通过点击列表项末尾的删除按钮删除当前列表项

7.与后台数据同步

目前为止我们进行的所有操作都是前台操作,数据并没有保存到数据库,实际开发中需要通过ajax与后台进行数据同步,但是例子里面为了演示方便,使用了localStorage代替ajax操作

<!doctype html>
<html>

<head>
    <title>TODO MVC</title>
    <meta charset="utf-8">
</head>

<body>
    <div id="app">
        <br/>
        <input type="text" v-model="newtodo" v-on:keyup.enter="addTodo">
        <ul>
            <li v-for="item in todolist"><button v-on:click="delTodo(item)">X</button></li>
        </ul>
    </div>
    <script src="http://cdn.bootcss.com/vue/2.2.6/vue.js"></script>
    <script>
        //定义一个全局的todoStorage对象进行存取操作
        var todoStorage = {
            fetch: function() {
                var list = JSON.parse(localStorage.getItem('todolist')) || [];
                return list;
            },
            save: function(list) {
                localStorage.setItem('todolist', JSON.stringify(list));
            }
        };
        var app = new Vue({
            el: '#app',
            data: {
                newtodo: '我系渣渣辉',
                //使用todoStorage对象的方法获取todolist
                todolist: todoStorage.fetch()
            },
            methods: {
                addTodo: function() {
                    this.todolist.push(this.newtodo);
                    this.newtodo = '';
                },
                delTodo: function(item) {
                    this.todolist.splice(this.todolist.indexOf(item), 1);
                }
            },
            //使用watch属性监听todolist的变化,一旦有变化就触发保存方法进行持久化
            watch: {
                todolist: function(todolist) {
                    todoStorage.save(todolist);
                }
            }
        });
    </script>
</body>

</html>

8.已办标记

这步我们需要通过多选框和样式对已办项目进行标记,首先必须修改一下数据结构,把原来原始的字符串事项修改为对象,如下

var todo = {
    name: '某某某',
    completed: false
};

使用completed属性标记是否已办

接下来需要稍微修改一下原来的代码——只修改addTodo方法即可应用新的数据结构,然后就可以添加已办功能的相关代码

<!doctype html>
<html>

<head>
    <title>TODO MVC</title>
    <meta charset="utf-8">
    <!--在内部样式表定义completed样式类-->
    <style>
        .completed {
            text-decoration: line-through;
        }
    </style>
</head>

<body>
    <div id="app">
        <br/>
        <input type="text" v-model="newtodo" v-on:keyup.enter="addTodo">
        <ul>
            <!--第二步:增加多选框和已办样式类的绑定,展现的内容相应地修改为item.name,添加一个completed样式类,这个类是否起作用取决于item.completed,多选框同样绑定到itme.completed-->
            <li v-for="item in todolist"><input type="checkbox" v-model="item.completed"><span v-bind:class="{completed:item.completed}"></span><button v-on:click="delTodo(item)">X</button></li>
        </ul>
    </div>
    <script src="http://cdn.bootcss.com/vue/2.2.6/vue.js"></script>
    <script>
        var todoStorage = {
            fetch: function() {
                var list = JSON.parse(localStorage.getItem('todolist')) || [];
                return list;
            },
            save: function(list) {
                localStorage.setItem('todolist', JSON.stringify(list));
            }
        };
        var app = new Vue({
            el: '#app',
            data: {
                newtodo: '我系渣渣辉',
                todolist: todoStorage.fetch()
            },
            methods: {
                addTodo: function() {
                    //第一步:push新的todo事项对象
                    this.todolist.push({
                        name: this.newtodo,
                        completed: false
                    });
                    this.newtodo = '';
                },
                delTodo: function(item) {
                    this.todolist.splice(this.todolist.indexOf(item), 1);
                }
            },
            watch: {
                todolist: function(todolist) {
                    todoStorage.save(todolist);
                }
            }
        });
    </script>
</body>

</html>

现在就可以通过点击多选框改变事项的已办待办状态

9.过滤|分类展示

实现全部、已办事项、待办事项的分类展示

<!doctype html>
<html>

<head>
    <title>TODO MVC</title>
    <meta charset="utf-8">
    <style>
        .completed {
            text-decoration: line-through;
        }
    </style>
</head>

<body>
    <div id="app">
        <br/>
        <input type="text" v-model="newtodo" v-on:keyup.enter="addTodo">
        <ul>
            <!--第四步:将todolist修改为filteredList-->
            <li v-for="item in todolist"><input type="checkbox" v-model="item.completed"><span v-bind:class="{completed:item.completed}"></span><button v-on:click="delTodo(item)">X</button></li>
        </ul>
        <!--第二步:新增三个按钮分别绑定click事件修改filter的值,@click属性是v-on:click的简写-->
        <button @click="filter='all'">all</button>
        <button @click="filter='active'">active</button>
        <button @click="filter='completed'">completed</button>
    </div>
    <script src="http://cdn.bootcss.com/vue/2.2.6/vue.js"></script>
    <script>
        var todoStorage = {
            fetch: function() {
                var list = JSON.parse(localStorage.getItem('todolist')) || [];
                return list;
            },
            save: function(list) {
                localStorage.setItem('todolist', JSON.stringify(list));
            }
        };
        var app = new Vue({
            el: '#app',
            data: {
                newtodo: '我系渣渣辉',
                todolist: todoStorage.fetch(),
                //第一步:新增filter标识位,默认为all,可选active、completed
                filter: 'all'
            },
            //第三步:添加计算属性,声明filteredList,这个值由data中的值计算得来,在这个例子里面依赖filter的值
            computed: {
                filteredList: function() {
                    switch (this.filter) {
                        case 'all':
                            return this.todolist;
                        case 'active':
                            return this.todolist.filter(function(item, i, a) {
                                return item.completed === false;
                            });
                        case 'completed':
                            return this.todolist.filter(function(item, i, arr) {
                                return item.completed === true;
                            });
                    }
                }
            },
            methods: {
                addTodo: function() {
                    this.todolist.push({
                        name: this.newtodo,
                        completed: false
                    });
                    this.newtodo = '';
                },
                delTodo: function(item) {
                    this.todolist.splice(this.todolist.indexOf(item), 1);
                }
            },
            watch: {
                todolist: function(todolist) {
                    todoStorage.save(todolist);
                }
            }
        });
    </script>
</body>

</html>

现在就可以通过点击按钮切换分类了

一个简单的TODO应用就完成了