2.模板与指令
2.1 模板语法
学习:插值表达式、js表达式、v-cloak
Vue 使用一种基于 HTML 的模板语法,使我们能够声明式地将其组件实例的数据绑定到呈现的 DOM 上。所有的 Vue 模板都是语法层面合法的 HTML,可以被符合规范的浏览器和 HTML 解析器解析。
{{ number + 1 }}
{{ ok ? 'YES' : 'NO' }}
{{ message.split('').reverse().join('') }}
<div :id="`list-${id}`"></div>
在底层机制中,Vue 会将模板编译成高度优化的 JavaScript 代码。结合响应式系统,当应用状态变更时,Vue 能够智能地推导出需要重新渲染的组件的最少数量,并应用最少的 DOM 操作。
2.1.1 文本插值
最基本的数据绑定形式是文本插值,它使用的是“Mustache[ˈmʌstæʃ]”语法 (即双大括号):
<span>Message: {{ msg }}</span>
双大括号标签会被替换为相应组件实例中
msg
属性的值。同时每次msg
属性更改时它也会同步更新。
2.1.2 js表达式
Vue 实际上在所有的数据绑定中都支持完整的 JavaScript 表达式:
这些表达式都会被作为 JavaScript ,以组件为作用域解析执行。
在 Vue 模板内,JavaScript 表达式可以被使用在如下场景上:
在文本插值中 (双大括号)
在任何 Vue 指令 (以
v-
开头的特殊 attribute) attribute 的值中绑定在表达式中的方法在组件每次更新时都会被重新调用,因此不应该产生任何副作用,比如改变数据或触发异步操作。
2.1.3 v-cloak
用于隐藏尚未完成编译的 DOM 模板。
当使用直接在 DOM 中书写的模板时,可能会出现一种叫做“未编译模板闪现”的情况:用户可能先看到的是还没编译完成的双大括号标签,直到挂载的组件将它们替换为实际渲染的内容。
v-cloak
会保留在所绑定的元素上,直到相关组件实例被挂载后才移除。配合像 [v-cloak] { display: none }
这样的 CSS 规则,它可以在组件编译完毕前隐藏原始模板。
[v-cloak] {
display: none;
}
<div v-cloak>
{{ message }}
</div>
直到编译完成前,
<div>
将不可见。
完整案例:06_mustache.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>06_模板语法</title>
<style>
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<!-- v-cloak -->
<div id="app" v-cloak>
<!-- 文本插值 -->
<div>{{ msg }}</div>
<!-- js表达式 -->
<div>{{ number + 1 }}</div>
<div>{{ flag ? 'ok': 'no' }}</div>
<div>{{ message.split('').reverse().join('-') }}</div>
<div v-bind:id="'list-' + id">111</div>
<div v-bind:id=`list-${id}`>222</div>
</div>
</body>
<script src="lib/vue.global.js"></script>
<script>
const { createApp } = Vue
const app = createApp({
data () {
return {
msg: 'hello vue',
number: 100,
flag: true,
message: 'hello world',
id: 100
}
}
})
app.mount('#app')
</script>
</html>
2.2 文本类指令
学习:v-text、v-html、v-pre
2.2.1 v-html & v-text
双大括号将会将数据插值为纯文本,而不是 HTML。若想插入 HTML,你需要使用 v-html 指令:
<div v-html="rawHTML"></div>
<div v-text="rawHTML"></div>
<div>{{rawHTML}}</div>
在网站上动态渲染任意 HTML 是非常危险的,因为这非常容易造成 XSS 漏洞。请仅在内容安全可信时再使用
v-html
,并且永远不要使用用户提供的 HTML 内容(script也属于HTML内容)。
2.2.2 v-pre
元素内具有 v-pre
,所有 Vue 模板语法都会被保留并按原样渲染。最常见的用例就是显示原始双大括号标签及内容。
<div v-pre>{{ rawHTML }}</div>
完整案例: 07_text.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>07_文本类指令</title>
</head>
<body>
<div id="app">
<!-- 解析输出 -->
<div v-html="msg"></div>
<!-- 转义输出 -->
<div v-text="msg"></div>
<!-- 文本插值 -->
<div>{{ msg }}</div>
<!-- v-pre 含有该指令的标签内部的语法不会被vue解析 -->
<div v-pre>{{ msg }}</div>
</div>
</body>
<script src="lib/vue.global.js"></script>
<script>
const { createApp } = Vue
const app = createApp({
data () {
return {
msg: '<h1>hello vue</h1>'
}
}
})
app.mount('#app')
</script>
</html>
2.3 属性绑定
学习:v-bind 以及简写形式
双大括号不能在 HTML attributes 中使用。想要响应式地绑定一个 attribute,应该使用 v-bind 指令:
<div v-bind:id="myId"></div>
如果绑定的值是
null
或者undefined
,那么该 attribute 将会从渲染的元素上移除。
因为 v-bind
非常常用,提供了特定的简写语法:
<div :id="myId"></div>
开头为
:
的 attribute 可能和一般的 HTML attribute 看起来不太一样,但它的确是合法的 attribute 名称字符,并且所有支持 Vue 的浏览器都能正确解析它。
布尔型 attribute 依据 true / false 值来决定 attribute 是否应该存在于该元素上。disabled 就是最常见的例子之一。
<button :disabled="flag">Button</button>
当 flag
为真值或一个空字符串 (即 <button disabled="">
) 时,元素会包含这个 disabled
attribute。而当其为其他假值时 attribute 将被忽略。
如果你有像这样的一个包含多个 attribute 的 JavaScript 对象:
data () {
return {
obj: {
a: 1,
b: 2
}
}
}
<div v-bind="obj"></div>
完整案例: 08_attribute.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>08_绑定属性以及缩写形式</title>
</head>
<body>
<div id="app">
<!-- 如果属性的值是变量,boolean类型,number类型,对象,数组,null,undefined,需要使用绑定属性 -->
<!-- 使用绑定属性遇到 null,undefined,则该属性自动不显示 -->
<div v-bind:msg="msg" v-bind:flag="true" v-bind:num="100" v-bind:obj="{a: 1, b: 2}" v-bind:arr="['a', 'b']" v-bind:nu="null" v-bind:un="undefined"></div>
<div :msg="msg" :flag="true" :num="100" :obj="{a: 1, b: 2}" :arr="['a', 'b']" :nu="null" :un="undefined"></div>
<!-- 注意隐式类型转换,非空即为真,确保时 使用绑定属性 -->
<button :disabled="false">按钮</button>
<!-- 如果属性的值需要拼接字符串完成,且含有变量,可以使用 绑定属性结合 字符串拼接以及模板字符串 实现 -->
<div v-bind:id="'list-' + id">111</div>
<div v-bind:id=`list-${id}`>222</div>
</div>
</body>
<script src="lib/vue.global.js"></script>
<script>
const { createApp } = Vue
const app = createApp({
data () {
return {
msg: 'hello vue',
id: 100
}
}
})
app.mount('#app')
</script>
</html>
如果一个属性的值是变量,boolean类型,number类型,对象,数组,null,undefined,使用绑定属性
2.4 事件绑定
学习:v-on以及简写形式,methods应用
我们可以使用 v-on
指令 (简写为 @
) 来监听 DOM 事件,并在事件触发时执行对应的 JavaScript。用法:v-on:click="methodName"
或 @click="handler"
。
事件处理器的值可以是:
内联事件处理器:事件被触发时执行的内联 JavaScript 语句 (与
onclick
类似)。方法事件处理器:一个指向组件上定义的方法的属性名或是路径。
·内联事件处理器·通常用于简单场景,例如:
<button @click="count++">加 1</button>
<p>Count is: {{ count }}</p>
随着事件处理器的逻辑变得愈发复杂,内联代码方式变得不够灵活。因此 v-on
也可以接受一个方法名或对某个方法的调用。
方法事件处理器
<button @click="greet">问候</button>
data () {
return {
name: 'Vue.js'
}
},
methods: {
greet(event) {
// 方法中的 `this` 指向当前活跃的组件实例
alert(`Hello ${this.name}!`)
// `event` 是 DOM 原生事件
if (event) {
alert(event.target.tagName)
}
}
}
除了直接绑定方法名,你还可以在内联事件处理器中调用方法。这允许我们向方法传入自定义参数以代替原生事件:
methods: {
say(message) {
alert(message)
}
}
<button @click="say('hello')">说 hello</button>
<button @click="say('bye')">说 bye</button>
内联事件处理器中访问原生 DOM 事件。你可以向该处理器方法传入一个特殊的 $event
变量,或者使用内联箭头函数:
<!-- 使用特殊的 $event 变量 -->
<button @click="warn('表单不能提交.', $event)">提交</button>
<!-- 使用内联箭头函数 -->
<button @click="(event) => warn('表单不能提交.', event)">提交</button>
methods: {
warn(message, event) {
// 这里可以访问 DOM 原生事件
if (event) {
event.preventDefault()
}
alert(message)
}
}
在处理事件时调用 event.preventDefault()
或 event.stopPropagation()
是很常见的。尽管我们可以直接在方法内调用,但如果方法能更专注于数据逻辑而不用去处理 DOM 事件的细节会更好。
为解决这一问题,Vue 为 v-on
提供了事件修饰符。
<!-- 单击事件将停止传递 -->
<a @click.stop="doThis"></a>
<!-- 提交事件将不再重新加载页面 -->
<form @submit.prevent="onSubmit"></form>
<!-- 修饰语可以使用链式书写 -->
<a @click.stop.prevent="doThat"></a>
<!-- 也可以只有修饰符 -->
<form @submit.prevent></form>
<!-- 仅当 event.target 是元素本身时才会触发事件处理器 -->
<!-- 例如:事件处理器不来自子元素 -->
<div @click.self="doThat">...</div>
<!-- 添加事件监听器时,使用 `capture` 捕获模式 -->
<!-- 例如:指向内部元素的事件,在被内部元素处理前,先被外部处理 -->
<div @click.capture="doThis">...</div>
<!-- 点击事件最多被触发一次 -->
<a @click.once="doThis"></a>
<!-- 滚动事件的默认行为 (scrolling) 将立即发生而非等待 `onScroll` 完成 -->
<!-- 以防其中包含 `event.preventDefault()` -->
<div @scroll.passive="onScroll">...</div>
<!-- 事件修饰符 -->
<!-- 阻止冒泡-原生 -->
<div class="parent" @click="clickParent1">
<div class="child" @click="clickChild1"></div>
</div>
<!-- 阻止冒泡-事件修饰符 -->
<div class="parent" @click="clickParent2">
<div class="child" @click.stop="clickChild2"></div>
</div>
methods: {
clickParent1 (event) {
console.log('parent1')
},
clickChild1 (event) {
event.stopPropagation()
console.log('child1')
},
clickParent2 (event) {
console.log('parent2')
},
clickChild2 (event) {
console.log('child2')
},
}
.parent {
width: 200px;
height: 200px;
background: #f66;
display: flex;
justify-content: center;
align-items: center;
margin-top: 10px;
}
.child {
width: 100px;
height: 100px;
border: 1px solid #ccc;
}
在监听键盘事件时,我们经常需要检查特定的按键。Vue 允许在 v-on
或 @
监听按键事件时添加按键修饰符
。
<!-- 按键修饰符 -->
<!-- 原生 -->
<input type="text" @keyup="print1">
<!-- vue -->
<input type="text" @keyup.enter="print2">
methods: {
print1 (event) {
if (event.keyCode === 13) {
console.log('打印1')
}
},
print2 () {
console.log('打印2')
}
}
.enter
.tab
.delete (捕获“Delete”和“Backspace”两个按键)
.esc
.space
.up
.down
.left
.right
你可以使用以下系统按键修饰符
来触发鼠标或键盘事件监听器,只有当按键被按下时才会触发
.ctrl
.alt
.shift
.meta
<!-- Alt + Enter -->
<input @keyup.alt.enter="clear" />
<!-- Ctrl + 点击 -->
<div @click.ctrl="doSomething">Do something</div>
完整案例: 09_event.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>09_绑定事件</title>
<style>
.parent {
width: 200px;
height: 200px;
background-color: #f66;
display: flex;
justify-content: center;
align-items: center;
}
.child {
width: 100px;
height: 100px;
background-color: #ccc;
}
</style>
</head>
<body>
<div id="app">
<!-- 内联事件处理器 -->
<button v-on:click="count++">加1</button> {{ count }}<br />
<!-- 方法事件处理器 -->
<button v-on:click="add">加1</button> {{ count }}<br />
<button @click="say('hello', $event)">say hello</button>
<button @click="say('bye', $event)">say bye</button>
<!-- 事件对象 - 阻止事件冒泡,阻止默认事件 -->
<form @submit="getUserInfo">
<input type="text" name="userName" placeholder="用户名" />
<input type="password" name="password" placeholder="密码" />
<input type="submit" value="提交">
</form>
<div class="parent" @click="print('parent', $event)">
<div class="child" @click="print('child', $event)"></div>
</div>
<!-- 事件修饰符 - 阻止事件冒泡,阻止默认事件 -->
<form @submit.prevent="getUserInfoVue">
<input type="text" name="userName" placeholder="用户名" />
<input type="password" name="password" placeholder="密码" />
<input type="submit" value="提交">
</form>
<div class="parent" @click="printVue('parent')">
<div class="child" @click.stop="printVue('child')"></div>
</div>
<!-- 事件对象 - 回车时打印数据 -->
<input type="text" @keyup="printData" />
<!-- 按键修饰符 - 回车时打印数据 -->
<input type="text" @keyup.enter="printDataVue" />
<!-- vue2中可以 根据 keyCode 作为修饰符, vue3中不支持 -->
<input type="text" @keyup.13="printDataVueCode" />
<!-- 使用系统修饰符可以自定义组合按键 -->
<!-- 用户点击 alt 加 enter时 清空输入框数据 -->
<!-- v-model 属于表单的输入绑定 -->
<input type="text" v-model="text" @keyup.alt.enter="clear"/> {{ text }}
<div @click.ctrl="doSomething">Do something</div>
</div>
</body>
<script src="lib/vue.global.js"></script>
<script>
const { createApp } = Vue
const app = createApp({
data () {
return {
count: 10,
name: 'Vue.js',
text: '1'
}
},
methods: { // 所有自定义的vue的事件都应该写在 methods 中
// 如果使用事件时,没有添加(),那么事件含有默认参数为 event
add (event) {
// this其实就是vue的实例
// this.count++
// this.count += 1
this.count = this.count + 1
console.log(this.name)
console.log(event) // PointerEvent 与原生js中的事件对象保持一致(react中使用的不是原生js的事件对象)
},
// 如果使用事件时添加(),并且还想用事件对象,那么传递事件对象的vue的专属参数 $event
say (msg, event) {
console.log(msg, event)
},
getUserInfo (event) {
event.preventDefault()
},
getUserInfoVue () {
},
print (msg, event) {
event.stopPropagation()
console.log(msg)
},
printVue (msg) {
console.log(msg)
},
printData (event) {
if (event.keyCode === 13) {
console.log('1')
}
},
printDataVue () {
console.log(2)
},
printDataVueCode () {
console.log(3)
},
clear () {
this.text = ''
},
doSomething () {
console.log('doSomething')
}
}
})
app.mount('#app')
</script>
</html>
2.5条件渲染
学习:v-if、v-else-if、v-else、v-show
v-if
指令用于条件性地渲染一块内容。这块内容只会在指令的表达式返回真值时才被渲染。
也可以使用 v-else
为 v-if
添加一个“else 区块”
一个 v-else
元素必须跟在一个 v-if
或者 v-else-if
元素后面,否则它将不会被识别。
<div v-if="grade >= 90">优</div>
<div v-else-if="grad" >= 80">良</div>
<div v-else-if="grade >= 70">中</div>
<div v-else-if="grade >= 60">差</div>
<div v-else>不及格</div>
data () {
return {
grade: 66
}
}
v-else
和v-else-if
也可以在<template>
上使用。想要切换不止一个元素,在这种情况下我们可以在一个
<template>
元素上使用v-if
,这只是一个不可见的包装器元素,最后渲染的结果并不会包含这个<template>
元素。---- tempalte是一个空标签
另一个可以用来按条件显示一个元素的指令是 v-show
。其用法基本一样:
不同之处在于 v-show
会在 DOM 渲染中保留该元素;v-show
仅切换了该元素上名为 display
的 CSS 属性。
v-show
不支持在<template>
元素上使用,也不能和v-else
搭配使用。
<!-- v-show -->
<div v-show="grade >= 90 && grade < 100">优</div>
<div v-show="grade >= 80 && grade < 90">良</div>
<div v-show="grade >= 70 && grade < 80">中</div>
<div v-show="grade >= 60 && grade < 70">差</div>
<div v-show="grade < 60">不及格</div>
完整案例:10_condition.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>10_条件渲染</title>
</head>
<body>
<div id="app">
<button @click="grade++"> 加</button> {{ grade }}<button @click="grade--">减</button>
<!-- v-if v-else-if v-else -->
<div v-if="grade >=90">优</div>
<div v-else-if="grade >=80">良</div>
<div v-else-if="grade >=70">中</div>
<div v-else-if="grade >=60">差</div>
<div v-else>不及格</div>
<hr />
<!-- v-show -->
<div v-show="grade >= 90">优</div>
<div v-show="grade >= 80 && grade < 90">良</div>
<div v-show="grade >= 70 && grade < 80">中</div>
<div v-show="grade >= 60 && grade < 70">差</div>
<div v-show="grade < 60">不及格</div>
<!-- 假如不同条件下需要同时控制多个元素的显示和不显示 -->
<div v-if="flag">1</div>
<div v-if="flag">2</div>
<div v-if="flag">3</div>
<!-- template 属于vue框架中的空标签,审查元素时不会被渲染出来 -->
<template v-if="flag">
<div>4</div>
<div>5</div>
<div>6</div>
</template>
</div>
</body>
<script src="lib/vue.global.js"></script>
<script>
const { createApp } = Vue
const app = createApp({
data () {
return {
grade: 61,
flag: true
}
}
})
app.mount('#app')
</script>
</html>
v-if
vsv-show
v-if
是“真实的”按条件渲染,因为它确保了在切换时,条件区块内的事件监听器和子组件都会被销毁与重建。
v-if
也是惰性的:如果在初次渲染时条件值为 false,则不会做任何事。条件区块只有当条件首次变为 true 时才被渲染。相比之下,
v-show
简单许多,元素无论初始条件如何,始终会被渲染,只有 CSSdisplay
属性会被切换。总的来说,
v-if
有更高的切换开销,而v-show
有更高的初始渲染开销。因此,如果需要频繁切换,则使用v-show
较好;如果在运行时绑定条件很少改变,则v-if
会更合适。
2.6列表渲染
学习:v-for 以及key属性
我们可以使用 v-for
指令基于一个数组(对象、字符串)来渲染一个列表。v-for
指令的值需要使用 item in items
形式的特殊语法,其中 items
是源数据的数组,而 item
是迭代项的别名
data() {
return {
items: [{ message: 'Foo' }, { message: 'Bar' }]
}
}
<li v-for="item in items">
{{ item.message }}
</li>
在 v-for
块中可以完整地访问父作用域内的属性和变量。v-for
也支持使用可选的第二个参数表示当前项的位置索引。
data() {
return {
parentMessage: 'Parent',
items: [{ message: 'Foo' }, { message: 'Bar' }]
}
}
<li v-for="(item, index) in items">
{{ parentMessage }} - {{ index }} - {{ item.message }}
</li>
列表渲染需要添加key值,对于多层嵌套的 v-for
,作用域的工作方式和函数的作用域很类似
完整案例: 11_list.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>11_列表渲染</title>
</head>
<body>
<div id="app">
<!-- 列表渲染中的 key 需要使用列表数据的唯一值
实在不得已情况下,可以使用 数组 的索引值作为key值
v-for="(item, index) of list" :key="index"
--4>
<!-- 没有key -->
<ul>
<li v-for="item of arr">
{{ item }}
</li>
</ul>
<button @click="beforeArrAdd">arr前追加数据</button>
<button @click="afterArrAdd">arr后追加数据</button>
<!-- 添加key -->
<ul>
<li v-for="item of arr" :key="item">
{{ item }}
</li>
</ul>
<ul>
<li v-for="item of items">
{{ item.message }}
</li>
</ul>
<button @click="getData">获取数据</button>
<ul>
<li v-for="item of list" :key="item.proid">
{{ item.proname }}
</li>
</ul>
<ul>
<li v-for="item of carList">
<div>{{ item.brand }}</div>
<ol>
<li v-for="itm in item.list">
{{ itm }}
</li>
</ol>
</li>
</ul>
</div>
</body>
<script src="lib/vue.global.js"></script>
<script src="lib/jquery.js"></script>
<script>
const { createApp } = Vue
const app = createApp({
data () {
return {
arr: ['a', 'b', 'c', 'd'],
items: [
{ message: 'Foo' },
{ message: 'Bar' }
],
list: [],
carList: [
{
brand: '宝马',
list: ['X5', 'X6']
},
{
brand: '雷克萨斯',
list: ['XT5', 'AT6']
}
]
}
},
methods: {
getData () {
// http://121.89.205.189:3001/apidoc/#api-Pro-GetProList
// 原生js ajax
// jQuery ajax --- 不要再vue以及react中使用jQuery,因为 jQuery 大量的api都是基于DOM的
// $.ajax({
// url: 'http://121.89.205.189:3001/api/pro/list',
// method: 'GET',
// success: res => { // ? 为什么要使用 箭头函数
// console.log(res)
// this.list = res.data
// }
// })
// fetch -- 需要先转换为 json 的格式,再使用
fetch('http://121.89.205.189:3001/api/pro/list').then(res => res.json()).then(res=> {
console.log(res)
this.list = res.data
})
}
},
methods: {
beforeArrAdd () {
this.arr.unshift('e')
},
afterArrAdd () {
this.arr.push('f')
}
}
})
app.mount('#app')
</script>
</html>
v-if 与 v-for 同时存在于一个元素上,会发生什么?
12_vue3_if_for.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>12_vue3的v-if与v-for优先级</title> </head> <body> <div id="app"> <ul> <li v-for="(item, index) of arr" :key="index" v-if="flag"> {{ item }} </li> </ul> </div> </body> <script src="lib/vue.global.js"></script> <script> Vue.createApp({ data () { return { arr: ['a', 'b', 'c', 'd'], flag: false } } }).mount('#app') </script> </html>
13_vue2_if_for.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>13_vue2的v-if与v-for优先级</title> </head> <body> <div id="app"> <ul> <li v-for="(item, index) of arr" :key="index" v-if="flag"> {{ item }} </li> </ul> </div> </body> <script src="lib/vue.js"></script> <script> new Vue({ data: { arr: ['a', 'b', 'c', 'd'], flag: false } }).$mount('#app') </script> </html>
通过
审查元素
得知:vue3中,v-if的优先级高于v-for
vue2中,v-for的优先级高于v-if
2.7 表单输入绑定
学习:v-model
在前端处理表单时,我们常常需要将表单输入框的内容同步给 JavaScript 中相应的变量
<input
:value="text"
@input="event => text = event.target.value">
v-model
指令帮我们简化了这一步骤:
<input v-model="text">
v-model
还可以用于各种不同类型的输入,<textarea>
、<select>
元素。它会根据所使用的元素自动使用对应的 DOM 属性和事件组合:
文本类型的
<input>
和<textarea>
元素会绑定value
property 并侦听input
事件;
<input type="checkbox">
和<input type="radio">
会绑定checked
property 并侦听change
事件;
<select>
会绑定value
property 并侦听change
事件
v-model
会忽略任何表单元素上初始的value
、checked
或selected
attribute。它将始终将当前绑定的 JavaScript 状态视为数据的正确来源。你应该在 JavaScript 中使用data
选项来声明该初始值。
完整案例: 14_model.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>14_表单输入绑定</title>
</head>
<body>
<div id="app">
<div>
用户名: <input type="text" v-model="userName" /> {{ userName }}
</div>
<div>
密 码: <input type="password" v-model="password" /> {{ password }}
</div>
<div>
性 别: <input type="radio" name="sex" v-model="sex" value="男"/>男<input type="radio" name="sex" v-model="sex" value="女"/>女 --- {{ sex }}
</div>
<div>
阶 段: <select v-model="lesson">
<option :value="1">一阶段</option>
<option :value="2">二阶段</option>
<option :value="3">三阶段</option>
</select> ---- {{ lesson === 1 ? '一阶段' : lesson === 2 ? '二阶段' : '三阶段' }}
</div>
<div>
爱 好:
<input type="checkbox" name="hobby" value="🏀" v-model="hobby"/>🏀
<input type="checkbox" name="hobby" value="⚽" v-model="hobby"/>⚽
<input type="checkbox" name="hobby" value="🏓" v-model="hobby"/>🏓
<input type="checkbox" name="hobby" value="⚾" v-model="hobby"/>⚾
<input type="checkbox" name="hobby" value="🌏" v-model="hobby"/>🌏
</div> ---- {{ hobby }}
<div>
备 注: <textarea v-model="note"></textarea> {{ note }}
</div>
<div>
<input type="checkbox" v-model="flag"/> 同意*******协议 --- {{ flag }}
</div>
<div>
<input type="button" value="提交" @click="submit" />
</div>
</div>
</body>
<script src="lib/vue.global.js"></script>
<script>
Vue.createApp({
data () {
return {
userName: '',
password: '',
sex: '男',
lesson: 1,
note: '',
hobby: ['🌏'], // 表示多选-数组
flag: false // 表示开关 - boolean
}
},
methods: {
submit () {
if (this.flag) {
console.log({
userName: this.userName,
password: this.password,
sex: this.sex,
hobby: this.hobby,
lesson: this.lesson,
note: this.note
})
} else {
alert('请先勾选用户协议')
}
}
}
}).mount('#app')
</script>
</html>
2.8 类与样式绑定
数据绑定的一个常见需求场景是操纵元素的 CSS class 列表和内联样式。因为 class
和 style
都是 attribute,我们可以和其他 attribute 一样使用 v-bind
将它们和动态的字符串绑定。但是,在处理比较复杂的绑定时,通过拼接生成字符串是麻烦且易出错的。因此,Vue 专门为 class
和 style
的 v-bind
用法提供了特殊的功能增强。除了字符串外,表达式的值也可以是对象或数组。
不管是类class还是样式style,都有对象和数组的写法
完整案例: 15_style.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>15_类与样式绑定</title>
<style>
.a {
font-size: 30px;
}
.b {
color: #f66;
}
</style>
</head>
<body>
<div id="app">
<input type="checkbox" v-model="flag" />
<div class="a b">群组选择器</div>
<!-- 在flag字段为真时,才显示红色的30px的字体 -->
<!-- class对象写法,key为定义的样式,value为条件 -->
<div :class="{ a: flag, b: flag }">class的对象写法</div>
<!-- class数组写法 -->
<div :class="[{a: flag}, {b: flag}]">class数组写法</div>
<!-- 三元运算符 -->
<div :class="flag ? 'a b' : ''">三元运算符 class写法</div>
<!-- style对象写法,小驼峰式以及短横线连接方式 -->
<div :style="{ color: color, fontSize: fontSize }">style对象写法</div>
<!-- style数组写法 -->
<div :style="[{color: color}, {fontSize: fontSize}]">style数组写法</div>
</div>
</body>
<script src="lib/vue.global.js"></script>
<script>
Vue.createApp({
data () {
return {
flag: false,
color: '#00f',
fontSize: '50px'
}
}
}).mount('#app')
</script>
</html>