官方文档 Getting Started | Next.js (nextjs.org)
安装
npx create-next-app@latest --ts
# or
yarn create next-app --typescript
# or
pnpm create next-app --ts
运行
npm run dev
项目结构
文件 | 内容 |
---|---|
pages | 页面文件 |
pages/api | api 数据接口 |
public | 静态资源文件 |
styles | 样式文件 |
next-env.d.ts | 确保 typescript 支持 |
next.config.ts | next 配置文件 |
路由
nextjs 有一个基于页面概念的文件系统路由器,存放在 pages 下.js
, .jsx
, .ts
, .tsx
文件都将作为组件,即文件路径 → 页面路由,例如这里的 index.tsx 映射为 index,pages/about.js
将映射为 /about
。
同时还支持动态路由,创建pages/user/[id].tsx
文件,然后访问user/1
,user/2
import { useRouter } from 'next/router'
const User = () => {
const router = useRouter()
const { id } = router.query
return <div>User id:{id} </div>
}
export default User
此时访问 http://localhost:3000/user/1 便可得到 User ID: 1
在 router 对象下没有 param 属性,都是存放在 query 参数中,例如访问 user/1?username=kuizuo,此时的 query 值为 {username: 'kuizuo', id: '2'}
不过这里有个比较有意思的点,如果你在上方代码中使用 console.log 打印 query 的话,在 vscode 中会打印出空对象{}
,而在浏览器中会打印一次空对象,一次真实的 query 对象(并且打印两遍)
数据渲染
如果你打开控制台,查看所返回的页面,你会发现响应中只有 User id:,这不就和 react 的 CSR(客户端)渲染没有区别吗,是的,确实是这样。因为上一部分的代码,并且从输出 query 也可以看的出来而不是 SSR(服务端)渲染。首先我要展示一下两者渲染的代码
CSR 客户端渲染
import { useEffect, useState } from 'react'
import { useRouter } from 'next/router'
const User = () => {
const router = useRouter()
const { id } = router.query
const [data, setData] = useState({
username: '',
email: '',
})
useEffect(() => {
fetch(`https://jsonplaceholder.typicode.com/users/${id}`)
.then(res => res.json())
.then(data => {
setData(data)
})
.catch(err => {})
}, [id])
return (
<div>
<p>username:{data.username} </p>
<p>email:{data.email} </p>
</div>
)
}
export default User
经常写 react 的肯定对上面的代码不陌生,前端向后端发送数据请求,接受到数据后赋值给 data,然后渲染出来。因为请求数据是需要耗时的,所以在页面显示完之后,会停顿一会在显示出数据(主要是我这边没写 loadding),并且由于 id 并不是第一时间获取到的(从上面的 id)。
从这里来看,客户端渲染不仅要获取页面组件,还要请求数据,最终再通过 js 渲染出来
SSR 服务 端渲染
next 中服务端渲染需要用到 getServerSideProps 函数,而后端的数据获取都是在该函数内来获取,并通过 prop 传入给前端组件中,来看实际代码
const User = ({ data }: { data: any }) => {
return (
<div>
<p>username:{data.username} </p>
<p>email:{data.email} </p>
</div>
)
}
export default User
export async function getServerSideProps(context: { query: { id: any } }) {
const { id } = context.query // 这里context.param也能获取到id
const res = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`)
const data = await res.json()
return {
props: {
data,
},
}
}
如果从页面显示来看,确实没什么区别,但如果打开控制台就能发现诸多不同。
首先就是请求的页面,是直接包含数据,相当于返回你一个页面,而在客户端渲染则是返回一个组件,需要自己去请求数据来展示。
同时查看控制台中的 Fetch/XHR 的是看不到请求的数据,因为这些数据并不是由前端发送的,而是由后端发送的(故不受跨域请求的限制)。
从这就能看出客户端渲染与服务端渲染的的区别了。