# vue directive 自定义指令
分为全局指令和局部指令
- 应用场景:权限按钮控制 + 默认登录获取焦点等
<template>
<div >display
<div v-role="33">anniu111</div>
<div v-roleall @click="btn">v-roleall</div>
</div>
</template>
<script>
export default{
directives:{
'role':{
bind:(el, binding, vnode,oldVnode)=>{
el.innerHTML="99999"
},
inserted: (el, binding, vnode,oldVnode) => {
// 如果没有权限就移除此节点
console.log(el,"------", binding,"-------", vnode)
if (false) {
el.parentNode.removeChild(el);
}else{
let a=document.createElement("a")
a.innerHTML="1111222211"
el.appendChild(a)
console.log(binding.value)
}
},
}
}
}
</script>
//main.js
Vue.directive('roleall', {
inserted: (el, binding, vnode) => {
console.log('全局指令')
}
})
指令定义函数提供了几个钩子函数(可选):
- bind: 只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个在绑定时执行一次的初始化动作。
- inserted: 被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于 document 中)。
- update: 被绑定元素所在的模板更新时调用,而不论绑定值是否变化。通过比较更新前后的绑定值,可以忽略不必要的模板更新(详细的钩子函数参数见下)。
- componentUpdated: 被绑定元素所在模板完成一次更新周期时调用。
- unbind: 只调用一次, 指令与元素解绑时调用。
# vue2自定义指令钩子函数参数
- el:指令所绑定的元素,可以用来直接操作 DOM 。
- binding:一个对象,包含以下属性:
- name:指令名,不包括 v- 前缀。
- value:指令的绑定值,例如:v-my-directive="1 + 1" 中,绑定值为 2。
- oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
- expression:字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中,表达式为 "1 + 1"。
- arg:传给指令的参数,可选。例如 v-my-directive:foo 中,参数为 "foo"。
- modifiers:一个包含修饰符的对象。例如:v-my-directive.foo.bar 中,修饰符对象为 { foo: true, bar: true }。
- vnode:Vue 编译生成的虚拟节点。
- oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用。
在很多时候,你可能想在 bind 和 update 时触发相同行为,而不关心其它的钩子。比如这样写:
Vue.directive('color-swatch', function (el, binding) {
el.style.backgroundColor = binding.value
})
- 更新和解绑调用,解绑不是直接dom移除,可以借助v-if或者$destory来处理
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue入门之自定义指令</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<div v-for ="(item,i) in number" :key='i'>{{i}}</div>
<div v-test="bgcolor" id='k' v-if='a'>
{{num}}{{age}}
</div>
<button @click='change'>解绑1</button>
<button @click="update">更新</button>
</div>
<button onclick="unbindApp()">解绑2</button>
<script type="text/javascript">
// 解绑
function unbindApp() {
app.$destroy();
//document.getElementById("k").parentNode.remove()
}
Vue.directive("test", {
//1-被绑定
bind: function (el, binding, vnode) {
console.log("1-bind 被绑定");
el.style.background= binding.value;
},
//2-被插入
inserted: function (el, binding, vnode) {
console.log("2-inserted 被插入");
},
//3-更新
update: function (el, binding, vnode) {
console.log("3-update 更新");
},
//4-更新完成
componentUpdated: function (el, binding, vnode) {
console.log("4-componentUpdated 更新完成");
},
//5-解绑(一旦解绑以后,再无关联)
unbind: function (el, binding, vnode) {
console.log("5-unbind 解绑");
}
});
var app = new Vue({
el: '#app',
data: {
num: 123,
color: 'red',
age: 12,
bgcolor:'#f00',
a:true,
number:5
},
methods:{
change(){
this.a=false
},
update(){
this.number=8
}
}
})
</script>
</body>
</html>
# vue3 自定义指令的修改
- 周期函数的变化
const myDirective = {
// 在绑定元素的 attribute 前
// 或事件监听器应用前调用
created(el, binding, vnode, prevVnode) {
// 下面会介绍各个参数的细节
},
// 在元素被插入到 DOM 前调用
beforeMount(el, binding, vnode, prevVnode) {},
// 在绑定元素的父组件
// 及他自己的所有子节点都挂载完成后调用
mounted(el, binding, vnode, prevVnode) {},
// 绑定元素的父组件更新前调用
beforeUpdate(el, binding, vnode, prevVnode) {},
// 在绑定元素的父组件
// 及他自己的所有子节点都更新后调用
updated(el, binding, vnode, prevVnode) {},
// 绑定元素的父组件卸载前调用
beforeUnmount(el, binding, vnode, prevVnode) {},
// 绑定元素的父组件卸载后调用
unmounted(el, binding, vnode, prevVnode) {}
}
如果生命周期中只需要 mounted ,updated,还可以和vue2一样简写
- v-pos:right 指令后可以类似v-on:onclik,right这个值在binding.arg获取
- v-pos:right='distance' distance可在binding.value中获取
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>lesson 29</title>
<style>
.header { position: absolute}
</style>
<script src="https://unpkg.com/vue@next"></script>
</head>
<body>
<div id="root"></div>
</body>
<script>
// 自定义指令 directive
const app = Vue.createApp({
data() {
return {
distance: 110
}
},
template: `
<div>
<div v-pos:right="distance" class="header">
<input />
</div>
</div>
`
});
app.directive('pos', (el, binding) => {
el.style[binding.arg] = (binding.value + 'px');
})
const vm = app.mount('#root');
</script>
</html>
# vue自带指令
# v-if 和 v-show
v-show隐藏则是为该元素添加css--display:none,dom元素依旧还在。v-if显示隐藏是将dom元素整个添加或删除
# v-for 和 v-if 优先级
v-for的优先级高于v-if,如果写在一起,则会先for循环,再去判断存不存在,浪费性能的操作!!
在vue3中,v-if的优先级则高于v-for,这样编译代码更合理
vue3 v-if
当它们处于同一节点,v-if 的优先级比 v-for 更高,这意味着 v-if 将没有权限访问 v-for 里的变量:
<!-- 这将抛出一个错误,因为“todo” property 没有在实例上定义 -->
<li v-for="todo in todos" v-if="!todo.isComplete">
{{ todo.name }}
</li>
可以把 v-for 移动到 <template> 标签中来修正:
<template v-for="todo in todos" :key="todo.name">
<li v-if="!todo.isComplete">
{{ todo.name }}
</li>
</template>
export function genElement (el: ASTElement, state: CodegenState): string {
if (el.parent) {
el.pre = el.pre || el.parent.pre
}
if (el.staticRoot && !el.staticProcessed) {
return genStatic(el, state)
} else if (el.once && !el.onceProcessed) {
return genOnce(el, state)
} else if (el.for && !el.forProcessed) {
return genFor(el, state)
} else if (el.if && !el.ifProcessed) {
return genIf(el, state)
} else if (xxx) {
}
# v-for 中的key作用
源码中:src\core\vdom\patch.js - updateChildren() 可以有明确逻辑。
<!DOCTYPE html> <html>
<head>
<title>03-key的作用及原理?</title> </head>
<body>
<div id="demo">
<p v-for="item in items" :key="item">{{item}}</p>
</div>
<script src="../../dist/vue.js"></script>
<script>
// 创建实例
const app = new Vue({
el: '#demo',
data: { items: ['a', 'b', 'c', 'd', 'e'] },
mounted () {
setTimeout(() => {
this.items.splice(2, 0, 'f')
}, 2000);
},
});
</script>
</body>
</html>
key的作用就是更新组件时判断两个节点是否相同的条件之一
当没设置key值时,会 默认key为undefined ,那么key和tag都是相等的,自然会patch当前的数据,因为a,b和之前一样的位置,所以不会发生dom操作,当处理到第三位数时,因为key和tag新老数据都一样,所以会patch,数据不对,会操作dom更新,把原来的c替换成现在的数据f,第四位d把替换成现在的c,以此类推,那么就会 patch五次,更新了三次 ,最后追加了一个e。
当设置了key,前五次patch都找到了首尾元素,因为数据一致,不需要操作dom,最后一次插入一个f结束
// 首次循环patch A
A B C D E
A B F C D E
// 第2次循环patch B
B C D E
B F C D E
// 第3次循环patch E
C D E
F C D E
// 第4次循环patch D
C D
F C D
// 第5次循环patch C
C
F C
// oldCh全部处理结束,newCh中剩下的F,创建F并插入到C前面
在大部分情况下,提高diff效率。相同就复用,不相同就删除旧的创建新的。除非是个极其简单的v-for循环,效率会高于带key值的v-for。
# v-for 循环对象
v-for也支持遍历对象,并且支持有一二三个参数:
- 一个参数: "value in object";
- 二个参数: "(value, key) in object";
- 三个参数: "(value, key, index) in object";