懒加载路由或元件的意思是当访问该路由,或需要显示该元件时,才载入该路由或元件。这做法会提升网页效能,在打包时,如果某路由或元件设定了懒加载,就会独立被拆成一个 JS 档案,而非塞进 app.js 这个打包後的主要 JS 档案里,因此 app.js 主档案的大小就会被缩小。当首次载入网站时,浏览器就花更少时间载入 app.js。而且会确保在载入完成当前页面的资源後,在空余时间才开始载入其他独立拆出来的 JS 档案。因此提高了网站的效能。
以下会再作详细解说。
打包 Vue 专案後,会产生一些 JS 档案。如果是大型应用程序或网站,可能会有一百个以上的路由。如果网络速度稍慢,载入页面的时间可能不止几秒钟了,最後使用者通常都会直接关掉。
但是,如果使用懒加载的话,当使用者进入此路由时,浏览器才载入这个路由。而不是一载入网站时,就把所有路由全都载入。所以,首次访问网站时的等候时间就能被缩短。
由最基本的开始说起,当你建立一个附有 Vue router 功能的 Vue CLI 的专案後,你会发现预设有以下程序码:
router/ index.js
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}
]
预设 /about
路由是采用懒加载的手法。现在示范自行新增一个 '/products'
的路由:
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
},
{
path: '/products',
name: 'Products',
component: Products
}
]
打开 Network 检查:
但既然 About 是懒加载,为何现在一打开首页,就马上载入 about.js
?打开 about.js
,会发现浏览器使用 prefetch 来载入 About。
Vue 官方文件有说明 prefetch 的意思:
<link rel="prefetch">
是一种 resource hint,用来告诉浏览器在页面加载完成后,利用空闲时间提前获取用户未来可能会访问的内容。
所以,Vue 的确有实现懒加载,做法就是以上提到,等当前页面的档案都载入完成後,才会在背後开始载入那些之後有需要用到的档案。
你也可以打开 page source 检查原始码,看到 Vue 目前以 prefetch 方法载入 about.js
:
接着,现在尝试把 Products 转用懒加载。看看与以上的档案大小差别。
{
path: '/products',
name: 'Products',
// webpackChunkName 是产生独立 JS 档时的档名
component: () => import(/* webpackChunkName: "products" */ '../views/Products.vue')
}
结果:
webpackChunkName
注解写上 products。有时候,我们想要把同类的路由,在打包时可以放在同一个 JS 档案里。我们只需要把 webpackChunkName 改为同一个即可。引用官方例子:
const UserDetails = () =>
import(/* webpackChunkName: "group-user" */ './UserDetails.vue')
const UserDashboard = () =>
import(/* webpackChunkName: "group-user" */ './UserDashboard.vue')
const UserProfileEdit = () =>
import(/* webpackChunkName: "group-user" */ './UserProfileEdit.vue')
此功能背後是 Webpack 本身的 Code Splitting 功能,即是把程序码分开一个个输出,也就是 经常提到 chunk / bundle 的意思。作用是让我们取得较小的档案,并更易控制优先载入哪个档案。把此概念应用到懒加载路由,就是把每个页面都分成一个 chunk 输出。
以上所有测试都是在首页 http://localhost:8080/#/
进行。当访问 About 页时,结果会怎样?
结果:
如果点击 All tab 来看,会发现有两个 about.js,第一个是 prefetch 载入,第二个只是 prefetch cache 而己:
点击 JS tab 会更清楚。当访问 /about
页时,其实只是载入之前已在首页 prefetch 取得的 about.js 而己:
总结来说,以上测试得知,使用懒加载路由可以缩减第一次载入网站时的等待时间。使用懒加载把页面分拆为一个个独立的 JS 档,而非全都放在 app.js 这个主档案里。然後,在第一次载入网站时,浏览器在载入完当前页面後,才会使用 prefetch 来载入目前还没需要用到的 JS 档案。
懒加载元件就是载入页面时,不会马上载入元件,而是等到需要时才载入。例如 Modal、Tooltip 等这些元素不会马上显示在页面里,而是等到使用者与网页互动时才会出现,因此适合使用懒加载。
以 About 页面懒载入 Img 元件为例,当按下按钮後才显示 Img 元件。
先讲 Vue 2,做法就是使用函式载入元件。注意,以下方法无法在 Vue 3 使用,Vue 3 需要用新语法defineAsyncComponent
,否则会报错。
Img.vue
<template>
<img src="https://images.unsplash.com/photo-1633479585706-69b9f3cc34aa?ixid=MnwxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwxMHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=700&q=60" alt="">
</template>
About.vue
<template>
<div class="about">
<h1>This is an about page</h1>
<button @click="show = !show"> Show Img </button>
<div v-if="show">
<Img />
</div>
</div>
</template>
export default {
components: {
// 为了清晰看到档名,这里使用 webpackChunkName 指定档名
// 以下会回传 Promise
Img: () => import(/* webpackChunkName: "Img" */'@/components/Img.vue')
},
data() {
return {
show: false,
}
}
}
如果要进阶设定其他 option,例如载入时的 loading 元件、timeout 限制等,就使用物件来写:
// 自行建立 Loading 元件
import Loading from '@/components/Loading.vue'
const Img = () => ({
component: import(/* webpackChunkName: "Img" */'@/components/Img.vue'),
loading: Loading,
timeout: 3000
})
export default {
components: {
Img
},
data() {
return {
show: false,
}
}
}
进入 About 页面:
按按钮显示 Img 元件:
直接载入 prefetch cache 的 Img.js 以及 disk cache 的图片。
Vue 3 需要使用 defineAsyncComponent
来实现懒载入元件。
About.vue
<template>
<div class="about">
<h1>This is an about page</h1>
<button @click="show = !show"> Show Img </button>
<div v-if="show">
<Img />
</div>
</div>
</template>
const Img = defineAsyncComponent( () => import(/* webpackChunkName: "Img" */'@/components/Img.vue'))
export default {
components: {
Img
},
data() {
return {
show: false,
}
}
}
如果要进阶设定其他 option:
// 自行建立 Loading 元件
import Loading from '@/components/Loading.vue'
import { defineAsyncComponent } from 'vue'
const Img = defineAsyncComponent( {
// 此属性在 Vue 2 命为 component
// 为了清晰看到档名,这里使用 webpackChunkName 指定档名
loader: () => import(/* webpackChunkName: "Img" */'@/components/Img.vue'),
delay: 2000,
timeout: 3000,
loadingComponent: Loading
})
export default {
components: {
Img,
Loading
},
data() {
return {
show: false
}
}
}
结果如同上面示范过的 Vue 2。
最後,Vue 3 有新增 Suspense
标签来处理懒加载元件,做法会稍有不同,但目前此语法仍在试验阶段,不建议在正式专案上使用,在此先省略不作解说。
defineAsyncComponent
来完成懒加载元件。How To Lazy Load Components In Vue.js!
Vue School - Lazy Loading Routes (Vue CLI Only)
Improving performance in your Vue 3 application by Lazy Loading components
A Deep Dive Into Async Components In Vue! (Should You Use This?)
<<: EP 28: Shell Routing for TopStore App
>>: [Day 21] Edge Impulse + BLE Sense实现唤醒词辨识(中)
学完component是怎麽传递之後,看似完美,如果某天PM丢出一个需求,初步了解状况後,发现有很多...
Keyword: KMM in exist project KMM这麽好,但是我们专案已经开发了五年...
Gophish钓鱼的寄送信件流程如下图 New Campaigns 钓鱼事件 完成前面的设定後接着选...
目标 承前篇 当学生资料修改或上传图档後,能够在 Grid 即时更新修正後的资料,并於点选学生展开显...
前言 各位早安,书接上回我们介绍了如何抓取图片 URL 并储存图片,今天我们要结合之前的爬虫功能从网...