# 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";
最后更新: 8/25/2022, 8:27:06 AM