使用 Express.js

使用 Express 时,热服务器模块重载可能不会在 SSR 之后工作。

这是因为 Express 有自己的路由栈,所以仅重载缓存并不足以让 Express 内部的路由重新加载。

在 Express 中添加全局回调

对于 Express 和 Next.js 的热模块重载,需要设置一个全局回调来清除 Express 的路由缓存。

这允许在不重启服务器的情况下识别路由更新。

server/express.js
import express from 'express';
import next from 'next';

const dev = process.env.NODE_ENV !== 'production';
const hostname = 'localhost';
const port = 3000;
const app = next({ dev, hostname, port });
const handle = app.getRequestHandler();

global.clearRoutes = () => {
  server._router.stack = server._router.stack.filter((k) => !(k && k.route && k.route.path));
};

app.prepare().then(() => {
  const server = express();

  server.all('*', (req, res) => {
    const parsedUrl = new URL(req.url, `http://${req.headers.host}`);
    const { pathname, query } = parsedUrl;

    handle(req, res, parsedUrl);
  });

  server.listen(port, () => {
    console.log(`> Ready on http://${hostname}:${port}`);
  });
});

在重新验证期间触发回调

_document.js 中添加一个全局回调,在重新验证期间清除 Express 路由缓存,允许在不重启服务器的情况下提供更新后的路由。

pages/_document.js
class MyDocument extends Document {
  static async getInitialProps(ctx) {
    if (ctx?.pathname && !ctx?.pathname?.endsWith('_error')) {
      await revalidate().then((shouldUpdate) => {
        if (shouldUpdate) {
          global.clearRoutes();
        }
      });
    }

    const initialProps = await Document.getInitialProps(ctx);
    return initialProps;
  }

  render() {
    return (
      <Html>
        <Head />
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}