Next.js我说迁就迁了!

4,324 阅读3分钟

起因

Next.js的优缺点就不再赘述了,这里列出在业务场景下迁移到Next.js的一些原因。

  • Next.js文件即路由的思想使我们不需要过多关注于路由,极大简化了相关的配置
  • 支持静态页面导出(SSG),无服务器、渲染极快
  • webpack完全可自定义,在项目起初没有选择cra等脚手架的一大原因就是webpack配置不自由
  • ...

迁移历程

期间经历了各种坑,大多是SSR导致的各种限制,在使用Next.js之前最好了解一些SSR的概念,就不至于遇到问题时束手无策。 以下是迁移的过程、遇到的问题以及对应的解决方案。

路由系统

使用react-route-dom我们需要这样组织我们的路由:

<BrowserRouter>
  <Routes>
    <Route path="/" element={<App />}>
      <Route index element={<Home />} />
      <Route path="teams" element={<Teams />}>
        <Route path="new" element={<NewTeamForm />} />
      </Route>
    </Route>
  </Routes>
</BrowserRouter>

Next.js中我们只需要把页面组件放到src/pages/*即可,例如: image.png 如果用到了api路由则使用Next.js提供的hooks

import { useRouter } from 'next/router'

环境变量

我们的项目区分多个平台需要在不同的平台加载不同的环境变量,在此之前Vite允许我们自定义模式及其环境变量,但Next.js只提供development``production``localtion,所以我们在开发/构建之前通过执行脚本来动态生成配置文件。

// package.json
{
  "scripts": {
    "dev": "yarn dev:public",
    "dev:dingtalk": "export APP_ENV=dingtalk && node loadEnv.js && next dev",
    "dev:public": "export APP_ENV=public && node loadEnv.js && next dev",
  }
}
// loadEnv.js
const fs = require('fs');
const APP_ENV = process.env.APP_ENV;
fs.writeFileSync(".env", `NEXT_PUBLIC_PLATFORM=${APP_ENV}`);

全局变量

由于Next.js默认开启SSR,服务端无法访问浏览器全局变量,所以应当检查window等全局变量是否使用,如果有则对服务端兼容处理。

const isServer = _ => typeof window === 'undefined';

const composeEnhancers = !isServer() ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose : compose;

同步服务端Redux Store

服务端也会创建一个Redux Storenext-redux-wrapper可以帮我们同步Redux Store

Redux Store 持久化

服务端渲染时服务端和客户端都会创建一个redux store,所以我们需要next-redux-wrapper保证两个store保持相同的状态。

由于业务需要刷新页面之后要保证store中的数据不丢失,我用到了redux-persist来缓存store中的数据,next-redux-wrapper提供了和redux-persist一起使用的文档,测试之后出现了页面空白的问题,经排查之后发现文档给出的示例代码存在书写错误导致组件没有挂载,顺便就提了个 PR。 image.png

反向代理

Next.js提供了重写的相关配置,复杂的请求需要自定义服务器实现。

// next.config.js
export default {
  async rewrites() {
    return [
      {
        source: '/api/:path*',
        destination: 'http://1xx.x.1xx.xx:1234/api/:path*',
      },
    ];
  },
};

依赖兼容

引入全局CSS

Next.js禁止依赖加载全局CSS,但是作为开发者我们无法保证所有依赖包都不加载全局CSS,如果依赖引入了全局CSS``Next.js会抛出异常。

RFC: github.com/vercel/next…

RFC已经提出一年多的时间了,目前还是没有进展,在了解背景之后我找到了一下两种解决方案(已测试可行): next-global-css next-transpile-modules

服务端导入依赖问题

极个别的依赖项在服务端引入时具有CJS的特征,需要.default才可以获取到依赖的值,暂时没搞明白原因,有懂哥的请告知,暂时的解决办法是进行兼容处理。

import logger from 'redux-logger';
import thunk from 'redux-thunk';

// 兼容处理
const thunkMiddleware = thunk.default ? thunk.default : thunk;
const loggerMiddleware = logger.default ? logger.default : logger;

CSS

详见:nextjs.org/docs/basic-…

为了防止命名冲突Next.js只允许在pages/_app.js中引入css,或者可以使用css module

静态资源

需要放在/public文件夹内,例:

/*
-public
--logo.png
*/

<Image src="/logo.png" width={16} height={16} />


SSG限制

生成静态页面之后,Next.js无法进行对next/image进行优化,需要在配置项中手动关闭。

// next.config.js
export default withTM({
  reactStrictMode: true,
  experimental: {
      images: {
        unoptimized: true,
      },
  }
});