# Vue2.x指令

* 指令(Directives)，指带有`v-`前缀的特殊属性。
* 当表达式的值改变时，将其产生的连带影响，响应式地作用于DOM，是Vue双向绑定最基础的表现。

## 内置指令

* **v-text**

  ```markup
  <div v-text="text"></div>
  ```

  绑定的参数与将作为元素内部的`textContent`，等价于

  ```markup
  <div>{{text}}</div>
  ```
* **v-html**

  ```markup
  <div v-html="html"></div>
  ```

  绑定的参数将作为元素的`innerHTML`内容，要注意防止xss攻击
* **v-bind**

  ```markup
  <div v-bind:class="className"></div>
  ```

  Vue中最基本的参数双向绑定，当用于原生的元素标签时，可以将参数绑定至标签的原生属性上，当用于封装的组件时，将作为`props`传入组件内。通常会使用以下的写法

  ```markup
  <div :class="className"></div>
  ```
* **v-on**

  ```markup
  <button v-on:click="handleClick(args, $event)">click me!</button>
  ```

  用于绑定事件，事件方法定义在`methods`内。对于有原生事件的标签来说，`v-on`能够直接支持。而对于封装的组件而言，可以在组件内使用`$emit()`来定义事件触发的条件。入参参数在没必要的情况下能够省略。通常会使用以下写法

  ```markup
  <button @click="handleClick(args, $event)"></button>
  ```

  * 事件修饰符：

  ```markup
  <!-- 使用方法 -->
  <button v-on:click.stop="handleClick">click me!</button>
  ```

  `.stop`: 阻止冒泡，调用`event.stopPropagation()`

  `.prevent`: 阻止默认事件，调用`event.preventDefault()`，如用于表单提交事件

  `.capture`: 添加事件侦听器时使用事件捕获模式，即元素自身触发的事件先在事件方法内处理，然后才交由内部元素进行处理

  `.self`: 只当事件在该元素本身（比如不是子元素）触发时触发回调

  `.once`: 事件只触发一次

  `.passive`: 用于滚动事件，滚动事件的默认行为 (即滚动行为) 将会立即触发
* **v-model**

  ```markup
  <input v-model="inputValue">
  ```

  元素双向绑定的数据，与`v-bind`类似，相比于前者，在组件封装中，使用`v-model`绑定的数据更偏向于组件的核心数据，如表单绑定的数据，输入框内的内容等。

  注意`v-model`前不要加 `:` ，否则其实际意义将会变成`v-bind:v-model`，与一个普通的`props`无异。

  在组件内部进行接收或者监听时，都使用`value`。另外，由于`prop`本身的单向传输性，当组件内部数据更新且需要返回给外部时，得使用`$emit('input', value)`，以下为一个简单的`v-model`在组件内外双向绑定的写法

  ```javascript
  export default {
    data() {
      return {
        innerValue: '',
      }
    }
    props: {
      value: [String, Number]
    },
    watch: {
      value(newVal) {
        this.innerValue = newVal;
      },
      innerValue(newVal) {
        this.$emit('input', newVal);
      }
    }
  }
  ```
* **v-for**

  ```markup
  <div v-for="item in items" :key="item">{{item.content}}</div>
  ```

  ```javascript
  export default {
    data() {
      return {
        items: [1,2,3]
      }
    }
  }
  ```

  操作dom的Vue指令，通过遍历一个数组来达到重复渲染当前元素的效果，如上述代码的实际渲染效果为

  ```markup
  <div>1</div>
  <div>2</div>
  <div>3</div>
  ```

  在Vue2.x中，要注意想要触发dom的变化，就需要触发监听数组变化的钩子，而当数组内对象层级过深时，钩子将不会触发，于是会导致model层更新，而view层没有响应的情况，此时可以使用`$forceUpdate`强制触发钩子。

  `v-for`的key属性，可以理解为一个主键，可用于提升dom重载时的性能。
* **v-if与v-show**

  ```markup
  <div v-if="isDicShow">1</div>
  <span v-show="isSpanShow">2</span>
  ```

  与`v-if`相伴的还有`v-else-if`与`v-else`，将作为`v-if`的一类一起讨论。

  `v-if`和`v-show`都通过赋值其的表达式的真假来控制所在元素的是否显示。两者的区别在于`v-if`绑定的表达式的真假性变化时，会导致元素的渲染或者销毁。而`v-show`仅仅是控制元素的`display`属性是否为`none`，绑定值为false时，我们依然可以通过审查元素找到该元素。关于两者的区别，其实是个老生常谈的问题，本质上，还是一个考虑性能的问题。

  * `v-if`无初始渲染消耗，但有较高的切换消耗，`v-show`则相反。
  * 两者的使用要根据实际业务场景进行考量，对首次打开性能要求较高的界面，可以考虑使用`v-if`，对于需要频繁切换的元素，则可以考虑使用`v-show`
* **v-cloak**

  ```markup
  <div class="demo-class" v-cloak>{{msg}}</div>
  ```

  ```css
  [v-cloak] {
    display: none;
  }
  ```

在开启禁止浏览器缓存或者网络比较慢的时候，即`vue.js` 加载较慢的时候，由于`Vue`实例并没有编译完成，所以对于类似 `{{msg}}` 的 `Mustache` 标签会被直接展示出来。只有当 `Vue` 编译加载完成后，参数的真正内容才会被展示出来。这就导致了界面渲染时会有一小段时间的闪动问题。

而 `v-cloak` 主要就是用于优化这个用户体验的问题，用CSS选择器来操作拥有该指令的样式，设置其为 `display:none` ，那么在`vue.js`加载期间，该元素就不会被展示出来，直到 `Vue` 实例加载完毕。

事实上 `v-cloak` 指令在实际编码过程中，用到的机会还是比较少的。在较为大型、工程化的项目当中，都会使用 `vue-router` 实现路由挂载，所以在非首页加载的情况下，`Vue`实例都早已加载完成，此时就不再有使用该指令的必要。

* **v-pre**

  ```markup
  <div v-pre>{{msg}}</div>
  ```

拥有 `v-pre` 指令的元素与其子元素会直接跳过 `Vue` 的编译过程，当作普通的dom进行渲染。跳过编译过程一来可用于在必要的时候展示 `{{msg}}` 这类 `Mustache` 标签。二来可用于缩短一些不含需要编译内容的dom的加载时间，从而提高整个界面的加载效率。从实际使用情况来说，对性能的提升还是较为有限的，不过可以作为界面性能优化的一个方面加以实施。

* **v-once**

  ```markup
  <div v-once>{{msg}}</div>
  ```

同样是一个用于提高界面性能的指令，可以从字面意思来理解，使用`v-once`指令的元素只会被编译一次，也就是说类似于`{{msg}}`的标签内的参数会被正确展示出来，但有且仅有这一次渲染，若界面出现重新渲染，该元素就不再会进行编译，只会展示第一次编译的内容。该指令一般用于有需要编译的`Vue`语法，但其依旧为静态内容，所以仅需编译一次，可以与`v-pre`相区分。

## 自定义指令

`Vue`除去以上内置指令外，还可以让开发人员根据生产需要开发自定义指令。通过对内置指令的介绍，很容易可以认识到，指令的作用其实主要用于对于DOM元素的操作上。比如官方文档里给的一个例子，就是用于做输入框获取焦点的。

自定义指令与`Vue-components`类似，可以进行全局挂载。比如获取输入框焦点这类比较常见的交互场景，就可以作为一个全局的自定义指令进行挂载。

```javascript
// 注册一个全局自定义指令 `v-focus`
Vue.directive('focus', {
  // 当被绑定的元素插入到 DOM 中时……
  inserted: function (el) {
    // 聚焦元素
    el.focus()
  }
})
```

同样的，也可以仅在某个界面内进行局部挂载。

```javascript
new Vue({
  el: "#app",
  directives: {
    focus: {
      inserted: function(el){
        el.focus();
      }
    }
  }
})
```

对于指令功能的实现，与`Vue`实例类似，都具有一个完整的生命周期及其对应的钩子函数，指令的生命周期整体如下。

```
bind => inserted => update => componentUpdated => unbind
```

对于生命周期的具体解释，以及钩子函数各个入参的含义，[官方文档](https://cn.vuejs.org/v2/guide/custom-directive.html)都已经给出了很明朗的解释，就不赘述。

另外，对于官方文档中提及的动态参数指令 `v-mydirective:[argument]="value"`，即可以在指令上绑定一个`[arguement]`来作为组件实例更新的一个入参，由于入参是一个动态的参数，所以可使指令的定义更加灵活。以下例子中的`dir`就是一个传入的入参，对应于指令方法中的`arg`。

```markup
<div v-pin:[dir]="200"></div>
```

```javascript
Vue.directive('pin', {
  bind: function (el, binding, vnode) {
    el.style.position = 'fixed'
    var s = (binding.arg == 'left' ? 'left' : 'top')
    el.style[s] = binding.value + 'px'
  }
})
```

而其实指令等号后面的部分同样也可以作为一个动态的参数传入，所以当等号后面部分的参数更新时，在`update`钩子内进行更新，同样也可以实现动态参数指令的效果。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://xiaobaiha.gitbook.io/tech-share/vue/vue2.x-zhi-ling.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
