React
的SSR
框架- 需要注意的是如果页面中有用js控制的部分(例如条件渲染),在SSR的时候不会直接渲染成DOM元素,虽然也能导出成静态HTML,但是仍然是前端js来控制的
基础配置
1 | npx create-next-app@latest # 初始化项目 |
next.config.js
- 每次修改必须重启应用
- 在
.env
中设置的环境变量默认不会在后端渲染中被前端看到,但是如果以NEXT_PUBLIC_
开头的环境变量,是能看到也能被前端直接使用的
1 | module.exports = { |
路由
路由定义
- 路由定义默认是根据pages文件夹下的文件名来的
1 | pages/blog/first-post.js → /blog/first-post |
路由常用方法
1 | import { useRouter } from 'next/router'; |
路由事件
包括:routeChangeStart、routeChangeComplete、routeChangeError、beforeHistoryChange、hashChangeStart、hashChangeComplete
页面组件
- 通过变量渲染html,需要用
<div dangerouslySetInnerHTML={{__html = ''}}
Layouts
全局定义页面的layout
所有页面都相同的layout可以这样做
1
2
3
4
5
6
7
8
9
10// pages/_app.js
import Layout from '../components/layout'
export default function MyApp({ Component, pageProps }) {
return (
<Layout>
<Component {...pageProps} />
</Layout>
)
}如果不是所有页面的layout都相同,可以在单独的page里面这样做
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58// pages/_app.js,在_app.js中声明使用getLayout方法来获取Layout
export default function MyApp({ Component, pageProps }) {
// Use the layout defined at the page level, if available
const getLayout = Component.getLayout || ((page) => page)
return getLayout(<Component {...pageProps} />)
}
// pages/_app.tsx,如果是typescript需要这样声明
type NextPageWithLayout = NextPage & {
getLayout?: (page: ReactElement) => ReactNode
}
type AppPropsWithLayout = AppProps & {
Component: NextPageWithLayout
}
export default function MyApp ({ Component, pageProps }: AppPropsWithLayout): ReactNode {
// 我这里和官网不一样,如果有getLayout就用getLayout,否则就用全局的
if (Component.getLayout) {
return Component.getLayout(<Component {...pageProps} />)
}
return (<Layout><Component {...pageProps} /></Layout>)
}
// components/layout.js,自定义一个layout
import Navbar from './navbar'
import Footer from './footer'
export default function CustomLayout({ children }) {
return (
<>
<Navbar />
<main>{children}</main>
<Footer />
</>
)
}
// components/layout.tsx, typescript版本
import React, { ReactElement } from 'react'
interface LayoutProps {
children: ReactElement
}
export default function Layout (props: LayoutProps): JSX.Element {
return (
<>
<div>header</div>
<main>{props.children}</main>
</>
)
}
// pages/index.js
import Layout from '../components/layout'
import NestedLayout from '../components/nested-layout'
export default function Page() {}
// 通过getLayout方法来定义
Page.getLayout = CustomLayout
Head
可以在
Head
里面插入全局的js,例如google analytics代码:1
2
3
4
5
6
7
8
9
10
11<Head>
<script
dangerouslySetInnerHTML={{
__html: `[google analytics tracking code here]`
}}
/>
<link
href="https://fonts.googleapis.com/css2?family=Inter&display=optional"
rel="stylesheet"
/> // 字体优化,能够在编译阶段就优化字体,这样在打开页面不会因为要获取字体文件而闪一下
</Head>
Image
- 需要在
next.config.js
中配置图片域名images.domains
- 可以设置width、height、quality、priority、responsive自动修改图片显示大小
- 但是毕竟是后端js程序在进行转换,不如直接使用
cloudinary
这样的服务速度快功能多 - 如果图片存储在第三方需要添加配置
images: loader: 'imgix'
- width和height必填,除非
layout=fill
1 | import Image from 'next/image' |
使用svg作为component
- 和其他框架一样,都是用
npm install @svgr/webpack --save-dev
- 需要做如下配置:
1 | // next.config.js 添加如下配置 |
Link
1 | <Link href=''> |
后端渲染SSR
- 在组件加载前就从接口获取数据,才能实现后端渲染,而不是前端去调用API
- 需要注意的是通过服务端获取的props,必须直接传递到html中去,不要用useEffect等去传递给另外一个变量,那样就不会直接渲染到HTML中去了,浏览网页源代码发现他们只是在一个变量上,对SEO十分不友好
getServerSideProps
和getInitialProps
都无法用在404页面上,如果是404页面只能在componentDidMount
或者useEffect(() => {}, [])
里面去请求获取数据了,官方说明- 判断当前是否是后端渲染有一个简单的办法,那就是
typeof window === undefined
1 | class MyComponent extends React.Components { |
Hook
获取window size
1 | // hooks/useWindowResize.js |
其他特性
动态引入模块
1 | const DynamicComponent = dynamic(() => import('../components/hello')) |
国际化i18N
- nextjs的国际化支持很棒,只要设定好需要哪些语言,就只要在切换语言的时候指定语言,而不需要更改页面中其他的地方
- 支持通过域名来切换语言,或者通过path前缀来切换语言
1 | // next.config.js |
TroubleShooting
- pages with
getServerSideProps
can not be exported. 需要将package.json
中的build
命令中的next export
去掉,它和getServerSideProps
不兼容 - getServerSideProps不起作用: 它只能做用于page,不能直接作用于component
- 初始进入admin太慢: 是因为js太大,尝试给nginx加上gzip试试
- rewrites会render两次: 我也不清楚原因,目前正在论坛上问https://github.com/vercel/next.js/discussions/27985
- 生成的静态页面会有next_data字段存储着所有的props信息:这样页面上其实就有两份数据了,但是看作者的意思是所有的后端渲染都会这样做的,无法去掉https://github.com/vercel/next.js/discussions/13418,所以最好用graphql等或者其他方式只保留需要的字段