• 框架入门经典项目TodoMVC


    一、项目介绍

    ①地址:http://todomvc.com/

    ②GitHub下载模板

    ③通过npm下载模板的样式

    ④通过npm下载Vuejs

    ⑤项目文件,主要修改app.js和index.html两个文件

    二、使用Vuejs需求实现(主体思路)

    ①列表渲染

    • 有数据的时候展示出来:v-if 的使用
    (function (Vue) {
        let todos=[
            {id:1,title:'睡觉',completed:true},
            {id:2,title:'美食',completed:false},
            {id:3,title:'代码',completed:true}
        ]
        new Vue({
            el:'#todoapp',
            data:{
                todos:todos,
            },
    })(Vue);
                        <li v-for="item of todos" v-bind:class='{completed:item.completed}'>
                            <div class="view">
                                <input class="toggle" type="checkbox" v-model='item.completed'>
                                <label>{{item.title}}</label>
                                <button class="destroy"></button>
                            </div>
                            <input class="edit" value="Create a TodoMVC template">
                        </li>
    • 没有数据的时候隐藏main部分:添加一个不会出现在页面的template模板,并且使用v-if,当todos没有数据的时候,长度为0
                <template v-if='todos.length'>
                <!-- This section should be hidden by default and shown when there are todos -->
                <section class="main"> ..... </section>
                <!-- This footer should hidden by default and shown when there are todos -->
                <footer class="footer"> .... </footer>
                </template>

    ②添加任务

    • 页面初始化获得焦点:自定义指令注册一个全局自定义指令 `v-focus`,然后在input里直接使用
        // 自定义指令,自动获取焦点
        Vue.directive('focus', {
            inserted: function (el) {
                  el.focus();
            }
          });
    <input class="new-todo" placeholder="What needs to be done?" @keyup.enter='addTodo' v-focus>
    • 敲回车添加到任务列表:鼠标抬起注册addTodo事件,追加数据
    • 不允许有非空数据:为空时,return返回
    • 添加完成后清空文本框:令event.target.value= ' '
    <header class="header">
         <h1>todos</h1>
         <input class="new-todo" placeholder="What needs to be done?" @keyup.enter='addTodo'>
    </header>
            methods:{
                // 添加任务
                addTodo(event){
                    let todoText=event.target.value.trim();
                    if(!todoText.length){
                        return
                    }
                    let id=this.todos[this.todos.length-1].id+1;
                    this.todos.push({
                        id:id,
                        title:todoText,
                        completed:false,
                    });
                    event.target.value='';
                },

    ③标记所有任务完成或者未完成:点击的时候注册toggleAll事件处理函数

    <input @click='toggleAll' id="toggle-all" class="toggle-all" type="checkbox">
                toggleAll(event){
                    let checked=event.target.checked;
                    this.todos.forEach(todo => todo.completed=checked);
                },

    ④任务项

    • 切换任务完成状态:v-bind绑定一个class=“{类名:布尔值}”,当布尔值为true,作用这个类名,当布尔值为false,则去除这个类名
    <li v-for="item of todos" v-bind:class='{completed:item.completed}'>
    • 删除单个任务项:@click=‘ removeTodo(index,$event) ’ ,传入两个参数,删除的索引index和事件$event(传参以后,正常的event获取不到),然后处理函数利用数组方法splice操作
    <button class="destroy" @click='removeTodo(index,$event)' ></button>
                removeTodo(delIndex,event){
                    this.todos.splice(delIndex,1);
                },
    • 双击label进入编辑模式:这里使用一个中间变量currentEditing,默认为null,也就是所有的任务项都没有editing样式,editing的样式取决于中间变量是否等价于当前任务项,当双击的时候,手动把中间量等于双击的当前任务项,这样editing样式就为true,也就是起作用了。
    <li v-for="(item,index) of todos" v-bind:class='{completed:item.completed,editing:item===currentEditing}'>
    <label @dblclick="currentEditing=item">{{item.title}}</label>
            data:{
                todos:todos,
                currentEditing:null,

    ⑤编辑任务项

    • 编辑文本框自动获得焦点:局部自定义指令,自动获取焦点‘ editing-focus
    <input class="edit" :value='item.title' @blur='saveEdit(item,index,$event)' @keyup.enter='saveEdit(item,index,$event)' @keyup.esc='currentEditing=null' v-editing-focus="item===currentEditing">
            directives:{
                // 局部自定义属性
                editingFocus:{
                    update(el,binding){
                        if(binding.value){
                            el.focus();
                        }
                    },
                },
            },
    • 在编辑文本框敲回车后者失去焦点后,如果为空,则直接删除这个item,如果不为空,保存这个数据,并去除editing样式:saveEdit处理函数,传入参数
    • 输入状态按下esc取消编辑:设置默认value属性是item的title,按下esc抬起的时候,令中间变量为null,去除editing样式
    <input class="edit" :value='item.title' @blur='saveEdit(item,index,$event)' @keyup.enter='saveEdit(item,index,$event)' @keyup.esc='currentEditing=null'>
                saveEdit(item,index,event){
                    var editText=event.target.value.trim();
                    // 如果为空,直接删除这个item
                    if(!editText.length){
                        return this.todos.splice(index,1);
                    }
                    // 如果不为空,修改title的值,然后去除eiditing样式
                    item.title=editText;
                    this.currentEditing=null;
                },

    ⑥其他(footer部分)

    • 显示所有未完成任务数:@click=‘ removeAllDone ’ ,处理事件利用数组方法filter过滤未完成数据,然后重新赋值给数据列表
    <button class="clear-completed" @click='removeAllDone'>Clear completed</button>
                removeAllDone(){
                    this.todos=this.todos.filter((item,index)=>{
                        return !item.completed;//return true,即item.completed为false
                    });
                },
    • 清除所有的已完成任务:利用计算属性computed的自定义方法leftCount(参考vue教程--计算属性),还有一种方法就是模板中调用处理函数,处理函数使用for循环来删除,但是删完需要把循环索引i--,但是这种方法没有缓存,每一次使用都要重新调用,推荐使用计算属性,效率更高。
    <span class="todo-count"><strong>{{leftCount}}</strong> item left</span>
            computed:{
                leftCount:function(){
                    return this.todos.filter(item => !item.completed).length
                }
            },
    • 将数据持久化到localStorage中(待完成):利用watch功能(配置deep,深度监视),计算属性用于需要在模板中绑定输出值,而watch观察者则用于根据需要数据的改变从而定制特殊功能业务
    • 路由状态切换:data里添加属性filterState默认为‘all’;计算属性computed增加filtertodos方法,过滤不同状态的路由;同时修改列表渲染为遍历filterTodos;在window里添加路由改变事件onhashchange,并且每次页面进来需要执行一次保持上一次的状态;改变点击时的样式,添加属性selected当为true时作用,即filterState会等于路由的时候,样式生效。
            data:{
                todos:todos,
                currentEditing:null,
                filterState:'all',
            },
            computed:{
                leftCount:function(){
                    return this.todos.filter(item => !item.completed).length
                },
                filterTodos:function(){
                    switch(this.filterState){
                        case 'active':
                            return this.todos.filter(item=>!item.completed);
                            break;
                        case 'completed':
                            return this.todos.filter(item=>item.completed);
                            break;
                        default:
                            return this.todos;
                            break;
                    };
                },
    <li v-for="(item,index) of filterTodos" v-bind:class='{completed:item.completed,editing:item===currentEditing}'>
        // 路由状态切换
        window.onhashchange=function(){
            var hash=window.location.hash.substr(2) || 'all';
            window.app.filterState=hash;
        };
        // 页面第一次进来,保持状态
        window.onhashchange();
                    <ul class="filters">
                        <li>
                            <a :class="{selected:filterState==='all'}" href="#/">All</a>
                        </li>
                        <li>
                            <a :class="{selected:filterState==='active'}" href="#/active">Active</a>
                        </li>
                        <li>
                            <a :class="{selected:filterState==='completed'}" href="#/completed">Completed</a>
                        </li>
                    </ul>

    三、项目完整代码和效果展示

    <!doctype html>
    <html lang="en">
        <head>
            <meta charset="utf-8">
            <meta name="viewport" content="width=device-width, initial-scale=1">
            <title>Template • TodoMVC</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>
            <!-- id="todoapp"vue管理模块入口 -->
            <section id="todoapp" class="todoapp">
                <header class="header">
                    <h1>todos</h1>
                    <input class="new-todo" placeholder="What needs to be done?" @keyup.enter='addTodo' v-focus>
                </header>
                <template v-if='todos.length'>
                <!-- This section should be hidden by default and shown when there are todos -->
                <section class="main">
                    <!-- @click='toggleAll'点击事件 -->
                    <input @click='toggleAll' id="toggle-all" class="toggle-all" type="checkbox" v-bind:checked='toggleState'>
                    <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 -->
                        <!-- vue列表渲染 -->
                        <li v-for="(item,index) of filterTodos" v-bind:class='{completed:item.completed,editing:item===currentEditing}'>
                            <div class="view">
                                <input class="toggle" type="checkbox" v-model='item.completed'>
                                <label @dblclick="currentEditing=item">{{item.title}}</label>
                                <button class="destroy" @click='removeTodo(index,$event)' ></button>
                            </div>
                            <input class="edit" :value='item.title' @blur='saveEdit(item,index,$event)' @keyup.enter='saveEdit(item,index,$event)' @keyup.esc='currentEditing=null' v-editing-focus="item===currentEditing">
                        </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>{{leftCount}}</strong> item left</span>
                    <!-- Remove this if you don't implement routing -->
                    <ul class="filters">
                        <li>
                            <a :class="{selected:filterState==='all'}" href="#/">All</a>
                        </li>
                        <li>
                            <a :class="{selected:filterState==='active'}" href="#/active">Active</a>
                        </li>
                        <li>
                            <a :class="{selected:filterState==='completed'}" href="#/completed">Completed</a>
                        </li>
                    </ul>
                    <!-- Hidden if no completed items are left ↓ -->
                    <button class="clear-completed" @click='removeAllDone'>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="node_modules/todomvc-common/base.js"></script>
            <script src="node_modules/vue/dist/vue.js"></script>
            <script src="js/app.js"></script>
        </body>
    </html>
    index.html
    (function (Vue) {
        // 数据
        let todos=[
            {id:1,title:'睡觉',completed:true},
            {id:2,title:'美食',completed:false},
            {id:3,title:'代码',completed:true}
        ];
        // 全局自定义指令,自动获取焦点
        Vue.directive('focus', {
            inserted: function (el) {
                  el.focus();
            }
        });
        
        // vue实例
        window.app=new Vue({
            el:'#todoapp',
            data:{
                todos:todos,
                currentEditing:null,
                filterState:'all',
                toggleAllstate:true,
            },
            computed:{
                leftCount:function(){
                    return this.todos.filter(item => !item.completed).length
                },
                filterTodos:function(){
                    switch(this.filterState){
                        case 'active':
                            return this.todos.filter(item=>!item.completed);
                            break;
                        case 'completed':
                            return this.todos.filter(item=>item.completed);
                            break;
                        default:
                            return this.todos;
                            break;
                    };
                },
                // 全选的联动效果
                toggleState:function(){
                    return this.todos.every(item=>item.completed);
                },
            },
            methods:{
                // 添加任务
                addTodo(event){
                    let todoText=event.target.value.trim();
                    if(!todoText.length){
                        return
                    }
                    const lastTodo=this.todos[this.todos.length-1];
                    const id=lastTodo?lastTodo.id+1:1;
                    this.todos.push({
                        id:id,
                        title:todoText,
                        completed:false,
                    });
                    event.target.value='';
                },
                // 点击全部完成或者未完成
                toggleAll(event){
                    let checked=event.target.checked;
                    this.todos.forEach(todo => todo.completed=checked);
                },
                // 删除单个任务项
                removeTodo(delIndex,event){
                    this.todos.splice(delIndex,1);
                },
                // 显示所有未完成任务数(删除所有已完成)
                removeAllDone(){
                    this.todos=this.todos.filter((item,index)=>{
                        return !item.completed;//return true,即item.completed为false
                    });
                },
                // 保存编辑项
                saveEdit(item,index,event){
                    var editText=event.target.value.trim();
                    // 如果为空,直接删除这个item
                    if(!editText.length){
                        return this.todos.splice(index,1);
                    }
                    // 如果不为空,修改title的值,然后去除eiditing样式
                    item.title=editText;
                    this.currentEditing=null;
                },
            },
            directives:{
                // 局部自定义属性
                editingFocus:{
                    update(el,binding){
                        if(binding.value){
                            el.focus();
                        }
                    },
                },
            },
        });
        // 路由状态切换
        window.onhashchange=function(){
            var hash=window.location.hash.substr(2) || 'all';
            window.app.filterState=hash;
        };
        // 页面第一次进来,保持状态
        window.onhashchange();
    })(Vue);
    app.js

  • 相关阅读:
    转载:从git仓库中删除不必要的文件
    问题:Swiper父容器隐藏时实例化组件,组件滑动失效
    图片预加载
    移动端苹果手机:图片没有加载完成前,白色边框线是怎么来的
    bower 安装依赖提示 EINVRES Request to https://bower.herokuapp.com/packages/xxx failed with 502
    H5序列帧播放
    盟军敢死队
    二维游戏开发的点滴
    用c语言开发游戏 快乐的痛 笑着哭
    ibatis
  • 原文地址:https://www.cnblogs.com/EricZLin/p/9369260.html
Copyright © 2020-2023  润新知