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/9965const 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"]}