前面我们已经提过next.js 构建于页面的概念,位于pages
目录下的对应类型的文件暴露的React 组件都是页面 …
动态路由示例
pages/posts/[id].js
那么这个路由它是一个动态路由,id 是一个动态路由参数,例如posts/1
,/posts/2
都可以访问,分别渲染不同的文章内容 …
Pre-rendering
预渲染,默认情况Next.js 会预渲染每一个页面,这就意味着Next.js 会提前为每一个页面生成HTML,而不是完全通过客户端JS 处理, 预渲染能够导致更好的性能以及SEO ..
对于每个页面每一个生成的HTML 会关联最小化的且必要的JS 代码,当页面被浏览器加载的时候,它的js代码将会被运行并让这个页面变得完全可以具有交互性(这个过程称为水和 - hydration)
两种类型的预渲染
- 静态生成
这是默认更推荐的,因为在构建阶段生成HTML并且在每次请求的时候都会重用 ..
因为静态生成相比服务端渲染性能更高,静态生成的页面能够被CDN 缓存而没有额外的配置就能够提高性能 …
有些情况服务端渲染可能是唯一的选择 …
- 服务端渲染
每次请求生成HTML
重要的是,你能够同时在上述两种预渲染中使用客户端数据抓取,这意味着页面的有些部分能够完全通过客户端js代码渲染 …
静态生成
它有两种情况,一种是无代码生成 ,反之 ..
这里只介绍有数据的情况 …
对于需要抓取外部数据进行预渲染的页面,我们可以使用以下函数
getStaticProps
对于页面内容依赖于外部数据的 ..
- 另一种就是页面路径依赖于外部数据
getStaticPaths
(通常还包括getStaticProps
)
示例:
// TODO: Need to fetch `posts` (by calling some API endpoint)
// before this page can be pre-rendered.
function Blog({ posts }) {
return (
<ul>
{posts.map((post) => (
<li>{post.title}</li>
))}
</ul>
)
}
export default Blog
Blog需要抓取外部数据,那么我们需要使用 getStaticProps
并且这个函数是一个promise函数,所以我们可以做出如下声明,即可符合约定:
function Blog({ posts }) {
// Render posts...
}
// This function gets called at build time
export async function getStaticProps() {
// Call an external API endpoint to get posts
const res = await fetch('https://.../posts')
const posts = await res.json()
// By returning { props: { posts } }, the Blog component
// will receive `posts` as a prop at build time
return {
props: {
posts,
},
}
}
export default Blog
然后在构建时,next.js就会帮我们运行数据抓取函数生成对应的HTML(也就是说它仅在构建时运行一次)…
路径依赖于外部数据
例如我们包含一个这样的路由文件
pages/posts/[id].js
那么这个id依赖于外部数据,因此为了处理路径,我们需要调用一个getStaticPaths
函数,同样将它暴露
// This function gets called at build time
export async function getStaticPaths() {
// Call an external API endpoint to get posts
const res = await fetch('https://.../posts')
const posts = await res.json()
// Get the paths we want to pre-render based on posts
const paths = posts.map((post) => ({
params: { id: post.id },
}))
// We'll pre-render only these paths at build time.
// { fallback: false } means other routes should 404.
return { paths, fallback: false }
}
这样我们在getStaticProps
中就可以根据获得的路径进行数据抓取
function Post({ post }) {
// Render post...
}
export async function getStaticPaths() {
// ...
}
// This also gets called at build time
export async function getStaticProps({ params }) {
// params contains the post `id`.
// If the route is like /posts/1, then params.id is 1
const res = await fetch(`https://.../posts/${params.id}`)
const post = await res.json()
// Pass post data to the page via props
return { props: { post } }
}
export default Post
什么时候使用静态生成
在页面仅仅只需要构建一次且能够通过被CDN缓存的情况,这比每一个请求渲染页面更加快速 …
你能够为多种类型的页面进行静态生成,包括
- 销售页面(部分静态生成)
- 博客文章和作品集
- 电子商务产品列表
- 帮助以及文档
另外,询问自己: “是否能够在用户的请求之前预渲染这个页面?”,如果是yes,那么你可以使用静态生成 ….
另一方面,静态生成并不是一个好的注意,如果你不能够在用户的请求前面预渲染一个页面,也许你的页面展示频繁更新的数据,并且有可能每一次请求都可能发生数据的改变 ….
这种情况,你需要如下事情:
- 静态生成结合客户端数据抓取: 你能够跳过页面的某些部分的预渲染并使用客户端js填充它们,为了学习这种方式查看数据抓取文档了解更多 ..
使用服务端渲染
Next.js 在每一个请求来的时候预渲染一个页面,它将更慢(因为页面不能够被CDN缓存),但是每一个预渲染的页面永远是最新的 ....
服务端渲染
如果一个页面使用服务端渲染(也成为 Server-Side Rendering),在每次请求的时候动态生成页面(也成为SSR或者动态渲染) …
对于服务端页面渲染,你需要export
一个叫做 getServerSideProps
的函数,这个函数在每一个请求到来时被服务器调用 …
举个例子,例如dashboard页面,具有频繁数据更新,通过getServerSideProps
抓取页面的数据 …
function Page({ data }) {
// Render data...
}
// This gets called on every request
export async function getServerSideProps() {
// Fetch data from external API
const res = await fetch(`https://.../data`)
const data = await res.json()
// Pass data to the page via props
return { props: { data } }
}
export default Page
这个函数类似于getStaticProps
,但是不同之处是getServerSideProps
在每个请求上都会运行,前者仅仅在构建时运行 …
为了了解更多数据抓取函数的工作方式,查看数据抓取文档 …
总结
预渲染最推荐使用静态生成,甚至是静态生成结合客户端数据抓取渲染,最后万不得已使用SSR ..