案例:todomvc
todos-项目初始化
下载todos基础模板
git clone https://github.com/tastejs/todomvc-app-template
安装todos依赖
cd todomvc-app-template npm install
安装vue.js
npm install vue
项目中引入vue.js
index.html
<!-- Scripts here. Don't remove ↓ --> <script src="node_modules/vue/dist/vue.global.js"></script> <script src="js/app.js"></script>
js/app.js
:(function (window) { const app = Vue.createApp({ data(){ return { // 任务列表 todos: [ {id: 1, name: '吃饭', flag: true}, {id: 2, name: '喝酒', flag: false}, {id: 3, name: '唱歌', flag: false} ] } } }) // 挂载 const vm = app.mount('.todoapp') // 非必要,方便于浏览器控制台通过vm调用vue实例对象 window.vm = vm })(window);
todos-任务的列表渲染
- 使用v-for指令把列表动态渲染 , 指定key
- 内容写死了, 使用插值表达式或者v-text完成
{{item.name}}
- 任务完成状态 通过控制completed类型
:class="{completed: item.flag}"
- 复选框选中的问题
<input class="toggle" type="checkbox" v-model="item.flag">
<ul class="todo-list">
<!-- 显示任务列表 -->
<!-- v-for 遍历todos列表,渲染到li标签内-->
<!--
如果任务完成了,应该给li添加一个completed类
如果任务正在被编辑,应该给li添加一个editing的类
任务完成状态 通过控制completed类型 :class="{completed: item.flag}"
-->
<!-- 复选框根据任务状态item.flag 来显示选中状态,双向绑定 -->
<li v-for="item in todos" :key="item.id" :class="{completed:item.flag}">
<div class="view">
<input class="toggle" type="checkbox" v-model="item.flag">
<label>{{ item.name }}</label>
<button class="destroy"></button>
</div>
<input class="edit" value="Create a TodoMVC template">
</li>
</ul>
todos-任务的删除
- 给删除按钮注册点击事件
- 获取到删除任务id
- 根据id把list中对应的任务删除即可
<ul class="todo-list">
<li v-for="item in todos" :key="item.id" :class="{completed:item.flag}">
<div class="view">
<input class="toggle" type="checkbox" v-model="item.flag">
<label>{{ item.name }}</label>
<!--do:给删除按钮注册点击事件-->
<button class="destroy" @click="delTodo(item.id)"></button>
</div>
<input class="edit" value="Create a TodoMVC template">
</li>
</ul>
在methods中添加delTodo函数
delTodo(id) {
// 过滤 只要把id不同留下来
this.todos = this.todos.filter(item => item.id !== id)
}
todos-任务的添加
- 获取到输入的内容
- 要注册keyup事件,回车的时候添加任务
- 把获取到内容添加到list中,最前面
<header class="header">
<h1>todos</h1>
<!--输入框与数据双向绑定-->
<!--注册回车事件-->
<input class="new-todo" placeholder="What needs to be done?" v-model="todoName" @keyup.enter="addTodo" autofocus>
</header>
在methods中添加addTodo函数
addTodo(){
this.todos.unshift({
id: +new Date(),
name:this.todoName,
flag: false
})
this.todoName=''
}
todos-任务的修改
- 给每一个lable都注册双击事件 dblclick
- 让当前这个li有editing这个类, input框就能显示出来
难点:怎么让点击的这个li有editing类 - 首先需要一个属性now,记录下来点击了任务的id ,初始 = -1
- 双击的时候,修改now为点击的任务的id
- 给li中的editing类添加判断
{editing: item.id === now}
- 给input框双向数据绑定
<input class="edit" v-model="item.name">
- 回车的时候,注册keyup事件 ,只需要editing类为false 只需要让now为-1
<ul class="todo-list">
<!-- 显示任务列表 -->
<!--add:如果任务正在被编辑,应该给li添加一个editing的类-->
<li v-for="item in todos" :key="item.id" :class="{completed:item.flag,editing:item.id===now}">
<div class="view">
<input class="toggle" type="checkbox" v-model="item.flag">
<!-- add: 添加双击监听,双击后显示编辑输入框-->
<label @dblclick="showEdit(item.id)">{{ item.name }}</label>
<button class="destroy" @click="delTodo(item.id)"></button>
</div>
<!--add:添加回车监听,回车后,输入框隐藏-->
<input class="edit" @keyup.enter="editTodo" v-model="item.name">
</li>
</ul>
在methods中添加showEdit,editTodo函数
// 显示修改框
showEdit(id){
this.now = id
},
// 修改任务
editTodo(){
this.now=-1
}
todos-底部的显示和隐藏
- 当任务列表的长度为0,没任务,底部就隐藏
- 当任务列表的长度不为0,底部就显示
<footer class="footer" v-show="isShowFooter">
vue实例中添加computed计算属性
computed:{
isShowFooter() {
// 当任务数量超过0,就显示footer
return this.todos.length > 0
}
}
todos-统计未完成的任务
<span class="todo-count"><strong>{{ unCompletedCount }}</strong> item left</span>
vue实例中添加computed计算属性
// 统计未完成的任务
unCompletedCount(){
// 过滤出未完成的任务
// this.todos.filter(item => !item.flag)
return this.todos.filter(item => !item.flag).length
}
todos-清空已经完成的任务
- 控制清空按钮的显示隐藏
- 点击清空按钮,把所有已经完成的任务清除
<!-- Hidden if no completed items are left ↓ -->
<!--add: 控制隐藏,监听点击事件-->
<button class="clear-completed" v-show="isShowClear" @click="clearTodo">Clear completed</button>
在计算属性computed中添加属性
// 控制清空按钮的显示隐藏
isShowClear(){
// 如果没有已完成任务,就隐藏
// 如果存在已完成任务,便显示
return this.todos.some(item => item.flag)
}
在方法methods中添加方法
// 清空已完成任务
clearTodo(){
// 过滤保留未完成任务
this.todos = this.todos.filter(item => !item.flag)
}
计算属性
计算属性:当计算属性依赖的数据发生改变的时候,计算属性会重新计算一次.
如果计算属性依赖的属性没有发生改变,那么计算属性就不会重新计算。
- 计算属性写到computed属性中
- 计算属性和data一样,也是用来提供的属性,computed中提供的属性是需要一定的计算才能得到结果的属性
- 计算属性写法上是一个函数,计算属性实质上是一个属性,对应的函数的返回值。
- 计算属性只会算一次,就会把结果存储起来,非常的高效
- 计算属性只有当依赖的数据发生了改变,就会重新计算
- 什么时候使用计算属性:需要在插值表达式或者指令中书写复杂的逻辑,都应该由计算属性来提供
基本使用
const app = Vue.createApp({
// data 提供普通属性
data() {
// Data函数应该返回一个对象
return {
n1:'',
n2:''
}
},
// methods 提供事件函数
methods: {
},
// computed 提供计算属性
computed:{
//n3依赖与n1和n2的值,当n1 和 n2发生改变的时候,这个函数就会执行。
//返回值就是n3的值
n3(){
return +this.n1 + +this.n2;
}
}
})
const vm = app.mount('#app')
计算属性是基于它们的依赖项进行缓存的
如果页面中需要使用多次计算属性的值,只会计算一次,计算属性只有在它的相关依赖发生改变时才会重新求值。
计算属性不能与data中的属性同名,因为无论是data中的属性还是计算属性,最终都是挂载到vm上的
计算属性的完整形态get/set
- 计算属性默认情况只提供了get,只能获取
- 计算属性默认不能修改,非要修改,比如v-model绑定的是一个计算属性,必须用到计算属性的完整形态,提供 set函数 (set的逻辑需要自己提供)
<body>
<div id="app">
<input type="text" placeholder="请输入你的姓" v-model="lastName"> +
<input type="text" placeholder="请输入你的名" v-model="firstName"> =
<input type="text" placeholder="你的名字是" v-model="fullName">
</div>
<script src="./node_modules/vue/dist/vue.global.js"></script>
<script>
const app = Vue.createApp({
// data 提供了vue中使用的数据
data() {
return {
lastName: '',
firstName: ''
}
},
// methods 对象: 提供vue中使用到的方法
methods: {},
// 计算属性的完整形态;如果需要修改计算属性的值,如果需要对计算属性使用双向绑定
computed: {
// 默认提供的仅仅是getter
// fullName() {
// return this.lastName + ' ' + this.firstName
// }
fullName: {
get() {
return this.lastName + ' ' + this.firstName
},
set(value) {
// value修改的全名
console.log(value)
this.lastName = value.split(' ')[0]
this.firstName = value.split(' ')[1]
}
}
}
})
const vm = app.mount('#app')
window.vm = vm
</script>
</body>
案例:todomvc
todos-通过计算属性控制全选
- 根据下面所有的任务来控制
<input id="toggle-all" class="toggle-all" type="checkbox" >
的check值:当下面所有的任务都完成,就选中,否则就不选中 - 选中toggle-all后,下面所有任务的checkbox均设置为选中;取消选中toggle-all后,下面所有任务的checkbox均设置为未选中
<!-- 1. checkbox 选中后,将列表所有checkbox选中 ,取消选中后,将列表所有checkbox取消选中-->
<!-- 2. 列表所有checkbox都选中后 ,toggle-all自动选中-->
<input id="toggle-all" class="toggle-all" type="checkbox" v-model="isCheckedAll">
在计算属性computed中添加属性
// toggle-all checkbox双向绑定值
// 改计算属性需要修改,提供set函数,不能直接写成函数。
isCheckedAll: {
get() {
// 当所有的任务都完成了,表示都选中了
return this.todos.every(item => item.flag)
},
set(value) {
// console.log(value)
// value修改后的计算属性,,,,,,应该修改todos中所有的任务的flag
this.todos.forEach(item => item.flag = value)
}
}
todos-底部ALL/Active/Completed不同状态的任务切换
- 控制选中样式
- 根据选中的状态,显示相应状态的任务(注意点:通过计算属性来显示,不能直接修改原列表)
<!--给每个a标签添加一个点击事件,通过不同的参数控制样式-->
<ul class="filters">
<li>
<a @click.prevent="change('ALL')" :class="{selected:filterType==='ALL'}" href="#/">All</a>
</li>
<li>
<a @click.prevent="change('Active')" :class="{selected:filterType==='Active'}" href="#/active">Active</a>
</li>
<li>
<a @click.prevent="change('Completed')" :class="{selected:filterType==='Completed'}" href="#/completed">Completed</a>
</li>
</ul>
data 中新增一个属性
// 过滤类型
filterType: 'ALL'
methods 中新增点击事件
// 切换过滤类型
change(filterType) {
// 控制样式判断值
this.filterType = filterType
}
computed 中新增计算属性来获取显示列表
// 用于显示的列表
showList() {
// 计算属性showList 要根据 filterType
if (this.filterType === 'Active') { // 返回未完成的任务
return this.todos.filter(item => !item.flag)
} else if (this.filterType === 'Completed') { // 返回已完成的任务
return this.todos.filter(item => item.flag)
} else { // 默认返回所有任务
return this.todos
}
}
修改原遍历列表为showList
<li v-for="item in showList" :key="item.id" :class="{completed:item.flag,editing:item.id===now}">
watch监视数据的变化
vue实例中提供了一个watch属性,用于监听vue实例中的属性的变化。
watch对应了一个对象,键是观察的属性,值是对应的回调函数。
基本使用
- 监听简单类型属性
watch: {
// 1. 只要msg的值发生了改变,function就会执行
// 2. 参数1:newValue 参数2:oldValue
// 监视属性完整形态
// 监视属性如果是复杂类型,比较麻烦
msg: function(value, oldValue) {
console.log('监听到msg发生变化了', value, oldValue)
}
}
监视对象类型属性
监视对象的时候,需要加上
deep: true
为了发现对象内部值的变化,可以在选项参数中指定
deep: true
- 如果是一个对象,无法监听到对象内部值的变化
- 需要加上deep
watch: {
user: {
deep: true,
handler: function(curr, old) {
//注意:如果监听的是对象,新值与旧值都是相同的,因为指向了同一个对象。
//https://cn.vuejs.org/v2/api/#vm-watch
console.log(curr.age, curr.name);
}
}
}
案例:todomvc
todos-使用监听属性把数据存储到浏览器
目前的案例效果,初始任务todos是写死的记录,编辑任务刷新浏览器又会恢复成初始记录。本次任务是将任务的变更情况存储到浏览器缓存中。
- 初始todos从浏览器缓存中获取
- 监听todos属性变化,将todos存储到浏览器缓存
在Vue.createApp上方添加
// 从浏览器localStorage中提取todos列表
let todos = JSON.parse(localStorage.getItem('todos')) || []
在data的return中,调整todos
return {
todos,
}
在watch中添加侦听
// 添加侦听
watch: {
// 侦听todos属性的变化
todos: {
deep: true,
handler(value) {
// 把最新的todos存储到localStorage中
localStorage.setItem('todos', JSON.stringify(value))
}
}
}
案例:todomvc-v1(已完结)
源码可参考:https://gitee.com/guanfuchang/todos_v1
第三天学习总结
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。