最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 是时候从vue2 迁移到 vue3了 (系列二)

    正文概述 掘金(小郭在深圳)   2021-05-01   477

    是时候从vue2 迁移到 vue3了 (系列二)

    上一篇文章:是时候从vue2 迁移到 vue3了 (系列一)

    16.内联模板 Attribute 非兼容

    变化概览:

    • 对内联特性的支持已被移除。

    2.x 语法

    在 2.x 中,Vue 为子组件提供了 inline-template attribute,以便将其内部内容用作模板,而不是将其作为分发内容。

    <my-component inline-template>
      <div>
        <p>它们被编译为组件自己的模板</p>
        <p>不是父级所包含的内容。</p>
      </div>
    </my-component>
    

    3.x 语法

    将不再支持此功能。

    迁移策略

    inline-template 的大多数用例都假设没有构建工具设置,所有模板都直接写在 HTML 页面中。

    选项1:使用 <script> 标签

    在这种情况下,最简单的解决方法是将

    <script type="text/html" id="my-comp-template">
      <div>{{ hello }}</div>
    </script>
    

    在组件中,使用选择器指向目标模板:

    const MyComp = {
      template: '#my-comp-template'
      // ...
    }
    

    这不需要任何构建设置,可以在所有浏览器中工作,不受任何内部 DOM HTML 解析警告的约束 (例如,你可以使用 camelCase prop 名称),并且在大多数 ide 中提供了正确的语法高亮显示。在传统的服务器端框架中,可以将这些模板拆分为服务器模板部分 (包括在主 HTML 模板中),以获得更好的可维护性。

    选项2:默认插槽

    以前使用 inline-template 的组件也可以使用默认插槽——进行重构,这使得数据范围更加明确,同时保留了内联编写子内容的便利性:

    <!-- 2.x 语法 -->
    <my-comp inline-template :msg="parentMsg">
      {{ msg }} {{ childState }}
    </my-comp>
    
    <!-- 默认 Slot 版本 -->
    <my-comp v-slot="{ childState }">
      {{ parentMsg }} {{ childState }}
    </my-comp>
    

    子级现在应该渲染默认 slot*,而不是提供的空模板:

    <!--
      在子模板中,在传递时渲染默认插槽
      在必要的 private 状态下。
    -->
    <template>
      <slot :childState="childState" />
    </template>
    

    17.key attribute 非兼容

    变化概览:

    • 新增:对于 v-if/v-else/v-else-if 的各分支项 key 将不再是必须的,因为现在 Vue 会自动生成唯一的 key。
      • 非兼容:如果你手动提供 key,那么每个分支必须使用唯一的 key。你不能通过故意使用相同的 key 来强制重用分支。
    • 非兼容:<template v-for> 的 key 应该设置在 <template> 标签上 (而不是设置在它的子节点上)。

    背景

    特殊的 key attribute 被用于提示 Vue 的虚拟 DOM 算法来保持对节点身份的持续跟踪。这样 Vue 可以知道何时能够重用和修补现有节点,以及何时需要对它们重新排序或重新创建。

    在条件分支中

    Vue 2.x 建议在 v-if/v-else/v-else-if 的分支中使用 key。

    <!-- Vue 2.x -->
    <div v-if="condition" key="yes">Yes</div>
    <div v-else key="no">No</div>
    

    这个示例在 Vue 3.x 中仍能正常工作。但是我们不再建议在 v-if/v-else/v-else-if 的分支中继续使用 key attribute,因为没有为条件分支提供 key 时,也会自动生成唯一的 key。

    <!-- Vue 3.x -->
    <div v-if="condition">Yes</div>
    <div v-else>No</div>
    

    非兼容变更体现在如果你手动提供了 key,那么每个分支都必须使用一个唯一的 key。因此大多数情况下都不需要设置这些 key。

    <!-- Vue 2.x -->
    <div v-if="condition" key="a">Yes</div>
    <div v-else key="a">No</div>
    
    <!-- Vue 3.x (recommended solution: remove keys) -->
    <div v-if="condition">Yes</div>
    <div v-else>No</div>
    
    <!-- Vue 3.x (alternate solution: make sure the keys are always unique) -->
    <div v-if="condition" key="a">Yes</div>
    <div v-else key="b">No</div>
    

    结合 <template v-for>

    在 Vue 2.x 中 <template> 标签不能拥有 key。不过你可以为其每个子节点分别设置 key。

    <!-- Vue 2.x -->
    <template v-for="item in list">
      <div :key="item.id">...</div>
      <span :key="item.id">...</span>
    </template>
    

    在 Vue 3.x 中 key 则应该被设置在 <template> 标签上。

    <!-- Vue 3.x -->
    <template v-for="item in list" :key="item.id">
      <div>...</div>
      <span>...</span>
    </template>
    

    类似地,当使用 <template v-for> 时存在使用 v-if 的子节点,key 应改为设置在 <template> 标签上。

    <!-- Vue 2.x -->
    <template v-for="item in list">
      <div v-if="item.isVisible" :key="item.id">...</div>
      <span v-else :key="item.id">...</span>
    </template>
    
    <!-- Vue 3.x -->
    <template v-for="item in list" :key="item.id">
      <div v-if="item.isVisible">...</div>
      <span v-else>...</span>
    </template>
    

    18.按键修饰符 非兼容

    变化概览:

    • 非兼容:不再支持使用数字 (即键码) 作为 v-on 修饰符
    • 非兼容:不再支持 config.keyCodes

    2.x 语法

    在 Vue 2 中,支持 keyCodes 作为修改 v-on 方法的方法。

    <!-- 键码版本 -->
    <input v-on:keyup.13="submit" />
    
    <!-- 别名版本 -->
    <input v-on:keyup.enter="submit" />
    

    此外,你也可以通过全局 config.keyCodes 选项。

    Vue.config.keyCodes = {
      f1: 112
    }
    
    <!-- 键码版本 -->
    <input v-on:keyup.112="showHelpText" />
    
    <!-- 自定别名版本 -->
    <input v-on:keyup.f1="showHelpText" />
    

    3.x 语法

    从KeyboardEvent.keyCode has been deprecated 开始,Vue 3 继续支持这一点就不再有意义了。因此,现在建议对任何要用作修饰符的键使用 kebab-cased (短横线) 大小写名称。

    <!-- Vue 3 在 v-on 上使用 按键修饰符 -->
    <input v-on:keyup.delete="confirmDelete" />
    

    因此,这意味着 config.keyCodes 现在也已弃用,不再受支持。

    19.移除 $listeners 非兼容

    变化概览:

    • $listeners 对象在 Vue 3 中已被移除。现在事件监听器是 $attrs 的一部分:
    {
      text: 'this is an attribute',
      onClose: () => console.log('close Event triggered')
    }
    

    2.x 语法

    在 Vue 2 中,你可以使用 this.$attrs 和 this.$listeners 分别访问传递给组件的 attribute 和事件监听器。结合 inheritAttrs: false,开发者可以将这些 attribute 和监听器应用到其它元素,而不是根元素:

    <template>
      <label>
        <input type="text" v-bind="$attrs" v-on="$listeners" />
      </label>
    </template>
    <script>
      export default {
        inheritAttrs: false
      }
    </script>
    

    3.x 语法

    在 Vue 3 的虚拟 DOM 中,事件监听器现在只是以 on 为前缀的 attribute,这样就成了 $attrs 对象的一部分,因此 $listeners 被移除了。

    <template>
      <label>
        <input type="text" v-bind="$attrs" />
      </label>
    </template>
    <script>
    export default {
      inheritAttrs: false
    }
    </script>
    

    如果这个组件接收一个 id attribute 和一个 v-on:close 监听器,那么 $attrs 对象现在将如下所示:

    {
      id: 'my-input',
      onClose: () => console.log('close Event triggered')
    }
    

    20.在 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')
          }
        }
      }
    }
    

    21. 渲染函数 API 非兼容

    变化概览:

    • 此更改不会影响 <template> 用户。

    • 以下是更改的简要总结:

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

    渲染函数参数

    2.x 语法

    在 2.x 中,render 函数会自动接收 h 函数 (它是 createElement 的传统别名) 作为参数:

    // Vue 2 渲染函数示例
    export default {
      render(h) {
        return h('div')
      }
    }
    

    3.x 语法

    在 3.x 中,h 现在是全局导入的,而不是作为参数自动传递。

    // Vue 3 渲染函数示例
    import { h } from 'vue'
    
    export default {
      render() {
        return h('div')
      }
    }
    

    渲染函数签名更改

    2.x 语法

    在 2.x 中,render 函数自动接收诸如 h 之类的参数。

    // Vue 2 渲染函数示例
    export default {
      render(h) {
        return h('div')
      }
    }
    

    3.x 语法

    在 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 格式化

    2.x 语法

    在 2.x 中,domProps 包含 VNode prop 中的嵌套列表:

    // 2.x
    {
      staticClass: 'button',
      class: { 'is-outlined': isOutlined },
      staticStyle: { color: '#34495E' },
      style: { backgroundColor: buttonColor },
      attrs: { id: 'submit' },
      domProps: { innerHTML: '' },
      on: { click: submitForm },
      key: 'submit-button'
    }
    

    3.x 语法

    在 3.x 中,整个 VNode prop 的结构都是扁平的。使用上面的例子,来看看它现在的样子。

    // 3.x 语法
    {
      class: ['button', { 'is-outlined': isOutlined }],
      style: [{ color: '#34495E' }, { backgroundColor: buttonColor }],
      id: 'submit',
      innerHTML: '',
      onClick: submitForm,
      key: 'submit-button'
    }
    

    注册组件

    2.x 语法

    在 2.x 中,注册一个组件后,把组件名作为字符串传给渲染函数的第一个参数,渲染函数会很好地工作:

    // 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')
      }
    }
    

    3.x 语法

    在 3.x 中,由于 VNode 是上下文无关的,不能再用字符串 ID 隐式查找已注册组件。相反地,需要使用一个导入的 resolveComponent 方法:

    // 3.x
    import { h, resolveComponent } from 'vue'
    
    export default {
      setup() {
        const ButtonCounter = resolveComponent('button-counter')
        return () => h(ButtonCounter)
      }
    }
    

    22.插槽统一 非兼容

    变化概览:

    • 此更改统一了 3.x 中的普通插槽和作用域插槽。
      • this.$slots 现在将插槽作为函数公开
      • 非兼容:移除 this.$scopedSlots

    2.x 语法

    当使用渲染函数时,即 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
    

    3.x 语法

    在 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()
    

    迁移策略

    大部分更改已经在 2.6 中发布。因此,迁移可以一步到位:

    • 在 3.x 中,将所有 this.$scopedSlots 替换为 this.$slots。
    • 将所有 this.$slots.mySlot 替换为 this.$slots.mySlot()。

    23. 过渡的 class 名更改 非兼容

    变化概览

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

    2.x 语法

    在 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;
    }
    

    3.x 语法

    为了更加明确易读,我们现在将这些初始状态重命名为:

    .v-enter-from,
    .v-leave-to {
      opacity: 0;
    }
    
    .v-leave-from,
    .v-enter-to {
      opacity: 1;
    }
    

    现在,这些状态之间的区别就清晰多了。

    <transition> 组件相关属性名也发生了变化:

    • leave-class 已经被重命名为 leave-from-class (在渲染函数或 JSX 中可以写为:leaveFromClass)
    • enter-class 已经被重命名为 enter-from-class (在渲染函数或 JSX 中可以写为:enterFromClass)

    24.Transition Group 根元素 非兼容

    变化概览:

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

    2.x 语法

    在 Vue 2 中,<transition-group> 像其它自定义组件一样,需要一个根元素。默认的根元素是一个 <span>,但可以通过 tag prop 定制。

    <transition-group tag="ul">
      <li v-for="item in items" :key="item">
        {{ item }}
      </li>
    </transition-group>
    

    3.x 语法

    在 Vue 3 中,我们有了片段的支持,因此组件不再需要根节点。所以,<transition-group> 默认不再渲染根节点。

    • 如果像上面的示例一样,已经在 Vue 2 代码中定义了 tag prop,那么一切都会和之前一样
    • 如果没有定义 tag prop,而且样式或其它行为依赖于 <span> 根元素的存在才能正常工作,那么只需将 tag="span" 添加到 <transition-group>:
    <transition-group tag="span">
      <!-- -->
    </transition-group>
    

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

    变化概览:

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

    2.x 语法

    默认情况下,传递给带有 v-on 的组件的事件监听器只有通过 this.$emit 才能触发。要将原生 DOM 监听器添加到子组件的根元素中,可以使用 .native 修饰符:

    <my-component
      v-on:close="handleComponentEvent"
      v-on:click.native="handleNativeClickEvent"
    />
    

    3.x 语法

    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>
    

    26.v-model 非兼容

    变化概览:

    • 非兼容:用于自定义组件时,v-model prop 和事件默认名称已更改:
      • prop:value -> modelValue;
      • event: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 和事件。但是,这仍然只允许在组件上使用一个 model。

    在 Vue 3 中,双向数据绑定的 API 已经标准化,减少了开发者在使用 v-model 指令时的混淆并且在使用 v-model 指令时可以更加灵活。

    2.x 语法

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

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

    如果要将属性或事件名称更改为其他名称,则需要在 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 : @change="pageTitle = $event" />
    
    

    使用 v-bind.sync

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

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

    如果需要的话,父级可以监听该事件并更新本地 data property。例如:

    <ChildComponent : @update: />
    
    

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

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

    3.x 语法

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

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

    v-model 参数

    若需要更改 model 名称,而不是更改组件内的 model 选项,那么现在我们可以将一个 argument 传递给 model:

    <ChildComponent v-model: />
    
    <!-- 是以下的简写: -->
    
    <ChildComponent : @update: />
    

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

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

    v-model 修饰符

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

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

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

    变化概览

    • 非兼容:两者作用于同一个元素上时,v-if 会拥有比 v-for 更高的优先级。

    介绍

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

    2.x 语法

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

    3.x 语法

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

    28.v-bind 合并行为 非兼容

    变化概览:

    • 不兼容:v-bind 的绑定顺序会影响渲染结果

    介绍

    在元素上动态绑定 attribute 时,常见的场景是在一个元素中同时使用 v-bind="object" 语法和单独的 property。然而,这就引出了关于合并的优先级的问题。

    2.x 语法

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

    <!-- template -->
    <div id="red" v-bind="{ id: 'blue' }"></div>
    <!-- result -->
    <div id="red"></div>
    

    3.x 语法

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

    <!-- template -->
    <div id="red" v-bind="{ id: 'blue' }"></div>
    <!-- result -->
    <div id="blue"></div>
    
    <!-- template -->
    <div v-bind="{ id: 'blue' }" id="red"></div>
    <!-- result -->
    <div id="red"></div>
    

    29.Watch on Arrays 非兼容

    变化概览:

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

    3.x 语法

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

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

    最后:

    本笔记主要基于官方文档 迁移策略 汇总而来。如有理解出入,请以官方文档为主。建议您以官方文档为主,本文为辅。这样您可以“以自己为主”审视的阅读,从而不被我的观点带偏

    分享下自己整理的部分知识点文章链接

    • 记录一下3月底到4月的前端开发工程师面经

    • 面试高频的手写JS代码

    • 某大厂面经

    • webpack全方位由浅到深讲解,做到简历上真正所谓的“熟悉”(系列一)

    • webpack全方位由浅到深讲解,做到简历上真正所谓的“熟悉”(系列二)

    • webpack全方位由浅到深讲解,做到简历上真正所谓的“熟悉”(系列三)

    • webpack全方位由浅到深讲解,做到简历上真正所谓的“熟悉”(系列四)

    • webpack全方位由浅到深讲解,做到简历上真正所谓的“熟悉”(系列五)


    起源地下载网 » 是时候从vue2 迁移到 vue3了 (系列二)

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元