3
作者:Apoorv Tyagi
译者:前端小智
来源:dev

有梦想,有干货,微信搜索 【大迁世界】 关注这个在凌晨还在刷碗的刷碗智。

本文 GitHub https://github.com/qq449245884/xiaozhi 已收录,有一线大厂面试完整考点、资料以及我的系列文章。

使用 Vue3 的 DefileAsyncComponent功能可让我们懒加载组件,说白了就是创建一个只有在需要时才会加载的异步组件。

这是改进初始页面加载的好方法,因为我们的应用程序将加载到较小的块中而不是必须在页面加载时加载每个组件。

image.png

在本文中,我们将学习有关defineAsyncComponent的所有知识,并学习一个懒加载弹出窗口的例子。

defineAsyncComponent 是啥?

const AsyncComp = defineAsyncComponent(
  () =>
    new Promise((resolve, reject) => {
      resolve({
        template: '<div>I am async!</div>'
      })
    })
)

defineAsyncComponent 可以接受一个返回 Promise 的工厂函数。Promise 的 resolve 回调应该在服务端返回组件定义后被调用。你也可以调用 reject(reason) 来表示加载失败。

defineAsyncComponent 可以从 vue 中导入,并使用:

import { defineAsyncComponent } from "vue" 

// simple usage 
const LoginPopup = defineAsyncComponent(() => import("./components/LoginPopup.vue"))

这是defineAsyncComponent的最简单方法,对于高阶用法,defineAsyncComponent 可以接受一个对象:

const AsyncPopup = defineAsyncComponent({ 
  loader: () => import("./LoginPopup.vue"),
   // 加载异步组件时要使用的组件
  loadingComponent: LoadingComponent,
   // 加载失败时要使用的组件
  errorComponent: ErrorComponent, 
  // 在显示 loadingComponent 之前的延迟 | 默认值:200(单位 ms)
  delay: 1000, 
  // 如果提供了 timeout ,并且加载组件的时间超过了设定值,将显示错误组件
  // 默认值:Infinity(即永不超时,单位 ms)
  timeout: 3000 
})

基础已经介绍完了,接着,我们来做个例子。

使用 defineAsyncComponent 异步加载 Popup 组件

在这个例子中,我们将使用一个由点击按钮触发的登录弹框。

我们不需要我们的应用程序在每次加载时都加载这个组件,因为只有在用户执行特定的动作时才需要它。

下面是 login 组件的实现:

// LoginPopup.vue
<template>
   <div class="popup">
       <div class="content">
           <h4> Login to your account </h4>
           <input type="text" placeholder="Email" />
           <input type="password" placeholder="Password" />
           <button> Log in </button>
       </div>
   </div>
</template>

<script>
</script>

<style scoped>
.popup {
    position: fixed;
    width: 100%;
    top: ; 
    left: ;
    height: 100%;
    background-color: rgba(, , , 0.2);
    display: flex;
    justify-content: center;
    align-items: center;
}
.content {
   min-width: 200px;
   width: 30%;
   background: #fff;
   height: 200px;
   padding: 10px;
   border-radius: 5px;
}
input[type="text"], input[type="password"] {
    border: ;
    outline: ;
    border-bottom: 1px solid #eee;
    width: 80%;
    margin:  auto;
    font-size: 0.5em;
}
button {
   border: ;
   margin-top: 50px;
   background-color:#8e44ad;
   color: #fff;
   padding: 5px 10px;
   font-size: 0.5em;
}
</style>

image.png

在其它组件中导入它:

<template>
  <button @click="show = true"> Login </button>
  <login-popup v-if="show" />
</template>

<script>
import LoginPopup from './components/LoginPopup.vue'
export default {
  components: { LoginPopup },
  data() {
    return {
      show: false
    }
  }
}
</script>

我们可以使用 defineAsyncComponent,只在需要的时候加载它(按钮被点击时使用v-if来切换)。

<!-- Use defineAsyncComponent  -->
<template>
  <button @click="show = true"> Login </button>
  <login-popup v-if="show" />
</template>

<script>
import { defineAsyncComponent } from 'vue'
export default {
  components: { 
    "LoginPopup" : defineAsyncComponent(() => import('./components/LoginPopup.vue'))
  },
  data() {
    return {
      show: false
    }
  }
}
</script>

这个用法看起来和上面的差不多,不急,我们 F12 打开控制台。

如果我们不使用defineAsyncComponent,一旦我们的页面加载,我们就会看到我们的应用程序从服务器上获得LoginPopup.vue。虽然在这个例子中,性能问题不那么严重,但如果我们有几十个组件这样做,性能上多多少少还是有影响的。

image.png

然而,如果我们使用defineAsyncComponent查看同一个标签,会注意到,当我们的页面加载时,LoginPopup.vue是没有的,这是因为它还没有被加载。

image.png

但,如果切换按钮,我们就可以看到它了:

image.png

这有助于我们实现最佳性能。我们只想在我们的页面初始加载时加载需要的组件。有条件渲染的组件在我们的页面加载时往往是不需要的,所以为什么要让我们的应用程序加载它们呢?

如何与异步的 setup 方法一起使用?

不管我们是否用defineAsyncComponent来异步加载,任何具有异步 setup 方法的组件都必须用<Suspense>来包装。

简而言之,创建一个异步 setup 函数是我们的一种选择,可以让我们的组件在渲染前等待一些API 调用或其他异步操作。

下面是带有异步setup的组件,使用setTimeout()模拟API调用

<template>
   <div class="popup">
       <div class="content">
            <p> Loaded API: {{ article }} </p>
           <h4> Login to your account </h4>
           <input type="text" placeholder="Email" />
           <input type="password" placeholder="Password" />
           <button> Log in </button>
       </div>
   </div>
</template>

<script>
const getArticleInfo = async () => {
     // wait 3 seconds to mimic API call
    await new Promise(resolve => setTimeout(resolve, 1000));
    const article = {
        title: 'My Vue 3 Article',
        author: 'Matt Maribojoc'
    }
    return article
}
export default {
    async setup() {
        const article = await getArticleInfo()
        console.log(article)
        return {
            article
        }
    }
}
</script>

我们可以使用或不使用defineAsyncComponent将它导入到组件中:

import LoginPopup from './components/LoginPopup.vue'

// OR 

const LoginPopup = defineAsyncComponent(() => import("./components/LoginPopup.vue"))

但如果我们想让它在我们的模板中渲染,我们需要把它包在一个Suspense元素中。这将等待我们的setup 解析后,然后再尝试渲染我们的组件。

Suspense的一个很好的特点是,我们可以使用槽和模板显示回退内容。回退内容将显示,直到setup函数解析,我们的组件准备好渲染。 请注意,v-if已经从组件本身移到了我们的Suspense 组件上,所以所有回退内容都会显示。

<template>
  <button @click="show = true"> Login </button>
  <Suspense v-if="show">
    <template #default>
      <login-popup  />
    </template>
    <template #fallback>
      <p> Loading... </p>
    </template>
  </Suspense>
</template>

下面是运行结果,会看到 "Loading…",然后在3秒后(setTimeout的硬编码值)组件渲染。

默认情况下,我们使用defineAsyncComponent定义的所有组件都是可Suspense的。

这意味着如果一个组件的父链中有Suspense,它将被视为该Suspense的一个异步依赖。我们的组件的加载、错误、延迟和超时选项将被忽略,而是由Suspense来处理。

总结

当构建包许多组件的大型项目时,defineAsyncComponent是非常有用的。当我们使用懒加载组件时,可以更快地加载页面,改善用户体验,最终提高应用的留存率和转换率。

我很想知道大家对这个特性的看法。如果你已经在你的应用中使用它了,记得下面留言分享一下哦。

~完,我是小智,今天准备去干点啥~


代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug

原文:https://learnvue.co/2021/06/l...

交流

有梦想,有干货,微信搜索 【大迁世界】 关注这个在凌晨还在刷碗的刷碗智。

本文 GitHub https://github.com/qq44924588... 已收录,有一线大厂面试完整考点、资料以及我的系列文章。


王大冶
68k 声望104.9k 粉丝