第三方库集成
本教程共 42 篇 · 第 36 篇 · 更新于 2026-07-30 · 约 6 分钟阅读
36. 第三方库集成
本节目标:掌握在 Next.js 中正确集成第三方库的方法,学会处理客户端/服务端兼容问题和打包优化。
客户端/服务端兼容性
Next.js 的 App Router 混合了 Server Components 和 Client Components。很多第三方库只支持浏览器环境,直接用在服务端会报错。
判断库是否兼容服务端
- 如果库使用了
window、document等浏览器 API,它只能在客户端运行 - 如果库涉及 DOM 操作,它只能在客户端运行
- 纯计算类库(如
lodash、date-fns)通常两端都能用
解决方案
方案一:标记为客户端组件
'use client'
import { SomeLibrary } from 'some-browser-only-library'
export function MyComponent() {
// 这个组件及其子树都会在客户端执行
return <SomeLibrary />
}
方案二:动态导入并关闭 SSR
import dynamic from 'next/dynamic'
const BrowserOnlyComponent = dynamic(
() => import('@/components/BrowserOnlyComponent'),
{ ssr: false }
)
export default function Page() {
return <BrowserOnlyComponent />
}
方案三:用 useEffect 延迟渲染
'use client'
import { useState, useEffect } from 'react'
export function ClientOnly({ children }) {
const [mounted, setMounted] = useState(false)
useEffect(() => {
setMounted(true)
}, [])
return mounted ? <>{children}</> : null
}
transpilePackages
默认情况下,Next.js 不会编译 node_modules 中的代码。如果某个依赖包发布的是 TypeScript 源码或现代语法,需要手动加入编译列表:
// next.config.ts
import type { NextConfig } from 'next'
const nextConfig: NextConfig = {
transpilePackages: ['@scope/ui', 'some-uncompiled-package'],
}
export default nextConfig
这个选项替代了以前的 next-transpile-modules 包。
什么时候需要 transpilePackages
- 依赖包发布的是 TypeScript 源码
- monorepo 中的共享包(Pages Router + Webpack 场景)
- Pages Router 下需要把 node_modules 依赖打包进路由
注意:包不能同时出现在
transpilePackages和serverExternalPackages中,否则构建会报错。
@next/third-parties
Next.js 提供了 @next/third-parties 包,优化了常见第三方服务的加载方式。
Google Tag Manager
import { GoogleTagManager } from '@next/third-parties/google'
export default function RootLayout({ children }) {
return (
<html lang="en">
<GoogleTagManager gtmId="GTM-XYZ" />
<body>{children}</body>
</html>
)
}
Google Analytics
import { GoogleAnalytics } from '@next/third-parties/google'
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>{children}</body>
<GoogleAnalytics gaId="G-XYZ" />
</html>
)
}
Google Maps
import { GoogleMapsEmbed } from '@next/third-parties/google'
export default function Page() {
return (
<GoogleMapsEmbed
apiKey="XYZ"
height={200}
width="100%"
mode="place"
q="Brooklyn+Bridge,New+York,NY"
/>
)
}
YouTube 嵌入
import { YouTubeEmbed } from '@next/third-parties/google'
export default function Page() {
return <YouTubeEmbed videoid="ogfYd705cRs" height={400} params="controls=0" />
}
注意:
@next/third-parties目前还是实验性库,建议安装时用@latest或canary标签。
常见库的集成方案
状态管理
Zustand:轻量级状态管理,天然支持 React Server Components。
'use client'
import { create } from 'zustand'
const useStore = create((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
}))
Redux:需要包裹 <Provider>,且 Provider 必须是客户端组件。
表单
React Hook Form + Zod:
'use client'
import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod'
const schema = z.object({
email: z.string().email(),
})
export function MyForm() {
const { register, handleSubmit } = useForm({
resolver: zodResolver(schema),
})
return <form onSubmit={handleSubmit(onSubmit)}>{/* ... */}</form>
}
动画
Framer Motion:
'use client'
import { motion } from 'framer-motion'
export function AnimatedBox() {
return (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.5 }}
/>
)
}
日期处理
date-fns:纯函数库,两端都能用。
import { format } from 'date-fns'
export default async function Page() {
const today = format(new Date(), 'yyyy-MM-dd')
return <p>今天是 {today}</p>
}
打包优化建议
- 按需导入:
import { Button } from 'antd'比import antd from 'antd'好 - 动态导入:非首屏组件用
dynamic()懒加载 - tree-shaking:确保库支持 ES Module,能被正确摇树
- 监控包大小:用
@next/bundle-analyzer分析打包产物(安装与配置方法见 §27)
集成第三方库时,最关键的是理解它能不能在服务端运行。不确定的时候,先用 'use client' 标记,再根据实际需求调整。