官方简介
Vue.js 使用了基于 HTML 的模板语法,允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据。
指令 (Directives) 是带有 v- 前缀的特殊 attribute。
# 一、基础指令
# 1、内容渲染
若 data 属性值为
undefined
,它是不会呈现是页面上的。
<div id="app">
<!-- 1、纯文本 v-text -->
<p v-text="text_message"></p>
<!-- 2、HTML标签字符串 v-html -->
<p v-html="html_message"></p>
<!-- 3、文本插值 {{ }} -->
<p>{{ text_message }}</p>
</div>
v-html 的应用场景
比如我们经常使用的 Ctrl + F,进行的关键字搜索,匹配到的关键字进行高亮显示。
解决思路:
使用正则匹配,加上 innerHTML 的内容进行实时更新
<div id="app">
<input v-model="keyword" type="text" />
<p v-for="item in essayData">
<span class="title" v-html="htmlHandle(item.title) + '-' + htmlHandle(item.author)"></span>
<span class="poet" v-html="htmlHandle(item.poetry_content)"></span>
</p>
</div>
<script>
let vm = new Vue({
el: '#app',
async created() {
// 1、文章数据
const { data: res } = await axios.get('./data.json').then(res=> )
this.essayData = res
},
data() {
return {
keyword: '',
essayData: []
}
},
methods: {
// 2、关键字高亮
htmlHandle(str) {
return str.replace(eval(`/${this.keyword}/g`), `<span class="highlight">$&</span>`)
}
}
})
</script>
即使标签内有内容,也会被 vue 指令:v-text
、v-html
的内容覆盖。
注意点:
使用 {{}}
的位置只能在内容节点,不能在属性节点。
当然,我们可以使用 ✍ 属性绑定的方式,到达在属性节点上使用响应式数据的目的。
v-once 一次渲染
其中,使用 v-once
可以限制渲染的次数(一次性)。
<span v-once>这个将不会改变: {{ const_message }}</span>
更多的,还有:
v-pre
:显示原始双大括号标签及内容v-cloak
:解决“未编译模板闪现”问题
# 2、Mustache 插值
对于 vue 对js 表达式的支持,我们在插值、指令(如v-bind
、v-on
、v-if
、v-for
)等中使用。
对于所有的数据绑定,Vue.js 都提供了完全的 JavaScript 表达式支持
- 正确示范
<div id="app">
<!-- 数学运算 -->
<p>{{ number + 1 }}</p>
<!-- 三目运算符 -->
<p>{{ ok ? 'yes' : 'no' }}</p>
<!-- 内置函数(推荐直接使用🎈计算属性) -->
<p>{{ message.split('').reverse().join('') }}</p>
<!-- 字符串拼接 -->
<p>{{'第' + rank + '名'}}</p>
<!-- 这里的数据是不用加this.的 -->
<button @click="isDisabled = !isDisabled">切换状态</button>:
</div>
- 错误案例
提示
虽然 Mustache 中是不能定义语句的,但它是支持调用 methods 中的函数的
{{ buttonFn() }}
<!-- 这是语句,不是表达式 -->
{{ var a = 1 }}
<!-- 流控制也不会生效,请使用三元表达式 -->
{{ if (ok) { return message } }}
# 二、v-bind 属性绑定
# 1、代码体验
属性绑定以placeholder
属性为例
<div id="app">
<div>{{ count }}</div>
<!-- 1、属性绑定(:) -->
<input type="text" v-bind:placeholder="tips" />
<img :src="imgsrc" alt="" />
<button :disabled="isDisabled">按钮</button>
</div>
# 2、绑定对象 ✨
应用
v-bind 绑定对象的主要应用于:给子组件批量传递参数 (也就是后续的组件中的 props 属性 )
使用:
<div id="app">
<h2 v-bind="infos">HelloWorld</h2>
<!-- 开发应用 -->
<hello-world v-bind="props"></hello-world>
<!-- <hello-world userName="lencamo" :age="22"></hello-world> -->
</div>
<script>
new Vue({
el: '#app',
data: {
infos: { title: 'haha', name: 'lencamo' },
props: { userName: 'lencamo', age: 22 }
}
})
</script>
渲染结果:
<h2 title="haha" name="lencamo">HelloWorld</h2>
# 3、class/style 绑定
详见 - 《vue 组件(下)》
先感受一下:
<div id="app">
<h3 :style="{opacity, color:'red'}">文字透明度</h3>
<div :id="'item' + rank"></div>
</div>
# 三、v-on 事件绑定
注意
官方推荐你始终使用 kebab-case 的事件名,因为v-on:myEvent
在 DOM 模板中会自动转换v-on:myevent
从而导致 myEvent 不可能被监听到。
# 1、代码体验
事件绑定以onclik
事件为例。
<div id="app">
<div>{{ count }}</div>
<!-- 2、事件绑定(@) -->
<button v-on:click="myFunction">点击我.</button>
<p id="demo" @click="myFunction">点击我.</p>
<p id="demo" @click="count++">点击我.</p>
</div>
<script>
new Vue({
el: '#app',
data: {
count: 0
},
methods: {
myFunction() {
this.count += 1
}
}
})
</script>
知识回顾:
<!-- 原生js事件处理 -->
<button onclick="myFunction()">点击我.</button>
# 2、绑定对象 ✨
应用
v-on 绑定对象的主要应用于:批量绑定事件(不常用)
使用:
<div id="app">
<div v-on="events"></div>
<!-- 等效于 -->
<!-- <div @click="divClickFn" @mousemove="divMousemoveFn"></div> -->
</div>
<script>
new Vue({
el: '#app',
data: {
events: { click: 'divClickFn', mousemove: 'divMousemoveFn' }
}
})
</script>
# 3、事件参数
若在函数内想要使用原生事件对象,则需要传入参数$event
,若被调用方法没有参数时,可以是可以省略$event
的
<div id="app">
<!-- 1、默认传递event对象 -->
<button @click="add1">测试按钮1</button>
<!-- 2、传递自定义参数 -->
<button @click="add2(1, numb)">测试按钮2</button>
<!-- 3、即想要event对象又想传递自定义参数 -->
<!-- $event的位置没有要求😂 -->
<button @click="add3(1, numb, $event)">测试按钮3</button>
</div>
<script>
const vm = new Vue({
el: '#app',
data: {
numb: 2
},
methods: {
add1(event) {
console.log(event)
console.log(vm === this) //true
},
add2(a, b) {
console.log(a + b)
},
add3(a, b, event) {
console.log(a + b)
console.log(event.target)
}
}
})
</script>
# 4、事件修饰符
在事件处理程序中调用 event.preventDefault()
或 event.stopPropagation()
是非常常见的需求。
.prevent
: 阻止默认行为
阻止默认行为
<!-- 1、传统方式 -->
<div id="app">
<a href="https://ren-sir.cn/" @click="myFunction">ren-sir网站</a>
</div>
<script>
new Vue({
el: '#app',
methods: {
myFunction(e) {
e.preventDefault()
}
}
})
</script>
<!-- 2、事件修饰符 -->
<div id="app">
<a href="https://ren-sir.cn/" @click.prevent="myFunction">ren-sir网站</a>
</div>
.stop
:阻止事件冒泡
阻止事件冒泡
<div id="root">
<div id="outbox" @click="divHandler">
<button @click.stop="btnHandler">验证是否冒泡</button>
</div>
</div>
<script type="text/JavaScript">
new Vue({
el: '#root',
methods: {
divHandler(){
console.log("发生了冒泡")
},
btnHandler(){
console.log("你点击了button")
}
},
});
</script>
在 组件标签 上绑定的所有事件(包括原始事件的名字 click、input 等等)都是自定义事件,都需要组件内的$emit
来触发才行。
.native
:监听组件根元素的原生事件 ✨
应用场景:
若组件内不支持某个原生事件名时(通过代码测试发现),.native 可以给组件内的根标签绑定该原生事件。
<div id="app">
<div class="box">
<Son @click.native="handlerFun" @mouseover="parentMoverFun"></Son>
</div>
</div>
<!-- 拓展:在element-ui中的<el-menu-item>组件🤔支持原生事件click -->
<el-menu-item index="3" @click="logoutFn">退出</el-menu-item>
按键修饰符
在监听键盘事件时,我们经常需要检查详细的按键。Vue 允许为 v-on 在监听键盘事件时添加按键修饰符。
常用的按键码别名:
enter、tab、delete、esc、space、up、down、left、right
思考:
防抖与节流
- 传统方式
<!-- 当$event.key===esc时,`vm.clearInput()`会被调用 -->
<input v-on:keyup="submit" />
<script>
new Vue({
el: '#app',
methods: {
submit() {
if (e.key === enter) {
//
}
}
}
})
</script>
- 使用按键修饰符
<!-- 在 `key` 是 `Enter` 时调用 `vm.submit()` -->
<input v-on:keyup.enter="submit" />
# 四、v-if 条件渲染
# 1、代码体验
<div id="app">
<div v-if="type === 'A'">A</div>
<div v-else-if="type === 'B'">B</div>
<div v-else-if="type === 'C'">C</div>
<div v-else>Not A/B/C</div>
</div>
应用场景(表单复用):
分组使用
v-if
指令时,建议写在<template>
标签中,因为它不会被渲染到页面上去。
<div id="app">
<template v-if="loginType === 'username'">
<label for="username">Username</label>
<input placeholder="Enter your username" id="username" />
</template>
<template v-else>
<label for="email">Email</label>
<input placeholder="Enter your email address" id="email" />
</template>
</div>
# 2、显示与隐藏
- v-if 是“真正”的条件渲染指令,因为它会确保在切换过程中条件块内的事件监听器和子组件(DOM 元素)适当地被销毁和重建。
v-if 切换过程中,会重新渲染组件(DOM 元素),所以原组件的状态会丢失,新组件会触发其生命周期。
- v-show 是简单的展示和隐藏,只是简单地切换元素的 CSS display 属性。
v-show 则是简单地基于 CSS 进行切换,元素始终会被渲染并保留在 DOM 中。
提示
v-show 是不支持在 template 上使用的(与其实现原理相关)
<div id="app">
<div v-if="flag">v-if控制的内容区域</div>
<div v-show="flag">v-show控制的内容区域</div>
</div>
<script>
new Vue({
el: '#app',
data: {
flag: false
}
})
</script>
# 五、v-for 列表渲染
在 vue 中我们可以用 v-for 指令基于一个数组来渲染一个列表。v-for 指令需要使用 item in items 形式的特殊语法。
当然,v-for 和 v-if 一样是可以结合 template 使用的;但是官方并不推荐 v-for 和 v-if 一起使用
# 1、代码体验
提示
你也可以用 of 🤔 替代 in 作为分隔符,因为它更接近 JavaScript 迭代器的语法
<div id="app">
<!-- 1、数组 -->
<ul id="example-1">
<!-- items通常来自于date和👀prop -->
<li v-for="(item, index) in items" :key="item.id">{{ item.message }}</li>
</ul>
<!-- 2、对象 -->
<ul id="example-2">
<li v-for="(value, key, index) in object">{{ index }}. {{ key }}: {{ value }}</li>
</ul>
<!-- 3、遍历字符串/数字(Iterable) -->
<!-- 当遍历数字时,相当于进行一个简单的for循环 -->
<script>
new Vue({
el: '#app',
data: {
parentMessage: 'Parent',
// 一个数组
items: [
{ id: 1, message: 'Foo'},
{ id: 2, message: 'Bar'},
……,
]
// 单个对象
object: {
title: 'How to do lists in Vue',
author: 'Jane Doe',
publish: '2016-04-10'
}
}
})
</script>
</div>
# 2、数组更新检测
由于 JavaScript 的限制,Vue 不能检测数组和对象的变化
<ul>
<li v-for="item in names">{{item}}</li>
</ul>
<button @click="changeName">修改数组</button>
<script>
let vm = new Vue({
el: '#app',
data: {
names: ['张三', '李四', '王五']
},
methods: {
changeName() {
// 1、替换数组
this.names = ['李华', '王刚']
// this.names = this.names.map((item) => item + 'R')
// 2、变更方法
this.names.push('李华')
}
}
})
</script>
# 3、key 属性绑定 🎈
v-for 中为什么要绑定 key 属性,我们可以先看看官方解释:
答案
当 Vue 正在更新使用 v-for 渲染的元素列表时,它默认使用“就地更新”的策略:
如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序,而是就地更新每个元素,并且确保它们在每个索引位置正确渲染
但是这种模式的高效性只适用于列表渲染输出的结果不依赖子组件状态或临时 DOM 状态的情况。
为了重用和重新排序现有元素,我们需要提供一个唯一 key 来保证能够跟踪每个节点
那 key 属性有什么作用呢,看看官方解释:
答案
https://v2.cn.vuejs.org/v2/api/#key
key 属性主要用在 Vue 的虚拟 DOM 算法,在新旧 nodes 对比时辨识 VNodes
- 如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法
- 而使用 key 时,它会基于 key 的变化重新排列元素顺序,并且会移除/销毁 key 不存在的元素
简单的说,就是在有 key 和没有 key 时,vue 采用的是两种算法
注意
2.2.0+ 的版本里,当在组件上使用 v-for 时,key 现在是必须的(建议不要使用:key="index"
)。
下面为了演示 v-for 中使用 key 属性的作用,我们可以创建一个列表渲染结果需要依赖临时 DOM 状态的情况(input 标签)
虚拟 DOM 和 Diff 算法
① 先匹配旧节点
② 根据新旧 VNode 列表个数,进行移除、新增节点,如果顺序是乱序的就使用 key 建立索引最大限度的使用旧节点
<div id="app">
<button @click.once="add">新增</button>
<ul v-for="(item, index) in list" :key="item.id">
<li>{{ index }}</li>
<li>{{ item.name }}</td>
<input type="text" /> <!-- 输入的数据是否会同步 -->
</ul>
</div>
<script>
new Vue({
el: '#app',
data: {
list: [
{ id: 1, name: '张三' },
{ id: 2, name: '李四' },
{ id: 3, name: '王五' }
]
},
methods: {
add() {
this.list.unshift({ id: 4, name: '李明' })
}
}
})
</script>
# 六、表单输入绑定
注意:v-model 指令常用于表单控件,如<input>
标签、<textarea>
文本域、<select>
选择框等
在 vue 中你可以使用 v-model 指令用于在表单控件和 Vue 实例的数据之间建立双向绑定,可以用来辅助开发者在不操作 DOM 的情况下,快速获取表单的数据。
# 1、代码体验
v-model 和前面的:
、@
一样,是 vue 提供的一种语法糖,其完整写法如下:
提示
在组件上也是可以使用 v-model 的,可以用它来创建具有完全自定义行为且可复用的输入组件(自定义输入组件 (opens new window))。
<input>
中的数据与数据源中的 username 同步。- 修改
<input>
内的值,其值会同步到数据源中
<div id="app">
<!-- 简写 -->
<input type="text" v-model="title" /><br />
标题:<span>{{title}}</span><br />
<br />
<!-- 完整👏 -->
<input v-bind:value="message" v-on:input="message = $event.target.value" /><br />
内容:<span>{{message}}</span><br />
</div>
<script>
new Vue({
el: '#app',
data: {
title: '语法糖',
message: '在文本区域🚩插值 (<textarea>{{text}}</textarea>) 并不会生效,可以使用 v-model 代替'
}
// methods: {
// inputChange(event) {
// this.message = event.target.value
// }
// }
})
</script>
<style>
input {
width: 80%;
}
</style>
# 2、v-model 绑定
-v-model
在内部会根据不同的控件类型(text、checkbox、radio 等等)自动选取正确的 property(value、checked 属性值等等)来绑定 并抛出不同的事件,v-model 本质上不过是语法糖。
下面的这些表单控件,通常默认情况下与对应的 value 属性或者 checked 属性绑定,并且可以省略这些属性
标签 | 绑定 | 说明 |
---|---|---|
text 和 textarea 元素 | value 属性 和 input 事件 | |
checkbox 和 radio 元素 | checked 属性 和 change 事件 | 多个复选框,绑定 value 到同一个数组;多个单选框,绑定到某个 value 值 |
select 选择框 | 使用 value 属性 和 change 事件 |
v-model 完整使用案例
<div id="app">
<form action="" @submit.prevent="submitFn">
账号:<input type="text" name="user" v-model="userInfo.userDt" /> <br /><br />
密码:<input type="password" name="password" v-model="userInfo.passwrodDt" /> <br /><br />
<!-- 多个radio-->
性别:
<label for="boy">男</label>
<input type="radio" name="sex" v-model="userInfo.sexDt" value="boy" id="boy" />
<label for="girl">女</label>
<input type="radio" name="sex" v-model="userInfo.sexDt" value="girl" id="girl" /> <br /><br />
<!-- 多个checkbox -->
爱好:
<label for="sing">唱跳</label>
<input type="checkbox" name="hobby" v-model="userInfo.hobbyDt" value="sing" id="sing" />
<label for="rap">rap</label>
<input type="checkbox" name="hobby" v-model="userInfo.hobbyDt" value="rap" id="rap" />
<label for="ball">打篮球</label>
<input type="checkbox" name="hobby" v-model="userInfo.hobbyDt" value="ball" id="ball" />
<br /><br />
所属地区:
<select name="location" v-model="userInfo.locationDt">
<option value="">请选择校区</option>
<option value="sc">四川</option>
<option value="bj">北京</option>
<option value="gd">广东</option>
</select>
<br /><br />
其他信息:
<textarea name="msg" v-model="userInfo.msgDt"></textarea> <br /><br />
<!-- 这里的CheckBox和前面的🤔不一样 -->
<!-- 不手动加上value属性,默认绑定的是checked -->
<input type="checkbox" name="isAccept" v-model="userInfo.isAcceptDt" /> 阅读并接受<a href="#"
>《用户协议》</a
>
<br /><br />
<button>提交</button>
</form>
</div>
<script type="text/JavaScript">
new Vue({
el: '#app',
data: {
userInfo: {
userDt: '',
passwrodDt: '',
sexDt: 'boy',
hobbyDt: [],
locationDt: '',
msgDt: '',
isAcceptDt: true // 默认绑定的是 checked
}
},
methods: {
submitFn() {
console.log(JSON.stringify(this.userInfo))
}
},
});
</script>
思考?
通过案例,我们已经发现了,checkbox 和 radio 元素默认绑定的是 checked 属性,但使用 value 后,就转而对选择 value 属性进行绑定,我们称这个现象叫值绑定。
值绑定
<div>
<select multiple v-model="fruits">
<option v-for="item in allFruits" :key="item.value" :value="item.value">{{ item.text }}</option>
</select>
<h2>多选:{{ fruits }}</h2>
</div>
<div>
<template v-for="item in allHobbies" :key="item.value">
<label :for="item.value">
<input :id="item.value" type="checkbox" v-model="hobbies" :value="item.value" /> {{item.text}}
</label>
</template>
<h2>爱好:{{ hobbies }}</h2>
</div>
# 3、v-model 修饰符 ✨
- .trim(字符串处理)
<!-- 自动过滤用户输入的首尾空白字符 -->
用户名:<input type="text" name="username" v-model.trim="userInfo.username" />
- .number(数字监听)
<!-- 自动将用户的输入值转为数值类型 -->
年龄:<input type="number" name="age" v-model.number="userInfo.age" />
- .lazy
<!-- 失去焦点时收集数据(即绑定的事件切换为了change事件) -->
<textarea name="msg" v-model.lazy="userInfo.msg"></textarea>
Bootstrap 4 的 IOS 风格开关
- 上面演示的是
type="text"
—— 绑定 value 属性 - 下面要演示的是
type="checkbox"
—— 绑定 checked 属性
<td>
<div class="custom-control custom-switch">
<!-- 使用 v-model 实现双向数据绑定 -->
<input
type="checkbox"
class="custom-control-input"
:id="'customSwitch' + item.id"
v-model="item.status"
/>
<!-- 使用 v-if 结合 v-else 实现按需渲染 -->
<!-- 对id、for属性进行v-bind属性✨绑定,保证for属性值的唯一性 -->
<label class="custom-control-label" :for="'customSwitch' + item.id" v-if="item.status"
>已启用</label
>
<label class="custom-control-label" :for="'customSwitch' + item.id" v-else>已禁用</label>
</div>
</td>
<td>
<a href="javascript:;" @click="remove(item.id)">删除</a>
</td>
<script type="text/javascript" src="./lib/vue-2.6.14.js"></script>
<script>
const vm = new Vue({
el: '#app',
data: {
list: [
// v-model与checked属性
{ id: 1, brand: '宝马', status: true, buildTime: new Date() },
{ id: 2, brand: '奔驰', status: false, buildTime: new Date() },
{ id: 3, brand: '奥迪', status: true, buildTime: new Date() }
]
},
methods: {
remove(id) {
// 假删除
// 使用filter过滤器对不等于当前id值的其他list数据进行放行
this.list = this.list.filters((item) => item.id != id)
}
}
})
</script>
对应 checked 属性的两种 💖 操作方式
DOM 操作:
checkboxObject.checked=<true/1 | false/0>
属性操作:
checked="" | checked="随便的values值即可"