02 Vue 指令
Vue 会根据不同的 指令,针对标签实现不同的 功能
概念:指令(Directives)是 Vue 提供的带有
v-前缀 的特殊标签属性。为啥要学:提高程序员操作 DOM 的效率。学习不同指令 → 解决不同业务场景需求
vue 中的指令按照不同的用途可以分为如下 6 大类:
- 内容渲染指令(
v-html、v-text) - 条件渲染指令(
v-show、v-if、v-else、v-else-if) - 事件绑定指令(
v-on) - 属性绑定指令(
v-bind) - 双向绑定指令(
v-model) - 列表渲染指令(
v-for)
- 内容渲染指令(
指令是 vue 开发中最基础、最常用、最简单的知识点。
内容渲染指令
- 内容渲染指令用来辅助开发者渲染 DOM 元素的文本内容。
v-text
v-text(类似 innerText)
- 使用语法:
<p v-text="uname">hello</p>,意思是将uame值渲染到p标签中 - 类似 innerText,使用该语法,会覆盖
p标签原有内容
- 使用语法:
v-html
v-html(类似 innerHTML)
- 使用语法:
<p v-html="intro">hello</p>,意思是将intro值渲染到p标签中 - 类似 innerHTML,使用该语法,会覆盖
p标签原有内容 - 类似 innerHTML,使用该语法,能够将 HTML 标签的样式呈现出来。
Warning
- 在网站上动态渲染任意 HTML 是非常危险的,因为容易导致 XSS 攻击。只在可信内容上使用
v-html,永不用在用户提交的内容上。 - 在 单文件组件 里,
scoped的样式不会应用在v-html内部,因为那部分 HTML 没有被 Vue 的模板编译器处理。如果你希望针对v-html的内容设置带作用域的 CSS,你可以替换为 CSS Modules 或用一个额外的全局<style>元素手动设置类似 BEM 的作用域策略。
- 使用语法:
<div id="app">
<h2>个人信息</h2>
<!-- v-text 和 v-html 是 vue 提供的特殊 html 属性,所以可以直接当成属性来使用 -->
<p v-text="uname">姓名:</p>
<p v-html="intro">简介:</p>
</div>
<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
uname: "Black Mamba 黑曼巴",
intro: "<span><a href='https://baike.baidu.com/item/科比·布莱恩特/318773'>牢大</a> 是个篮球运动员</span>"
}
});
// v-text 和 v-html 的区别
// 1. v-text 会将插值的内容,直接以文本的形式渲染到页面中
// 2. v-html 会将插值的内容,以 html 的形式渲染到页面中
console.log(app.uname);
console.log(app.intro);
</script>条件渲染指令
条件判断指令,用来辅助开发者按需控制 DOM 的显示与隐藏。条件渲染指令有如下两个,分别是:v-show 和 v-if。(v-else 和 v-else-if 是辅助 v-if 进行判断渲染)
v-show
- 作用:控制元素显示隐藏
- 语法:
v-show="表达式"表达式值为 true 显示,false 隐藏 - 原理:切换
display:none和display:block控制显示隐藏 - 场景:频繁切换显示隐藏的场景
v-if
- 作用:控制元素显示隐藏(条件渲染)
- 语法:
v-if="表达式"表达式值 true 显示,false 隐藏 - 原理:基于条件判断,是否创建 或 移除元素节点
- 场景:要么显示,要么隐藏,不频繁切换的场景
<!--
v-show 底层原理:切换 css 的 display: none 来控制显示隐藏
v-if 底层原理:根据 判断条件 控制元素的 创建 和 移除(条件渲染)
-->
<div id="app">
<div v-show="flag" class="box">我是 v-show 控制的盒子</div>
<div v-if="flag" class="box">我是 v-if 控制的盒子</div>
<button @click="flag = !flag">切换显示隐藏</button>
</div>
<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
flag: true
}
});
console.log(app.flag);
// 查看页面源代码,发现
// v-show 控制的盒子,始终存在于页面中,只是 display: none
// v-if 控制的盒子,当 flag 为 true 时,会显示,当 flag 为 false 时,会隐藏
</script>v-else 和 v-else-if
<div id="app">
<div class="box box1">
<ul>
<li>name: <code>{{name}}</code></li>
<li>age: {{age}}</li>
<li>gender: {{gender}}</li>
<li>score: {{score}}</li>
</ul>
<hr />
<p v-text="name"></p>
<p v-if="gender === 'male'">性别:♂ 男</p>
<p v-else>性别:♀ 女</p>
<p v-show="age >= 18">成年:✅</p>
<p v-if="scoore >= 90">成绩评定 A:奖励电脑一台</p>
<p v-else-if="scoore >= 70">成绩评定 B:奖励周末郊游</p>
<p v-else-if="scoore >= 60">成绩评定 C:奖励零食礼包</p>
<p v-else>成绩评定 D:惩罚一周不能玩手机</p>
</div>
<div class="box box2">
<ul>
<li>name: <code>{{name}}</code></li>
<li>age: {{age}}</li>
<li>gender: {{gender}}</li>
<li>score: {{score}}</li>
</ul>
<hr />
<p v-html="name"></p>
<p v-if="gender === 'male'">性别:♂ 男</p>
<p v-else>性别:♀ 女</p>
<p v-if="scoore >= 90">成绩评定 A:奖励电脑一台</p>
<p v-else-if="scoore >= 70">成绩评定 B:奖励周末郊游</p>
<p v-else-if="scoore >= 60">成绩评定 C:奖励零食礼包</p>
<p v-else>成绩评定 D:惩罚一周不能玩手机</p>
</div>
</div>
<script src="./js/vue.js"></script>
<script>
const box1 = new Vue({
el: ".box1",
data: {
name: "Black Mamba 黑曼巴",
age: 20,
gender: "male",
scoore: 80,
},
});
const box2 = new Vue({
el: ".box2",
data: {
name: "<a href='#'>李可儿</a>",
age: 13,
gender: "female",
scoore: 98,
},
});
</script>事件绑定指令
- 作用:注册事件 = 添加监听 + 提供处理逻辑
v-on
使用 Vue 时,如需为 DOM 注册事件。
<button v-on:事件名="内联语句">按钮</button><button v-on:事件名="处理函数">按钮</button>- 事件处理函数应该写到一个跟 data 同级的配置项
methods中 - methods 处理函数内的
this指向 Vue 实例
- 事件处理函数应该写到一个跟 data 同级的配置项
<button v-on:事件名="处理函数(实参)">按钮</button>- 如果不传递任何参数,则方法无需加小括号;methods 方法中可以直接使用
e当做事件对象 - 如果传递了参数,则实参
$event表示事件对象,固定用法。
- 如果不传递任何参数,则方法无需加小括号;methods 方法中可以直接使用
v-on:简写为@
<div id="app">
<button @click="count++">+</button>
<span>{{count}}</span>
<button @click="count--">-</button>
</div>
<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
count: 99,
},
});
console.log(app.count);
</script><div id="app">
<button @click="showMsg">切换显示隐藏</button>
<p v-show="flag" class="box">hello vue~</p>
</div>
<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
flag: true
},
methods: {
// showMsg 函数中,this 指向的是 app 实例
showMsg() {
this.flag = !this.flag;
}
}
});
</script><div id="app">
<div class="box">
<h3>小黑自动售货机</h3>
<button @click="buy(5)" v-if="money >= 5">可乐 5 元</button>
<button @click="buy(10)" v-if="money >= 10">咖啡 10 元</button>
<button @click="buy(8)" v-if="money >= 8">牛奶 8 元</button>
</div>
<p>银行卡余额:{{ money }}元</p>
</div>
<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
money: 20,
},
methods: {
buy(price) {
this.money -= price;
},
},
});
</script>属性绑定指令
v-bind
作用: 动态设置 html 的标签属性 比如:
src、url、title- 语法:
v-bind:属性名='表达式' v-bind:可以简写成:<img v-bind:src="url" />=><img :src="url" />(v-bind可以省略)
- 语法:
<div id="app">
<img :src="imgUrl" alt="" :title="imgTitle">
</div>
<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
// 随机图片
imgUrl: "https://via.placeholder.com/200x200",
imgTitle: "Black Mamba 黑曼巴",
},
});
</script>案例 波仔的学习之旅
需求:默认展示数组中的第一张图片,点击上一页下一页来回切换数组中的图片
实现思路:
- 数组存储图片路径 ['url1','url2','url3',…]
- 可以准备个下标
index去数组中取图片地址。 - 通过
v-bind给src绑定当前的图片地址 - 点击上一页下一页只需要修改下标的值即可
- 当展示第一张的时候,上一页按钮应该隐藏。展示最后一张的时候,下一页按钮应该隐藏
<div id="app">
<button @click="index--" v-if="index > 0">prev</button>
<img :src="imgUrl[index]" alt="" />
<button @click="index++" v-if="index < imgUrl.length - 1">next</button>
</div>
<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
index: 0,
imgUrl: [
"https://s2.loli.net/2024/01/17/aL2Wr9tsTeukFJB.png",
"https://s2.loli.net/2024/01/17/AdNXS9TtMRxHUaE.png",
"https://s2.loli.net/2024/01/17/hqEmJvTRuctAWI7.gif",
"https://s2.loli.net/2024/01/17/7Bmg8f9jKMbWhuQ.gif",
"https://s2.loli.net/2024/01/17/iw41FNkYr6ohGOC.gif",
"https://s2.loli.net/2024/01/17/R9scXpMECuaVHbg.gif",
],
},
});
</script>列表渲染指令
Vue 提供了 v-for 列表渲染指令,用来辅助开发者基于一个数组来循环渲染一个列表结构。
v-for
v-for 指令需要使用 (item, index) in arr 形式的特殊语法,其中:
item是数组中的每一项index是每一项的索引,不需要可以省略arr是被遍历的数组
此语法也可以遍历对象和数字
<div id="app">
<h3>小黑水果店</h3>
<ul>
<!-- <li>西瓜 - 0</li> -->
<!-- <li>苹果 - 1</li> -->
<li v-for="(item, index) in fruits" :key="index">{{item}} - {{index}}</li>
</ul>
</div>
<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
fruits: ["西瓜", "苹果", "香蕉", "橘子", "梨子", "葡萄"],
},
});
</script>案例 小黑的书架
需求:
- 根据左侧数据渲染出右侧列表
v-for - 点击删除按钮时,应该把当前行从列表中删除(获取当前行的
id,利用filter进行过滤)
<div id="app">
<h3>小黑的书架</h3>
<ul>
<li v-for="(item, index) in books" :key="item.id">
<span>{{index + 1}}.</span>
<span class="book-name">{{item.name}}</span>
<span class="book-author">{{item.author}}</span>
<button @click="removeBook(item.id)">删除</button>
</li>
</ul>
</div>
<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
books: [
{ id: 1, name: '《红楼梦》', author: '曹雪芹' },
{ id: 2, name: '《西游记》', author: '吴承恩' },
{ id: 3, name: '《水浒传》', author: '施耐庵' },
{ id: 4, name: '《三国演义》', author: '罗贯中' }
]
},
methods: {
removeBook(id) {
// filter() 方法创建一个新数组,其包含通过所提供函数实现的测试的所有元素
// 接收一个函数作为参数,该函数用于测试数组中的每个元素
// 如果函数返回 true,该元素将被包含在新数组中;否则,该元素将被排除
this.books = this.books.filter(item => item.id !== id)
}
}
});
</script>v-for 中的 key
- 语法: key="唯一值"
- 作用:给列表项添加的唯一标识。便于 Vue 进行列表项的正确排序复用。
- **为什么加 key:**Vue 的默认行为会尝试原地修改元素(就地复用)
注意
- key 的值只能是字符串 或 数字类型
- key 的值必须具有唯一性
- 推荐使用
id作为 key(唯一),不推荐使用index作为 key(index会变化,不对应)
<div id="app">
<h3>小黑的书架</h3>
<div class="box box1">
<ul>
<span class="keyword">有 key</span>
<li v-for="(item, index) in books" :key="item.id">
<span>{{index + 1}}.</span>
<span class="book-name">{{item.name}}</span>
<span class="book-author">{{item.author}}</span>
<button @click="removeBook(item.id)">删除</button>
</li>
</ul>
</div>
<div class="box box2">
<ul>
<span class="keyword">无 key</span>
<li v-for="(item, index) in books">
<span>{{index + 1}}.</span>
<span class="book-name">{{item.name}}</span>
<span class="book-author">{{item.author}}</span>
<button @click="removeBook(item.id)">删除</button>
</li>
</ul>
</div>
</div>
<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
books: [
{ id: 1, name: '《红楼梦》', author: '曹雪芹' },
{ id: 2, name: '《西游记》', author: '吴承恩' },
{ id: 3, name: '《水浒传》', author: '施耐庵' },
{ id: 4, name: '《三国演义》', author: '罗贯中' }
]
},
methods: {
removeBook(id) {
// filter() 方法创建一个新数组,其包含通过所提供函数实现的测试的所有元素
// 接收一个函数作为参数,该函数用于测试数组中的每个元素
// 如果函数返回 true,该元素将被包含在新数组中;否则,该元素将被排除
this.books = this.books.filter(item => item.id !== id)
}
}
});
// 去控制台给第一个 li 标签添加 `background-color: pink;` 行内样式
// 然后点击删除按钮,会发现>无 key 的第一个 li 标签的背景色没有被删除
// 这是因为没有给 v-for 指令添加 key 属性,导致 Vue 无法识别每个 li 标签的唯一性
document.querySelector('.box1 li:nth-of-type(1)').style.backgroundColor = 'pink'
document.querySelector('.box2 li:nth-of-type(1)').style.backgroundColor = 'pink'
</script>双向绑定指令
所谓双向绑定就是:
- 数据改变后,呈现的页面结果会更新
- 页面结果更新后,数据也会随之而变
v-model
作用: 给表单元素(
input、radio、select)使用,双向绑定数据,可以快速 获取 或 设置 表单元素内容语法:
v-model="变量"需求: 使用双向绑定实现以下需求
- 点击登录按钮获取表单中的内容
- 点击重置按钮清空表单中的内容
<!--
v-model 可以让数据和视图,形成双向数据绑定
1. 数据变化,视图自动更新
2. 视图变化,数据自动更新
可以快速 [获取] 或 [设置] 表单元素的内容
-->
<div id="app">
账户:<input type="text" v-model="uname" placeholder="请输入账户名称" />
<br /><br />
密码:<input type="password" v-model="passwd" placeholder="请输入账户密码" />
<br /><br />
<button @click="login">登录</button>
<button @click="reset">重置</button>
</div>
<script src="./js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
uname: "",
passwd: "",
},
methods: {
login() {
console.log(this.uname, this.passwd);
},
reset() {
this.uname = "";
this.passwd = "";
},
},
});
</script>综合案例 小黑记事本
列表渲染
- Vue 实例中的
data中准备数据用于列表初始渲染 .todo-list列表中的li元素使用v-for指令,遍历数组,渲染列表项{{item.name}}使用插值表达式,渲染数据
data: {
// 准备数据用于列表初始渲染
todos: [
{ id: 1, name: '学习 HTML' },
{ id: 2, name: '学习 CSS' },
{ id: 3, name: '学习 JavaScript' },
{ id: 4, name: '学习 Vue' },
]
}
<ul class="todo-list">
<!-- 列表项,使用 v-for 指令,遍历数组,渲染列表项 -->
<li class="todo" v-for="(item, index) in todos" :key="item.id">
<div class="view">
<span class="index">{{item.index + 1}}.</span>
<label>{{item.name}}</label>
<button @click="remove(item.id)" class="destroy"></button>
</div>
</li>
</ul>删除功能
button按钮使用@click="remove(item.id)"(v-on:click="remove(item.id)"的简写) 绑定点击事件- Vue 实例中添加
methods事件处理函数。 todos.filter方法用于过滤数组,返回一个新数组,不会修改原数组todos.findIndex方法用于查找数组中满足条件的元素,返回满足条件的元素的索引,如果没有找到,返回 -1todos.splice方法用于删除数组中的元素,第一个参数是索引,第二个参数是删除的个数
<button @click="remove(item.id)" class="destroy"></button>
methods: {
// 删除任务
remove(id) {
// this.todos = this.todos.filter(item => item.id !== id)
// 根据 id 找到对应的索引
const index = this.todos.findIndex(item => item.id === id)
// 根据索引,删除对应的元素
// splice() 方法用于删除数组中的元素,第一个参数是索引,第二个参数是删除的个数
this.todos.splice(index, 1)
}
}添加功能
input输入框使用v-model指令,双向绑定数据data中准备初始数据用于绑定button按钮使用@click="add"(v-on:click="add"的简写) 绑定点击事件- Vue 实例中添加
methods事件处理函数。 todos.push方法用于向数组中添加元素
<input v-model="newTodo" placeholder="请输入任务" class="new-todo" />
data: {
// 准备数据用于绑定
newTodo: ''
}
<button @click="addTodo" class="add">添加任务</button>
methods: {
// 添加任务
addTodo() {
// 判断输入框中是否有内容
if (this.newTodo.trim().length === 0) console.log('输入任务名称不能为空') return
// 准备数据
const todo = {
id: Date.now(),
name: this.newTodo.trim()
}
// 添加到数组中
this.todos.push(todo)
// 清空输入框
this.newTodo = ''
}
}底部统计 和 清空
- 底部统计使用
v-html指令 (或者直接使用 插值表达式),渲染数据 - 清空按钮使用
@click="clear"(v-on:click="clear"的简写) 绑定点击事件 todos中没有任务时 (todos.length长度为 0),使用v-show指令隐藏底部统计和清空按钮
<span class="todo-count">合 计:<strong> {{todos.length}} </strong></span>
<button @click="clearTodos" class="clear-completed">清空任务</button>
// 清空任务
clearTodos() {
// 清空数组
this.todos = []
}
<footer class="footer" v-show="todos.length > 0">功能总结
列表渲染
v-for key的设置插值表达式
删除功能
v-on调用传参filter过滤 覆盖修改原数组
添加功能
v-model绑定push修改原数组添加
底部统计和清空
数组.length累计长度v-show控制隐藏- 覆盖数组清空列表
<!-- 主体区域 -->
<section id="app">
<!-- 输入框 -->
<header class="header">
<h1>小黑记事本</h1>
<input placeholder="请输入任务" class="new-todo" />
<button class="add">添加任务</button>
</header>
<!-- 列表区域 -->
<section class="main">
<ul class="todo-list">
<li class="todo">
<div class="view">
<span class="index">1.</span> <label>吃饭饭</label>
<button class="destroy"></button>
</div>
</li>
</ul>
</section>
<!-- 统计和清空 -->
<footer class="footer">
<!-- 统计 -->
<span class="todo-count">合 计:<strong> 1 </strong></span>
<!-- 清空 -->
<button class="clear-completed">清空任务</button>
</footer>
</section>
<!-- 底部 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {},
});
</script><!-- 主体区域 -->
<section id="app">
<!-- 输入框 -->
<header class="header">
<h1>小黑记事本</h1>
<input placeholder="请输入任务" class="new-todo" />
<button class="add">添加任务</button>
</header>
<!-- 列表区域 -->
<section class="main">
<ul class="todo-list">
<!-- 列表项,使用 v-for 指令,遍历数组,渲染列表项 -->
<li class="todo" v-for="(item, index) in todos" :key="item.id">
<div class="view">
<span class="index">{{index+1}}.</span>
<label>{{item.name}}</label>
<button class="destroy"></button>
</div>
</li>
</ul>
</section>
<!-- 统计和清空 -->
<footer class="footer">
<!-- 统计 -->
<span class="todo-count">合 计:<strong> 1 </strong></span>
<!-- 清空 -->
<button class="clear-completed">清空任务</button>
</footer>
</section>
<!-- 底部 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
// 准备数据用于列表初始渲染
todos: [
{ id: 1, name: '学习 HTML' },
{ id: 2, name: '学习 CSS' },
{ id: 3, name: '学习 JavaScript' },
{ id: 4, name: '学习 Vue' },
]
},
});
</script><!-- 主体区域 -->
<section id="app">
<!-- 输入框 -->
<header class="header">
<h1>小黑记事本</h1>
<input v-model="newTodo" placeholder="请输入任务" class="new-todo" />
<button @click="addTodo" class="add">添加任务</button>
</header>
<!-- 列表区域 -->
<section class="main">
<ul class="todo-list">
<!-- 列表项,使用 v-for 指令,遍历数组,渲染列表项 -->
<li class="todo" v-for="(item, index) in todos" :key="item.id">
<div class="view">
<span class="index">{{index+1}}.</span>
<label>{{item.name}}</label>
<button class="destroy" @click="removeTodo(item.id)"></button>
</div>
</li>
</ul>
</section>
<!-- 统计和清空 -->
<footer class="footer">
<!-- 统计 -->
<span class="todo-count">合 计:<strong> 1 </strong></span>
<!-- 清空 -->
<button class="clear-completed">清空任务</button>
</footer>
</section>
<!-- 底部 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
// 准备数据用于列表初始渲染
newTodo: '',
todos: [
{ id: 1, name: '学习 HTML' },
{ id: 2, name: '学习 CSS' },
{ id: 3, name: '学习 JavaScript' },
{ id: 4, name: '学习 Vue' },
]
},
methods: {
removeTodo(id) {
// this.todos = this.todos.filter(item => item.id !== id)
// 根据 id 找到对应的索引
const index = this.todos.findIndex(item => item.id === id)
// 根据索引,删除对应的元素
// splice() 方法用于删除数组中的元素,第一个参数是索引,第二个参数是删除的个数
this.todos.splice(index, 1)
},
addTodo() {
if (this.newTodo.trim().length === 0) {
console.log('输入任务名称不能为空');
return;
}
const todo = {
id: +Date.now(),
name: this.newTodo.trim()
}
this.todos.push(todo)
this.newTodo = ''
}
}
});
</script><!-- 主体区域 -->
<section id="app">
<!-- 输入框 -->
<header class="header">
<h1>小黑记事本</h1>
<input placeholder="请输入任务" class="new-todo" />
<button class="add">添加任务</button>
</header>
<!-- 列表区域 -->
<section class="main">
<ul class="todo-list">
<!-- 列表项,使用 v-for 指令,遍历数组,渲染列表项 -->
<li class="todo" v-for="(item, index) in todos" :key="item.id">
<div class="view">
<span class="index">{{index+1}}.</span>
<label>{{item.name}}</label>
<button class="destroy" @click="removeTodo(item.id)"></button>
</div>
</li>
</ul>
</section>
<!-- 统计和清空 -->
<footer class="footer">
<!-- 统计 -->
<span class="todo-count">合 计:<strong> 1 </strong></span>
<!-- 清空 -->
<button class="clear-completed">清空任务</button>
</footer>
</section>
<!-- 底部 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
// 准备数据用于列表初始渲染
todos: [
{ id: 1, name: '学习 HTML' },
{ id: 2, name: '学习 CSS' },
{ id: 3, name: '学习 JavaScript' },
{ id: 4, name: '学习 Vue' },
]
},
methods: {
removeTodo(id) {
// this.todos = this.todos.filter(item => item.id !== id)
// 根据 id 找到对应的索引
const index = this.todos.findIndex(item => item.id === id)
// 根据索引,删除对应的元素
// splice() 方法用于删除数组中的元素,第一个参数是索引,第二个参数是删除的个数
this.todos.splice(index, 1)
}
}
});
</script><!-- 主体区域 -->
<section id="app">
<!-- 输入框 -->
<header class="header">
<h1>小黑记事本</h1>
<input v-model="newTodo" placeholder="请输入任务" class="new-todo" />
<button @click="addTodo" class="add">添加任务</button>
</header>
<!-- 列表区域 -->
<section class="main">
<ul class="todo-list">
<!-- 列表项,使用 v-for 指令,遍历数组,渲染列表项 -->
<li class="todo" v-for="(item, index) in todos" :key="item.id">
<div class="view">
<span class="index">{{index+1}}.</span>
<label>{{item.name}}</label>
<button class="destroy" @click="removeTodo(item.id)"></button>
</div>
</li>
</ul>
</section>
<!-- 统计和清空 -->
<footer class="footer" v-show="todos.length > 0">
<!-- 统计 -->
<span class="todo-count">合 计:<strong> {{todos.length}} </strong></span>
<!-- 清空 -->
<button @click="clearTodos" class="clear-completed">清空任务</button>
</footer>
</section>
<!-- 底部 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
// 准备数据用于列表初始渲染
newTodo: "",
todos: [
{ id: 1, name: "学习 HTML" },
{ id: 2, name: "学习 CSS" },
{ id: 3, name: "学习 JavaScript" },
{ id: 4, name: "学习 Vue" },
],
},
methods: {
removeTodo(id) {
// this.todos = this.todos.filter(item => item.id !== id)
// 根据 id 找到对应的索引
const index = this.todos.findIndex((item) => item.id === id);
// 根据索引,删除对应的元素
// splice() 方法用于删除数组中的元素,第一个参数是索引,第二个参数是删除的个数
this.todos.splice(index, 1);
},
addTodo() {
// 判断输入框中是否有内容
if (this.newTodo.trim().length === 0) {
console.log("输入任务名称不能为空");
return;
}
// 准备数据
const todo = {
id: +Date.now(),
name: this.newTodo.trim(),
};
// 添加到数组尾部
this.todos.push(todo);
// 清空输入框
this.newTodo = "";
},
clearTodos() {
// 清空数组
this.todos = [];
},
},
});
</script><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="./index-min.css" />
<title>记事本</title>
</head>
<body>
<!-- 主体区域 -->
<section id="app">
<!-- 输入框 -->
<header class="header">
<h1>小黑记事本</h1>
<input v-model="newTodo" placeholder="请输入任务" class="new-todo" />
<button @click="addTodo" class="add">添加任务</button>
</header>
<!-- 列表区域 -->
<section class="main">
<ul class="todo-list">
<!-- 列表项,使用 v-for 指令,遍历数组,渲染列表项 -->
<li class="todo" v-for="(item, index) in todos" :key="item.id">
<div class="view">
<span class="index">{{index+1}}.</span>
<label>{{item.name}}</label>
<button class="destroy" @click="removeTodo(item.id)"></button>
</div>
</li>
</ul>
</section>
<!-- 统计和清空 -->
<footer class="footer" v-show="todos.length > 0">
<!-- 统计 -->
<span class="todo-count">合 计:<strong> {{todos.length}} </strong></span>
<!-- 清空 -->
<button @click="clearTodos" class="clear-completed">清空任务</button>
</footer>
</section>
<!-- 底部 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
// 准备数据用于列表初始渲染
newTodo: "",
todos: [
{ id: 1, name: "学习 HTML" },
{ id: 2, name: "学习 CSS" },
{ id: 3, name: "学习 JavaScript" },
{ id: 4, name: "学习 Vue" },
],
},
methods: {
removeTodo(id) {
// this.todos = this.todos.filter(item => item.id !== id)
// 根据 id 找到对应的索引
const index = this.todos.findIndex((item) => item.id === id);
// 根据索引,删除对应的元素
// splice() 方法用于删除数组中的元素,第一个参数是索引,第二个参数是删除的个数
this.todos.splice(index, 1);
},
addTodo() {
// 判断输入框中是否有内容
if (this.newTodo.trim().length === 0) {
console.log("输入任务名称不能为空");
return;
}
// 准备数据
const todo = {
id: +Date.now(),
name: this.newTodo.trim(),
};
// 添加到数组尾部
this.todos.push(todo);
// 清空输入框
this.newTodo = "";
},
clearTodos() {
// 清空数组
this.todos = [];
},
},
});
</script>
</body>
</html>指令修饰符
什么是指令修饰符?
- 所谓指令修饰符就是通过
.指明一些指令后缀 - 不同的后缀封装了不同的处理操作 —> 简化代码
按键修饰符
@keyup.enter—> 当点击 Enter(回车键) 键的时候才触发
<!-- 主体区域 -->
<section id="app">
<!-- 输入框 -->
<header class="header">
<h1>小黑记事本</h1>
<input @keydown.enter="addTodo" v-model="newTodo" placeholder="请输入任务" class="new-todo" />
<button @click="addTodo" class="add">添加任务</button>
</header>
<!-- 列表区域 -->
<section class="main">
<ul class="todo-list">
<!-- 列表项,使用 v-for 指令,遍历数组,渲染列表项 -->
<li class="todo" v-for="(item, index) in todos" :key="item.id">
<div class="view">
<span class="index">{{index+1}}.</span>
<label>{{item.name}}</label>
<button class="destroy" @click="removeTodo(item.id)"></button>
</div>
</li>
</ul>
</section>
<!-- 统计和清空 -->
<footer class="footer" v-show="todos.length > 0">
<!-- 统计 -->
<span class="todo-count">合 计:<strong> {{todos.length}} </strong></span>
<!-- 清空 -->
<button @click="clearTodos" class="clear-completed">清空任务</button>
</footer>
</section>
<!-- 底部 -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
// 准备数据用于列表初始渲染
newTodo: "",
todos: [
{ id: 1, name: "学习 HTML" },
{ id: 2, name: "学习 CSS" },
{ id: 3, name: "学习 JavaScript" },
{ id: 4, name: "学习 Vue" },
],
},
methods: {
removeTodo(id) {
// this.todos = this.todos.filter(item => item.id !== id)
// 根据 id 找到对应的索引
const index = this.todos.findIndex((item) => item.id === id);
// 根据索引,删除对应的元素
// splice() 方法用于删除数组中的元素,第一个参数是索引,第二个参数是删除的个数
this.todos.splice(index, 1);
},
addTodo() {
// 判断输入框中是否有内容
if (this.newTodo.trim().length === 0) {
console.log("输入任务名称不能为空");
return;
}
// 准备数据
const todo = {
id: +Date.now(),
name: this.newTodo.trim(),
};
// 添加到数组尾部
this.todos.push(todo);
// 清空输入框
this.newTodo = "";
},
clearTodos() {
// 清空数组
this.todos = [];
},
},
});
</script><div id="app">
<h3>@keyup.enter → 监听键盘回车事件</h3>
<input @keyup.enter="print" v-model="username" type="text" placeholder="请输入用户名" />
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
username: "",
},
methods: {
print(e) {
// if (e.key === 'Enter') {
// console.log('键盘回车的时候触发', this.username)
// }
console.log("键盘回车的时候触发", this.username);
},
},
});
</script>v-model 修饰符
v-model.trim—> 去除首位空格v-model.number—> 转数字
<div id="app">
<h3>v-model 修饰符 .trim .number</h3>
<ul>
<li><code>.trim</code> 用于移除字符串头尾的空格</li>
<li><code>.number</code> 用于将字符串转换为数字</li>
</ul>
姓名:<input v-model.trim="username" type="text" placeholder="请输入姓名"><br>
年龄:<input v-model.number="age" type="text" placeholder="请输入年龄"><br>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
username: '',
age: '',
},
});
</script>事件修饰符
@事件名.stop—> 阻止冒泡@事件名.prevent—> 阻止默认行为@事件名.stop.prevent—> 可以连用 即阻止事件冒泡也阻止默认行为
<h3>@事件名.stop → 阻止冒泡</h3>
<fieldset>
<legend>有 事件修饰符 stop</legend>
<div id="app1">
<div @click="fatherFn" class="father">
<div @click.stop="sonFn" class="son">儿子</div>
</div>
</div>
</fieldset>
<fieldset>
<legend>无 事件修饰符 stop</legend>
<div id="app2">
<div @click="fatherFn" class="father">
<div @click="sonFn" class="son">儿子</div>
</div>
</div>
</fieldset>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app1 = new Vue({
el: "#app1",
methods: {
fatherFn() {
alert("老父亲被点击了");
},
sonFn(e) {
// e.stopPropagation()
alert("儿子被点击了");
},
},
});
const app2 = new Vue({
el: "#app2",
methods: {
fatherFn() {
alert("老父亲被点击了");
},
sonFn(e) {
// e.stopPropagation()
alert("儿子被点击了");
},
},
});
</script><div id="app">
<h3>@事件名.prevent → 阻止默认行为</h3>
<a @click.prevent href="http://www.baidu.com">阻止默认行为</a>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
});
</script>v-bind 对于样式操作的增强
为了方便开发者进行样式控制,Vue 扩展了 v-bind 的语法,可以针对 class 类名 和 style 行内样式 进行控制。
操作 class
语法
<div :class="对象/数组">这是一个 div</div>对象语法
- 当 class 动态绑定的是对象时,键就是类名,值就是布尔值
- 如果值是true,就有这个类,否则没有这个类
- 适用场景:一个类名,来回切换
<div class="box" :class="{ 类名1: 布尔值, 类名2: 布尔值 }"></div>数组语法
- 当 class 动态绑定的是数组时 → 数组中所有的类,都会添加到盒子上
- 本质就是一个 class 列表
- 使用场景:批量添加或删除类
<div class="box" :class="[ 类名1, 类名2, 类名3 ]"></div><div id="app">
<fieldset>
<legend>原始</legend>
<div class="box">黑马程序员</div>
</fieldset>
<fieldset>
<legend>:class="{ pink: true, big: true }"</legend>
<div class="box" :class="{ pink: true, big: true }">黑马程序员</div>
</fieldset>
<fieldset>
<legend>:class="{ pink: true, big: false }"</legend>
<div class="box" :class="{ pink: true, big: false }">黑马程序员</div>
</fieldset>
<fieldset>
<legend>:class="['pink', 'big']"</legend>
<div class="box" :class="['pink', 'big']">黑马程序员</div>
</fieldset>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {},
});
</script>案例 京东秒杀 tab 高亮导航
核心思路:所谓切换高亮,其实就是改下标
- 基于数据动态染 tab ->
v-for - 准备下标记录高亮的是哪一人 tab →
activeIndex - 基于下标,动态控制 class 类名 →
v-bind:class="{active: activeIndex === index}"
<div id="app">
<ul>
<li><a class="active" href="#">京东秒杀</a></li>
<li><a href="#">每日特价</a></li>
<li><a href="#">品类秒杀</a></li>
</ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
list: [
{ id: 1, name: "京东秒杀" },
{ id: 2, name: "每日特价" },
{ id: 3, name: "品类秒杀" },
],
},
});
</script><div id="app">
<ul>
<!-- list: 使用 v-for 遍历数组数据,生成列表元素 -->
<!-- 鼠标点击后动态修改 activeIndex 的值,实现高亮效果 -->
<li v-for="(item, index) in tabs" :key="item.id" @click="activeIndex = index">
<a :class="{ active: activeIndex === index }" :href="'javascript:;'">
{{ item.name }}
</a>
</li>
</ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
activeIndex: 2,
tabs: [
{ id: 1, name: "京东秒杀" },
{ id: 2, name: "每日特价" },
{ id: 3, name: "品类秒杀" },
],
},
});
</script>操作 style
语法
<div class="box" :style="{ CSS属性名1: CSS属性值, CSS属性名2: CSS属性值 }"></div><div id="app">
<!-- v-bind 对于样式操作的增强,可以使用 v-bind 对应的属性,实现动态设置样式 -->
<!-- CSS property 名可以用驼峰式 (camelCase) 或短横线分隔 (kebab-case,记得用引号括起来) 来命名 -->
<!-- 驼峰式:background-color: 'green' → backgroundColor: 'green' -->
<!-- 短横线分隔:font-size: 22px → "font-size": "22px" -->
<div class="box" :style="{ width: '400px', height: '400px', backgroundColor: 'green' }"></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {},
});
</script>案例 进度条效果
核心思路:所谓进度条,其实就是改宽度
:style="{width: percent + '%'}"→v-bind绑定样式percent: 0→data中准备数据
<div id="app">
<div class="progress">
<div class="inner">
<span>50%</span>
</div>
</div>
<button>设置 25%</button>
<button>设置 50%</button>
<button>设置 75%</button>
<button>设置 100%</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {},
});
</script><div id="app">
<div class="progress">
<div class="inner" v-bind:style="{ width: progress + '%' }">
<span>{{progress}}%</span>
</div>
</div>
<button @click="progress = 0">设置 0%</button>
<button @click="progress = 25">设置 25%</button>
<button @click="progress = 50">设置 50%</button>
<button @click="progress = 75">设置 75%</button>
<button @click="progress = 100">设置 100%</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
progress: 50,
},
});
</script>v-model 应用于其他表单元素
- 常见的表单元素都可以用
v-model绑定关联 → 快速 获取 或 设置 表单元素的值 - 它会根据 控件类型 自动选取 正确的方法 来更新元素
- 输入框
input:text——>value - 文本域
textarea——>value - 复选框
input:checkbox——>checked - 单选框
input:radio——>checked - 下拉菜单
select——>value
<div id="app">
<h3>小黑学习网</h3>
<label>姓名:
<input type="text" v-model="username" placeholder="请输入姓名" />
</label>
<br /><br />
<label>是否单身:
<input type="checkbox" v-model="isSingle" />
</label>
<br /><br />
<!--
前置理解:
1. name: 给单选框加上 name 属性 可以分组 → 同一组互相会互斥
2. value: 给单选框加上 value 属性,用于提交给后台的数据
结合 Vue 使用 → v-model
-->
<label>性别: <input type="radio" name="gender" v-model="gender" value="1" />男
<input type="radio" name="gender" v-model="gender" value="2" />女
</label>
<br /><br />
<!--
前置理解:
1. option 需要设置 value 值,提交给后台
2. select 的 value 值,关联了选中的 option 的 value 值
结合 Vue 使用 → v-model
-->
<label>所在城市:
<select v-model="cityId">
<option value="10001">北京</option>
<option value="10002">上海</option>
<option value="10003">成都</option>
<option value="10004">南京</option>
</select>
</label>
<br /><br />
<label>自我描述:
<textarea v-model="desc"></textarea>
</label>
<button>立即注册</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
username: '',
isSingle: false,
gender: '2',
cityId: '10001',
desc: '',
},
});
setTimeout(() => {
app.username = '黑马程序员';
app.isSingle = true;
app.gender = '1';
app.cityId = '10004';
app.desc = '我是一个爱学习的好青年';
}, 3000);
</script>