NextJS
caution
- NextJS 的构建相当复杂 - lambda、server action、pages、app
- dev 能运行,build 后可能又无法运行
- 不要用于后端太重的场景
- 构建会遇到各种问题
- 前后端 Refresh 的频度不一样 - 后端 refresh 时慢
- vercel/next.js
- 页面自定义
- 默认特殊页面内容
_document.js
- 自定义文档内容 - HTML
- 在
<Main/>
之外的组建都不会在页面初始化 - 只做 SSR - 不要在这里做应用逻辑 - 在 app 做
- 不要在这里做布局
- 不要在这里用事件
- 不能在这里导入 CSS
_app.js
- 自定义应用
- 问题
- #8311 - Setting-Up Socket.io-based Serverless API Route
- #9965 - Server-Sent Events don't work in Next API routes
- #9524 - Static Generation / SSG Improvements
- #706 - Add support to transpile modules inside node_modules
- 跨项目转译 - martpie/next-transpile-modules
- 配合 lerna 使用有问题
- 被转译模块不能使用绝对路径,除非添加 package
import 'libs/utils'
不可以import './utils'
可以import '@wener/core/libs/utils'
可以
- #5054 - Truly static pages without react app
- PR #11949
- 页面导出
const config={unstable_runtimeJS: false}
- 页面导出
- PR #11949
- 参考
- Persistent Layout Patterns in Next.js
- https://github.com/kirill-konshin/next-redux-wrapper
- i18n https://github.com/isaachinman/next-i18next/issues/274
- unicodeveloper/awesome-nextjs
- nextauthjs/next-auth
- vercel/platforms
- template for site builders and low-code tools.
- Layouts RFC
- getInitialProps
- 只能用于页面组件不能用于子组件
- 在第一次渲染会执行,之后会在客户端做转换
- 不要能服务端的模块,通过 API 实现调用
- 内置了 node-fetch
- 增量缓存的页面位于 .next/server/pages
- 可以考虑映射到外部 - 需要拷贝初始数据
caution
- 无法按页面切分 CSS
- 最终会生成单个 css 文件
- 只有
_app
可以导入全局 css
单一 HTML 入口 - 不像 vite 支持多入口- rewrites 会在 build 时生成 router-manifest, 因此 start 时配置的变量 无法 产生影响
- ESM 使用还有问题
- 目前 main 最好还是指向 cjs
- import ESM module fails with custom typescript server #36940
- next.config.js 只能是 cjs #32239
- next 不会检测使用 next.config.cjs, 因此目前别无选择
- NextJS 自身时 bundle 后的 CJS,重写为 ESM 难度高
- next.js#36940
- ESM 相关问题 alineacms/alinea#31
- 路径不支持 UTF8 #10084
快速开始
# 初始化项目
mkdir my-web && cd my-web
# 依赖
# npm install --save next react react-dom
# npm install --save-dev typescript @types/react @types/node
yarn add next@latest react@latest react-dom@latest
yarn add --dev typescript @types/react @types/node
# 首页
mkdir pages
cat << INDEX > pages/index.tsx
import React from 'react'
function Home({ pid }) {
return <div>NextJS Running on {pid}!</div>
}
Home.getInitialProps = function () {
return { pid: process.pid }
}
export default Home
INDEX
# 启动服务
./node_modules/.bin/next
# 访问 http://localhost:3000
# 其它常用目录
mkdir -p public libs hooks types components modules reducers hooks
Tips
# 基础依赖
yarn add next@latest react@latest react-dom@latest
yarn add --dev typescript @types/react @types/node
# Next 扩展增强插件
# 已内建
# yarn add @zeit/next-css @zeit/next-sass @next/mdx
yarn add next-transpile-modules @next/bundle-analyzer
# 已内建
# yarn add dotenv tsconfig-paths-webpack-plugin
yarn add --dev @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties
yarn add --dev babel-plugin-import
# 服务端开发
# isomorphic-unfetch isomorphic-ws
# 常用工具
yarn add moment lodash date-fns
yarn add --dev @types/lodash
# UI 框架
yarn add antd
# 测试
yarn add --dev ts-node jest ts-jest @types/jest
提示
目录结构
- next.config.js - 配置文件
- next-env.d.ts - 针对 TS 的类型定义
- pages - 页面 - 可直接访问
_document.js
_app.js
- api - 接口 - 通过
/api/*
访问
自定义 _app.js
function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />;
}
// Only uncomment this method if you have blocking data requirements for
// every single page in your application. This disables the ability to
// perform automatic static optimization, causing every page in your app to
// be server-side rendered.
//
// MyApp.getInitialProps = async (appContext) => {
// // calls page's `getInitialProps` and fills `appProps.pageProps`
// const appProps = await App.getInitialProps(appContext);
//
// return { ...appProps }
// }
export default MyApp;
自定义 _document.js
// _document is only rendered on the server side and not on the client side
// Event handlers like onClick can't be added to this file
// ./pages/_document.js
import Document, { Html, Head, Main, NextScript } from 'next/document';
class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx);
return { ...initialProps };
}
render() {
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
export default MyDocument;