# Vue3 学习手册 (opens new window)

# Vue 声明和参数 data 声明

  • Vue2 使用 new Vue() 初始化 而 Vue3 则使用 createApp()

  • 初始化组件的参数声明 data 必须是 function

  • 案例比较

    <body>
      <div id="app">
        <h1>{{msg}}</h1>
      </div>
    
      <!-- Vue2 -->
      <script>
        let app = new Vue({
          el: "#app",
          data() {
            return {
              counter: 0,
              msg: `我是标签`
            }
          }
        })
      </script>
    
      <!-- Vue3 -->
      <script>
        Vue.createApp({
          data() {
            return {
              msg: `我是标签`
            }
          }
        }).mount('#app')
      </script>
    </body>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
  • 涉及改动点

# Template 的挂载

  • Vue2 中 挂载一个具有 templete 的应用时 被渲染的内容会 替换我们要挂载的目标元素

  • Vue3 中 则会 使用 innerHTML 作为子元素插入

  • 案例比较

    <body>
      <div id="app"></div>
    
      <!-- Vue2 -->
      <script>
        let app = new Vue({
          el: "#app",
          data() {
            return {
              counter: 0,
              msg: `我是标签`
            }
          },
          template: `<h1>{{msg}}</h1>`
        })
      </script>
    
      <!-- Vue3 -->
      <script>
        Vue.createApp({
          data() {
            return {
              msg: `我是标签`
            }
          },
          template: `<h1>{{msg}}</h1>`
        }).mount('#app')
      </script>
    </body>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    Vue2 Vue3
  • 涉及改动点

# 数据渲染遍历和判断

  • Vue2 遍历时可以直接绑定 ref 属性到 $refs

  • Vue3 遍历时则需要自定义函数进行动态获取

  • 遍历渲染和判断渲染时 不需要再指定 Key

  • 案例比较

    <body>
      <div id="app">
        <!-- Vue2 -->
        <div v-for="item in list" :ref="item" :key="item">{{item}}</div>
    
        <!-- Vue3 -->
        <div v-for="item in list" :ref="setRefs">{{item}}</div>
      </div>
    
      <!-- Vue2 -->
      <script>
        let app = new Vue({
          el: "#app",
          data() {
            return {
              list: [11, 22, 33],
            }
          }
        })
      </script>
    
      <!-- Vue3 -->
      <script>
        Vue.createApp({
          data() {
            return {
              list: [11, 22, 33],
              itemRefs: []
            }
          },
          methods: {
            setRefs(el) {
              if (el) {
                this.itemRefs.push(el)
              }
            },
          },
        }).mount('#app')
      </script>
    </body>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
  • 涉及改动点

# 子组件渲染父组件传递的 attribute

  • Vue2 中子组件的根结点会自动渲染父组件传递的 classstyle

  • Vue3 中子组件只有通过 v-bind="$attrs" 渲染 attribute

  • 行内的 attribute 优先级不再永远高于 v-bind

  • 案例展示

    <body>
      <div id="app">
        <my-inp id="my-id" class="my-class" style="color: red;"></my-inp>
      </div>
      <template id="myInput">
        <label>
          <!-- 渲染前 -->
          账号: <input type="text" v-bind="$attrs" id="old-id">
    
          <!-- 渲染后 -->
          账号: <input type="text" id="old-id" class="my-class" style="color: red;">
        </label>
      </template>
      <script>
        Vue.createApp({
          components: {
            "my-inp": {
              inheritAttrs: false,
              template: `#myInput`
            }
          }
        }).mount('#app')
      </script>
    </body>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
  • 涉及改动点

# 数组侦听

  • Vue2 中 对于数组的操作可以触发 watch 的侦听

  • Vue3 中 则需要开启 deep 模式

  • 案例展示

    <body>
      <div id="app">
        <button @click="add">add</button>
        <ul>
          <li v-for="item in list">{{item}}</li>
        </ul>
      </div>
      <script>
        Vue.createApp({
          data() {
            return {
              list: [11, 22, 33]
            }
          },
          methods: {
            add() {
              this.list.push(Date.now())
            }
          },
          watch: {
            list: {
              handler() {
                console.log(this.list);
              },
              deep: true
            }
          }
        }).mount('#app')
      </script>
    </body>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
  • 涉及改动点

# 父子组件间事件的触发

  • Vue2 中 通过 .native 事件修饰符 可以使得子组件的根结点继承 DOM 原生触发器

  • Vue3 中 默认子组件的根结点自动继承 DOM 原生触发器 需要通过 emits:[事件名] 来单独设置

  • 版本比较

    <body>
      <div id="app">
        <h1>{{num}}</h1>
        <hr>
    
        <!-- Vue2 -->
        <my-btn @click.native="fatherClick"></my-btn>
    
        <!-- Vue3 -->
        <my-btn @click="fatherClick"></my-btn>
      </div>
      
      <template id="myBtn">
        <section>
          <button>sonBtn1</button>
          <button @click="sonClick">sonBtn</button>
        </section>
      </template>
    
      <!-- Vue2 -->
      <script>
        let app = new Vue({
          el: "#app",
          data: {
            num: 100
          },
          methods: {
            fatherClick() {
              this.num++
            }
          },
          components: {
            "my-btn": {
              template: "#myBtn",
              methods: {
                sonClick() {
                  this.$emit("click");
                }
              }
            }
          }
        })
      </script>
    
      <!-- Vue3 -->
      <script>
        Vue.createApp({
          data() {
            return {
              num: 100
            }
          },
          methods: {
            fatherClick() {
              this.num++
            }
          },
          components: {
            "my-btn": {
              template: "#myBtn",
              emits: ['click'],
              methods: {
                sonClick() {
                  this.$emit("click");
                }
              }
            }
          }
        }).mount('#app')
      </script>
    </body>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
  • 涉及改动点

# 隔代组件传递参数 ProvideInject

# 动画 (opens new window)

  • 动画类名 leave-class 已经被重命名为 leave-from-class

  • 动画类名 enter-class 已经被重命名为 enter-from-class

  • transition-group 支持多个根结点

  • 案例展示

    <body>
      <style>
        .fade-enter-active,
        .fade-leave-active {
          transition: opacity 0.5s ease;
        }
    
        .fade-enter-from,
        .fade-leave-to {
          opacity: 0;
        }
      </style>
    
      <div id="app">
        <button @click="isLogin = !isLogin">Click</button>
        <transition name="fade" mode="out-in">
          <fieldset v-if="isLogin">
            <legend>Login</legend>
            <p>login...</p>
          </fieldset>
          <fieldset v-else>
            <legend>Regist</legend>
            <p>Regist...</p>
          </fieldset>
        </transition>
    
      </div>
    
      <script>
        Vue.createApp({
          data() {
            return {
              isLogin: true
            }
          }
        }).mount('#app')
      </script>
    </body>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
  • 涉及改动点

# 异步组件

  • 通过 defineAsyncComponent 显式声明异步组件

  • component 重命名为 loader

  • 案例展示

    <template>
      <div id="Index">
        <button @click="show = true">加载异步组件</button>
        <AsyncCom v-if="show"></AsyncCom>
      </div>
    </template>
    
    <script>
    import { defineAsyncComponent } from "vue";
    import LoadingComponent from "@/components/LoadingComponent.vue";
    import ErrorComponent from "@/components/LoadingComponent.vue";
    
    export default {
      data() {
        return {
          show: false,
        };
      },
      components: {
        AsyncCom: defineAsyncComponent({
          loader: () => import("@/components/AsyncCom.vue"),
          delay: 2000,
          timeout: 3000,
          loadingComponent: LoadingComponent,
          ErrorComponent: ErrorComponent,
        }),
      },
    };
    </script>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
  • 涉及改动点

# 自定义指令

# API 变动

# 生命周期事件