【Next.js 14】性能优化相关知识点

前言

Next.js内置了许多优化功能,包括自动压缩和缓存等。本篇将介绍在Next.js中相关性能优化内容。

对Next.js14项目搭建还不熟悉的,可先参考我的另一篇文章:给上市公司从0到1搭建Next.js14项目

1. Image

使用Next.js提供的Image组件来渲染图片。Image组件扩展了HTML 元素,具有图像自动优化的特性

1. 本地图片

对于本地图片,使用import方式导入图像文件。Next.js 将根据导入的文件自动确定图像的宽度和高度,也可使用width和height手动指定图片的宽高

js 复制代码
import Image from 'next/image'
import profilePic from './me.png'
 
export default function Page() {
  return (
    <Image
      src={profilePic}
      alt="Picture of the author"
    />
  )
}

提示:不要使用 require 导入图像文件,使用require之后只能声明为客户端组件

js 复制代码
<Image src={require('@/assets/images/arrow.png')} width={24} height={24} alt="加载中" />

2. 远程图片

对于远程图片,由于不能事先分析出图片的大小,因此需手动提供图片的宽高

js 复制代码
import Image from 'next/image'
 
export default function Page() {
  return (
    <Image
      src="https://s3.amazonaws.com/my-bucket/profile.png"
      alt="Picture of the author"
      width={500}
      height={500}
    />
  )
}

并且需配置域名才能正常使用,修改next.config.mjs

js 复制代码
const nextConfig = {
  images: {
    remotePatterns: [
      {
        protocol: "https",
        hostname: "s3.amazonaws.com",
      },
    ],
  },
};

3. 图片优先级

为图像添加优先级属性,该属性将使图像成为页面的最大内容绘制(LCP)元素。这样可以让Next.js特别优先加载这个图像,从而提升LCP(最大内容渲染)的性能

js 复制代码
import Image from 'next/image'
import profilePic from '../public/me.png'
 
export default function Page() {
  return <Image src={profilePic} alt="Picture of the author" priority />
}

4. 图片懒加载

默认情况下,图片将懒加载,即达到可视区时才会去加载

js 复制代码
<Image src={profilePic} alt="" loading={"lazy"}/>

也可手动指定图片立即加载,即使图片不在可视区

js 复制代码
<Image src={profilePic} alt="" loading={"eager"}/>

5. 不优化图片

使用Image组件默认会将图片转为webp格式,在某些情况下,也会更改图片的宽高。如果不想图片的大小被改变,可使用unoptimized,源图像将按原样提供

js 复制代码
<Image src={profilePic} alt="" unoptimized />

也可全局统一配置使用原样图片,修改next.config.mjs

js 复制代码
const nextConfig = {
  images: {
    unoptimized: true,
  },
};

6. 图片自适应

图片的宽高要么通过本地导入,程序自动确定图片的宽高;要么手动指定图片的宽高。除此以外,图片组件可通过指定 fill 属性自动填充父元素

默认情况下,使用了 fill 属性的图片组件将被分配为 position: "absolute",因为父元素也需使用position: "relative"、position: "fixed", 或 position: "absolute" 进行指定

js 复制代码
import Image from 'next/image';

import One from '@/assets/images/th.jpg';

export default async function Page() {
  return (
    <div style={{ width: 150, height: 250, position: 'relative' }}>
      <Image src={One} alt="图片" fill={true} objectFit="cover" />
    </div>
  );
}

2. script

使用Next.js提供的Script组件来加载第三方脚本文件,Next.js将确保脚本只加载一次

1. 在路由布局文件中引入

js 复制代码
import Script from 'next/script'
 
export default function DashboardLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <>
      <section>{children}</section>
      <Script src="https://example.com/script.js" />
    </>
  )
}

2. 在根布局文件中引入

js 复制代码
import Script from 'next/script'
 
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>{children}</body>
      <Script src="https://example.com/script.js" />
    </html>
  )
}

3. 脚本加载时机

Script 组件可通strategy属性控制脚本加载时机

  1. beforeInteractive:在可交互(水合)之前加载

  2. afterInteractive:在可交互之后尽早加载(默认)

  3. lazyOnload:在浏览器空闲时加载

3. 懒加载

默认情况下,Next.js的服务端组件会自动进行代码分割。这意味着页面上用到的服务端组件,会被分割成独立的块,只有在需要时才会被加载。但对于客户端组件来说,需要开发者手动进行控制以实现按需加载

1. 动态加载组件

Next.js提供了next/dynamic这个包,它结合了React.lazy和Suspense两个API,来实现客户端组件的懒加载

js 复制代码
'use client'
 
import { useState } from 'react'
import dynamic from 'next/dynamic'
 
// 客户端组件
const ComponentA = dynamic(() => import('../components/A'))
const ComponentB = dynamic(() => import('../components/B'))
 
export default function ClientComponentExample() {
  const [showMore, setShowMore] = useState(false)
 
  return (
    <div>
      {/* 立即加载,但在一个单独的客户端包中 */}
      <ComponentA />
 
      {/* 只在满足条件时按需加载 */}
      {showMore && <ComponentB />}
      <button onClick={() => setShowMore(!showMore)}>Toggle</button>
    </div>
  )
}

如果客户端组件使用到了window等浏览器对象时,在服务端预呈现会报错,因为在服务端没有该对象。此时可禁用客户端组件的预呈现功能,直接在客户端渲染

js 复制代码
const ComponentC = dynamic(() => import('../components/C'), { ssr: false })

2. 动态加载第三方包

可以使用import()函数按需加载外部库。这个例子使用外部库fuse.js进行模糊搜索。只有在用户输入搜索输入后,才会在客户机上加载该模块。

js 复制代码
'use client'
 
import { useState } from 'react'
 
const names = ['Tim', 'Joe', 'Bel', 'Lee']
 
export default function Page() {
  const [results, setResults] = useState()
 
  return (
    <div>
      <input
        type="text"
        placeholder="Search"
        onChange={async (e) => {
          const { value } = e.currentTarget
          // Dynamically load fuse.js
          const Fuse = (await import('fuse.js')).default
          const fuse = new Fuse(names)
 
          setResults(fuse.search(value))
        }}
      />
      <pre>Results: {JSON.stringify(results, null, 2)}</pre>
    </div>
  )
}

3. 自定义加载样式

next/dynamic结合了React.lazy和Suspense两个API,在Suspense中可自定义加载组件,在next/dynamic中同样可以自定义加载组件,只需传入第二个参数

js 复制代码
import dynamic from 'next/dynamic'
 
const WithCustomLoading = dynamic(
  () => import('../components/WithCustomLoading'),
  {
    loading: () => <p>Loading...</p>,
  }
)

4. 预加载,预连接,DNS预解析

Next.js暂未直接支持这些配置,可使用ReactDOM将link标签安全地插入到文档的<head>

  1. 新建app/preload-resources.tsx
js 复制代码
'use client'
 
import ReactDOM from 'react-dom'
 
export function PreloadResources() {
  ReactDOM.preload('...', { as: '...' })
  ReactDOM.preconnect('...', { crossOrigin: '...' })
  ReactDOM.prefetchDNS('...')
 
  return null
}

ReactDOM.preload('...', { as: '...' }) 相当于:

js 复制代码
<link rel="preload" href="..." as="..." />

ReactDOM.preconnect('...', { crossOrigin: '...' }) 相当于:

js 复制代码
<link rel="preconnect" href="..." crossorigin />

ReactDOM.prefetchDNS('...') 相当于:

js 复制代码
<link rel="dns-prefetch" href="..." />
  1. 修改app/layout.tsx
js 复制代码
import PreloadResources from './preload-resources';

const RootLayout = ({ children }: React.PropsWithChildren) => (
  <html lang="en">
    <body>
      <PreloadResources />
      {children}
    </body>
  </html>
);

export default RootLayout;

5. 打包分析

  1. 安装
js 复制代码
npm i cross-env -D npm i @next/bundle-analyzer -D
  1. 修改next.config.mjs
js 复制代码
import bundleAnalyzer from '@next/bundle-analyzer';

const withBundleAnalyzer = bundleAnalyzer({
  enabled: process.env.ANALYZE === 'true', //当环境变量ANALYZE为true时开启
});

const nextConfig = {};

export default withBundleAnalyzer(nextConfig);
  1. 修改 package.json

将启动参数build修改为

js 复制代码
"build": "cross-env ANALYZE=true next build",
  1. 运行 npm run build 进行打包,.next文件夹下会生成analyze文件夹

.next/analyze/nodejs.html:显示的是服务端文件包的大小,即.next/server文件夹下的资源

.next/analyze/client.html:显示的是客户端文件包的大小,即.next/static文件夹下的资源

6. gzip压缩

默认情况下,Next.js 在使用 next start 或自定义服务器时,会使用 gzip 来压缩渲染的内容和静态文件。

如果想关闭gzip压缩,修改next.config.mjs

js 复制代码
const nextConfig = {
  compress: false,
};

结尾

对Next.js感兴趣的,可先关注我,后续将继续更新相关内容

相关推荐
崔庆才丨静觅9 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606110 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了10 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅10 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅10 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅11 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment11 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅11 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊11 小时前
jwt介绍
前端
爱敲代码的小鱼11 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax