vue的常见指令

 

一、vue 的指令

1、v-text

2、v-html

3、v-show

4、v-if/v-esle-if/v-else

(1)、v-if

(2)、v-if 与 v-show

5、v-for

(1)、v-for 渲染一个数组

(2)、 v-for 渲染一个对象

(3)、v-if 与 v-for

(4)、v-for 之 key

(5)、显示过滤/排序后的结果

6、v-on

(1)、v-on 指令的修饰符

(2)、在内联事件处理器中访问事件参数

(3)、vue 中的特殊事件

7、v-bind

8、v-model

(1)、双向绑定的语法糖

(2)、v-model 的修饰符——表单

9、v-slot

10、v-pre(使用频率很低)

11、v-once(使用频率很低)

二、自定义指令 与 指令的生命周期

1、注册自定义指令

(1)、注册全局指令——Vue.directive() 方法

(2)、注册局部指令——directives 属性

2、案例


 

一、vue 的指令

指令的本质:语法糖,标志位。在编译阶段 render 函数里,会把指令编译成 JavaScript 代码。

vue 的指令:

  • v-text
  • v-html(不建议使用)
  • v-show
  • v-if / v-else-if / v-else
  • v-for
  • v-bind
  • v-on
  • v-model
  • v-slot
  • v-pre(使用频率很低)
  • v-once(使用频率很低)
  • v-cloak(使用频率极低,不细介绍)

1、v-text

v-text 指令,会把该元素下面的所有内容替换掉。

<div v-text="hello vue">hello world</div>

现实结果是:hello vue

2、v-html

v-html 指令,会用一个HTML标签字符串,替换该元素下面的所有内容。

但是,不建议使用v-html指令,因为它会导致被恶意者进行XSS攻击的潜在风险。

 
  1. <div v-html="'<span style=&quot;color:red&quot;>hello vue</span>'">

  2.  
  3.   hello world

  4.  
  5. </div>

现实结果是:字体颜色为红色的 hello vue

3、v-show

v-show 指令,控制元素的显示隐藏,元素存在并占据空间。

元素隐藏时,相当于给该元素添加了 CSS 样式:display:none;

 
  1. <div v-show="show">hello vue</div>

  2.  
  3. <button @click="show = !show">changeShow</button>

4、v-if/v-esle-if/v-else

(1)、v-if

v-if指令,控制元素是否加载。

v-esle-if/v-else指令不能单独使用,必须配合v-if一起使用。

 
  1. <div v-if="number===1">hello vue {{number}}</div>

  2.  
  3. <div v-else-if="number===2">hello world {{number}}</div>

  4.  
  5. <div v-else>hello someone {{number}}</div>

(2)、v-if 与 v-show

  • v-if:有更高的切换开销;
  • v-show:有更高的初始化开销。

若需要频繁的切换则使用 v-show 比较好,否则使用 v-if 比较好。

5、v-for

v-for 指令,for循环,基于源数据多次渲染元素或模板块。

v-for 既可以渲染一个数组,也可以渲染一个对象。

(1)、v-for 渲染一个数组

 
  1. <div v-for="(item, idx) in [1, 2, 3]" :key="idx">

  2.  
  3.     {{item}}

  4.  
  5. </div>

  6. // 渲染的结果:

  7. // 1

  8. // 2

  9. // 3

(2)、 v-for 渲染一个对象

 
  1. <div v-for="(val, key) in {one: 1, two: 2}" :key="key">

  2.  
  3.     {{key}}: {{val}}

  4.  
  5. </div>

  6. // 渲染的结果:

  7. // one: 1

  8. // two: 2

(3)、v-if 与 v-for

同时使用 v-if 和 v-for 是不推荐的,因为这样二者的优先级不明显。请查看风格指南获得更多信息。

当 v-if 与 v-for 一起使用时:

  • 在 vue2 中 v-for 比 v-if 有更高的优先级。这意味着 v-if 将分别重复运行于每个 v-for 循环中。
  • 在 vue3 中 v-if 比 v-for 有更高的优先级。这意味着 v-if 的条件将无法访问到 v-for 作用域内定义的变量别名。

vue3 官网之 v-for 与 v-if

(4)、v-for 之 key

①、为什么需要给 v-for 设置 key?

这牵扯到 vue 的 vnode 的 Diff 算法的特点,请参见此文

②、在 v-for 中直接用 index 作为 key 的值有什么不好?

例如:

 
  1. <template>

  2. <div v-for="(item, index) in list" :key="index" >{{item.name}}</div>

  3. </template>

  4. <script>

  5. export default {

  6. data() {

  7. return {

  8. list: [

  9. {

  10. id: 1,

  11. name: "Person1"

  12. },

  13. {

  14. id: 2,

  15. name: "Person2"

  16. },

  17. {

  18. id: 3,

  19. name: "Person3"

  20. },

  21. {

  22. id:4,

  23. name:"Person4"

  24. }]

  25. }

  26. },

  27. }

  28. </script>

此时,删除 “Person4” 是正常的,但是如果我删除 “Person2” 就会出现问题。

删除前

idindexname
10Person1
21Person2
32Person3
43Person4

删除后

idindexname
10Person1
31Person3
42Person4

可见,数组的 index 下标始终是从 0 开始依次递增不间断的,当其中某一项被删除后,被删节点之后的 index 下标会自动全部做减 1 更新。所以,删除了 id 是 2 的节点时,被删节点之后的 index 下标全部做减 1 更新了。所以,当 DOM 内容比较复杂时,建议设置并使用唯一的 id 属性,来作为 key 的值。

(5)、显示过滤/排序后的结果

显示过滤/排序后的结果

6、v-on

v-on指令,可简写为“@”,绑定事件监听器。

<button v-on:click="number = number + 1">number++</button>

(1)、v-on 指令的修饰符

v-on指令有一系列的修饰符:

  • .stop - 调用 event.stopPropagation(),禁止事件冒泡:
<a @click.stop="doThis"></a>
  • .prevent - 调用 event.preventDefault(),禁止事件的默认行为:
<form @submit.prevent="onSubmit"></form>
  • .passive - (2.3.0) 以 { passive: true } 模式添加侦听器,立即执行事件的默认行为,会导致 `event.preventDefault()` 无效:
 
  1. <div @scroll.passive="onScroll">...</div>

  2. // 滚动事件的默认行为 (即滚动行为) 将会立即触发,而不会等待 `onScroll` 完成。

  • .capture - 添加事件侦听器时使用 capture 模式,内部元素触发的事件先在此处理,然后才交由内部元素进行处理:
<div @click.capture="doThis">...</div>
  • .self - 只当事件是从侦听器绑定的元素本身(event.target)触发时才触发回调:
<div @click.self="doThat">...</div>
  • .native - 监听组件根元素的原生事件:
<base-input v-on:focus.native="onFocus"></base-input>
  • .once - 只触发一次回调:
<a @click.once="doThis"></a>
  • .left - (2.2.0) 只当点击鼠标左键时触发。
  • .right - (2.2.0) 只当点击鼠标右键时触发。
  • .middle - (2.2.0) 只当点击鼠标中键时触发。  
  • .{keyCode | keyAlias} ——键盘事件——只当事件是从特定键触发时才触发回调。比如:
    • .enter - 只有在 `key` 是 `Enter` 时才触发回调。
<input v-on:keyup.enter="submit">

(2)、在内联事件处理器中访问事件参数

有时我们需要在内联事件处理器中访问原生 DOM 事件。你可以向该处理器方法传入一个特殊的 $event 变量,或者使用内联箭头函数:

 
  1. <template>

  2. <!-- 使用特殊的 $event 变量 -->

  3. <button @click="warn('Form cannot be submitted yet.', $event)">

  4. Submit

  5. </button>

  6.  
  7. <!-- 使用内联箭头函数 -->

  8. <button @click="(event) => warn('Form cannot be submitted yet.', event)">

  9. Submit

  10. </button>

  11. </template>

  12.  
  13. <script setup lang="ts">

  14. function warn(message, event) {

  15. // 这里可以访问原生事件

  16. if (event) {

  17. event.preventDefault()

  18. }

  19. alert(message)

  20. }

  21. </script>

(3)、vue 中的特殊事件

vue 监听页面滚动事件 

 
  1. export default {

  2. mounted () {

  3. window.addEventListener('scroll', this.handleScroll)

  4. },

  5. beforeDestroy () {

  6. window.removeEventListener('scroll', this.handleScroll)

  7. },

  8. methods: {

  9. // 监听页面滚动

  10. handleScroll (e) {

  11. // ...

  12. }

  13. }

  14. }

 

7、v-bind

v-bind 指令,可简写为“:”,动态地绑定一个或多个属性,或一个来自父组件的 prop 里的表达式。

在自定义组件时,若要对 prop 进行“双向绑定”,可以用“v-bind 指令与 .sync 修饰符”相结合来实现。

例如:在一个包含 title prop 的自定义组件中,若想让其父组件可以监听那个事件并根据需要更新一个本地的数据 property,可以这样来实现:

<text-document v-bind:title.sync="doc.title"></text-document>

注意: 注意带有 .sync 修饰符的 v-bind 不能和表达式一起使用 (例如 v-bind:title.sync=”doc.title + ‘!’” 是无效的)。取而代之的是,你只能提供你想要绑定的 property 名,类似 v-model。

当我们用一个对象同时设置多个 prop 的时候,也可以将这个 .sync 修饰符和 v-bind 配合使用:

<text-document v-bind.sync="doc"></text-document>

这样会把 doc 对象中的每一个 property (如 title) 都作为一个独立的 prop 传进去,然后各自添加用于更新的 v-on 监听器。

注意:将 v-bind.sync 用在一个字面量的对象上,例如 v-bind.sync=”{ title: doc.title }”,是无法正常工作的,因为在解析一个像这样的复杂表达式的时候,有很多边缘情况需要考虑。 

8、v-model

(1)、双向绑定的语法糖

v-model 指令,是双向绑定的语法糖。

双向绑定:当数据变化视图同步更新,当视图更新数据也会同步更新。

双向绑定的原理请戳此链接:vue 的双向绑定原理_青蛙king的博客-CSDN博客_vue数据双向绑定原理一句话概括

<input v-model="msg"/>

v-model 实际上是 value 属性和 input 事件结合的简写形式。它等同于:

 
  1. <input :value="msg" @input="handleChange">

  2. <script>

  3. export default {

  4. data() {

  5. return {

  6. msg: ""

  7. }

  8. },

  9. methods: {

  10. handleChange(e){

  11. this.msg = e.target.value;

  12. }

  13. },

  14. }

  15. </script>

(2)、v-model 的修饰符——表单

  • .lazy:在默认情况下,v-model 在每次 input 事件触发后将输入框的值与数据进行同步 。如果你想实现在 change 事件之后进行同步,那么建议给 v-model 添加 lazy 修饰符。
 
  1. <!-- 在“change”时而非“input”时更新 -->

  2. <input v-model.lazy="msg" />

  • .number:将用户输入的字符串转换成 number。
<input v-model.number="age" type="text" />
  • .trim:将用户输入的前后的空格去掉。
<input v-model.trim="msg" />

【拓展】

v-model 在内部为不同的输入元素使用不同的 property 并抛出不同的事件:

  • text 和 textarea 元素使用 value 属性和 input 事件;
  • checkbox 和 radio 使用 checked property 和 change 事件;
  • select 字段将 value 作为 prop 并将 change 作为事件。

9、v-slot

v-slot 指令,用来定义一个 具名插槽 或 作用域插槽。可以缩写为 #。

插槽:用来分发内容,传递复杂的内容。

具体请戳这里:vue 插槽 slot_青蛙king的博客-CSDN博客

10、v-pre(使用频率很低)

v-pre 指令,跳过这个元素和它的子元素的编译过程。

<span v-pre>{{ this will not be compiled }}</span>

11、v-once(使用频率很低)

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

<span v-once>This will never change: {{msg}}</span>

 

二、自定义指令 与 指令的生命周期

指令的周期: 5个 (bind、inserted、update、componentUpdated、unbind)。

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

1、注册自定义指令

(1)、注册全局指令——Vue.directive() 方法

 
  1. <script>

  2. // 注册一个全局自定义指令 `v-focus`

  3. Vue.directive('focus', {

  4. // 当被绑定的元素插入到 DOM 中时……

  5. inserted: function (el) {

  6. // 聚焦元素

  7. el.focus()

  8. }

  9. })

  10. </script>

(2)、注册局部指令——directives 属性

 
  1. <script>

  2. directives: {

  3. focus: {

  4. // 指令的定义

  5. inserted: function (el) {

  6. el.focus()

  7. },

  8. // ...

  9. }

  10. }

  11. </script>

2、案例

【案例一】

 
  1. // 注册一个局部的自定义指令v-append-text:不替换原来的内容,直接插入其后。

  2. <template>

  3. <div>

  4. <button @click="show = !show">{{show ? "销毁" : "插入"}}</button>

  5. <button v-if="show" v-append-text="`hello ${number}`" @click="number++">按钮</button>

  6. </div>

  7. </template>

  8. <script>

  9. export default {

  10. directives: {

  11. // 这里定义一个名为appendText的指令,使用时要像这样去用:v-append-text

  12. appendText: {

  13. /**

  14. * 指令的生命周期 (bind、inserted、update、componentUpdated和unbind)

  15. **/

  16. bind(){

  17. console.log("bind");

  18. },

  19. inserted(el, binding){

  20. el.appendChild(document.createTextNode(binding.value));

  21. console.log("inserted", el, binding);

  22. },

  23. update() {

  24. console.log("update");

  25. },

  26. componentUpdated(el, binding){

  27. el.removeChild(el.childNodes[el.childNodes.length - 1]);

  28. el.appendChild(document.createTextNode(binding.value));

  29. console.log("componentUpdated");

  30. },

  31. unbind(){

  32. console.log("unbind");

  33. }

  34. }

  35. },

  36. data() {

  37. return {

  38. number: 1,

  39. show: true

  40. };

  41. }

  42. }

  43. </script>

【案例二】

也可以这样定义一个指令:

 
  1. import Vue from 'vue'

  2.  
  3. const ctx = 'clickOutsideContext'

  4. const nodeList = []

  5. const isServer = Vue.prototype.$isServer // 当前 Vue 实例是否运行于服务器

  6. let seed = 0

  7. let startClick

  8.  
  9. const on = (function () {

  10. if (!isServer && document.addEventListener) {

  11. return function (element, event, handler) {

  12. if (element && event && handler) {

  13. element.addEventListener(event, handler, false)

  14. }

  15. }

  16. } else {

  17. return function (element, event, handler) {

  18. if (element && event && handler) {

  19. element.attachEvent('on' + event, handler)

  20. }

  21. }

  22. }

  23. })()

  24.  
  25. function createDocumentHandler (el, binding, vnode) {

  26. return function (mouseup = {}, mousedown = {}) {

  27. // node.contains( otherNode ) 如果 otherNode 是 node 的后代节点或是 node 节点本身.则返回true , 否则返回 false.

  28. if (

  29. !vnode ||

  30. !vnode.context ||

  31. !mouseup.target ||

  32. !mousedown.target ||

  33. el.contains(mouseup.target) ||

  34. el.contains(mousedown.target) ||

  35. el === mouseup.target || (vnode.context.focusElment &&

  36. (vnode.context.focusElment.contains(mouseup.target) ||

  37. vnode.context.focusElment.contains(mousedown.target)))

  38. ) {

  39. return

  40. }

  41. if (binding.expression &&

  42. el[ctx].methodName &&

  43. vnode.context[el[ctx].methodName]) {

  44. vnode.context[el[ctx].methodName]()

  45. } else {

  46. el[ctx].bindingFn && el[ctx].bindingFn()

  47. }

  48. }

  49. }

  50.  
  51. if (!isServer) {

  52. on(document, 'mousedown', e => (startClick = e))

  53. on(document, 'mouseup', e => {

  54. nodeList.forEach(node => node[ctx].documentHandler(e, startClick))

  55. })

  56. }

  57.  
  58. export default {

  59. // 只调用一次,指令第一次绑定到元素时调用。在这里可以进行一次性的初始化设置

  60. bind (el, binding, vnode) {

  61. nodeList.push(el)

  62. const id = seed++

  63. el[ctx] = {

  64. id,

  65. documentHandler: createDocumentHandler(el, binding, vnode),

  66. methodName: binding.expression,

  67. bindingFn: binding.value

  68. }

  69. },

  70. // 所在组件的 VNode 更新时调用

  71. update (el, binding, vnode) {

  72. el[ctx].documentHandler = createDocumentHandler(el, binding, vnode)

  73. el[ctx].methodName = binding.expression

  74. el[ctx].bindingFn = binding.value

  75. },

  76. // 只调用一次,指令与元素解绑时调用

  77. unbind (el, binding, vnode) {

  78. const len = nodeList.length

  79. for (let i = 0; i < len; i++) {

  80. if (nodeList[i][ctx].id === el[ctx].id) {

  81. nodeList.splice(i, 1)

  82. break

  83. }

  84. }

  85. delete el[ctx]

  86. }

  87. }

将指令统一导出: 

 
  1. import clickoutside from './modules/clickoutside'

  2.  
  3. export { clickoutside }

然后在main.js里注册指令:

Object.keys(directives).forEach(k => Vue.directive(k, directives[k]))

在vue组件中使用自定义指令:

 
  1. <template>

  2. <div class='popup' v-clickoutside='closeGlobal'></div>

  3. </template>

  4. <script>

  5. export default {

  6. methods: {

  7. closeGlobal () {

  8. // ...

  9. }

  10. }

  11. }

  12. </script>

  13.  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值