简介
在生产环境中优化性能有时是一项艰难的任务。对网站性能的微调是不容忽视的,因为它的缺点是网页速度慢,用户体验差。这些网站往往加载缓慢,图片渲染缓慢,从长远来看,会导致网站访问者的跳出率增加,因为大多数用户不愿意等待内容弹出。
在本教程中,我们将介绍在Next.js应用程序中加快网站性能的不同模式。
目标和先决条件
在本文结束时,读者将清楚地了解如何在Next.js网络应用中实现性能最大化。
要跟上这篇文章,需要事先了解Next.js框架。
什么是动态导入和代码拆分?
动态导入,也被称为代码拆分,指的是将一捆捆的JavaScript代码分割成更小的块,然后将其拼凑在一起并加载到应用程序的运行时间中,以此来大幅提升网站性能的做法。
它是作为JavaScript静态导入的升级版而开发的,静态导入是在JavaScript模块的顶层使用导入语法为模块或组件添加导入的标准方式。
虽然这是一种常用的方法,但在性能优化方面存在一些缺点,特别是在以下情况下:
- 大型代码库,由于构建过程将所有需要的文件编译成一个单一的捆绑包,所以会产生较大的捆绑包尺寸,导致加载时间延长
- 需要某些用户操作的网页,如点击导航菜单项来触发页面加载。在这里,所需的页面只有在满足导航条件时才会呈现,当组件被静态导入时,可能会触发缓慢的初始页面加载
动态导入与静态导入有何不同?
与静态导入不同,动态导入通过应用一种被称为代码分割的方法来工作。代码拆分是将代码分为不同的包,这些包使用树形格式平行排列,其中的模块是动态加载的--模块只有在需要时才被导入并包含在JavaScript包中。代码被分割得越多,包的大小就越小,页面的加载速度就越快。
这种方法创建了多个捆绑包,在网页运行时动态加载。动态导入利用了写为内联函数调用的导入语句。
让我们来看看一个比较。假设我们希望在我们的应用程序中导入一个导航组件;下面是一个导航组件的静态和动态导入的例子。
静态导入:
import Nav from './components/Nav'
export default function Home() {
return (
<div>
<Nav/>
</div>
)
}
动态导入:
import dynamic from "next/dynamic";
import { Suspense } from "react";
export default function Home() {
const Navigation = dynamic(() => import("./components/Nav.js"), {
suspense: true,
});
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<Navigation />
</Suspense>
</div>
);
}
这里,导航组件的相对部分在import() 块中指定。注意,next/dynamic 不允许在import() 的参数中使用模板字面或变量。
另外,react/suspense 有一个指定的回退元素,在导入的组件可用之前,该元素会被显示。
Next.js中动态导入的好处
因实施动态导入而优化网站性能,又会带来以下网站的好处:
- 更快的页面加载:网站加载和显示内容的速度是至关重要的,因为你的受众希望快速完成工作,不会为缓慢的网页停留。
- 动态导入对图像加载时间也有积极影响
- 低跳出率:跳出率,指的是用户在没有与网站互动的情况下退出你的网页的比率,通常表明(并且是由)缓慢的加载时间造成的。较低的跳出率通常意味着更快的网站性能
- 改进网站互动时间:这涉及到TTI,即互动时间,即用户要求采取行动和用户得到结果之间的时间间隔。这些互动可以包括点击链接、滚动页面、在搜索栏中输入信息、向购物车中添加物品等。
- 更好的网站转换率:随着更多的用户从使用一个优化良好的网站中获得满足感,他们将更有可能进行转换
有了所有这些好处,你可能正在考虑如何在你的应用程序中使用动态导入。那么,最大的问题是,我们如何在Next.js应用程序中实现动态导入和代码拆分?下一节将介绍如何实现这一目标的详细步骤。
在Next.js中实现动态导入和代码拆分
Next.js通过next/dynamic 模块,可以轻松地在Next应用程序中创建动态导入,如上图所示。next/dynamic 模块实现了React组件的懒惰加载导入,并且是建立在React Lazy之上。
它还利用了React Suspense库,允许应用程序推迟加载组件,直到它们被需要,从而提高初始加载性能,因为更轻的JavaScript构建。
动态导入命名的出口
在本文早些时候,我们演示了使用next/dynamic 来导入一个组件。但我们也可以为从另一个文件导出的函数或方法做动态导入。这一点演示如下:
import React from 'react'
export function SayWelcome() {
return (
<div>Welcome to my application</div>
)
}
const SayHello = () => {
return (
<div>SayHello</div>
)
}
export default SayHello
在上面的代码中,我们有一个组件,SayHello ,和一个命名导入,SayWelcome 。我们可以为SayWelcome 方法做一个动态显式导入,如下所示:
import dynamic from "next/dynamic";
import { Suspense } from "react";
export default function Home() {
const SayWelcome = dynamic(
() => import("./components/SayHello").then((res) => res.SayWelcome)
);
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<SayWelcome />
</Suspense>
</div>
);
}
上面的代码导入了SayHello 组件,然后从响应中返回SayWelcome 输出。
动态导入多个组件
假设我们有UserDetails 和UserImage 组件。我们可以导入并显示这两个组件,如下所示:
import dynamic from 'next/dynamic'
const details = dynamic(() => import('./components/UserDetails'))
const image = dynamic(() => import('./components/UserImage'))
function UserAccount() {
return (
<div>
<h1>Profile Page</h1>
<details />
<image />
</div>
)
}
const App = () => {
return (
<>
<UserAccount />
)
</>
}
export default App
在上面的代码中,我们为UserDetails 和UserImage 组件添加了动态导入,然后我们把这些组件放在一起,成为一个单一的组件,UserAccount 。最后,我们在应用程序中返回了UserAccount 组件。
用于客户端渲染的动态导入
通过next/dynamic 模块,我们还可以禁用导入组件的服务器端渲染,而在客户端渲染这些组件。这特别适用于那些不需要太多用户互动或有外部依赖的组件,如API。这可以通过在导入组件时将ssr 属性设置为false 来实现:
import dynamic from 'next/dynamic'
const HeroItem = dynamic(() => import('../components/HeroItem'), {
ssr: false,
})
const App = () => {
return (
<>
<HeroItem />
)
</>
}
在这里,HeroItem 组件的服务器端渲染设置为false ,因此它在客户端进行渲染。
库的动态导入
除了导入本地组件之外,我们还可以为外部依赖添加动态导入。
例如,假设我们希望使用Axiosfetch,在用户请求时从API获取数据。我们可以为Axios 定义一个动态导入并实现它,如下所示:
import styles from "../styles/Home.module.css";
import { React, useState } from "react";
export default function Home() {
const [search, setSearch] = useState("");
let [response, setResponse] = useState([]);
const api_url = `https://api.github.com/search/users?q=${search}&per_page=5`;
return (
<div className={styles.main}>
<input
type="text"
placeholder="Search Github Users"
value={search}
onChange={(e) => setSearch(e.target.value)}
/>
<button
onClick={async () => {
// dynamically load the axios dependency
const axios = (await import("axios")).default;
const res = await axios.get(api_url).then((res) => {
setResponse(res);
});
}}
>
Search for GitHub users
</button>
<div>
<h1>{search} Results</h1>
<ul>
{response?.data ? (
response && response?.data.items.map((item, index) => (
<span key={index}>
<p>{item.login}</p>
</span>
))
) : (
<p>No Results</p>
)}
</ul>
</div>
</div>
);
}
在上面的代码中,我们有一个输入字段来搜索GitHub上的用户名。我们使用useState() Hook来管理和更新输入字段的状态,我们设置了Axios 的依赖关系,以便在点击Search for GitHub users 按钮时动态导入。
当响应返回时,我们对其进行映射,并显示五个用户的usernames ,这些用户的名字与输入字段中的搜索查询相对应。
下面的GIF演示了上述代码块的操作:

结论
在这篇文章中,我们了解了动态导入/代码拆分,它的优点,以及如何在Next.js应用程序中使用它。
总的来说,如果你想缩短网站的加载时间,动态导入和代码拆分是一个必须的方法。如果你的网站有图片,或者要显示的结果取决于用户的互动,那么动态导入将大大提升网站的性能和用户体验。