用vue实现一个todoMVC备忘录

此项目是基于vue官网上的todoMVC示例仿写而成。

基于MVC设计思想,用到了vue2中的双向绑定、侦听器深度监听、计算属性、过滤器、自定义指令,以及基于localStorage的永久化存储和路由监听触发事件。此项目覆盖了vue的大部分基础知识。

功能点:

  1. 刷新页面,列表中的内容不会丢失,保持刷新前的状态。
  2. input输入框输入内容,回车后内容渲染到input下方列表,原input框内容清空。
  3. 双击input输入框旁边向下的方向箭头,选中所有列表,箭头高亮。再次点击,取消全部选中,箭头变暗。
  4. 点击列表单行文字部分,进入input编辑框,修改内容,回车或者鼠标点击别处保存,按Esc键退出input框,内容回到编辑前。
  5. 点击ALL(全部事项)、Active(未完成事项)、Completed(已完成事项),列表渲染状态切换。item left的数值与Active条数一致。点击Clear completed,删除 Completed相关列表。

那么,接下来我们按照功能点一个个实现它们吧!~

 

刷新页面,列表中的内容不会丢失,保持刷新前的状态

用到的知识点:localStorage 永久储存

定义一个key名,然后通过localStorage将数据储存在本地。如果取出,则通过key名将储存在本地中的数据取出。

let STORAGE_KEY = 'todos-vuejs'
    let todoStorage = {
        fetch: function () {
            let todos = JSON.parse(localStorage.getItem(STORAGE_KEY) || "[]")
            todos.forEach((value, index) => {
                value.id = index
            })
            todoStorage.uid = todos.length
            return todos
        },
        save: function (todos) {
            localStorage.setItem(STORAGE_KEY, JSON.stringify(todos))
        }
    }

 

input输入框输入内容,回车后内容渲染到input下方列表,原input框内容清空

用到的知识点:双向绑定,事件修饰符

通过v-model="newTodo“ 双向绑定,再用@keyup.enter回车修饰符触发addTodo事件。

addTodo事件中将双向绑定的newTodo的值trim()去掉空格,然后push进todos列表数组中。最后再将newTodo的值清空。

<header class="header">
        <h1>备忘录</h1>
        <input class="new-todo"
               autofocus autocomplete="off"
               placeholder="准备做什么?"
               v-model="newTodo"
               @keyup.enter="addTodo">
    </header>
data: {
            todos: todoStorage.fetch(),
            newTodo: '',
        },
methods: {
    addTodo: function () {
                let value = this.newTodo && this.newTodo.trim()
                if (!value) {
                    return
                }
                this.todos.push({
                    id: todoStorage.uid++,
                    title: value,
                    completed: false
                })
                this.newTodo = ''
            },
    }

 

点击input输入框旁边向下的方向箭头,选中所有列表,箭头高亮。再次点击,取消全部选中,箭头变暗

用到的知识点:计算属性(计算属性默认只有 getter ,不过在需要时你也可以提供一个 setter )

点击input输入框旁边向下的方向箭头,实际上就是点击了checkbox,值为true,随即便触发了allDone中的set与get,通过forEach遍历将todos数组列表中的每一项的todo.completed变为true,即列表全部选中。再次点击取消,即又触发allDone中的set与get,列表全部取消选中。

单独点击列表单行checkbox即单行选中状态,v-model绑定值为todo.completed,与上面的allDone计算属性中的数据相呼应。

<input type="checkbox" id="toggle-all" class="toggle-all" v-model="allDone">
<label for="toggle-all"></label>
<ul class="todo-list">
            <li v-for="todo in filteredTodos"
                :key="todo.id"
                class="todo"
                :class="{completed:todo.completed, editing: todo == editedTodo}">
                <div class="view">
                    <input type="checkbox" class="toggle" v-model="todo.completed">
                </div>
            </li>
        </ul>
computed: {
            allDone: {
                get: function () {
                    return this.remaining === 0
                },
                set: function (value) {
                    this.todos.forEach((todo) => {
                        todo.completed = value
                    })
                }
            }
        },

 

双击列表单行文字部分,进入input编辑框,修改内容,回车或者鼠标点击别处保存,按Esc键退出input框,内容回到编辑前

用到的知识点:双击事件,事件修饰符,自定义指令,深度监听

@dblclick="editTodo(todo)" 双击触发editTodo(todo)事件,将当前todo.title值存入this.beforeEditCache中。

未双击前input设为隐藏,双击后根据条件判断 将label隐藏,input显示,失焦点或者退出 则值反过来。

 事件修饰符用到了:@blur失去焦点修饰符与@keyup.enter回车修饰符和@keyup.esc退出修饰符。

v-todo-focus="todo == editedTodo" 自定义指令当指令值为true时,input聚焦(详情可见vue自定义指令

当数据结构层级超过一层,常规的watch监听不一定能触发,此时需要深度监听。例如这里的todos数据改变。

<ul class="todo-list">
            <li v-for="todo in filteredTodos"
                :key="todo.id"
                class="todo"
                :class="{completed:todo.completed, editing: todo == editedTodo}">
                <div class="view">
                    <input type="checkbox" class="toggle" v-model="todo.completed">
                    <label @dblclick="editTodo(todo)">{{todo.title}}</label>
                    <button class="destroy" @click="removeTodo(todo)"></button>
                </div>
                <input type="text" class="edit"
                       v-model="todo.title"
                       v-todo-focus="todo == editedTodo"
                       @blur="doneEdit(todo)"
                       @keyup.enter="doneEdit(todo)"
                       @keyup.esc="cancelEdit(todo)">
            </li>
        </ul>
watch: {//深度监听
            todos: {
                handler: function (todos) {
                    todoStorage.save(todos)
                },
                deep: true
            }
        },
 methods: {//方法
            removeTodo: function (todo) {
                this.todos.splice(this.todos.indexOf(todo), 1)
            },
            editTodo: function (todo) {
                this.beforeEditCache = todo.title
                this.editedTodo = todo
            },
            doneEdit: function (todo) {
                if (!this.editedTodo) {
                    return
                }
                this.editedTodo = null
                todo.title = todo.title.trim()
                if (!todo.title) {
                    this.removeTodo(todo)
                }
            },
            cancelEdit: function (todo) {
                this.editedTodo = null
                todo.title = this.beforeEditCache
            },
           
        },
        directives: {//自定义指令
            'todo-focus': function (el, binding) {
                if (binding.value) {
                    el.focus()
                }
            }
        }

 

点击ALL(全部事项)、Active(未完成事项)、Completed(已完成事项),列表渲染状态切换。item left的数值与Active条数一致。点击Clear completed,删除 Completed相关列表

用到的知识点:过滤器,路由监听

这里用到了2个过滤器,第一个filters变量是自己定义的,内部有三个方法,分别过滤:全部,未完成的,已完成的。第二个过滤器为vue内部过滤器,通过双花括号{{  |  }}  语法实现。

路由监听则通过window.location.hash取参数值,通过正则取参数后,再结合自定义过滤器来实现todos数据列表过滤。

另外需要注意在window下监听hashchange,否则当url改变时,列表会无法更新。

<footer class="footer" v-show="todos.length" v-cloak>
        <span class="todo-count">
            <strong>{{remaining}}</strong>{{remaining | pluralize}} left
        </span>
        <ul class="filters">
            <li><a href="#/all" :class="{selected: visibility == 'all'}">ALL</a></li>
            <li><a href="#/active" :class="{selected: visibility == 'active'}">Active</a></li>
            <li><a href="#/completed" :class="{selected: visibility == 'completed'}">Completed</a></li>
        </ul>
        <button class="clear-completed" @click="removeCompleted" v-show="todos.length > remaining">
            Clear completed
        </button>
    </footer>
let filters = {//自定义的过滤器
        all: function (todos) {
            return todos
        },
        active: function (todos) {
            return todos.filter((value) => {
                return !value.completed
            })
        },
        completed: function (todos) {
            return todos.filter((value) => {
                return value.completed
            })
        }
    }

let app = new Vue({
        data: {
            todos: todoStorage.fetch(),
            visibility: 'all'
        },
        computed: {
            filteredTodos: function () {
                return filters[this.visibility](this.todos)
            },
            remaining: function () {
                return filters.active(this.todos).length
            },
        },
        filters: {//vue内部过滤器
            pluralize: function (n) {
                return n === 1 ? 'item' : 'items'
            }
        },
        methods: {
            removeCompleted: function () {
                this.todos = filters.active(this.todos)
            }
        },
})

function onHashChange() {//监听路由
        let visibility = window.location.hash.replace(/#\/?/, '')
        if (filters[visibility]) {
            app.visibility = visibility
        } else {
            window.location.hash = ''
            app.visibility = 'all'
        }
    }

    window.addEventListener('hashchange', onHashChange)
    onHashChange()

 

最后挂载即可

app.$mount('.todoapp')

 

项目展示链接:https://a-tione.github.io/todoMVC/index.html 用vue实现一个便利贴

项目代码:https://github.com/A-Tione/todoMVC

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值