Next.js 学习
- 基于React的服务器端渲染框架
- 特点:
- 默认服务端渲染,以文件系统为基础的客户端路由,没有路由配置文件
- 代码自动分割使页面加载更快
- 使用 ES6 module
- 对 SEO 友好,提升手机及低功耗设备的性能,快速显示首页
文件系统的路由:
- 创建
./pages
文件夹,该文件夹只能存放页面级别的组件 - 默认访问
pages/index.js
文件,其他的页面需要通过访问该路径,e.g./test
来访问pages/test.js
文件,或者 访问pages/test/index.js
文件(推荐这种目录结构)
路由跳转:
通过组件实现:
import Link from 'next/link'
export const NextRoute = () => (
<div>
<Link href='/next-route/aaa'>
<a style={{color: "red"}}>Teacher</a>
</Link>
</div>
)
- 使用
Link
组件,添加href
属性,表示需要跳转的路径 href
可以是一个字符串,也可以是 URL 对象,可以自动格式化生成 URL 字符串,{{pathname: '/next-route/aaa', query: {id: 11}}
=>'/next-route/aaa?id=11'
- 不能直接在
Link
组件中写字符串,需要用一个标签包起来,可以是<a>
,<p>
,<button>
等 Link
组件只能包含一个 React Element child- 不能直接给
Link
组件添加样式,因为Link
是一个 HOC,可以给子元素设置样式
通过命名式路由实现:
import Router from 'next/router'
export const NextRoute = () => (
<div>
<span onClick={()=> Router.push('/next-route/bbb' }>Student</span>
</div>
)
- 也可以使用 URL 对象语法,e.g.
Router.push({pathname: '/next-route/bbb', query: {id: 1}})
注意:
- 如果没有匹配到,默认会去找 pages/_error.js
中定义的组件
- 路由跳转不会向服务器发送请求页面的请求,只会请求 js 文件
组件:
普通组件:
- 一般放在
./components
文件夹下,然后在适当的页面引入并使用,不要放在./pages
下,因为组件不需要 url
布局组件:
-
一般放在
./layout
文件夹下,使用props.children
属性import Mynav from '../components/Mynav' export const MyLayout = (props) => ( <div> <Mynav></Mynav> // e.g. 包括两个 Link 用来导航 {props.children} </div> )
使用:
import MyLayout from '../../layouts/MyLayout' export const NextRoute = () => ( <div> <MyLayout> <p>Hello World</p> // 作为 children 传递到 MyLayout 组件中 </MyLayout> </div> )
全局布局组件:
-
创建
_app.js
,这样可以不用在每个页面中都单独引入布局组件import React from 'react' import App, { Container } from 'next/app' // 替换成自己的布局组件 const Layout = (props) => ( <div className="layout">{props.children}</div> ) // 使用模板 export default class MyApp extends App { render(){ const { Component, pageProps } = this.props return ( <Container> <Layout> // 布局组件 <Component {...pageProps}/> // 页面级别的组件 </Layout> </Container> ) } }
路由的参数查询
-
使用
withRouter
,这是一个 HOC,会将当前的路由对象绑定到组件的props
属性上 -
可以通过
props.router.query
获取路由的参数import { withRouter } from 'next/router' const Detail = (props) => ( <div>这是${props.router.query.id}号选手的详情</div> ) export default withRouter(Detail)
浅层路由
-
可以通过
Link
组件的as
属性给路径起别名,e.g. 原始的/next-route/user/detail?id=1
=>/next-route/user/1
<div> <Link as={`/next-route/user/${item.id}`} href={`/next-route/user/detail?id=${item.id}`}> <p>Detail</p> </Link> </div>
服务端支持 Clean URLs:
-
如果直接通过刷新使用别名后的地址是无法获取到对应的页面,可以通过服务端来支持浅层路由(Clean URLs)
-
安装
express
, 在项目的根目录创建server.js
const express = require('express'); const next = require('next'); const env = process.env.NODE_ENV; const dev = env !== 'production'; const app = next({ dev }); // 创建一个 next 实例 const handle = app.getRequestHandler(); // 得到用来处理请求的函数 app.prepare() // 实例构建好之后 .then(() => { const server = express(); // 创建 express 服务器 // Default catch-all handler to allow Next.js to handle all other routes server.all('*', (req, res) => handle(req, res)); server.listen(3000, (err) => { // 监听端口号 if (err) { throw err; } console.log(`[Express] Ready on http://localhost:3000`); }); });
-
修改
package.json
的script
"script": { "dev": "node server.js", "build": "next build", "start": "NODE_ENV=production node server.js" }
-
创建自定义路由,根据实际情况调整参数,并添加在
server.js
中server.get("*", ...)
之前:server.get('/teacher/:id', (req, res) => { const actualPage = '/teacher/detail' const queryParams = { id: req.params.id } app.render(req, res, actualPage, queryParams) })