Skip to main content

NextJS Cookbook

// 判断服务端
const isServer = typeof window === 'undefined';

配置问题排查

// 输出 Webpack 的匹配规则
config.module.rules.forEach((rule) => console.log(`Rule Test ${rule.test} Use`, rule.use));

页面基础代码

import React from 'react';
import { NextPage } from 'next';

const Page: NextPage = () => {
return <div></div>;
};

export default Page;

API 基础代码

import { NextApiRequest, NextApiResponse } from 'next';

const handler = async (req: NextApiRequest, res: NextApiResponse) => {
res.status(200).json({ version: process.env.APP_VERSION || '1.0.0' });
};

export default handler;

页面跳转

// 初始阶段跳转
Page.getInitialProps = async ({ res }) => {
// 目标地址
let targetUrl = '...';
if (!res) {
window.location.href = targetUrl;
} else {
res.writeHead(302, { Location: targetUrl });
res.end();
}
return {};
};

// 页面内跳转
function MyPage() {
const router = useRouter();
useEffect(() => {
if (!auth) {
router.push('...');
}
}, []);
}

// API 跳转
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
res.clearCookie('authenticated');
res.writeHead(302, { Location: '/login' });
res.end();
};

SSE - Servr-Send Event

import { NextApiRequest, NextApiResponse } from 'next';

export const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

// curl -Nv localhost:3000/test/see
// https://zeit.co/pricing#limits Zeit free only allowed 10s/req
// NOTE Zeit deploy not works - because the request will be buffered
// https://github.com/zeit/next.js/issues/9965
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Content-Type', 'text/event-stream;charset=utf-8');
res.setHeader('Cache-Control', 'no-cache, no-transform');
res.setHeader('X-Accel-Buffering', 'no');

for (let i = 0; i < 5; i++) {
res.write(`data: Hello seq ${i}\n\n`);
await sleep(1000);
}
res.end('done\n');
};

export default handler;
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<title>EventSource test</title>
</head>
<body>
<h1>EventSource test</h1>
<ul></ul>

<script type="text/javascript">
var logger = document.getElementsByTagName('ul')[0],
socket = new EventSource('/');

var log = function (text) {
logger.innerHTML += '<li>' + text + '</li>';
};

socket.onopen = function () {
log('OPEN');
};

socket.onmessage = function (event) {
log('MESSAGE: ' + event.data);
};

socket.addEventListener('update', function (event) {
log('UPDATE(' + event.lastEventId + '): ' + event.data);
});

socket.onerror = function (event) {
log('ERROR: ' + event.message);
};
</script>
</body>
</html>

CSS 导入字体

config.module.rules.push({
test: /\.(eot|woff|woff2|ttf)$/,
use: {
loader: 'url-loader',
options: {
limit: 100000,
name: '[name].[ext]',
},
},
});

导入 SVG

  • url-loader inline 会生成 dataurl - react 可能导致异常
  • next-images
// Typescript 确保不会出现类型错误
declare module '*.svg';

declare module '*.png';
declare module '*.jpg';
declare module '*.jpeg';
declare module '*.gif';

使用 @svgr/webpack

yarn add --dev @svgr/webpack babel-loader
config.module.rules.push({
test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
use: [
{
loader: 'babel-loader',
},
{
loader: '@svgr/webpack',
options: {
babel: false,
icon: true,
},
},
],
});
// 区分 js 或 css 导入
config.module.rules.push({
test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
issuer: {
test: /\.[tj]sx?$/,
},
use: ['babel-loader', '@svgr/webpack', 'file-loader'],
});

config.module.rules.push({
test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
loader: 'url-loader',
});

使用 inline-react-svg

  • 不可以 使用 tsconfig 中的 path
  • 会用 SVGO 优化
yarn add --dev babel-plugin-inline-react-svg
{
"presets": ["next/babel"],
"plugins": ["inline-react-svg"]
}