421 vue day02
第二天
TODOMVC
一 、准备工作
-
演示效果 :
当前任务:敲代码、视频、游戏
-
git clone https://github.com/tastejs/todomvc-app-template.git
-
安装依赖包 :
npm i
二、配置 vue
-
安装 vue :
npm i vue
-
导入 vue :
<script src="./node_modules//vue/dist/vue.js"></script>
在
index.html
里的app.js
导入之前导入,因为 app.js 里 就要用到 vue 了 -
实例化 vue :在
app.js
中创建 vue 示例,并设置好边界el:'#app'
找到 index.html ,给 section 标签添加一个 id
-
测试 vue :
data 中随便来一个 msg 看能不能显示到视图中
三、列表渲染
-
todoList 数组里面都是对象
id name done
id : (唯一标识 ) 删除需要
name : (名称) 展示需要
done : (是否完成任务) 选中状态需要 -
去掉线 :
:class="{ completed : item.done }"
-
多选框选中状态 :
v-model='item.done'
# 列表展示
1. 数据 数组 => list
2. 遍历 v-for='item in list' :key='item.id'
3. 处理任务名称 : {{ item.name }}
4. 处理选中状态 <input v-model='item.done' >
5. 横线 :class='{ completed : item.done }'
四、添加任务
# 添加任务
- 拿到数据
1. 回车 注册键盘事件 @keyup='addTodo'
2. 拿到数据 input <===> todoName , this.todoName
3. if(e.keyCode===13) { 按回车键了 }
- 添加任务
1. 判断文本框不能为空
2. 处理 id , 数组里最后一个元素的 id+1
const id =
this.list.length === 0 ? 1 : this.list[this.list.length - 1].id + 1
3. 添加任务
4. 添加任务完清空内容
// 添加任务
addTodo( e ) {
console.log('添加任务',this.todoName);
// 0. 判断不能为空
if (this.todoName.trim() === '') {
return;
}
// 1. 添加任务
if (e.keyCode === 13) {
this.todoList.push({
id : 4,
name : this.todoName,
done:false
})
//2. 清空文本框内容
this.todoName = ''
}
}
五 、删除任务
传 id,过滤不是 id 的
this.todoList = this.todoList.filter(item => item.id != id)
# 删除任务
1. 注册点击事件
2. 拿到 id
3. 根据 id 删除任务
> 过滤出来不等于当前 id 的
> this.list = this.list.filter( item => item.id != id)
六、编辑任务 (难点)
画图
三步 :
- 在
data
中存一个数据 :editId
, 记录选中的文本框 :class = {editing: item.id === editId }
判断 选中的是哪一个,是选一个都返回true, 显示编辑状态- 双击 :
@dblclick="editTodo(item.id)"
- 在editTodo 函数里 保存选中的id
this.editId = id
- 回车 : 编辑状态消除 : this.editId = -1
- Vue中数据更新的特点:
只要Vue中的数据发生改变,页面中所有的指令和表达式都会被重新计算一次
# 编辑任务
1. 显示编辑状态 (难点)
- 第一步 : data 里新加一个 editId : -1
- 第二步 : item.id == editId
- 第三步 : 双击 => 拿到双击任务的 id => 赋值 editId this.editId = id
> vue 中, data 里的数据,一旦发生了变化,,当前页面的指令和表达式会重新计算
2. 修改任务 v-model='item.name'
3. 隐藏编辑状态
`this.editId = -1`
七、事件修饰符
01-事件修饰符.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.fa {
width: 70px;
height: 70px;
margin-bottom: 10px;
background: green;
text-align: center;
}
button {
margin-top: 20px;
text-align: center;
}
</style>
</head>
<body>
<!--
事件修饰符
1. 事件.prevent: 阻止默认行为
2. 事件.stop: 阻止冒泡
3. 事件.capture: 捕获
4. 事件.self: 点击自己才会被触发
5. 事件.once: 点击只触发一次
6. 事件.passive: 移动端提高性能
-->
<div id="app">
<a href="https://baidu.com" @click.prevent="fn0">链接</a>
<hr>
<!-- 点击按钮,输出fn2,说明阻止了冒泡。如果去掉stop,则输出fn2、fn1。 -->
<div class="fa" @click="fn1">
<button class="btn" @click.stop='fn2'>按钮</button>
</div>
<!-- 点击按钮,先后输出fn2、fn1,说明先后执行fn2、fn1 -->
<div class="fa" @click="fn1">
<button @click.capture='fn2'>按钮</button>
</div>
<!-- 点击按钮,先后输出fn1、fn2,说明先后执行fn1、fn2 -->
<div class="fa" @click.capture="fn1">
<button @click='fn2'>按钮</button>
</div>
<div class="fa" @click.self="fn1">
<button @click='fn2'>按钮</button>
</div>
<div class="fa" @click="fn1">
<button @click.once='fn2'>按钮</button>
</div>
</div>
<script src="./vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
msg: ''
},
methods: {
fn0() {
console.log('fn0');
},
fn1() {
console.log('fn1');
},
fn2() {
console.log('fn2');
}
}
})
</script>
</body>
</html>
八、按键修饰符
记住 keyCode 太麻烦了
- 只有在键盘事件中生活效, (keydown、keypress、 keyup)
- 语法 : @keyup.enter='事件函数'
.enter 就是一个按键修饰符, 意思就是当按回车的时候, 事件才会被触发 - @keyup.13 也可以, 但是 keyCode 也是要记住的
- 完善 TodoMVC + 按键修饰符
# 按键修饰符
keyup/keydown/keypress
> 判断回车的例子
> 第一个版本 : @keyup='addTodo' ==> if(e.keyCode==13) { }
> 第二个版本 : @keyup.13='addTodo' => 不要判断了
> 第三个版本 : @keyup.enter='addTodo' 按键修饰符
```js
.enter
.tab
.delete (捕获“删除”和“退格”键)
.esc
.space
.up
.down
.left
.right
```
<button @click="delTodo(item.id)" class="destroy"></button>
// 删除任务
delTodo(id) {
// console.log(id)
// 拿到id, 直接删除不好删, 过滤, 过滤出来不等于当前id的元素, 新的数组, 把之前的list覆盖掉就可以了
this.list = this.list.filter(item => item.id != id)
}
九、v-if 和 v-show
-
代码
-
<h1 v-if='isShow'>我是h1 v-if</h1> <h1 v-show='isShow'>我是h1 v-show</h1>
-
异同点
-
v-if 和 v-show 的异同点 1. 相同点 : 可以切换元素的显示与隐藏 2. 不同点 : 切换显示和隐藏的实现不同 v-if : 显示:创建节点 隐藏: 删除节点 v-show : 显示: display:block 隐藏 : display:none 3. 使用场景 : v-if因为要不断的创建和删除来切换显示与隐藏 ,所以性能不高 v-if : 切换次数不频繁的时候, v-show : 切换次数频繁的时候
-
完善 TodoMVC + v-show
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<!--
v-if 和 v-show 切换元素的显示和隐藏
格式 v-if/v-show='布尔' true => 显示 false=> 隐藏
相同点 : v-if 和 v-show 都能够切换元素的显示和隐藏
不同点 : 实现的方式不一样
v-if 显示:创建节点 隐藏:删除节点
v-show 显示:display:block 隐藏:display:none
因为 v-if 是通过不断的创建和删除节点来切换显示和隐藏的,所以性能不好
所以要决定使用哪个,看切换的频繁与否
切换的频繁 : v-show 【切换的不频繁,也可以使用v-show。】
切换的不频繁 : v-if
-->
<div id="app">
<!-- <h1 v-if='isShow'>测试 v-if </h1> -->
<h1 v-show='isShow'>测试 v-show </h1>
</div>
<script src="./vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
isShow: false
}
})
</script>
</body>
</html>
十、Footer 的显示与隐藏
-
直接写
v-show="todoList.length > 0"
-
封装到一个函数里
-
组件 :
v-show="isFooter()"
-
// 代码显示 isFooterShow() { // 只要 vue里的数据发生了变化, 页面中所有的指令和表达式都会重新计算 // 所以只要文本框里的内容发生改变, todoName也会发送改变, 这里就会不断的打印,性能不好 console.log('改变了'); return this.todoList.length > 0 } // 底部的显示与隐藏 【这种方式,只要输入框的值一改变,todoName就会改变,页面的指令、表达式就会重新计算。】 【每输入一个字符,都会输出 '重新计算了'。】 isFooterShow1() { console.log('重新计算了') return this.list.length > 0 }
-
有问题
5.1 只要 vue 里的data数据发生了变化, 页面中所有的指令和表达式都会重新计算
5.2 所以只要文本框里的内容发生改变,todoName 也会发送改变,这里就会不断的打印, 性能不好
5.3 我们要做的是 只要数组列表的个数改变才会影响底部的变化, 文本不管内容文字再多都不应该影响底部的变化
5.4 需要计算属性
# 底部的显示与隐藏
> 方式 1 : 表达式 v-show='list.length > 0'
> 问题 : 代码过多, 直接在这里写就很不方便了
-----
> 方式 2 : 函数 v-show='isFooterShow1()'
> isFooterShow1(){
return this.list.length > 0
}
> 问题 : 与数组毫无管理的数据(比如data 里的 todoName), 只要随意改变, 就会重新计算, 性能不好
> 原因 : vue 中, data 里的数据todoName,一旦发生了变化, 当前页面的指令(v-show)和表达式会重新计算
----
> 方式 3 : 计算属性 computed (重要 死了都要会)
十一、计算属性 computed
计算属性其实就是一个属性
-
说明 : 计算属性只跟随相关的数据变化而变化 ,解决底部显示隐藏问题,
-
怎么使用?
- 在 computed 里面
- 写起来像一个方法
- 用起来像一个属性
-
特点 :
-
计算属性一定要有返回值, 返回的值,就会标签要展示的内容
-
计算属性可以使用data里之前已知的值
-
(重要) 只要 计算属性
相关的数据
发生了变化,计算属性会重新计算
-
(说一下 😃 num1就是totalNum计算属性的相关属性,所以num1变了,计算属性会重新计算,
但是num2不是相关的属性,所以不管你num2怎么变,计算属性都不会重新计算
-
-
注意点 :
- 4.1. 一定要有返回值
- 4.2. 用起来的时候,不能当方法用,因为它本来就是一个属性
- 4.3. 计算属性(computed里面的属性) 不能和 data里的属性重名
-
什么时候使用 计算属性?
- 根据已知的值,得到一个新值
- 并且 , 新值只想跟相关的数据(已知的值)的变化而变化 (实时更新)
-
案例 : 计算器
num1 <input type="text" v-model="num1" /> +
num2 <input type="text" v-model="num2" /> = <span>{{ num3 }}</span>
<hr />
<input type="text" v-model="test" />
- 完善 TodoMVC + 计算属性 + 底部显示与隐藏 / 左边的剩余未完成数 / 右边清除完成按钮显示与隐藏
05-计算属性的基本使用.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Document</title>
</head>
<body>
<!--
计算属性 computed
1. 说明 : 计算属性就是一种属性 【Vue实例对象的属性。】
2. 如何使用 ?
- 写在 computed 里面
- 写起来像一个方法/函数
- 用起来像一个属性
3. 特点 :
- 一定要有返回值, 返回值就是计算属性得到的值
- 可以使用data里存在的值
- ** 计算属性只会随着相关的数据发生变化而变化, 只要相关的数据发生了变化, 计算属性会重新计算 **
4. 注意点
- 一定要有返回值
- 不能当方法用 【否则报错:函数未定义。】
- 不能和data里的数据重名 【否则报错:变量已声明。】
5. 以后何时使用计算属性 ?
- 根据已知值(data里值), 想得到一个新值
- 想得到一个效果 : 新值只和相关的数据变化有关, 相关的数据变化, 新值会随着变化, 其他值不受影响 【相关的数据变化,只会影响新值,不会影响别的值,新值也不受不相关的数据的影响。】
-->
<div id="app">
<h1>{{ totalNum }}</h1>
</div>
<script src="./vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
num1: 100,
num2: 200
},
computed: {
totalNum() {
console.log('重新计算了')
return this.num1 + 20
},
num3() {
return this.num1 * this.num2
}
}
})
</script>
</body>
</html>
06-计算属性小案例.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Document</title>
</head>
<body>
<div id="app">
num1 : <input type="text" v-model="num1" /> + num2 :
<input type="text" v-model="num2" /> =
<span>{{ totalNum }}</span>
<hr />
<input type="text" v-model="num3" />
</div>
<script src="./vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
num1: '',
num2: '',
num3: ''
},
computed: {
// totalNum 只跟num1 、num2有关,跟num3无关,不管num3如何变化。
totalNum() {
// 诸如'111'的数字类型的字符串,前面加上 加号+ ,就转换为数字类型。
return +this.num1 + +this.num2
}
}
})
</script>
</body>
</html>
十二、key
- 说明 :
- Vue 中推荐, 在使用 v-for 的时候,添加 key 属性
看官网
-
介绍 就地复用,原地打滚
默认跟着索引走。
<!-- 显示组件 -->
<p v-for="(item,index) in list" :key="index">
{{ item.name}} <input type="text" />
</p>
<!-- 数据 -->
data: { list : [
{ id : 1, name : '老罗' },
{ id : 2, name : '涛涛' },
{ id : 3, name : '聪聪' }
]}
<!-- 演示 -->
vm.list.unshift({id:4,name:'马哥'})
- 怎么使用 key
- 如果数组的元素是一个对象 : 使用对象里固定属性,唯一
- 一般情况下,对象里都有 id, 99%都是取
item.id
- 如果数组的元素是一个简单类型,不是一个对象, 就可以取索引作为 key
- 语法 :
:key='item.id'
- 以后,写了
v-for
之后,立马写好:key
- 完善 TodoMVC + key
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<!--
key
1. vue官网推荐, 我们以后使用v-for的时候, 加上key
2. 如果我们不加key的话, 会出现`就地复用`的策略 (了解) 【默认值是索引。】
3. 解决就地复用的问题 : 添加一个key属性,并且给key一个正确的值
4. 如何给key赋值呢??
1. 如果数组里的元素是一个对象(90%), key取对象里的属性(固定、唯一), :key='item.id'
2. 如果数组里的元素不是一个对象, 真的很简单, :key='index' (不要让顺序发生了改变)
-->
<div id="app">
<p v-for='(item,index) in list' :key='item.name'>
{{ item.id }} {{ item.name }} <input type="text">
</p>
</div>
<script src="./vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
list1: ['张三', '李四'],
list: [{
id: 1,
name: '聪聪',
done: false
}, {
id: 2,
name: '浪涛',
done: false
}, {
id: 3,
name: '傻春',
done: true
}]
}
})
</script>
</body>
</html>
十三、条件渲染指令 v-else 和 v-else-if
条件渲染指令
if(){ // v-if
}else if(){ // v-else-if
}else {} // v-else
- v-else : 两种情况的
<h1 v-if="num > 40">第一个</h1>
<h1 v-else>第二个</h1>
- v-else-if : 三种以上情况
<h1 v-if="num >= 40">第一个</h1>
<h1 v-else-if="num >= 30 && num < 40">第二个</h1>
<h1 v-else>第三个</h1>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<!--
条件渲染指令
if(){ // v-if
}else if(){ // v-else-if
}else {} // v-else
-->
<div id="app">
<!--
只显示一种
-->
<h1 v-if='length >= 18'>特别长</h1>
<h1 v-else-if='length >= 12'>很长</h1>
<h1 v-else>一般般</h1>
</div>
<script src="./vue.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
length: 25
}
})
</script>
</body>
</html>