Vue3学习补充<一>

181 阅读4分钟

异步组件

在大型应用中,我们可能需要将应用分割成小一些的代码块,并且只在需要的时候才从服务器加载一个模块。为了实现这个效果,Vue 有一个 defineAsyncComponent 方法:

import { defineAsyncComponent } from 'vue'const AsyncComp = defineAsyncComponent({
  // 工厂函数
  loader: () => import('./Foo.vue'),
  // 加载异步组件时要使用的组件
  loadingComponent: LoadingComponent,
  // 加载失败时要使用的组件
  errorComponent: ErrorComponent,
  // 在显示 loadingComponent 之前的延迟 | 默认值:200(单位 ms)
  delay: 200,
  // 如果提供了 timeout ,并且加载组件的时间超过了设定值,将显示错误组件
  // 默认值:Infinity(即永不超时,单位 ms)
  timeout: 3000,
  // 定义组件是否可挂起 | 默认值:true
  suspensible: false,
  /**
   *
   * @param {*} error 错误信息对象
   * @param {*} retry 一个函数,用于指示当 promise 加载器 reject 时,加载器是否应该重试
   * @param {*} fail  一个函数,指示加载程序结束退出
   * @param {*} attempts 允许的最大重试次数
   */
  onError(error, retry, fail, attempts) {
    if (error.message.match(/fetch/) && attempts <= 3) {
      // 请求发生错误时重试,最多可尝试 3 次
      retry()
    } else {
      // 注意,retry/fail 就像 promise 的 resolve/reject 一样:
      // 必须调用其中一个才能继续错误处理。
      fail()
    }
  }
})

变化概览:

  • 使用defineAsyncComponent方法显示的定义异步组件
  • component选项被重命名为loader
  • Loader函数本身不再接收resolvereject参数,且必须返回一个Promise

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

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

带有选项的更高阶的组件语法

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

3.x语法

在Vue3中,由于函数式组件被定义为纯函数,因此异步组件需要将其包裹在新的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
})

Vue Router 支持一个类似的机制来异步加载路由组件,也就是俗称的懒加载。尽管类似,但是这个功能和 Vue 所支持的异步组件是不同的。当用 Vue Router 配置路由组件时,你应该使用 defineAsyncComponent

$attrs

变化概览:

$attrs现在包含了所有传递给组件地attribute,包括classstyle

2.x行为

Vue 2 的虚拟 DOM 实现对 classstyle attribute 有一些特殊处理。因此,与其它所有 attribute 不一样,它们没有被包含在 $attrs 中。

  • $attrs 中的 attribute 将不再被自动添加到根元素中,而是由开发者决定在哪添加。
  • 但是 classstyle 不属于 $attrs,它们仍然会被应用到组件的根元素中:
<template>
  <label>
    <input type="text" v-bind="$attrs" />
  </label>
</template>
<script>
export default {
  inheritAttrs: false
}
</script>

像这样使用时:

<my-component id="my-id" class="my-class"></my-component>

……将生成以下 HTML:

<label class="my-class">
  <input type="text" id="my-id" />
</label>

3.x行为

$attrs 包含了所有的 attribute,这使得把它们全部应用到另一个元素上变得更加容易了。现在上面的示例将生成以下 HTML:

<label>
  <input type="text" id="my-id" class="my-class" />
</label>

$children

变化概览

$children 实例 property 已从 Vue 3.0 中移除,不再支持。

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

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

自定义指令

指令的钩子函数已经被重命名,以更好地与组件的生命周期保持一致

2.x语法

在 Vue 2 中,自定义指令通过使用下列钩子来创建,以对齐元素的生命周期,它们都是可选的:

  • bind - 指令绑定到元素后调用。只调用一次。
  • inserted - 元素插入父 DOM 后调用。
  • update - 当元素更新,但子元素尚未更新时,将调用此钩子。
  • componentUpdated - 一旦组件和子级被更新,就会调用这个钩子。
  • unbind - 一旦指令被移除,就会调用这个钩子。也只调用一次。

3.x语法

然而,在 Vue 3 中,我们为自定义指令创建了一个更具凝聚力的 API。正如你所看到的,它们与我们的组件生命周期方法有很大的不同,即使钩子的目标事件十分相似。我们现在把它们统一起来了:

  • created - 新增!在元素的 attribute 或事件监听器被应用之前调用。
  • bind → beforeMount
  • inserted → mounted
  • beforeUpdate:新增!在元素本身被更新之前调用,与组件的生命周期钩子十分相似。
  • update → 移除!该钩子与 updated 有太多相似之处,因此它是多余的。请改用 updated
  • componentUpdated → updated
  • beforeUnmount:新增!与组件的生命周期钩子类似,它将在元素被卸载之前调用。
  • unbind -> unmounted

Mixin合并行为变更

const Mixin = {
  data() {
    return {
      user: {
        name: 'Jack',
        id: 1
      }
    }
  }
}
​
const CompA = {
  mixins: [Mixin],
  data() {
    return {
      user: {
        id: 2
      }
    }
  }
}

在 Vue 2.x 中,生成的 $data 是:

{
  "user": {
    "id": 2,
    "name": "Jack"
  }
}

在 3.0 中,其结果将会是:

{
  "user": {
    "id": 2
  }
}

emits选项

Vue 3 现在提供一个 emits 选项,和现有的 props 选项类似。这个选项可以用来定义一个组件可以向其父组件触发的事件。

同2.x一样,在父组件注册事件,在3.x中将父组件注册的事件通过emits来接收,在setup函数的参数中引入{ emit },通过emit('methods-name')触发