美文网首页
Vue2到Vue3的变化细节

Vue2到Vue3的变化细节

作者: 小宁子呀 | 来源:发表于2022-01-16 17:22 被阅读0次

    1. 异步组件(新增

    • 通过defineAsyncComponent方法显式地定义异步组件
    • component 选项被重命名为 loader
    • Loader 函数本身不再接收 resolve 和 reject 参数,且必须返回一个 Promise
    • 注意:VueRouter支持一个类似的机制来异步加载路由组件,也就是俗称的懒加载。尽管类似,但是这个功能和Vue支持的异步组件是不同的
    • 当用 Vue Router 配置路由组件时,你不应该使用 defineAsyncComponent
    • Vue2

    以前,异步组件是通过将组件定义为返回 Promise 的函数来创建的,例如:

    const asyncModal = () => import('./Modal.vue')
    

    或者,对于带有选项的更高阶的组件语法

    const asyncModal = {
      component: () => import('./Modal.vue'),
      delay: 200,
      timeout: 3000,
      error: ErrorComponent,
      loading: LoadingComponent
    }
    
    • Vue3

    现在,在 Vue 3 中,由于函数式组件被定义为纯函数,因此异步组件需要通过将其包裹在新的 defineAsyncComponent 助手方法中来显式地定义

    import { defineAsyncComponent } from 'vue'
    import ErrorComponent from './components/ErrorComponent.vue'
    import LoadingComponent from './components/LoadingComponent.vue'
    
    // 不带选项的异步组件
    const asyncModal = defineAsyncComponent(() => import('./Modal.vue'))
    
    // 带选项的异步组件
    const asyncModalWithOptions = defineAsyncComponent({
      loader: () => import('./Modal.vue'),
      delay: 200,
      timeout: 3000,
      errorComponent: ErrorComponent,
      loadingComponent: LoadingComponent
    })
    

    此外,与 2.x 不同,loader 函数不再接收 resolve 和 reject 参数,且必须始终返回 Promise

    // 2.x 版本
    const oldAsyncComponent = (resolve, reject) => {
      /* ... */
    }
    
    // 3.x 版本
    const asyncComponent = defineAsyncComponent(
      () =>
        new Promise((resolve, reject) => {
          /* ... */
        })
    )
    

    2. this.$children(移除

    • Vue2

    在 2.x 中,开发者可以使用 this.$children 访问当前实例的直接子组件:

    <template>
      <div>
        <img alt="Vue logo" src="./assets/logo.png">
        <my-button>Change logo</my-button>
      </div>
    </template>
    
    <script>
    import MyButton from './MyButton'
    
    export default {
      components: {
        MyButton
      },
      mounted() {
        console.log(this.$children) // [VueComponent]
      }
    }
    </script>
    
    • Vue3

    在 3.x 中,children property 已被移除,且不再支持。如果你需要访问子组件实例,我们建议使用refs

    3. 使用 vue: 前缀来解决 DOM 内模板解析问题(非兼容

    提示:本节仅影响直接在页面的 HTML 中写入 Vue 模板的情况。 在 DOM 模板中使用时,模板受原生 HTML 解析规则的约束。一些 HTML 元素,例如 ul、ol、table 和 select 对它们内部可以出现的元素有限制,以及一些像 li、tr、和 option 只能出现在特定的其他元素中

    • Vue2

    <!-- 在 Vue 2 中,我们建议在原生标签上使用 is attribute 来绕过这些限制: -->
    
    <table>
      <tr is="blog-post-row"></tr>
    </table>
    
    • Vue3

    <!-- 随着 is 的行为发生变化,现在将元素解析为 Vue 组件需要添加一个 vue: 前缀: -->
    
    <table>
      <tr is="vue:blog-post-row"></tr>
    </table>
    

    4. Data 选项(非兼容

    非兼容:组件选项 data 的声明不再接收纯 JavaScript object,而是接收一个 function。
    非兼容:当合并来自 mixin 或 extend 的多个 data 返回值时,合并操作现在是浅层次的而非深层次的 (只合并根级属性)。

    • Vue2

    在 2.x 中,开发者可以通过 object 或者是 function 定义 data 选项。

    <!-- 例如: -->
    
    <!-- Object 声明 -->
    <script>
      const app = new Vue({
        data: {
          apiKey: 'a1b2c3'
        }
      })
    </script>
    
    <!-- Function 声明 -->
    <script>
      const app = new Vue({
        data() {
          return {
            apiKey: 'a1b2c3'
          }
        }
      })
    </script>
    
    <!-- 虽然这种做法对于具有共享状态的根实例提供了一些便利,但是由于其只可能存在于根实例上,因此变得混乱。 -->
    
    • Vue3

    在 3.x 中,data 选项已标准化为只接受返回 object 的 function。
    使用上面的示例,代码只可能有一种实现:

    <script>
      import { createApp } from 'vue'
    
      createApp({
        data() {
          return {
            apiKey: 'a1b2c3'
          }
        }
      }).mount('#app')
    </script>
    

    5. emits选项(新增

    Vue 3 现在提供一个 emits 选项,和现有的 props 选项类似。这个选项可以用来定义一个组件可以向其父组件触发的事件
    强烈建议使用 emits 记录每个组件所触发的所有事件。这尤为重要,因为我们移除了 .native 修饰符。任何未在 emits 中声明的事件监听器都会被算入组件的 $attrs,并将默认绑定到组件的根节点上

    • Vue2

    在 Vue 2 中,你可以定义一个组件可接收的 prop,但是你无法声明它可以触发哪些事件:

    <template>
      <div>
        <p>{{ text }}</p>
        <button v-on:click="$emit('accepted')">OK</button>
      </div>
    </template>
    <script>
      export default {
        props: ['text']
      }
    </script>
    
    • Vue3

    和 prop 类似,现在可以通过 emits 选项来定义组件可触发的事件:

    <template>
      <div>
        <p>{{ text }}</p>
        <button v-on:click="$emit('accepted')">OK</button>
      </div>
    </template>
    <script>
      export default {
        props: ['text'],
        emits: ['accepted']
      }
    </script>
    
    <!-- 该选项也可以接收一个对象,该对象允许开发者定义传入事件参数的验证器,和 props 定义里的验证器类似。 -->
    

    6. 事件API(非兼容

    on,off 和 $once 实例方法已被移除,组件实例不再实现事件触发接口

    • Vue2

    在 2.x 中,Vue 实例可用于触发由事件触发器 API 通过指令式方式添加的处理函数 (on,off 和 $once)。这可以用于创建一个事件总线,以创建在整个应用中可用的全局事件监听器:

    // eventBus.js
    const eventBus = new Vue()
    export default eventBus
    
    // ChildComponent.vue
    import eventBus from './eventBus'
    export default {
      mounted() {
        // 添加 eventBus 监听器
        eventBus.$on('custom-event', () => {
          console.log('Custom event triggered!')
        })
      },
      beforeDestroy() {
        // 移除 eventBus 监听器
        eventBus.$off('custom-event')
      }
    }
    
    // ParentComponent.vue
    import eventBus from './eventBus'
    export default {
      methods: {
        callGlobalCustomEvent() {
          eventBus.$emit('custom-event') // 当 ChildComponent 已被挂载时,控制台中将显示一条消息
        }
      }
    }
    
    • Vue3

    根组件事件

    <!-- 静态的事件监听器可以通过 prop 的形式传递给 createApp 以添加到根组件中。 -->
    createApp(App, {
      // 监听 'expand' 事件
      onExpand() {
        console.log('expand')
      }
    })
    

    事件总线

    <!-- 事件总线模式可以被替换为使用外部的、实现了事件触发器接口的库,例如 mitt 或 tiny-emitter -->
    <!-- 它提供了与 Vue 2 相同的事件触发器 API -->
    // eventBus.js
    import emitter from 'tiny-emitter/instance'
    export default {
      $on: (...args) => emitter.on(...args),
      $once: (...args) => emitter.once(...args),
      $off: (...args) => emitter.off(...args),
      $emit: (...args) => emitter.emit(...args),
    }
    

    7. 过滤器(移除

    • Vue2

    在 2.x 中,开发者可以使用过滤器来处理通用文本格式,虽然这看起来很方便,但它需要一个自定义语法,打破了大括号内的表达式“只是 JavaScript”的假设,这不仅有学习成本,而且有实现成本

    <template>
      <h1>Bank Account Balance</h1>
      <p>{{ accountBalance | currencyUSD }}</p>
    </template>
    
    <script>
      export default {
        props: {
          accountBalance: {
            type: Number,
            required: true
          }
        },
        filters: {
          currencyUSD(value) {
            return '$' + value
          }
        }
      }
    </script>
    
    • Vue3

    在 3.x 中,过滤器已移除,且不再支持。取而代之的是,我们建议用方法调用或计算属性来替换它们。以上面的案例为例,以下是一种实现方式。

    <template>
      <h1>Bank Account Balance</h1>
      <p>{{ accountInUSD }}</p>
    </template>
    
    <script>
      export default {
        props: {
          accountBalance: {
            type: Number,
            required: true
          }
        },
        computed: {
          accountInUSD() {
            return '$' + this.accountBalance
          }
        }
      }
    </script>
    

    8. 片段(新增

    Vue 3 现在正式支持了多根节点的组件,也就是片段!

    • Vue2

    在 2.x 中,由于不支持多根节点组件,当其被开发者意外地创建时会发出警告。结果是,为了修复这个问题,许多组件被包裹在了一个 div 中。

    <template>
      <div>
        <header>...</header>
        <main>...</main>
        <footer>...</footer>
      </div>
    </template>
    
    • Vue3

    在 3.x 中,组件可以包含多个根节点!但是,这要求开发者显式定义 attribute 应该分布在哪里。

    <template>
      <header>...</header>
      <main v-bind="$attrs">...</main>
      <footer>...</footer>
    </template>
    

    9. propsData(移除

    propsData 选项之前用于在创建 Vue 实例的过程中传入 prop,现在它被移除了。如果想为 Vue 3 应用的根组件传入 prop,请使用 createApp 的第二个参数

    • Vue2

    在 2.x 中,我们可以在创建 Vue 实例的时候传入 prop:

    const Comp = Vue.extend({
      props: ['username'],
      template: '<div>{{ username }}</div>'
    })
    new Comp({
      propsData: {
        username: 'Evan'
      }
    })
    
    • Vue3

    propsData 选项已经被移除。如果你需要在实例创建时向根组件传入 prop,你应该使用 createApp 的第二个参数:

    const app = createApp(
      {
        props: ['username'],
        template: '<div>{{ username }}</div>'
      },
      { username: 'Evan' }
    )
    

    10. 在 prop 的默认函数中访问this(非兼容

    生成 prop 默认值的工厂函数不再能访问 this。
    取而代之的是:

    • 组件接收到的原始 prop 将作为参数传递给默认函数;
    • inject API 可以在默认函数中使用。
    import { inject } from 'vue'
    export default {
      props: {
        theme: {
          default (props) {
            // `props` 是传递给组件的、
            // 在任何类型/默认强制转换之前的原始值,
            // 也可以使用 `inject` 来访问注入的 property
            return inject('theme', 'default-theme')
          }
        }
      }
    }
    

    11. 渲染函数API(非兼容

    此更改不会影响 template 用户。
    以下是更改的简要总结:

    • h 现在是全局导入,而不是作为参数传递给渲染函数
    • 更改渲染函数参数,使其在有状态组件和函数组件的表现更加一致
    • VNode 现在有一个扁平的 prop 结构
    • Vue2

    <!-- 渲染函数参数 -->
    
    <!-- 在 2.x 中,render 函数会自动接收 h 函数 (它是 createElement 的惯用别名) 作为参数: -->
    export default {
      render(h) {
        return h('div')
      }
    }
    
    <!-- 渲染函数签名更改 -->
    
    <!-- 在 2.x 中,render 函数自动接收参数,如 h 函数。 -->
    export default {
      render(h) {
        return h('div')
      }
    }
    
    <!-- VNode Prop 格式化 -->
    
    <!-- 在 2.x 中,domProps 包含 VNode prop 中的嵌套列表: -->
    {
      staticClass: 'button',
      class: { 'is-outlined': isOutlined },
      staticStyle: { color: '#34495E' },
      style: { backgroundColor: buttonColor },
      attrs: { id: 'submit' },
      domProps: { innerHTML: '' },
      on: { click: submitForm },
      key: 'submit-button'
    }
    
    <!-- 注册组件 -->
    
    <!-- 在 2.x 中,注册一个组件后,把组件名作为字符串传递给渲染函数的第一个参数,它可以正常地工作: -->
    Vue.component('button-counter', {
      data() {
        return {
          count: 0
        }
      },
      template: `
        <button @click="count++">
          Clicked {{ count }} times.
        </button>
      `
    })
    export default {
      render(h) {
        return h('button-counter')
      }
    }
    
    • Vue3

    <!-- 渲染函数参数 -->
    
    <!-- 在 3.x 中,h 函数现在是全局导入的,而不是作为参数自动传递。 -->
    import { h } from 'vue'
    export default {
      render() {
        return h('div')
      }
    }
    
    <!-- 渲染函数签名更改 -->
    
    <!-- 在 3.x 中,由于 render 函数不再接收任何参数,它将主要在 setup() 函数内部使用。这还有一个好处:可以访问在作用域中声明的响应式状态和函数,以及传递给 setup() 的参数。 -->
    import { h, reactive } from 'vue'
    export default {
      setup(props, { slots, attrs, emit }) {
        const state = reactive({
          count: 0
        })
    
        function increment() {
          state.count++
        }
    
        // 返回渲染函数
        return () =>
          h(
            'div',
            {
              onClick: increment
            },
            state.count
          )
      }
    }
    
    <!-- VNode Prop 格式化 -->
    
    <!-- 在 3.x 中,整个 VNode prop 的结构都是扁平的。使用上面的例子,来看看它现在的样子。 -->
    {
      class: ['button', { 'is-outlined': isOutlined }],
      style: [{ color: '#34495E' }, { backgroundColor: buttonColor }],
      id: 'submit',
      innerHTML: '',
      onClick: submitForm,
      key: 'submit-button'
    }
    
    <!-- 注册组件 -->
    
    <!-- 在 3.x 中,由于 VNode 是上下文无关的,不能再用字符串 ID 隐式查找已注册组件。取而代之的是,需要使用一个导入的 resolveComponent 方法: -->
    import { h, resolveComponent } from 'vue'
    export default {
      setup() {
        const ButtonCounter = resolveComponent('button-counter')
        return () => h(ButtonCounter)
      }
    }
    
    

    12. 插槽统一(非兼容

    此更改统一了 3.x 中的普通插槽和作用域插槽。
    以下是变化的变更总结:

    • this.$slots 现在将插槽作为函数公开
    • 非兼容:移除 this.$scopedSlots
    • Vue2

    <!-- 当使用渲染函数,即 h 时,2.x 曾经在内容节点上定义 slot 数据 property。 -->
    // 2.x 语法
    
    h(LayoutComponent, [
      h('div', { slot: 'header' }, this.header),
      h('div', { slot: 'content' }, this.content)
    ])
    
    <!-- 此外,可以使用以下语法引用作用域插槽: -->
    // 2.x 语法
    
    this.$scopedSlots.header
    
    • Vue3

    <!-- 在 3.x 中,插槽以对象的形式定义为当前节点的子节点: -->
    // 3.x Syntax
    
    h(LayoutComponent, {}, {
      header: () => h('div', this.header),
      content: () => h('div', this.content)
    })
    
    <!-- 当你需要以编程方式引用作用域插槽时,它们现在被统一到 $slots 选项中了。 -->
    // 2.x 语法
    
    this.$scopedSlots.header
    
    // 3.x 语法
    
    this.$slots.header()
    

    13. 过渡的class名更改(非兼容

    过渡类名 v-enter 修改为 v-enter-from、过渡类名 v-leave 修改为 v-leave-from。

    • Vue2

    <!-- 在 v2.1.8 版本之前,每个过渡方向都有两个过渡类:初始状态与激活状态。 -->
    <!-- 在 v2.1.8 版本中,引入了 v-enter-to 来定义 enter 或 leave 变换之间的过渡动画插帧。然而,为了向下兼容,并没有变动 v-enter 类名: -->
    
    .v-enter,
    .v-leave-to {
      opacity: 0;
    }
    
    .v-leave,
    .v-enter-to {
      opacity: 1;
    }
    
    <!-- 这样做会带来很多困惑,类似 enter 和 leave 含义过于宽泛,并且没有遵循类名钩子的命名约定。 -->
    
    • Vue3

    <!-- 为了更加明确易读,我们现在将这些初始状态重命名为: -->
    
    .v-enter-from,
    .v-leave-to {
      opacity: 0;
    }
    
    .v-leave-from,
    .v-enter-to {
      opacity: 1;
    }
    
    <!-- 现在,这些状态之间的区别就清晰多了。 -->
    

    14. Transition 作为根节点(非兼容

    当使用 <transition> 作为根结点的组件从外部被切换时将不再触发过渡效果

    • Vue2

    <!-- 在 Vue 2 中,通过使用 <transition> 作为一个组件的根节点,过渡效果存在从组件外部触发的可能性: -->
    <!-- 模态组件 -->
    <template>
      <transition>
        <div class="modal"><slot/></div>
      </transition>
    </template>
    <!-- 用法 -->
    <modal v-if="showModal">hello</modal>
    <!-- 切换 showModal 的值将会在模态组件内部触发一个过渡效果。
    这是无意为之的,并不是设计效果。一个 <transition> 原本是希望被其子元素触发的,而不是 <transition> 自身。
    这个怪异的现象现在被移除了。 -->
    
    • Vue3

    <!-- 换做向组件传递一个 prop 就可以达到类似的效果: -->
    
    <template>
      <transition>
        <div v-if="show" class="modal"><slot/></div>
      </transition>
    </template>
    <script>
    export default {
      props: ['show']
    }
    </script>
    <!-- 用法 -->
    <modal :show="showModal">hello</modal>
    

    15. Transition Group 根元素(非兼容

    <transition-group> 不再默认渲染根元素,但仍然可以用 tag attribute 创建根元素

    • Vue2

    <!-- 在 Vue 2 中,<transition-group> 像其它自定义组件一样,需要一个根元素。默认的根元素是一个 <span>,但可以通过 tag attribute 定制。 -->
    
    <transition-group tag="ul">
      <li v-for="item in items" :key="item">
        {{ item }}
      </li>
    </transition-group>
    
    • Vue3

    <!-- 在 Vue 3 中,我们有了片段的支持,因此组件不再需要根节点。所以,<transition-group> 不再默认渲染根节点。 -->
    <!-- 如果像上面的示例一样,已经在 Vue 2 代码中定义了 tag attribute,那么一切都会和之前一样 -->
    <!-- 如果没有定义 tag attribute,而且样式或其它行为依赖于 <span> 根元素的存在才能正常工作,那么只需将 tag="span" 添加到 <transition-group>: -->
    
    <transition-group tag="span">
      <!-- -->
    </transition-group>
    

    16. 移除v-on.native修饰符(非兼容

    v-on 的 .native 修饰符已被移除

    • Vue2

    <!-- 默认情况下,传递给带有 v-on 的组件的事件监听器只能通过 this.$emit 触发。要将原生 DOM 监听器添加到子组件的根元素中,可以使用 .native 修饰符: -->
    
    <my-component
      v-on:close="handleComponentEvent"
      v-on:click.native="handleNativeClickEvent"
    />
    
    • Vue3

    <!-- v-on 的 .native 修饰符已被移除。同时,新增的 emits 选项允许子组件定义真正会被触发的事件。 -->
    
    <!-- 因此,对于子组件中未被定义为组件触发的所有事件监听器,Vue 现在将把它们作为原生事件监听器添加到子组件的根元素中 (除非在子组件的选项中设置了 inheritAttrs: false)。 -->
    
    <my-component
      v-on:close="handleComponentEvent"
      v-on:click="handleNativeClickEvent"
    />
    
    // MyComponent.vue
    
    <script>
      export default {
        emits: ['close']
      }
    </script>
    

    17. v-model(非兼容

    以下是对变化的总体概述:

    • 非兼容:用于自定义组件时,v-model prop 和事件默认名称已更改:
      • prop:value -> modelValue;
      • 事件:input -> update:modelValue;
    • 非兼容:v-bind 的 .sync 修饰符和组件的 model 选项已移除,可在 v-model 上加一个参数代替;
    • 新增:现在可以在同一个组件上使用多个 v-model 绑定;
    • 新增:现在可以自定义 v-model 修饰符。

    在 Vue 2.0 发布后,开发者使用 v-model 指令时必须使用名为 value 的 prop。如果开发者出于不同的目的需要使用其他的 prop,他们就不得不使用 v-bind.sync。此外,由于v-model 和 value 之间的这种硬编码关系的原因,产生了如何处理原生元素和自定义元素的问题。
    在 Vue 2.2 中,我们引入了 model 组件选项,允许组件自定义用于 v-model 的 prop 和事件。但是,这仍然只允许在组件上使用一个 v-model。
    在 Vue 3 中,双向数据绑定的 API 已经标准化,以减少开发者在使用 v-model 指令时的混淆,并且更加灵活。

    • Vue2

    在 2.x 中,在组件上使用 v-model 相当于绑定 value prop 并触发 input 事件:

    <ChildComponent v-model="pageTitle" />
    
    <!-- 是以下的简写: -->
    
    <ChildComponent :value="pageTitle" @input="pageTitle = $event" />
    

    如果想要更改 prop 或事件名称,则需要在 ChildComponent 组件中添加 model 选项:

    <!-- ParentComponent.vue -->
    
    <ChildComponent v-model="pageTitle" />
    
    // ChildComponent.vue
    
    export default {
      model: {
        prop: 'title',
        event: 'change'
      },
      props: {
        // 这将允许 `value` 属性用于其他用途
        value: String,
        // 使用 `title` 代替 `value` 作为 model 的 prop
        title: {
          type: String,
          default: 'Default title'
        }
      }
    }
    

    所以,在这个例子中 v-model 是以下的简写:

    <ChildComponent :title="pageTitle" @change="pageTitle = $event" />
    

    使用 v-bind.sync
    在某些情况下,我们可能需要对某一个 prop 进行“双向绑定”(除了前面用 v-model 绑定 prop 的情况)。为此,我们建议使用 update:myPropName 抛出事件。例如,对于在上一个示例中带有 title prop 的 ChildComponent,我们可以通过下面的方式将分配新 value 的意图传达给父级:

    this.$emit('update:title', newValue)
    

    然后父组件可以在需要时监听该事件,并更新本地的 data property。例如:

    <ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />
    

    为了方便起见,我们可以使用 .sync 修饰符来缩写,如下所示:

    <ChildComponent :title.sync="pageTitle" />
    
    • Vue3

    在 3.x 中,自定义组件上的 v-model 相当于传递了 modelValue prop 并接收抛出的 update:modelValue 事件:

    <ChildComponent v-model="pageTitle" />
    
    <!-- 是以下的简写: -->
    
    <ChildComponent
      :modelValue="pageTitle"
      @update:modelValue="pageTitle = $event"
    />
    

    v-model 参数
    若需要更改 model 的名称,现在我们可以为 v-model 传递一个参数,以作为组件内 model 选项的替代:

    <ChildComponent v-model:title="pageTitle" />
    
    <!-- 是以下的简写: -->
    
    <ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />
    

    这也可以作为 .sync 修饰符的替代,而且允许我们在自定义组件上使用多个 v-model。

    <ChildComponent v-model:title="pageTitle" v-model:content="pageContent" />
    
    <!-- 是以下的简写: -->
    
    <ChildComponent
      :title="pageTitle"
      @update:title="pageTitle = $event"
      :content="pageContent"
      @update:content="pageContent = $event"
    />
    

    v-model 修饰符
    除了像 .trim 这样的 2.x 硬编码的 v-model 修饰符外,现在 3.x 还支持自定义修饰符:

    <ChildComponent v-model.capitalize="pageTitle" />
    

    18. v-if 与 v-for 的优先级对比(非兼容

    非兼容:两者作用于同一个元素上时,v-if 会拥有比 v-for 更高的优先级。
    Vue.js 中使用最多的两个指令就是 v-if 和 v-for,因此开发者们可能会想要同时使用它们。虽然不建议这样做,但有时确实是必须的,于是我们想提供有关其工作方式的指南。

    • Vue2

    2.x 版本中在一个元素上同时使用 v-if 和 v-for 时,v-for 会优先作用

    • Vue3

    3.x 版本中 v-if 总是优先于 v-for 生效

    19. v-bind 合并行为(非兼容

    不兼容:v-bind 的绑定顺序会影响渲染结果
    在一个元素上动态绑定 attribute 时,同时使用 v-bind="object" 语法和独立 attribute 是常见的场景。然而,这就引出了关于合并的优先级的问题

    • Vue2

    在 2.x 中,如果一个元素同时定义了 v-bind="object" 和一个相同的独立 attribute,那么这个独立 attribute 总是会覆盖 object 中的绑定。

    <!-- 模板 -->
    <div id="red" v-bind="{ id: 'blue' }"></div>
    <!-- 结果 -->
    <div id="red"></div>
    
    • Vue3

    在 3.x 中,如果一个元素同时定义了 v-bind="object" 和一个相同的独立 attribute,那么绑定的声明顺序将决定它们如何被合并。换句话说,相对于假设开发者总是希望独立 attribute 覆盖 object 中定义的内容,现在开发者能够对自己所希望的合并行为做更好的控制。

    <!-- 模板 -->
    <div id="red" v-bind="{ id: 'blue' }"></div>
    <!-- 结果 -->
    <div id="blue"></div>
    
    <!-- 模板 -->
    <div v-bind="{ id: 'blue' }" id="red"></div>
    <!-- 结果 -->
    <div id="red"></div>
    

    20. VNode 生命周期事件(非兼容

    在 Vue 2 中,我们可以通过事件来监听组件生命周期中的关键阶段。这些事件名都是以 hook: 前缀开头,并跟随相应的生命周期钩子的名字。
    在 Vue 3 中,这个前缀已被更改为 vnode-。额外地,这些事件现在也可用于 HTML 元素,和在组件上的用法一样

    • Vue2

    在 Vue 2 中,这些事件名和相应的生命周期钩子一致,并带有 hook: 前缀:

    <template>
      <child-component @hook:updated="onUpdated">
    </template>
    
    • Vue3

    在 Vue 3 中,事件名附带的是 vnode- 前缀:

    <template>
      <child-component @vnode-updated="onUpdated">
    </template>
    

    21. 侦听数组(非兼容

    非兼容: 当侦听一个数组时,只有当数组被替换时才会触发回调。如果你需要在数组被改变时触发回调,必须指定 deep 选项

    • Vue2

    • Vue3

    当使用 watch 选项侦听数组时,只有在数组被替换时才会触发回调。换句话说,在数组被改变时侦听回调将不再被触发。要想在数组被改变时触发侦听回调,必须指定 deep 选项。

    watch: {
      bookList: {
        handler(val, oldVal) {
          console.log('book list changed')
        },
        deep: true
      },
    }
    

    相关文章

      网友评论

          本文标题:Vue2到Vue3的变化细节

          本文链接:https://www.haomeiwen.com/subject/rngchrtx.html