vue.js实现任务列表(ToDoMVC)

案例介绍


需求说明


开始

# 下载模板到本地,重命名为 todomvc-vue
# --depth=1 表示只下载最后一次的 commit,其它历史记录不要,这样可以提高下载速度
git clone https://github.com/tastejs/todomvc-app-template.git todomvc-vue --depth=1

# 切换到 todomvc-vue 目录中,安装依赖项
cd todomvc-vue
npm install

# 打开 todomvc-vue 中的 index.html 预览模板

配置 browser-sync 浏览器同步测试工具

  1. 安装依赖

    
    # 也可以 npm i -D browser-sync
    
    npm install --save-dev browser-sync
  2. 配置 scripts

    "scripts": {
    "dev": "browser-sync start --server --files \"*.html, css/*.css, js/*.js\"",
    "start": "npm run dev"
    }
  3. 启动开发服务

    
    # 或者 npm start
    
    npm run dev

    主要实现一个日常任务的记录功能。
    模板下载链接:https://github.com/tastejs/todomvc-app-template
    业务代码:
    index.html

<!doctype html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>todos</title>
        <link rel="stylesheet" href="node_modules/todomvc-common/base.css">
        <link rel="stylesheet" href="node_modules/todomvc-app-css/index.css">
        <!-- CSS overrides - remove if you don't need it -->
        <link rel="stylesheet" href="css/app.css">
    </head>
    <body>
        <section id = "app" class="todoapp">
            <header class="header">
                <h1>{{ message }}</h1>
                <input class="new-todo" 
                       v-model="todoText"
                       @keydown.enter="handleNewTodo" 
                       placeholder="What needs to be done?" autofocus>
            </header>
            <!-- This section should be hidden by default and shown when there are todos -->
            <template v-if="todos.length">
                <section class="main">
                <input id="toggle-all"  v-model="toggleAllStat" class="toggle-all" type="checkbox" >
                <label for="toggle-all">Mark all as complete</label>
                <ul class="todo-list">
                    <!-- These are here just to show the structure of the list items -->
                    <!-- List items should get the class `editing` when editing and `completed` when marked as completed -->
                    <!--当双击的时候,就把currentEditing = 当前双击的这个对象,对任务项 class 的 editing 都有一个判定:currentEditing===本身 则 edting 样式给本身-->
                    <li v-for="(item,index) in filterTodos" :class = "{completed:item.completed,editing:currentEditting === item}">
                        <div class="view">
                            <input class="toggle" type="checkbox" v-model = "item.completed">
                                <label @dblclick = "handleGetEdittingDblclick(item)">
                                        {{ item.title }}
                                </label>
                            <button class="destroy" @click = "handleRemoveTodo(index)"></button>
                        </div>
                        <!--由于有双击取消不保存的功能(v-bind),这里不使用双向数据绑定(v-model)-->
                        <input class="edit" :value="item.title" 
                        @keydown.enter = "handleSaveEdit(item,index,$event)" 
                        @blur = "handleSaveEdit(item,index,$event)"
                        @keydown.esc = "handleCanceEditEsc">
                    </li>

                </ul>
            </section>
            <!-- This footer should hidden by default and shown when there are todos -->
            <footer class="footer">
                <!-- This should be `0 items left` by default -->
                <span class="todo-count"><strong>{{ remainingCount }}</strong> item left</span>
                <!-- Remove this if you don't implement routing -->
                <ul class="filters">
                    <li>
                        <a :class="{selected:filterText === ''}" href="#/">All</a>
                    </li>
                    <li>
                        <a :class="{selected:filterText === 'active'}" href="#/active">Active</a>
                    </li>
                    <li>
                        <a :class="{selected:filterText === 'completed'}" href="#/completed">Completed</a>
                    </li>
                </ul>
                <!-- Hidden if no completed items are left ↓ -->
                <!--every方法,条件中 item.completed 全部为true,则返回 true
                    some 方法,条件中 item.completed 只要有一个 true 则为true,必须所有的为 false 才为false-->
                <button v-if="todos.some(item => item.completed)" class="clear-completed" @click = "handleClearCompleted">Clear completed</button>
            </footer>
            </template>
        </section>
        <footer class="info">
            <p>Double-click to edit a todo</p>
            <!-- Remove the below line ↓ -->
            <p>Template by <a href="http://sindresorhus.com">Sindre Sorhus</a></p>
            <!-- Change this out with your name and url ↓ -->
            <p>Created by <a href="http://todomvc.com">you</a></p>
            <p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
        </footer>
        <!-- Scripts here. Don't remove ↓ -->

        <script src = "https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
        <script src="js/app.js"></script>
    </body>
</html>

app.js




;(function(){
    // const todos = [
    //  {
    //      id:1,
    //      title:'吃饭',
    //      completed:true
    //  },
    //  {
    //      id:2,
    //      title:'睡觉',
    //      completed:false
    //  },
    //  {
    //      id:3,
    //      title:'写代码',
    //      completed:true
    //  },
    // ]
    window.app = new Vue({
        data:{
            message:'todos_lzm1',
            todos:JSON.parse(window.localStorage.getItem('todos') || '[]'),
            todoText:'',
            currentEditting:null,
            filterText:'all'
        },

        //计算属性是vue的一大特色
        //一种带有行为的属性,本质是方法,本质是方法但是不能当做方法来调用,必须当做属性来使用
        //好处:相比方法的优势是会缓存计算的结果,效率很高
        //计算属性不是方法,只能当做属性来使用
        //
        computed:{
            //该成员就是一个方法,但是在使用的时候不能调用,
            //简写方式,
            // remainingCount(){
            //  return this.todos.filter(t => !t.completed).length

            // }
            //完整写法
            remainingCount:{
                //当你访问 remainingCount 时会默认调用 get() 方法
                get(){
                    return this.todos.filter(t => !t.completed).length
                },
                //当你 实例.remainingCount = xxx 的时候会自动调用 set 方法
                // set(){
                // }
            },

            toggleAllStat:{
                get(){
                    //计算属性知道他依赖todos
                    //当 todos 发生改变的时候,计算属性也会发生变化
                    return this.todos.every(t => t.completed)

                },
                set(){
                    //在自己的方法中调用自己就是访问自己的 get 方法
                    const checked = !this.toggleAllStat
                    this.todos.forEach(item => {
                    item.completed = checked
                })


                }
            },

            filterTodos(){
                switch(this.filterText){
                    case 'active':
                        return this.todos.filter(t => !t.completed)
                        break
                    case 'completed':
                        return this.todos.filter(t => t.completed)
                        break
                    default:
                    return this.todos
                        break
                }
            }
        },
        //监视成员的改变
        watch:{
            //当监视到 todos 改变时会调用 handler 方法
            todos:{
            handler(val,oldval){
                //监视到 todos 的变化,把 todos 本次存储记录数据的状态
                window.localStorage.setItem('todos',JSON.stringify(this.todos))

            },
            deep:true //深度监视,只有这样才能监视到 todos 孩子...孩子...的变化
            }
        },

        methods:{
            handleNewTodo(){//添加新的要完成的任务
                //console.log('111')
                const todoText = this.todoText.trim()
                if(!todoText.length){//非空判断
                    return
                }
                const todos = this.todos
                todos.push({//将要添加的内容push到todos中
                    //如果数据元素为空就给1,否则就是最后一个元素的 id + 1
                    id:todos.length ? todos[todos.length-1].id+1:1,
                    title:todoText,
                    completed:false
                })
                this.todoText=''//添加完后清空
            },

            // handleToggleAll(e){//选中和取消所有任务项
            //  //0.绑定 checkbox 的 change 事件
            //  //1.获取 CheckBox 的选中状态
            //  //2.循环所有子任务项的选中状态,将 checkbox 的选中状态赋值给它们
            //  const checked = e.target.checked
            //  this.todos.forEach(item => {
            //      item.completed = checked
            //  })
            // },
            //当事件函数没有传参数的时候,第一个参数就是默认的事件原对象:event
            //当事件函数有参数的时候,就没有办法获取事件原函数:event
            //可在传递参数的时候手动在调用方法的时候 $event 来接受 event 事件对象
            handleRemoveTodo(index){//删除单个任务项
                //console.log(index)
                this.todos.splice(index,1)
            },

            handleGetEdittingDblclick(todo){//双击获取编辑框
                console.log(todo)
                this.currentEditting = todo
            },

            //保存编辑任务,敲回车保存
            handleSaveEdit(todo,index,e){
                //0.注册绑定事件处理函数
                //1.获取文本框的数据
                //2.数据校验
                //      如果数据为空,则删除文本框
                //      否则保存编辑
                //console.log(e.target.value)
                const value = e.target.value
                if(!value.length){//若为空,则删除
                    this.todos.splice(index,1)
                }else{    //保存
                    todo.title = value 
                    this.currentEditting = null//取消样式
                }

            },

            //按下esc键取消编辑,不保存
            handleCanceEditEsc(){
                //取消样式即可
                this.currentEditting = null
            },

            handleClearCompleted(){ //点击clear completed清除已完成的选项
                //注意不要在foreach中删除元素,可以使用for循环,每删一个可以手动的改变索引
                for (var i = 0; i < this.todos.length; i++) {
                    if(this.todos[i].completed){
                        this.todos.splice(i,1)
                        i--
                    }
                }

            },

            //获取剩余未完成任务数量
            // getRemainCount(){
            //  return this.todos.filter(t => !t.completed).length

            // }
        }
    }).$mount('#app')

    //该事件只有 change 的时候才会执行,页面初始化的时候不会执行
    //注册 hash(锚点) 的改变事件
window.onhashchange = function(){
    app.filterText = window.location.hash.substr(2)
}
//页面初始化的时候调用一次,保持路由状态
window.onhashchange()
})()

效果展示:
这里写图片描述

猜你喜欢

转载自blog.csdn.net/ooo123lll/article/details/80272074