## 入门

### 介绍

Vue 是一套用于构建用户界面的渐进式框架

- Vue 2.x 官方文档 (v2.cn.vuejs.org)
- Vue Router 3.x 官方文档 (v3.router.vuejs.org)
- Vue 3 备忘清单

注意：Vue 2.x 版本对应 Vue Router 3.x 路由版本

#### 快速创建 Vue 项目

    npx @vue/cli create hello-world

参考: Vue CLI 创建一个项目

### 声明式渲染

    <div id="app">
      {{ message }}
    </div>

    var app = new Vue({
      el: '#app',
      data: {
        message: 'Hello Vue!'
      }
    })

### 基础例子

    <div id="example">
      <p>原始信息: "{{ message }}"</p>
      <p>
        计算的反向信息: "{{ reversedMessage }}"
      </p>
    </div>

    var vm = new Vue({
      el: '#example',
      data: {
        message: 'Hello'
      },
      computed: {
        // 计算属性的 getter
        reversedMessage: function () {
          // `this` 指向 vm 实例
          return this.message.split('')
              .reverse().join('')
        }
      }
    })

结果

    原始信息:  "Hello"
    计算的反向信息:  "olleH"

### 绑定元素属性

    <div id="app-2">
      <span v-bind:title="message">
        鼠标悬停几秒钟查看此处动态绑定的提示信息！
      </span>
    </div>

    var app2 = new Vue({
      el: '#app-2',
      data: {
        message: '页面加载于 ' + new Date()
            .toLocaleString()
      }
    })

### 条件

    <div id="app-3">
      <p v-if="seen">现在你看到我了</p>
    </div>

    var app3 = new Vue({
      el: '#app-3',
      data: {
        seen: true
      }
    })

控制切换一个元素是否显示

### 循环

    <div id="app-4">
      <ol>
        <li v-for="todo in todos">
          {{ todo.text }}
        </li>
      </ol>
    </div>

    var app4 = new Vue({
      el: '#app-4',
      data: {
        todos: [
          { text: '学习 JavaScript' },
          { text: '学习 Vue' },
          { text: '整个牛项目' }
        ]
      }
    })

### 点击事件处理

    <div id="app-5">
      <p>{{ message }}</p>
      <button v-on:click="reverseMessage">
        反转消息
      </button>
    </div>

    var app5 = new Vue({
      el: '#app-5',
      data: {
        message: 'Hello Vue.js!'
      },
      methods: {
        reverseMessage: function () {
          this.message = this.message.split('')
                .reverse().join('')
        }
      }
    })

### 输入事件处理

    <div id="app-6">
      <p>{{ message }}</p>
      <input v-model="message">
    </div>

`v-model` 指令，它能轻松实现表单输入和应用状态之间的双向绑定

    var app6 = new Vue({
      el: '#app-6',
      data: {
        message: 'Hello Vue!'
      }
    })

## 模板语法

### 文本

    <span>Message: {{ msg }}</span>
    <span v-once>
      这个将不会改变: {{ msg }}
    </span>

使用 `v-once` 指令，执行一次性地插值，当数据改变时，插值处的内容不会更新

### 原始 HTML

    <p>解释为普通文本: {{ rawHtml }}</p>
    <p>
      使用 v-html 指令:
      <span v-html="rawHtml"></span>
    </p>

使用 `v-html` 指令，输出真正的 `HTML`

### 属性

    <div v-bind:id="dynamicId"></div>
    <button v-bind:disabled="isDisabled">
      Button
    </button>

如果 `isDisabled` 的值是 null/undefined/false 则 `disabled` 不会被渲染出来

### JavaScript 表达式

    <div id="app">
      <span>消息: {{ msg }}</span>
      <span>{{ msg + '这是字符串' }}</span>
      <span>{{ isWorking ? '是':'否' }}</span>
      <span>{{ msg.getDetials() }}</span>
      <div v-bind:id="'list-' + id"></div>
    <div>

### 指令

    <p v-if="seen">
      现在你看到我了
    </p>

`v-if` 指令将根据表达式 `seen` 的值的真假来插入/移除 <p> 元素

### 指令参数

    <a v-bind:href="url">...</a>

`v-bind` 指令将该元素 `href` 属性与表达式 `url` 的值绑定

    <a v-on:click="doSomething">...</a>

`v-on` 指令，用于监听 DOM 事件，oSomething 是事件名

### 指令动态参数 v2.6

    <a v-on:[eventName]="doSomething">...</a>

当 `eventName` 的值为 `focus` 时，`v-on:[eventName]` 将等价于 `v-on:focus`

### 指令修饰符

    <form v-on:submit.prevent="onSubmit">
      ...
    </form>

`.prevent` 修饰符告诉 `v-on` 指令对于触发的事件调用 `event.preventDefault()`

### 指令缩写

    <!-- 完整语法 -->
    <a v-bind:href="url">...</a>
    <!-- 缩写 -->
    <a :href="url">...</a>
    <!-- 动态参数的缩写 (2.6.0+) -->
    <a :[key]="url"> ... </a>

## Class 与 Style 绑定

### 对象语法

    <div v-bind:class="{ active: isActive }">

    </div>

传给 `v-bind:class` 一个对象，以动态地切换 `class`

### 与普通的 class 属性共存

    <div
      class="static"
      v-bind:class="{
        active: isActive,
        'text-danger': hasError
      }"
    ></div>

如下 data

    data: {
      isActive: true,
      hasError: false
    }

结果渲染为

    <div class="static active"></div>

### 绑定的数据对象不必内联定义在模板里

    <div v-bind:class="classObject"></div>

如下 data

    data: {
      classObject: {
        active: true,
        'text-danger': false
      }
    }

结果渲染为

    <div class="static active"></div>

### 三元表达式

    <div v-bind:class="[
      isActive ? activeClass : ''
    ]">
    </div>

### 数组

    <div v-bind:class="[
      { active: isActive }, errorClass
    ]"></div>

### 数组语法

    <div v-bind:class="[
      activeClass, errorClass
    ]">
    </div>

如下 data

    data: {
      activeClass: 'active',
      errorClass: 'text-danger'
    }

结果渲染为

    <div class="active text-danger"></div>

### 内联样式

    <div v-bind:style="{
        color: activeColor,
        fontSize: fontSize + 'px'
    }"></div>

如下 data

    data: {
      activeColor: 'red',
      fontSize: 30
    }

结果渲染为

    <div style="color: red; font-size: 30px;"></div>

### 内联样式对象通常更好

    <div v-bind:style="styleObject"></div>

如下 data

    data: {
      styleObject: {
        color: 'red',
        fontSize: '13px'
      }
    }

同样的，对象语法常常结合返回对象的计算属性使用

### 内联样式数组语法

    <div v-bind:style="[
      baseStyles, overridingStyles
    ]"></div>

### 内联样式多重值

    <div :style="{
      display: ['-webkit-box', 'flex']
    }"></div>

## 条件渲染

### v-if

    <h1 v-if="awesome">Vue is awesome!</h1>
    <h1 v-else>Oh no 😢</h1>

### v-else-if

    <div v-if="type === 'A'">A</div>
    <div v-else-if="type === 'B'">B</div>
    <div v-else-if="type === 'C'">C</div>
    <div v-else>
      Not A/B/C
    </div>

`@2.1.0` 新增，必须紧跟在带 `v-if` 或者 `v-else-if` 的元素之后

### v-else

    <div v-if="Math.random() > 0.5">
      现在你看到我了
    </div>
    <div v-else>
      现在你看不见我了
    </div>

`v-else` 元素必须紧跟在带 `v-if` 或者 `v-else-if` 的元素的后面

### <template> 上使用 v-if 条件渲染分组

    <template v-if="ok">
      <p>Paragraph 1</p>
    </template>

### 用 key 管理可复用的元素

    <template v-if="loginType === 'username'">
      <label>Username</label>
      <input placeholder="输入用户名" key="username-input">
    </template>
    <template v-else>
      <label>Email</label>
      <input placeholder="输入邮箱" key="email-input">
    </template>

### v-show

    <h1 v-show="ok">
      Hello!
    </h1>

带有 `v-show` 的元素始终会被渲染并保留在 DOM 中，只是简单地切换元素的 `CSS` 属性 `display`

## 列表渲染

### v-for

    <ul id="example-1">
      <li
        v-for="item in items"
        :key="item.message">
        {{ item.message }}
      </li>
    </ul>

    var example1 = new Vue({
      el: '#example-1',
      data: {
        items: [
          { message: 'Foo' },
          { message: 'Bar' }
        ]
      }
    })

### v-for 可选的第二个参数(索引)

    <li v-for="(item, index) in items">
      {{ index }}
      {{ item.message }}
    </li>

如下 `data`

    data: {
      items: [
        { message: 'Foo' },
        { message: 'Bar' }
      ]
    }

也可以用 `of` 替代 `in` 作为分隔符

    <div v-for="item of items"></div>

### v-for 使用对象

    <li v-for="value in object">
      {{ value }}
    </li>

如下 data

    data: {
      object: {
        title: 'How to do lists in Vue',
        author: 'Jane Doe',
        publishedAt: '2016-04-10'
      }
    }

渲染结果

    How to do lists in Vue
    Jane Doe
    2016-04-10

提供第二个的参数为 property 名称 (也就是键名)

    <div v-for="(value, name) in object">
      {{ name }}: {{ value }}
    </div>

还可以用第三个参数作为索引

    <div v-for="(value,name,index) in object">
      {{ index }}. {{ name }}: {{ value }}
    </div>

### v-for/v-if

    <li
      v-for="todo in todos"
      v-if="!todo.isComplete"
    >
      {{ todo }}
    </li>

只渲染未完成的 todo，下面加上 `v-else` 示例

    <ul v-if="todos.length">
      <li v-for="todo in todos">
        {{ todo }}
      </li>
    </ul>
    <p v-else>No todos left!</p>

注意: `v-for` 和 `v-if` 不推荐一起使用参考官方文档

### 组件上使用 v-for

    <my-component
      v-for="(item, index) in items"
      v-bind:item="item"
      v-bind:index="index"
      v-bind:key="item.id"
    ></my-component>

`2.2.0+` 的版本里，当在组件上使用 `v-for` 时，`key` 现在是必须的

## 事件处理

### 监听事件

    <div id="example-1">
      <button v-on:click="counter += 1">
        +1
      </button>
      <p>按钮已被点击 {{ counter }} 次。</p>
    </div>

    var example1 = new Vue({
      el: '#example-1',
      data: {
        counter: 0
      }
    })

### 事件处理方法

    <div id="example-2">
      <!-- `greet` 是在下面定义的方法名 -->
      <button v-on:click="greet">
        你好
      </button>
    </div>

    var example2 = new Vue({
      el: '#example-2',
      data: {
        name: 'Vue.js'
      },
      // 在 `methods` 对象中定义方法
      methods: {
        greet: function (event) {
          // `this` 在方法里指向当前 Vue 实例
          alert('Hello ' + this.name + '!')
          // `event` 是原生 DOM 事件
          if (event) {
            alert(event.target.tagName)
          }
        }
      }
    })

也可以用 JavaScript 直接调用方法

    example2.greet() // => 'Hello Vue.js!'

### 内联处理器中的方法

    <div id="example-3">
      <button v-on:click="say('hi')">
        弹出 hi
      </button>
      <button v-on:click="say('what')">
        弹出 what
      </button>
    </div>

    new Vue({
      el: '#example-3',
      methods: {
        say: function (message) {
          alert(message)
        }
      }
    })

访问原始的 DOM 事件，用特殊变量 $event

    <button v-on:click="say('what', $event)">
      提交
    </button>

    methods: {
      say: function (message, event) {
        // 现在我们可以访问原生事件对象
        if (event) {
          event.preventDefault()
        }
        alert(message)
      }
    }

### 事件修饰符

    <!-- 阻止单击事件继续传播 -->
    <a v-on:click.stop="doThis"></a>
    <!-- 提交事件不再重载页面 -->
    <form v-on:submit.prevent="submit"></form>
    <!-- 修饰符可以串联 -->
    <a v-on:click.stop.prevent="doThat"></a>
    <!-- 只有修饰符 -->
    <form v-on:submit.prevent></form>
    <!-- 添加事件监听器时使用事件捕获模式 -->
    <!-- 即内部元素触发的事件先在此处理 -->
    <!-- 然后交由内部元素进行处理 -->
    <div v-on:click.capture="doThis">...</div>
    <!-- 当 event.target 是当前元素自身时触发 -->
    <!-- 即事件不是从内部元素触发的 -->
    <div v-on:click.self="doThat">...</div>
    <!-- 点击事件将只会触发一次 -->
    <a v-on:click.once="doThis"></a>

### 事件修饰符 passive

    <!-- 滚动事件的默认行为(即滚动行为)会立即触发 -->
    <!-- 而不会等待 `onScroll` 完成  -->
    <!-- 包含 event.preventDefault() 的情况 -->
    <p v-on:scroll.passive="onScroll">
      ...
    </p>

这个 `.passive` 修饰符尤其能够提升移动端的性能。

### 按键修饰符

    <!-- 在 key 是 Enter 时调用 vm.submit() -->
    <input v-on:keyup.enter="submit">
    <!-- 在 key 是 PageDown 时被调用 -->
    <input v-on:keyup.page-down="onPageDown">
    <!-- Alt + C -->
    <input v-on:keyup.alt.67="clear">
    <!-- Ctrl + Click -->
    <div v-on:click.ctrl="doSomething">

### .exact 修饰符

    <!-- 即使 Alt 或 Shift 被一同按下时也会触发 -->
    <button v-on:click.ctrl="onClick">
    <!-- 有且只有 Ctrl 被按下的时候才触发 -->
    <button v-on:click.ctrl.exact="ctrlClick">
    <!-- 没有任何系统修饰符被按下的时候才触发 -->
    <button v-on:click.exact="onClick">

## 计算属性和侦听器

### 基础例子

    <div id="example">
      <p>Original message: "{{ message }}"</p>
      <p>
        计算的反向消息： "{{ reversedMessage }}"
      </p>
    </div>

    var vm = new Vue({
      el: '#example',
      data: {
        message: 'Hello'
      },
      computed: {
        // 计算属性的 getter
        reversedMessage: function () {
          // `this` 指向 vm 实例
          return this.message.split('')
                      .reverse().join('')
        }
      }
    })

### 计算属性缓存 vs 方法

    <p>
      计算的反向消息："{{ reversedMessage() }}"
    </p>

在组件中，我们可以将同一函数定义为一个方法而不是一个计算属性

    methods: {
      reversedMessage: function () {
        return this.message.split('')
                    .reverse().join('')
      }
    }

两种方式的最终结果确实是完全相同的。然而，不同的是计算属性是基于它们的响应式依赖进行缓存的。

### 计算属性 vs 侦听属性

    <div id="demo">{{ fullName }}</div>

    var vm = new Vue({
      el: '#demo',
      data: {
        firstName: 'Foo',
        lastName: 'Bar',
        fullName: 'Foo Bar'
      },
      watch: {
        firstName: function (val) {
          this.fullName =
              val + ' ' + this.lastName
        },
        lastName: function (val) {
          this.fullName =
              this.firstName + ' ' + val
        }
      }
    })

上面代码是命令式且重复的。将它与计算属性的版本进行比较：

    var vm = new Vue({
      el: '#demo',
      data: {
        firstName: 'Foo',
        lastName: 'Bar'
      },
      computed: {
        fullName: function () {
          return this.firstName
            + ' ' + this.lastName
        }
      }
    })

### 计算属性的 setter

    computed: {
      fullName: {
        get: function () {         // getter
          return this.firstName + ' ' + this.lastName
        },
        set: function (newValue) { // setter
          var names = newValue.split(' ')
          this.firstName = names[0]
          this.lastName = names[names.length - 1]
        }
      }
    }

## 表单输入绑定

### 文本

    <input v-model="msg" placeholder="编辑我">
    <p>msg is: {{ msg }}</p>

### 多行文本

    <span>Multiline message is:</span>
    <textarea
      v-model="message"
      placeholder="添加多行"></textarea>
    <p>{{ message }}</p>

### 复选框

    <input
      type="checkbox"
      id="checkbox"
      v-model="checked"
    >
    <label for="checkbox">{{ checked}}</label>

### 多个复选框

    <input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
    <label for="jack">Jack</label>
    <input type="checkbox" id="john" value="John" v-model="checkedNames">
    <label for="john">John</label>
    <input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
    <label for="mike">Mike</label>
    <br>
    <span>Checked names: {{ checkedNames }}</span>

如下 data

    new Vue({
      el: '...',
      data: {
        checkedNames: []
      }
    })

### 单选按钮

    <div id="example-4">
      <input type="radio" id="one" value="One"
        v-model="picked">
      <label for="one">One</label>
      <br>
      <input type="radio" id="two" value="Two"
        v-model="picked">
      <label for="two">Two</label>
      <div>Picked: {{ picked }}</div>
    </div>

    new Vue({
      el: '#example-4',
      data: {
        picked: ''
      }
    })

### 选择框

    <select v-model="selected">
      <option disabled value="">请选择</option>
      <option>A</option>
      <option>B</option>
      <option>C</option>
    </select>
    <span>Selected: {{ selected }}</span>

    new Vue({
      el: '...',
      data: {
        selected: ''
      }
    })

### 选择框(数组)

    <select v-model="selected" multiple>
      <option>A</option>
      <option>B</option>
      <option>C</option>
    </select>
    <div>Selected: {{ selected }}</div>

    new Vue({
      el: '...',
      data: {
        selected: []
      }
    })

### v-for 渲染的动态选项

    <select v-model="selected">
      <option
        v-for="option in options"
        v-bind:value="option.value"
      >
        {{ option.text }}
      </option>
    </select>
    <span>Selected: {{ selected }}</span>

    new Vue({
      el: '...',
      data: {
        selected: 'A',
        options: [
          { text: 'One', value: 'A' },
          { text: 'Two', value: 'B' },
          { text: 'Three', value: 'C' }
        ]
      }
    })

### 值绑定

    <!-- 当选中时，pc 为字符串 "a" -->
    <input type="radio" v-model="pc" value="a">

    <!-- toggle 为 true 或 false -->
    <input type="checkbox" v-model="toggle">
    <!-- 选中第一个选项时selected为字符串 abc -->
    <select v-model="selected">
      <option value="abc">ABC</option>
    </select>

### 单选按钮

    <input
      type="radio"
      v-model="pick"
      v-bind:value="a">

当选中时

    vm.pick === vm.a

### 复选框

    <input
      type="checkbox"
      v-model="toggle"
      true-value="yes"
      false-value="no"
    >

    // 当选中时
    vm.toggle === 'yes'
    // 当没有选中时
    vm.toggle === 'no'

### 选择框的选项

    <select v-model="selected">
      <!-- 内联对象字面量 -->
      <option v-bind:value="{ number: 123 }">
        123
      </option>
    </select>

当选中时

    typeof vm.selected // => 'object'
    vm.selected.number // => 123

### 修饰符

    <!-- lazy:在“change”时而非“input”时更新 -->
    <input v-model.lazy="msg">

    <!-- number:自动将用户的输入值转为数值类型 -->
    <input v-model.number="age" type="number">

    <!-- trim:自动过滤用户输入的首尾空白字符 -->
    <input v-model.trim="msg">

## 组件基础

### 单文件组件

- 将 `HTML`/`CSS`/`JS` 三部分存放到一个 `Hello.vue` 文件中

      <template>
        <p>{{ title }} World!</p>
      </template>
      <script>
        export default {
          name: 'Hello',
          props: {
            title: {
              type: String,
              default: 'Hello'
            }
          },
          data: function() {
            return {
              greeting: "Hello"
            };
          }
        };
      </script>
      <style scoped>
        p {
          font-size: 2em;
          text-align: center;
        }
      </style>

- 使用 `Hello.vue` 组件

      <script>
        import Vue from "vue";
        import Hello from "./Hello";

        export default {
          components: { Hello }
        }
      </script>
      <template>
        <div>
          <Hello :title="'aaaa'"></Hello>
        </div>
      </template>

### 基本示例

    Vue.component('button-counter', {
      data: function () {
        return {
          count: 0
        }
      },
      template: `
        <button v-on:click="count++">
          你点击了我 {{ count }} 次
        </button>
      `
    })

组件是可复用的 `Vue` 实例

    <div id="components-demo">
      <button-counter></button-counter>
    </div>

    new Vue({
      el: '#components-demo'
    })

组件的复用

    <div id="components-demo">
      <button-counter></button-counter>
      <button-counter></button-counter>
      <button-counter></button-counter>
    </div>

### 单个根元素

    Vue.component('blog-post', {
      props: ['post'],
      template: `
        <div class="blog-post">
          <h3>{{ post.title }}</h3>
          <div v-html="post.content"></div>
        </div>
      `
    })

    <blog-post
      v-for="post in posts"
      v-bind:key="post.id"
      v-bind:post="post"
    >
    </blog-post>

### 向子组件传递数据

    Vue.component('blog-post', {
      props: ['title'],
      template: '<h3>{{ title }}</h3>'
    })

当值传递给一个 `prop` `attribute` 的时候，变成了组件实例的一个 `property`

    <blog-post title="写博客"></blog-post>
    <blog-post title="如此有趣"></blog-post>

### `data` 必须是一个函数

    data: function () {
      return {
        count: 0
      }
    }

组件的 `data` 选项必须是一个函数

### 监听子组件事件

    Vue.component('blog-post', {
      props: ['post'],
      template: `
        <div class="blog-post">
          <h3>{{ post.title }}</h3>
          <button
            v-on:click="$emit('enlarge-txt')"
          >放大文字
          </button>
          <div v-html="post.content"></div>
        </div>
      `
    })

    <blog-post
      ...
      v-on:enlarge-text="postFontSize += 0.1"
    ></blog-post>

可以使用 `$emit` 的第二个参数来提供这个值

    <button
      v-on:click="$emit('enlarge-text', 0.1)"
    >
      Enlarge text
    </button>

通过 `$event` 访问到被抛出的这个值

    <blog-post
      ...
      v-on:enlarge-text="postFontSize += $event"
    ></blog-post>

如果这个事件处理函数是一个方法

    <blog-post
      ...
      v-on:enlarge-text="onEnlargeText"
    ></blog-post>

那么这个值将会作为第一个参数传入这个方法

    methods: {
      onEnlargeText: function(enlargeAmount) {
        this.postFontSize += enlargeAmount
      }
    }

### 在组件上使用 v-model

自定义事件也可以用于创建支持 `v-model` 的自定义输入组件。

    <input v-model="searchText">

等价于

    <input
      v-bind:value="searchText"
      v-on:input="searchText = $event.target.value"
    >

当用在组件上时，v-model 则会这样：

    <custom-input
      v-bind:value="searchText"
      v-on:input="searchText = $event"
    ></custom-input>

为了让它正常工作，这个组件内的 <input> 必须：

- 将其 `value` attribute 绑定到一个名叫 `value` 的 `prop` 上
- 在其 `input` 事件被触发时，将新的值通过自定义的 `input` 事件抛出

    Vue.component('custom-input', {
      props: ['value'],
      template: `
        <input
          v-bind:value="value"
          v-on:input="$emit('input', $event.target.value)"
        >
      `
    })

现在 `v-model` 就应该可以在这个组件上完美地工作起来了

    <custom-input
      v-model="searchText"
    >
    </custom-input>

### 通过插槽分发内容

    <alert-box>
      发生了不好的事情。
    </alert-box>

    Vue.component('alert-box', {
      template: `
        <div class="demo-alert-box">
          <strong>Error!</strong>
          <slot></slot>
        </div>
      `
    })

### 动态组件示例

    <div id="dynamic-component-demo" class="demo">
      <button
        v-for="tab in tabs"
        v-bind:key="tab"
        v-bind:class="['tab-button', { active: currentTab === tab }]"
        v-on:click="currentTab = tab"
      >
        {{ tab }}
      </button>
      <component v-bind:is="currentTabComponent" class="tab"></component>
    </div>

    <script>
      Vue.component("tab-home", {
        template: "<div>Home component</div>"
      });
      Vue.component("tab-posts", {
        template: "<div>Posts component</div>"
      });
      Vue.component("tab-archive", {
        template: "<div>Archive component</div>"
      });
      new Vue({
        el: "#dynamic-component-demo",
        data: {
          currentTab: "Home",
          tabs: ["Home", "Posts", "Archive"]
        },
        computed: {
          currentTabComponent: function() {
            return "tab-" + this.currentTab.toLowerCase();
          }
        }
      });
    </script>

### 解析 DOM 模板时的注意事项

有些 HTML 元素，诸如 `<ul>`、`<ol>`、`<table>` 和 `<select>`，对于哪些元素可以出现在其内部是有严格限制的。有些元素，诸如 `<li>`、`<tr>` 和 `<option>`，只能出现在其它某些特定的元素内部

    <table>
      <blog-post-row></blog-post-row>
    </table>

`<blog-post-row>` 会被作为无效的内容提升到外部

如果我们从以下来源使用模板的话，这条限制是不存在的

- 字符串 (例如：`template: '...'`)
- 单文件组件 (`.vue`)
- `<script type="text/x-template">`

## 过渡 & 动画

### 单元素/组件的过渡

    <template>
      <button v-on:click="show = !show">
        切换
      </button>
      <transition name="fade">
        <p v-if="show">切换内容</p>
      </transition>
    </template>

    <script>
      export default {
        data: function() {
          return {
            show: true
          };
        }
      };
    </script>

    <style scoped>
      .fade-enter-active, .fade-leave-active {
        transition: opacity .5s;
      }
      /* .fade-leave-active 低于 2.1.8 版本 */
      .fade-enter, .fade-leave-to {
        opacity: 0;
      }
    </style>

### CSS 过渡

    <template>
      <button @click="show = !show">
        切换渲染
      </button>
      <transition name="slide-fade">
        <p v-if="show">切换内容</p>
      </transition>
    </template>
    <script>
      export default {
        data: function() {
          return {
            show: true
          };
        }
      };
    </script>
    <style scoped>
      /* 可以设置不同的进入和离开动画 */
      /* 设置持续时间和动画函数 */
      .slide-fade-enter-active {
        transition: all .3s ease;
      }
      .slide-fade-leave-active {
        transition: all .8s cubic-bezier(1.0, 0.5, 0.8, 1.0);
      }
      /* .slide-fade-leave-active 用于 2.1.8 以下版本 */
      .slide-fade-enter, .slide-fade-leave-to {
        transform: translateX(10px);
        opacity: 0;
      }
    </style>

### CSS 动画

    <template>
      <button @click="show = !show">切换展示</button>
      <transition name="bounce">
        <p v-if="show">切换内容</p>
      </transition>
    </template>
    <script>
      export default {
        data: function() {
          return {
            show: true
          };
        }
      };
    </script>
    <style scoped>
    .bounce-enter-active {
      animation: bounce-in .5s;
    }
    .bounce-leave-active {
      animation: bounce-in .5s reverse;
    }
    @keyframes bounce-in {
      0% {
        transform: scale(0);
      }
      50% {
        transform: scale(1.5);
      }
      100% {
        transform: scale(1);
      }
    }
    </style>

### 过渡的类名

:- | :-
--- | ---
`v-enter` | 定义进入过渡的开始状态 #
`v-enter-active` | 定义进入过渡生效时的状态 #
`v-enter-to` (2.1.8) | 定义进入过渡的结束状态 #
`v-leave` | 定义离开过渡的开始状态 #
`v-leave-active` | 定义离开过渡生效时的状态 #
`v-leave-to` (2.1.8) | 定义离开过渡的结束状态 #

如果你使用了 `<transition name="my-tran">`，那么 `v-enter` 会替换为 `my-tran-enter`。

#### 自定义过渡的类名

:- | :-
--- | ---
`enter-class` | #
`enter-active-class` | #
`enter-to-class` (2.1.8+) | #
`leave-class` | #
`leave-active-class` | #
`leave-to-class` (2.1.8+) | #

    <transition
      name="custom-classes-transition"
      enter-active-class="animated tada"
      leave-active-class="animated bounceOutRight"
    >
      <p v-if="show">hello</p>
    </transition>

### 显性的过渡持续时间

`<transition>` 组件上的 `duration` prop 定制一个显性的过渡持续时间 (以毫秒计)：

    <transition :duration="1000">
      ...
    </transition>

你也可以定制进入和移出的持续时间：

    <transition :duration="{
      enter: 500,
      leave: 800
    }">
      ...
    </transition>

### 初始渲染的过渡

可以通过 `appear` attribute 设置节点在初始渲染的过渡

    <transition appear>
      <!-- ... -->
    </transition>

这里默认和进入/离开过渡一样，同样也可以自定义 CSS 类名

    <transition
      appear
      appear-class="custom-appear-class"
      appear-to-class="custom-appear-to-class"
      appear-active-class="custom-appear-active-class"
    >
      <!-- ... -->
    </transition>

自定义 JavaScript 钩子：

    <transition
      appear
      v-on:before-appear="customBeforeAppearHook"
      v-on:appear="customAppearHook"
      v-on:after-appear="customAfterAppearHook"
      v-on:appear-cancelled="customAppearCancelledHook"
    >
      <!-- ... -->
    </transition>

无论是 `appear` attribute 还是 `v-on:appear` 钩子都会生成初始渲染过渡

### JavaScript 钩子

    <transition
      v-on:before-enter="beforeEnter"
      v-on:enter="enter"
      v-on:after-enter="afterEnter"
      v-on:enter-cancelled="enterCancelled"

      v-on:before-leave="beforeLeave"
      v-on:leave="leave"
      v-on:after-leave="afterLeave"
      v-on:leave-cancelled="leaveCancelled"
    >
      <!-- ... -->
    </transition>

钩子函数可以结合 CSS `transitions`/`animations` 使用，也可以单独使用

### 列表的进入/离开过渡

    <template>
      <button v-on:click="add">添加</button>
      <button v-on:click="remove">删除</button>
      <transition-group name="list" tag="p">
        <span v-for="item in items" v-bind:key="item" class="list-item">
          {{ item }}
        </span>
      </transition-group>
    </template>
    <script>
      export default {
        data: function() {
          return {
            items: [1,2,3,4,5,6,7,8,9],
            nextNum: 10
          };
        },
        methods: {
          randomIndex: function () {
            return Math.floor(Math.random() * this.items.length)
          },
          add: function () {
            this.items.splice(this.randomIndex(), 0, this.nextNum++)
          },
          remove: function () {
            this.items.splice(this.randomIndex(), 1)
          },
        }
      };
    </script>
    <style scoped>
      .list-item {
        display: inline-block;
        margin-right: 10px;
      }
      .list-enter-active, .list-leave-active {
        transition: all 1s;
      }
      /* .list-leave-active 适用于 2.1.8 以下版本 */
      .list-enter, .list-leave-to {
        opacity: 0;
        transform: translateY(30px);
      }
    </style>

## Vue 2 API 参考

### 全局配置

:- | :-
--- | ---
`silent` | 取消所有的日志与警告 #
`optionMergeStrategies` | 自定义合并策略的选项 #
`devtools` | 是否允许 devtools 检查 #
`errorHandler` | 未捕获错误的处理函数 (开发模式生效) #
`warnHandler` (2.4.0) | 运行时警告处理函数 #
`ignoredElements` | 忽略 Vue 之外的 (自定义元素) #
`keyCodes` | `v-on` 自定义键位别名 #
`performance` (2.2.0) | 性能追踪 #
`productionTip` (2.2.0) | 是否生成生产提示 #

### 全局 API

:- | :-
--- | ---
`Vue.extend` | Vue 构造器，创建一个“子类” #
`Vue.nextTick` | 执行延迟回调 #
`Vue.set` | 向响应式对象中添加一个属性 #
`Vue.delete` | 删除对象的 property #
`Vue.directive` | 注册或获取全局指令 #
`Vue.filter` | 注册或获取全局过滤器 #
`Vue.component` | 注册或获取全局组件 #
`Vue.use` | 安装 Vue.js 插件 #
`Vue.mixin` | 全局注册一个混入 #
`Vue.compile` | 将模板字符串编译成 render 函数 #
`Vue.observable` (2.6.0) | 让一个对象可响应 #
`Vue.version` | Vue 安装版本号 #

### 数据

:- | :-
--- | ---
`data` | 实例的数据对象 #
`props` | 接收来自父组件的数据 #
`propsData` | 创建实例时传递 props #
`computed` | 计算属性将被混入到 Vue 实例中 #
`methods` | 将被混入到 Vue 实例中 #
`watch` | 对象键是观察的表达式，值是回调函数 #

### DOM

:- | :-
--- | ---
`el` | 实例的挂载目标 #
`template` | 字符串模板作为 Vue 实例的标识使用 #
`render` | 字符串模板的代替方案 #
`renderError` (2.2.0) | `render`错误时提供另一种渲染 #

### 生命周期钩子

:- | :-
--- | ---
`beforeCreate` | 实例初始化之后 #
`created` | 实例创建完成后被立即同步调用 #
`beforeMount` | 在挂载开始之前被调用 #
`mounted` | 实例被挂载后调用 #
`beforeUpdate` | 数据改变后 DOM 更新之前调用 #
`updated` | 数据更改更新完毕之后被调用 #
`activated` | keep-alive 缓存组件激活时调用 #
`deactivated` | keep-alive 缓存的组件失活时调用 #
`beforeDestroy` | 实例销毁之前调用 #
`destroyed` | 实例销毁后调用 #
`errorCaptured` (2.5.0) | 来自后代组件的错误时被调用 #

### 资源

:- | :-
--- | ---
`directives` | 包含 Vue 实例可用指令的哈希表 #
`filters` | 包含 Vue 实例可用过滤器的哈希表 #
`components` | 包含 Vue 实例可用组件的哈希表 #

### 组合

:- | :-
--- | ---
`parent` | 指定已创建的实例之父实例 #
`mixins` | 接收一个混入对象的数组 #
`extends` | 声明扩展另一个组件 #
`provide/inject` (2.2.0) | 祖组件向所有子孙后代注入依赖 #

### 其它

:- | :-
--- | ---
`name` | 允许组件模板递归地调用自身 #
`delimiters` | 改变纯文本插入分隔符 #
`functional` | 使组件无状态和无实例 #
`model` (2.2.0) | 使用 v-model 时定制 prop 和 event #
`inheritAttrs` (2.4.0) | #
`comments` (2.4.0) | 是否保留模板中的`HTML`注释 #

### 实例方法 / 数据

:- | :-
--- | ---
`vm.$watch` | 观察 Vue 实例上的一个表达式 或者一个函数计算结果的变化 #
`vm.$set` | 全局 `Vue.set` 的别名 #
`vm.$delete` | 全局 `Vue.delete` 的别名 #

### 实例 property

:- | :-
--- | ---
`vm.$data` | 观察的数据对象 #
`vm.$props` (2.2.0) | 组件接收的 props 对象 #
`vm.$el` | 实例使用的根 DOM 元素 #
`vm.$options` | 实例的初始化选项 #
`vm.$parent` | 父实例 #
`vm.$root` | 当前组件树的根实例 #
`vm.$children` | 当前实例的直接子组件 #
`vm.$slots` | 访问被插槽分发的内容 #
`vm.$scopedSlots` (2.1.0) | 访问作用域插槽 #
`vm.$refs` | DOM 元素和组件实例 #
`vm.$isServer` | 是否运行于服务器 #
`vm.$attrs` (2.4.0) | 包含父作用域中不作为 prop 被识别的属性绑定 ( #
`vm.$listeners` (2.4.0) | 包含了父作用域中的 (不含 .native 修饰器的) `v-on` 事件监听器 #

### 实例方法 / 事件

:- | :-
--- | ---
`vm.$on` | 监听当前实例上的自定义事件 #
`vm.$once` | 监听一个自定义事件，只触发一次 #
`vm.$off` | 移除自定义事件监听器 #
`vm.$emit` | 触发当前实例上的事 #

### 实例方法 / 生命周期

:- | :-
--- | ---
`vm.$mount` | 手动地挂载一个未挂载的实例 #
`vm.$forceUpdate` | 迫使 Vue 实例重新渲染 #
`vm.$nextTick` | 回调延迟执行 #
`vm.$destroy` | 完全销毁一个实例 #

### 指令

:- | :-
--- | ---
`v-text` | 更新元素的 `textContent` #
`v-html` | 更新元素的 `innerHTML` #
`v-show` | 切换元素的 `display` css 属性 #
`v-if` | 有条件地渲染元素 #
`v-else` | #
`v-else-if` (2.1.0) | #
`v-for` | 多次渲染元素或模板块 #
`v-on` | 绑定事件监听器 #
`v-bind` | 动态地绑定一个或多个属性 #
`v-model` | 创建双向绑定 #
`v-slot` | 提供插槽或接收 prop 的插槽 #
`v-pre` | 跳过元素和它的子元素编译过程 #
`v-cloak` | 保持在元素上直到实例结束编译 #
`v-once` | 只渲染元素和组件一次 #

### 特殊 attribute

:- | :-
--- | ---
`key` | 用在 Vue 的虚拟 DOM 算法 #
`ref` | 元素或子组件注册引用信息 #
`is` | 限制是否更新 #
`slot` | 推荐 2.6.0 新增的 `v-slot` #
`slot-scope` | 推荐 2.6.0 新增的 `v-slot` #
`scope` | `2.5.0` 新增的 `slot-scope` 取代 #

### 内置的组件

:- | :-
--- | ---
`<component>` | 渲染一个`元组件`为动态组件 #
`<transition>` | 单个元素/组件的过渡效果 #
`<transition-group>` | 多个元素/组件的过渡效果 #
`<keep-alive>` | 不活动的实例缓存不销毁 #
`<slot>` | 组件模板中的内容分发插槽 #

### v-on (事件)修饰符

:- | :-
--- | ---
`v-on:click.stop` # | 调用 event.stopPropagation()。
`v-on:click.prevent` # | 调用 event.preventDefault()。
`v-on:click.capture` # | 添加事件侦听器时使用 capture 模式。
`v-on:click.self` # | 只当事件是从侦听器绑定的元素本身触发时才触发回调。
`v-on:click.{keyCode|keyAlias}` # | 只当事件是从特定键触发时才触发回调。
`v-on:click.native` # | 监听组件根元素的原生事件。
`v-on:click.once` # | 只触发一次回调。
`v-on:click.passive` (2.3.0) # | 以 { passive: true } 模式添加侦听器

### v-on (鼠标)修饰符

:- | :-
--- | ---
`v-on:click.left` # | 只当点击鼠标左键时触发
`v-on:click.right` # | 只当点击鼠标右键时触发
`v-on:click.middle` # | 只当点击鼠标中键时触发

在 (2.2.0) 中新增

### 系统修饰键

:- | :-
--- | ---
`v-on:keyup.ctrl` (2.1.0) | #
`v-on:keyup.alt` (2.1.0) | #
`v-on:keyup.shift` (2.1.0) | #
`v-on:keyup.meta` (2.1.0) | #

### Keyboard 按键修饰符

:- | :-
--- | ---
`v-on:keyup.enter` | #
`v-on:keyup.tab` | #
`v-on:keyup.delete` | 捕获“删除”和“退格”键 #
`v-on:keyup.esc` | #
`v-on:keyup.space` | #
`v-on:keyup.up` | #
`v-on:keyup.down` | #
`v-on:keyup.left` | #
`v-on:keyup.right` | #

### v-bind 修饰符

:- | :-
--- | ---
`v-bind.prop` # | 作为一个 DOM property 绑定而不是作为 attribute 绑定。(差别在哪里？)
`v-bind.camel` (2.1.0+) # | 将 kebab-case attribute 名转换为 camelCase。
`v-bind.sync` (2.3.0+) # | 语法糖，会扩展成一个更新父组件绑定值的 v-on 侦听器。

## 另见

- Vue Essentials Cheat-Sheet.pdf (vuemastery.com)
- Vue 2 官方文档 (vuejs.org)
- Vue 2 实例方法事件 (marozed.com)