VUE|模板语法
1 模板
1) 什么是模板
什么是模板
由Vue解析的HTML字符串
Vue的主要工作
- 编译模板
- 挂载
2) 如何确定模板
确定模板有几种方式
- 没有指定
template
选项, 以容器的innerHTML
做为模板 - 指定
template
选项, 以template
选项做为模板 - 指定
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>
2 模板语法
1) 什么是模板语法
什么是模板语法
在模板字符串中, 具有特殊意义的语法
2) 模板语法分类
模板语法分为
- 插值语法: 在
{{}}
中书写的语法 - 指令语法: 以
v-
开头的语法
插值语法
语法
在{{}}
中书写的语法
{{}}
里书写js表达式,叫做 插值表达式
应用
主要应用于文本节点
示例
<div id="app">
{{msg}}
</div>
指令语法
语法
以v-
开头的指令.
在属性值中书写js表达式, 叫做 指令表达式
应用
主要应用于属性节点
示例
<a v-bind:href="url">百度</a>
3 插值语法细节
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 } }}
4 属性绑定指令
如果要将某一个属性的值和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语句
5 事件绑定指令
1) 什么是事件绑定
将JS事件和对应的处理函数绑定
2) 语法
v-on:事件名="函数名"
// 简写
@事件名="函数名"
示例
给一个按钮绑定点击事件
<button @click="handleClick">点击</button>
methods: {
handleClick() {
alert('hello')
}
}
methods
中定义的函数会挂载到vm
实例对象上methods
中定义的普通函数内部this
指向vm
methods
中, 不推荐使用箭头函数
3) 事件修饰符
在绑定事件的时候, 可以添加事件修饰符, 常用的事件修饰符如下:
.prevent
: 阻止默认行为.stop
: 阻止冒泡.once
: 事件只会触发一次
示例
<!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>
<!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>
4) 按键修饰符
主要针对 键盘事件
常用按键修饰符
- .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>
指令语法的完整示意图:
6 双向绑定指令
1) 什么是双向绑定
双向绑定指令 通常应用于 表单元素
- 数据的改变 会 影响视图
- 视图的改变 会 影响数据
2) 原理
表单元素, 如input``select``textarea
是具有交互性的: 用户可以操作
当用户操作时, 会触发特定的事件
如: input事件
,change事件
在事件处理的回调中, 可以对数据进行修改
示例
双向绑定原理
- 通过绑定
input
框的value属性和data中的一个变量, 实现了数据->视图的绑定 - 监听
input
框的input事件, 当事件触发时, 更新data中的变量, 实现了视图->数据的绑定
3) 简写指令
v-model: 双向绑定指令
将表单元素的 值 和 data中定义的状态绑定
示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<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" v-model="username" />
</div>
<script>
const { createApp } = Vue
createApp({
data() {
return {
username: '',
}
},
}).mount('#app')
</script>
</body>
</html>
** 注意**
不同的表单元素, 绑定的属性和监听的事件不同
实例:实现一个Form表单
<template>
<div class="form">
<div class="form-container">
<el-form>
<el-form-item label="用户名">
<el-input v-model="ruleForm.name" />
</el-form-item>
<el-form-item label="密码">
<el-input v-model="ruleForm.password" />
</el-form-item>
<el-form-item label="出生日期">
<el-date-picker
v-model="ruleForm.date"
type="date"
placeholder="Pick a date"
style="width: 100%"
/>
</el-form-item>
<el-form-item label="性别" prop="resource">
<el-radio-group v-model="ruleForm.gender">
<el-radio label="男" />
<el-radio label="女" />
</el-radio-group>
</el-form-item>
<el-form-item label="爱好">
<el-checkbox-group v-model="ruleForm.type">
<el-checkbox label="吃饭" name="type" />
<el-checkbox label="睡觉" name="type" />
<el-checkbox label="听歌" name="type" />
<el-checkbox label="打游戏" name="type" />
</el-checkbox-group>
</el-form-item>
<el-form-item label="所在区域">
<el-select v-model="ruleForm.region" p>
<el-option label="上海" value="上海" />
<el-option label="武汉" value="武汉" />
</el-select>
</el-form-item>
<el-form-item label="个人签名">
<el-input v-model="ruleForm.desc" type="textarea" />
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
export default {
data() {
return {
ruleForm: {
name: '',
password: '',
region: '',
date: '',
type: [],
desc: '',
},
}
},
}
</script>
<style scoped>
.form {
all: initial;
}
.form-container {
margin-left: 30%;
margin-right: 30%;
}
</style>
7 条件渲染
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
8 列表渲染
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"
9 样式绑定
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栏切换
参考答案
工程化后的结果
<template>
<body>
<div class="body">
<nav>
<ul class="menu-tab">
<li
v-for="(item, index) in items"
:key="index"
class="menu-item"
:class="index == active ? 'current' : ''"
@click="handleClick(index)"
>
<a>{{ item.name }}</a>
</li>
</ul>
</nav>
<div class="panel">
<div class="view-container">
<router-view />
</div>
</div>
</div>
</body>
</template>
<script setup>
import { ref } from 'vue'
import { useRouter } from 'vue-router'
const router = useRouter()
if (router.currentRoute.value.path !== '/') router.replace('/')
const items = ref([
{ name: 'Home', route: '/' },
{ name: 'About', route: '/about' },
{ name: 'Todo', route: '/todo' },
{ name: 'Form', route: '/form' },
])
const active = ref(0)
function handleClick(i) {
this.active = i
router.replace(this.items[i].route)
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
li {
list-style: none;
}
.menu-tab {
display: flex;
justify-content: space-between;
margin: 0 auto;
height: 60px;
border: 1px solid #fff;
background-color: #fff;
box-sizing: border-box;
}
.menu-tab .menu-item {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
font-weight: bold;
color: #666;
background-color: #f9f9f9;
cursor: pointer;
border-bottom: 2px solid var(--el-border-color);
}
.menu-tab .menu-item:hover {
color: #2c3e50;
}
.menu-tab .menu-item.current {
color: #2c3e50;
background-color: #fff;
border-left: 2px solid var(--el-border-color);
border-right: 2px solid var(--el-border-color);
border-top: 2px solid var(--el-border-color);
border-bottom: none;
}
.body {
width: 90%;
margin: 0 auto;
height: 100vh;
display: flex;
flex-direction: column;
background-color: #fff;
}
nav a {
font-weight: bold;
}
.panel {
flex: 1;
border-left: 2px solid var(--el-border-color);
border-right: 2px solid var(--el-border-color);
border-bottom: 2px solid var(--el-border-color);
margin-bottom: 20px;
}
.view-container {
padding: 40px;
}
body {
background-color: #f9f9f9;
}
</style>