vue2的指令和自定义指令

Vue.js指令 (Directives) 是带有 v- 前缀的特殊特性。指令特性的值预期是单个 JavaScript 表达式 (v-for 是例外情况)。我们可以将指令看作特殊的HTML特性,作用是:当表达式的值改变时,将其产生的连带影响,响应式地作用于绑定的DOM。

1. vue内置指令

内置指令指的就是Vue.js自带的指令,开箱即用,一共有14个:

  • 内容渲染指令
    • v-text
    • v-html
    • v-pre
    • v-cloak
    • v-once
  • 条件渲染指令
    • v-for
    • v-if
    • v-else
    • v-else-if
    • v-show
  • 属性绑定指令
    • v-model
    • v-bind
    • v-on
  • 插槽指令
    • v-slot

1-1. v-text

该指令的作用是更新DOM元素的文本内容,使用率很低,一般都是用双花括号来代替了,可以忽略

语法如下:

<template>
  <div>
    <span v-text="msg"></span>
  </div>
</template>

<script>
export default {
  name: "home",
  data() {
    return {
      msg: '我是一个文本信息'
    }
  }
}
</script>

1-2. v-html

该指令的作用是更新DOM元素的innerHTML,内容按普通HTML插入,用处挺多的,比如渲染字符串html,动态插入元素等。(滥用可能会引起XSS攻击,比如用户提交"<a href="https://有害网站">xxx</a>",会被渲染成html标签)

语法如下:

<template>
  <div>
    <div v-html="span"></div>
  </div>
</template>

<script>
export default {
  name: "home",
  data() {
    return {
      span: '<div><span>我是span</span></div>'
    }
  }
}
</script>

1-3. v-pre

该指令不需要表达式,作用是跳过绑定了这个指令的元素和它的子元素的编译过程。跳过大量没有指令的节点会加快编译,但是绑定了后,会直接显示双花括号{{ xxx }},按需使用。

1-4. v-cloak

该指令不需要表达式。这个指令会保持在dom元素上,直到其关联的实例结束编译才失效。作用是配合css选择器[v-cloak],来实现隐藏未编译完成的元素,直到实例准备完毕。

语法如下:

<template>
  <div>
    <p v-cloak>{{ text }}</p>
  </div>
</template>

<script>
export default {
  name: "home",
  data() {
    return {
      text: '这是一段text文本'
    }
  }
}
</script>

<style scoped>
[v-cloak] {
  display: none;
}
</style>

1-5. v-once

该指令不需要表达式,作用是只渲染元素和组件一次。随后的重新渲染,元素/组件及其所有的子节点将被视为静态内容并跳过。这可以用于优化更新性能。

例子如下:

<template>
  <div class="app">
    <h1 v-once>{{ time }}</h1>
  </div>
</template>

<script>
export default {
  name: 'home',
  data(){
    return {
      time: new Date().getTime()
    }
  },
  mounted() {
    setInterval(() => {
      this.time = new Date().getTime()
    }, 1000)
  }
}
</script>

因为h1标签带上了v-once指令,所以只渲染了一次,后续计时器不停修改值,也不会再渲染,显示的时间戳也就始终停在第一次。

1-6. v-for

基于源数据,多次渲染元素或模板块。数据源可以是stringarrayobjectnumberIterable(有迭代接口的值)。一般v-for会搭配内置属性key使用,避免渲染出现问题。这也是一个经典面试题,具体原因可以看:传送门1传送门2

例子如下:

<template>
  <div class="app">
    <!--1 -->
    <h1 v-for="(item, index) in obj" :key="index">{{item}}</h1>
    <!--2 -->
    <h1 v-for="item in 10" :key="item">{{item}}</h1>
    <!--3 -->
    <div v-for="item in arr" :key="item">
      <span>姓名:{{item.name}}</span>
      <span>年龄:{{item.age}}</span>
    </div>
  </div>
</template>

<script>
export default {
  name: 'home',
  data(){
    return {
      obj: {
        a: 100,
        b: 200,
        c: 300
      },
      arr: [
        { name: 'x', age: 10 },
        { name: 'xx', age: 11 },
        { name: 'xxx', age: 12 }
      ]
    }
  }
}
</script>

1-7. v-if 和 v-else 和 v-if-else

根据表达式之真假值来渲染元素。在切换时绑定该指令的元素/组件会被销毁并重建。如果是绑定在template标签上,将提出template的内容作为条件块。

当和 v-for 一起使用时,v-for 的优先级比 v-if 更高

该指令可触发组件过渡效果

例子如下:

<template>
  <div class="app">
    <h1 v-if="num < 2">num小于2</h1>
    <h2 v-else-if="num <= 5">num小于等于5</h2>
    <h3 v-else>num大于5</h3>
  </div>
</template>

<script>
export default {
  name: 'home',
  data(){
    return {
      num: Math.round(Math.random() * 10)
    }
  }
}
</script>

1-8. v-show

根据表达式之真假值,切换元素的CSS display属性,当值为false时,display为none

该指令可触发组件过渡效果

例子如下:

<template>
  <div class="app">
    <h1 v-show="num < 5">我是H1</h1>
  </div>
</template>

<script>
export default {
  name: 'home',
  data(){
    return {
      num: 0
    }
  },
  mounted() {
    setInterval(() => {
      this.num = Math.round(Math.random() * 10)
    }, 1000)
  }
}
</script>

1-9. v-model

在表单控件或者组件上创建双向绑定的值,本质上来说,它是一个语法糖,它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理。比如:v-model="foo",等价于 :value="foo"加上@input="foo = $event"的便捷写法。

在大部分情况下,以下两种写法是等价的:

<input v-model="name" />

<input :value="name" @input="name = $event" />

拥有以下修饰符:

  • .lazy:取代 input 监听 change 事件
  • .number:输入字符串转为有效的数字
  • .trim:过滤首尾空格

1-9-1. 表单上使用v-model

<template>
  <div class="app">
    <input type="text" v-model="text">
  </div>
</template>

<script>
export default {
  name: 'home',
  data(){
    return {
      text: ''
    }
  }
}
</script>

1-9-2. 组件上使用v-model

  • 父组件
<template>
  <div>
    <h1>{{ total }}</h1>
    <hello-world v-model="total" @input="handleInput" />
  </div>
</template>

<script>
import helloWorld from '@/components/HelloWorld'

export default {
  name:'home',
  components:{
    helloWorld
  },
  data(){return {
    total: 0
  }},
  methods: {
    handleInput(v) {
      this.total = v
    }
  }
}
</script>
  • 子组件(子组件接收的props值字段必须是value
<template>
  <div class="app">
    <button @click="handleChange">增加</button>
  </div>
</template>

<script>
export default {
  name: 'holle',
  props: ['value'],
  methods: {
    handleChange() {
      this.$emit('input', this.value + 1)
    }
  }
}
</script>

1-9-3. 使用.sync修饰符实现v-model等效

  • 父组件
<template>
  <div>
    <h1>{{ total }}, {{ age }}</h1>
    <hello-world :a.sync="total" :b.sync="age" @input="handleInput" />
  </div>
</template>

<script>
import helloWorld from '@/components/HelloWorld'

export default {
  name:'home',
  components:{
    helloWorld
  },
  data(){return {
    total: 0,
    age: 0
  }},
  methods: {
    handleInput(v1, v2) {
      this.total = v1
      this.age = v2
    }
  }
}
</script>
  • 子组件
<template>
  <div class="app">
    <button @click="handleChange">增加</button>
  </div>
</template>

<script>
export default {
  name: 'holle',
  props: ['a', 'b'],
  methods: {
    handleChange() {
      this.$emit('input', this.a + 1, this.b + 2)
    }
  }
}
</script>

1-10. v-bind

动态的绑定一个或多个属性到组件上,可以绑定class、style等任意值。可以简写为:。拥有以下修饰符:

  • .sync:语法糖,扩展成一个更新父组件绑定值的 v-on 侦听器
  • .camel:kebab-case转camelCase
  • .prop:作为一个DOM property绑定而不是作为attribute绑定,通过自定义属性存储变量,避免暴露数据。防止污染HTML结构

例子:

<template>
  <div>
    <h1 :class="['aaa','bbb', num < 100 ? 'ccc':'ddd']"></h1>
    <h2 :style="mode"></h2>
    <input type="text" :value="msg">
    <img v-bind:src="imageSrc" />
  </div>
</template>

<script>
export default {
  name:'aaa',
  data(){
    return {
      num: 200,
      mode: {
        color: 'red',
        fontSize: '20px'
      },
      msg: '',
      imageSrc: 'https://www.baidu.com/xxx.png'
    }
  }
}
</script>

1-11. v-on

绑定事件监听器。事件类型由参数指定。可以简写为@。拥有以下修饰符:

  • .stop:调用 event.stopPropagation(),阻止冒泡
  • .prevent:调用 event.preventDefault(),阻止元素发生默认的行为
  • .capture:添加事件侦听器时使用 capture 捕获模式
  • .self:事件从侦听器绑定的元素本身触发时才触发回调
  • .keyCode:当事件是从特定键触发时才触发回调
  • .native:监听组件根元素的原生事件
  • .once:只触发一次回调
<button v-on:click="add"></button>
// 动态事件
<button @[event]="change"></button>
// 事件对象
<button @click="add('hello', $event)"></button>
// 按键事件
<input @keyup.enter="onEnter" />
// 按键代码
<input @keyup.13="onEnter" />

1-12. v-slot

slot,也称插槽,是组件的一块HTML模版,这块模版由父组件提供。可以说是子组件暴露的一个让父组件传入自定义内容的接口,来实现内容分发。一般都写在template标签上,可以简写为#

1-12-1. 匿名插槽

匿名插槽用slot标签来确定渲染的位置,里面可以放置父组件没传内容时的默认内容,一个组件里,匿名插槽只能有一个。slot="default"可以忽略不写。

  • 父组件
<template>
  <div>
    <aaaChildren></aaaChildren>

    <aaaChildren>
      <h1>我是父组件的内容</h1>
    </aaaChildren>
  </div>
</template>

<script>
import aaaChildren from './children/index.vue'

export default {
  name:'aaa',
  components:{
    aaaChildren
  }
}
</script>
  • 子组件
<template>
  <div class="aaa_children">
    <slot>
      <h1>我是默认内容</h1>
    </slot>
  </div>
</template>

<script>
  export default {
    name:'aaaChildren'
  }
</script>

1-12-2. 具名插槽

slot标签上加入name属性,使得父级可以将内容插入对应的位置中。具名插槽可以有很多个,使用的时候需要带上名字做区分。

  • 父组件
<template>
  <div>
    <aaaChildren>
      <template #text>111</template>
      <template #footer>222</template>
    </aaaChildren>
  </div>
</template>

<script>
import aaaChildren from './children/index.vue'

export default {
  name:'aaa',
  components:{
    aaaChildren
  }
}
</script>
  • 子组件
<template>
	<div class="aaa_children">
		<slot name="text">
			<h1>我是text插槽</h1>
		</slot>
		<div>
			<slot name="footer">
				<h2>我是footer插槽</h2>
			</slot>
		</div>
	</div>
</template>

<script>
export default {
	name: 'aaaChildren'
}
</script>

1-12-3. 作用域插槽

作用域插槽在slot上绑定属性来将子组件的信息传给父组件使用,这些属性会被挂在父组件slot-scope接受的对象上。绑定时用的key,就是父组件获取数据的键。

  • 父组件
<template>
  <div>
    <aaaChildren>
      <template #default="scope">
        <h1>{{ scope.info.age }}</h1>
      </template>
      <template #text="scope">
        <h2>{{ scope.data }}</h2>
      </template>
    </aaaChildren>
  </div>
</template>

<script>
import aaaChildren from './children/index.vue'

export default {
  name:'aaa',
  components:{
    aaaChildren
  }
}
</script>
  • 子组件
<template>
  <div class="aaa_children">
    <slot :info="user"></slot>
    <slot name="text" :data="name"></slot>
  </div>
</template>

<script>
  export default {
    name:'aaaChildren',
    props:[],
    components:{},
    data(){
      return {
        user: {
          age: 18,
          name: '小明'
        },
        name: '卡卡罗特'
      }
    }
  }
</script>

2. 自定义指令

自定义指令。是Vue暴漏了自定义指令的API给我们,让我们除了使用内置指令外,我们还可以自己定义指令(比如:复制粘贴指令,图片懒加载指令,防抖指令等等),定义好后和内置指令的方式非常类似。它可以有全局的和局部的两种。指令也有自己的钩子函数,如下:

  • bind:只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置
  • inserted:被绑定元素插入父节点时调用 (仅保证父节点存在,但不一定已被插入文档中)
  • update:所在组件的VNode更新时调用,但是可能发生在其子VNode更新之前。指令的值可能发生了改变,也可能没有。但是你可以通过比较更新前后的值来忽略不必要的模板更新
  • componentUpdated:指令所在组件的 VNode 及其子 VNode 全部更新后调用
  • unbind:只调用一次,指令与元素解绑时调用

以上钩子函数都有4个参数,如下:

  • el:指令所绑定的元素,可以用来直接操作 DOM
  • binding:一个对象,包含这些属性
    • name:指令名,不包括 v- 前缀
    • value:指令的绑定值,例如:v-bold=“1 + 1” 中,绑定值为 2
    • oldValue:指令绑定的前一个值,仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用
    • expression:字符串形式的指令表达式。例如 v-bold=“1 + 1” 中,表达式为 “1 + 1”
    • arg:传给指令的参数,可选。例如 v-bold:foo 中,参数为 “foo”
    • modifiers:一个包含修饰符的对象。例如:v-bold.foo.bar 中,修饰符对象为 { foo: true, bar: true }
  • vnode:Vue 编译生成的虚拟节点
  • oldVnode:上一个虚拟节点,仅在 update 和 componentUpdated 钩子中可用

2-1. 全局自定义指令

通过Vue实例身上的directive() API注册一个全局自定义指令。例子如下:

  • 首先在src文件夹下创建一个directive文件夹,里面创建一个index.js文件,写入以下代码
/**
 * 安装指令
 * @param Vue vue实例
 */
export default function installDirective(Vue) {
  // 粗体指令
	Vue.directive('bold', {
    inserted(el) {
      el.style.fontWeight = 'bold'
    }
  })
}
  • 然后在main.js里,引入directive,然后使用Vue.use安装指令
import Vue from 'vue'
import App from './App.vue'
import initDirective from '@/directive'

Vue.use(initDirective)

new Vue({
  render: h => h(App),
}).$mount('#app')
  • 使用如下:
<template>
  <div v-bold>123</div>
</template>

2-2. 局部自定义指令

组件中也接受一个directives的选项,在组件中定义的自定义指令,就是局部自定义指令,其参数和全局指令一致。例子如下:

<template>
	<div v-bold>123</div>
</template>

<script>
export default {
	name: 'aaaChildren',
	directives: {
		bold: {
			inserted: function (el) {
				el.style.fontWeight = 'bold'
			}
		}
	}
}
</script>

2-3. 动态参数和简写

指令的参数可以是动态,这样可以灵活运用,例子如下,可以动态修改高度的指令:

  • 注册指令:
/**
 * 安装指令
 * @param Vue vue实例
 */
export default function initDirective(Vue) {
  // 简写 在bind和update时触发相同行为
  Vue.directive('height', (el, binding) => {
    el.style.backgroundColor = 'skyblue'
    el.style.height = `${binding.value}px`
  })
}
  • 使用指令:
<template>
  <div>
    <button @click="addHeight">点击增高</button>
    <div v-height="h"></div>
  </div>
</template>

<script>
export default {
  name:'aaa',
  data(){
    return {
      h: 100
    }
  },
  methods: {
    addHeight() {
      this.h += 2
    }
  }
}
</script>

如果看了觉得有帮助的,我是@鹏多多,欢迎 点赞 关注 评论;

往期文章

个人主页

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
Vue.js是一种流行的JavaScript框架,用于构建用户界面。它提供了一种简洁的方式来处理数据和DOM的交互。在Vue.js中,自定义指令是一种扩展Vue.js功能的方式,可以用于在DOM元素上添加特定的行为。 在Vue.js 2中,自定义指令通过`Vue.directive`方法来定义。自定义指令可以用于操作DOM元素、监听事件、修改样式等。例如,我们可以创建一个自定义指令来实现点击元素时改变背景颜色的功能: ```javascript Vue.directive('change-color', { bind: function (el, binding) { el.addEventListener('click', function () { el.style.backgroundColor = binding.value; }); } }); ``` 在上面的例子中,我们使用`bind`钩子函数来绑定指令,并在元素上添加点击事件监听器。当点击元素时,会根据指令的值来改变元素的背景颜色。 而在Vue.js 3中,自定义指令的写法有所改变。Vue.js 3引入了`createApp`方法来创建应用程序实例,并使用`app.directive`方法来定义自定义指令。例如,我们可以使用Vue.js 3的语法来重新实现上述的自定义指令: ```javascript const app = Vue.createApp({}); app.directive('change-color', { mounted(el, binding) { el.addEventListener('click', function () { el.style.backgroundColor = binding.value; }); } }); app.mount('#app'); ``` 在上面的例子中,我们使用`mounted`钩子函数来绑定指令,并在元素上添加点击事件监听器。当点击元素时,会根据指令的值来改变元素的背景颜色。 总结一下,Vue.js 2和Vue.js 3中的自定义指令的定义方式有所不同,但都可以用于扩展Vue.js的功能,实现特定的行为。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鹏多多.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值