模板
1) 什么是模板
由Vue解析的HTML字符串Vue的主要工作
1编译模板
2挂载
2) 如何确定模板
确定模板有几种方式
1没有指定template选项, 以容器的innerHTML做为模板
2指定template选项, 以template选项做为模板
3指定render选项, 以render函数做为模板
优先级: render函数 > template > 容器
render函数在后续有专门的章节介绍, 这里我们先介绍template选项
示例
<!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" />
<title>Document</title>
<script src="../node_modules/vue/dist/vue.global.js"></script>
</head>
<body>
<!-- 容器指定的innerHTML模板 < template指定模板 -->
<div id="app">由容器指定的模板</div>
<script>
const { createApp, h } = Vue
const app = createApp({
template: `<h1>由template指定的模板</h1>`,
render: () => h('div', '由render函数指定的模板'),
})
app.mount('#app')
</script>
</body>
</html>
模板语法
1) 什么是模板语法
什么是模板语法
在模板字符串中, 具有特殊意义的语法
2) 模板语法分类
模板语法分为
●插值语法: 在{{}}中书写的语法
●指令语法: 以v-开头的语法
插值语法
语法
在{{}}中书写的语法
{{}}里书写js表达式, 叫做插值表达式
应用
主要应用于文本节点
示例
<div id="app">
{{msg}}
</div>
指令语法
语法
以v-开头的指令.
在属性值中书写js表达式, 叫做指令表达式
应用
主要应用于属性节点
示例
<a v-bind:href="url">百度</a>
插值语法细节
1) 直接使用实例上的属性
在{{}}内, 可以直接访问当前实例上的属性
示例
<!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" />
<title>Document</title>
<script src="../node_modules/vue/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<!-- 直接使用instance实例上的属性 -->
姓名: {{name}} <br />
女友: {{gf.name}} <br />
喜欢的书: {{books[0].name}} <br />
</div>
<script>
const { createApp } = Vue
const vm = createApp({
data() {
return {
name: 'xiaoming',
age: 20,
gf: {
name: 'xiaomei',
age: 18,
},
books: [{ name: 'Vue' }, { name: 'React' }],
}
},
}).mount('#app')
</script>
</body>
</html>
2) 使用表达式
在{{}}内, 可以书写js表达式, 但是不能书写语句
示例
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
错误的情况
<!-- 这是一个语句,而非表达式 -->
{{ var a = 1 }}
<!-- 条件控制也不支持,请使用三元表达式 -->
{{ if (ok) { return message } }}
插值语法细节
1) 直接使用实例上的属性
在{{}}内, 可以直接访问当前实例上的属性
示例
<!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" />
<title>Document</title>
<script src="../node_modules/vue/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<!-- 直接使用instance实例上的属性 -->
姓名: {{name}} <br />
女友: {{gf.name}} <br />
喜欢的书: {{books[0].name}} <br />
</div>
<script>
const { createApp } = Vue
const vm = createApp({
data() {
return {
name: 'xiaoming',
age: 20,
gf: {
name: 'xiaomei',
age: 18,
},
books: [{ name: 'Vue' }, { name: 'React' }],
}
},
}).mount('#app')
</script>
</body>
</html>
2) 使用表达式
在{{}}内, 可以书写js表达式, 但是不能书写语句
示例
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
错误的情况
<!-- 这是一个语句,而非表达式 -->
{{ var a = 1 }}
<!-- 条件控制也不支持,请使用三元表达式 -->
{{ if (ok) { return message } }}
属性绑定指令
如果要将某一个属性的值和data中的状态绑定, 需要使用绑定指令
属性绑定
将一个元素的一个属性值和data中的一个变量绑定
<!-- 完整的写法 -->
<a v-bind:href="url">百度</a>
<!-- 简写(推荐) -->
<a :href="xzd">新中地</a>
vue官方更推荐简写的方式
const vm = createApp({
data() {
return {
url: 'http://www.baidu.com',
}
},
}).mount('#app')
将a标签href属性和data中的url变量绑定
●相当于<a href="http://baidu.com"></a>
💡特别说明
在指令后面的引号中, 可以使用类似于{{}}中一样的语法.
●可以使用js表达式
●直接访问vm实例上的属性和方法
●不能是js语句
事件绑定指令
1) 什么是事件绑定
将JS事件和对应的处理函数绑定
2) 语法
v-on:事件名="函数名"
// 简写
@事件名="函数名"
示例
给一个按钮绑定点击事件template部分
<button @click="handleClick">点击</button>
js部分
methods: {
handleClick() {
alert('hello')
}
}
●methods中定义的函数会挂载到vm实例对象上
●methods中定义的普通函数内部this指向vm
●methods中, 不推荐使用箭头函数
3) 是否加括号
●不加括号(70%): 直接写函数名称, 可以在回调函数中, 直接拿到事件对象event
●加括号(20%): 加括号主要为了传参, 在回调函数中, 拿到参数
●加括号(10%): 有时, 除了拿到参数外, 还需要原生的事件对象, 通过$event传递
示例
<!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" />
<title>Document</title>
<script src="../node_modules/vue/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<!-- 访问vm实例上的属性 -->
<!-- 70%: 在指令表达式中不加(). 就可以通过默认参数获取事件对象 -->
<button @click="handleClick">点击了{{count}}次</button>
<!-- 20%: 加() 目的是为了传参数 -->
<button @click="handleClick(100)">点击了{{count}}次</button>
<!-- 10%: 加() 在参数的最后, 通过$event也可以将事件对象传递 -->
<button @click="handleClick(100, $event)">点击了{{count}}次</button>
</div>
<script>
const { createApp } = Vue
const vm = createApp({
data() {
return {
count: 0,
}
},
methods: {
// 在methods中定义的就是事件回调函数, 默认情况可以接收事件对象 做为参数
handleClick(a, b) {
console.log(a, b)
// 事件回调. 当点击事件触发, 会被调用
this.count++
},
},
}).mount('#app')
// const div = document.querySelector('#app')
// div.addEventListener('click', function (e) {
// console.log(e)
// })
</script>
</body>
</html>
4) 事件修饰符
在绑定事件的时候, 可以添加事件修饰符, 常用的事件修饰符如下:
●.prevent: 阻止默认行为
●.stop: 阻止冒泡
●.once: 事件只会触发一次
示例prevent示例
<!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" />
<title>Document</title>
<script src="../node_modules/vue/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<a @click.prevent="" href="http://www.baidu.com">baidu</a>
</div>
<script>
const { createApp } = Vue
const vm = createApp({}).mount('#app')
// 原生js方式
// document.querySelector('a').addEventListener('click', function (e) {
// // 调用事件对象的preventDefault方法, 阻止默认行为
// // 对于a元素而言, 默认行为就是跳转页面
// e.preventDefault()
// })
</script>
</body>
</html>
stop示例
<!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" />
<title>Document</title>
<script src="../node_modules/vue/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<ul @click="handleUl">
<!--
1. `.stop` 阻止事件向上冒泡
2. 事件修饰符可以连用
-->
<li @click.stop="handleLi">1</li>
<li>2</li>
<li>3</li>
</ul>
</div>
<script>
const { createApp } = Vue
const vm = createApp({
methods: {
handleUl() {
console.log('ul被点击了...')
},
handleLi() {
console.log('li被点击了...')
},
},
}).mount('#app')
</script>
</body>
</html>
5) 按键修饰符
主要针对键盘事件
常用按键修饰符
●.enter: 回车键
●.up: 上
●.down: 下
●.left: 左
●.right: 右
系统功能键
●.ctrl
●.alt
●.shift
示例
<!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" />
<title>Document</title>
<script src="../node_modules/vue/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<input type="text" @keyup.enter="submit" />
</div>
<script>
const { createApp } = Vue
const vm = createApp({
methods: {
submit() {
console.log('输入了数据, 按下回车键')
},
},
}).mount('#app')
</script>
</body>
</html>
指令语法的完整示意图:
双向绑定指令
1) 什么是双向绑定
双向绑定指令通常应用于表单元素
●数据的改变会影响视图
●视图的改变会影响数据
2) 原理
表单元素, 如inputselecttextarea是具有交互性的:用户可以操作
当用户操作时, 会触发特定的事件如:input事件change事件
在事件处理的回调中, 可以对数据进行修改双向绑定原理
1通过绑定input框的value属性和data中的一个变量, 实现了数据->视图的绑定
2监听input框的input事件, 当事件触发时, 更新data中的变量, 实现了视图->数据的绑定
3) 简写指令
v-model: 双向绑定指令
将表单元素的 值 和 data中定义的状态绑定💡注意
不同的表单元素, 绑定的属性和监听的事件不同
作业
使用v-model实现一个form表单
应用实例条件渲染
1) 什么是条件渲染
当条件满足时, 渲染到页面
主要指令:v-if和v-show
2) 示例
<!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" />
<title>Document</title>
<script src="../node_modules/vue/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<!-- 当一个内容有复杂逻辑时, 使用v-if -->
<div v-if="flag">我是通过v-if控制的</div>
<div v-else>我是通过v-else控制的</div>
<!-- 当一个内容需要切换显示时, 使用v-show -->
<div v-show="flag">我是通过v-show控制的</div>
<div v-show="!flag">我是通过v-show控制的</div>
</div>
<script>
const { createApp } = Vue
const vm = createApp({
data() {
return {
flag: true,
}
},
}).mount('#app')
</script>
</body>
</html>
●当flag为true时, 两个元素都可以显示
●当flag为false时, 两个元素都不显示,区别是
○v-if: 不会创建元素
○v-show: 创建元素, 但是display=none
3) 使用表达式
在指令表达式中, 除了使用变量外, 也可以使用表达式
<div v-if="flag == true">这是用v-if渲染的元素</div>
案例
💡需求
通过按钮控制元素的显示/隐藏
示例
<!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" />
<title>Document</title>
<script src="../node_modules/vue/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<button @click="flag = !flag">切换显示/隐藏</button>
<div v-show="flag">div显示的内容</div>
</div>
<script>
const { createApp } = Vue
const vm = createApp({
data() {
return {
flag: true,
}
},
methods: {
toggle() {
// if (this.flag == true) {
// this.flag = false
// } else {
// this.flag = true
// }
this.flag = !this.flag
},
},
}).mount('#app')
</script>
</body>
</html>
●绑定按钮的点击事件
○当flag==true时点击, flag先取反, 再保存, 此时flag为false
○当flag==false时点击, flag先取反, 再保存, 此时flag为true
列表渲染
1) 什么是列表渲染
列表渲染也称"循环渲染", 通过v-for遍历数组或者对象
2) 遍历数组
获取元素
如果只希望得到每个数组元素的值, 不需要得到下标
语法
v-for="item in items"
示例
<!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" />
<title>Document</title>
<script src="../node_modules/vue/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<ul>
<!-- 在指令表达式中, in前面的参数表示数组元素 -->
<!-- <li v-for="item in items">{{item}}</li> -->
<!-- (元素, 下标) in 数组 -->
<li v-for="(value, key) in items">{{key}} -- {{value}}</li>
</ul>
</div>
<script>
const { createApp } = Vue
const vm = createApp({
data() {
return {
items: ['test1', 'test2', 'test3'],
}
},
}).mount('#app')
</script>
</body>
</html>
获取元素和下标
如果只希望得到每个数组元素的值和下标
语法
v-for="(item, index) in items"
3) 遍历对象
●只取值:v-for="value in obj"
●取键和值:v-for="(value, key) in obj"
●取键和值和索引:v-for="(value, key, index) in obj"
4) 绑定key
在遍历时, 通常会给每个渲染的元素绑定一个唯一值key
什么是key
key是Vue内置的属性,不会渲染到DOM中
语法
<li v-for="item in items" :key="item.id">
key的作用
key是vue给每个元素定义的唯一标识, 来用复用DOM
💡需求
1制作一个人员列表, 如下
2在输入框中依次输入对应的名字
3当点击按钮时, 在列表的最上方添加一个用户:小强
示例
<!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" />
<title>Document</title>
<script src="../node_modules/vue/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<h1>用户列表</h1>
<button @click="addUser">添加一个用户</button>
<ul>
<li v-for="user in users" :key="user.id">
{{user.name}}
<input type="text" />
</li>
</ul>
</div>
<script>
const { createApp } = Vue
const vm = createApp({
data() {
return {
users: [
{ id: 1, name: '小明' },
{ id: 2, name: '小美' },
{ id: 3, name: '小胖' },
],
}
},
methods: {
addUser() {
this.users.unshift({ id: 4, name: '小强' })
},
},
}).mount('#app')
</script>
</body>
</html>
期望的结果
实际的情况
为什么会出现这种现象呢?
原因
vue在渲染每个元素时, 会给元素设置一个自定义属性key
●根据元素的key直接复用之前的DOM, 不会生成新的DOM
样式绑定
1) 什么是样式绑定
通过绑定class属性 或者style属性 修改样式
2) 绑定class属性
常见有两种语法
●数组写法
●对象写法
示例
<!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" />
<title>Document</title>
<script src="../node_modules/vue/dist/vue.global.js"></script>
<style>
.red {
color: red;
}
.blue {
color: skyblue;
}
</style>
</head>
<body>
<div id="app">
<!-- 原生的写法 -->
<span class="red blue">一段文字</span>
<!-- 绑定class属性 -- 对象的写法 -->
<span :class="obj">对象的写法</span>
<!-- 绑定class属性 -- 数组的写法(推荐) -->
<span :class="arr">数组的写法</span>
<span :class="foo">绑定一个变量</span>
<span :class="flag ? 'red' : 'blue'">使用表达式</span>
</div>
<script>
const { createApp } = Vue
const vm = createApp({
data() {
return {
obj: {
red: true,
blue: true,
},
arr: ['red', 'blue'],
foo: 'red',
flag: true,
}
},
}).mount('#app')
</script>
</body>
</html>
3) 绑定style属性对象写法
示例
<!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" />
<title>Document</title>
<script src="../node_modules/vue/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<!-- 原生的写法 -->
<div style="font-size: 32px; color: red">原生的写法</div>
<!-- 绑定style属性 -- 对象写法 -->
<div :style="obj">对象的写法</div>
</div>
<script>
const { createApp } = Vue
const vm = createApp({
data() {
return {
obj: {
// 'font-size': '32px',
fontSize: '32px',
color: 'red',
},
}
},
}).mount('#app')
</script>
</body>
</html>
4) 作业需求
实现京东tab栏切换
参考答案
<!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" />
<title>Document</title>
<!-- 1.1 引入vue.js -->
<script src="../node_modules/vue/dist/vue.global.js"></script>
<!-- 2.2 实例静态页面(CSS部分) -->
<style>
* {
margin: 0;
padding: 0;
}
li {
list-style: none;
}
.menu-tab {
display: flex;
justify-content: space-between;
margin: 50px auto;
height: 40px;
width: 700px;
border: 1px solid #eee;
border-bottom: 1px solid #e4393c;
background-color: #f7f7f7;
box-sizing: border-box;
}
.menu-tab .menu-item {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
font-size: 14px;
color: #666;
cursor: pointer;
}
.menu-tab .menu-item:hover {
color: #e4393c;
}
.menu-tab .menu-item.current {
color: #fff;
background-color: #e4393c;
}
</style>
</head>
<body>
<!-- 1.2 编写页面容器 -->
<div id="app">
<!-- 2.1 实例静态页面(HTML部分) -->
<ul class="menu-tab">
<!-- <li
v-for="(item, index) in items"
class="menu-item"
:class="index == active ? 'current' : '' "
@click="active = index"
> -->
<li
v-for="(item, index) in items"
class="menu-item"
:class="index == active ? 'current' : '' "
@click="handleClick(index)"
>
{{item}}
</li>
</ul>
</div>
<!-- 1.3 创建vue实例对象 -->
<script>
const { createApp } = Vue
const vm = createApp({
data() {
return {
items: ['商品介绍', '规格与包装', '售后保障', '商品评价(2000+)', '手机社区'],
active: 0,
}
},
methods: {
handleClick(i) {
console.log(i)
this.active = i
},
},
}).mount('#app')
</script>
</body>
</html>