问题简述
近期时常有其他团队同学询问运行 Next.js 项目遇见的如下报错, 考虑到这块资料较少, 所以本次就简单记录一下。
1 | # 错误信息1 |
1 | # 错误信息2 |
问题原因
错误信息1 的原因是 Node 环境进行时发现 node_modules 中有 js 文件 require 了 *.scss 文件, 因为 Node 默认只能解析 .js, .mjs, .json, .node 后缀文件。
注意 SSR 项目会打包出两份文件, 一份是正常的客户端渲染时在浏览器端运行, 一份是服务端渲染时 Node 端运行。Node 端运行的这份代码通常会把 node_modules 的包设置为 externals, 这样能有效避免 node_modules 中某个包如果不 externals 会存在一个引用在 bundle.js, 一个引用在 node_modules 中。externals 后如 react 就仅 node_modules 中一个实例。
1 | // node_modules/pkg/index.js |
如何扩展 Node 可识别的文件类型了, 了解过 ts-node 实现就比较清楚了。 如下代码即可设置 Node 对于 .scss 文件的处理函数。这里我们无需真实转换, 设置一个空函数忽略即可。
1 | require.extensions['.scss'] = () => {} |
作为对照, 可以看下如下 Node 处理 .json 文件的逻辑
1 | // lib/internal/modules/cjs/loader.js |
综上所述, next.config.js 加入如下代码, next dev 就解决 报错1 了。为什么 next export / next build 命令还会有问题了? 原因是 next 进行 SSG 时, 是按照每个页面一个单独的线程并行进行的任务, 如下的赋值语句子线程不能生效。
1 | require.extensions['.scss'] = () => {} |
那么我们可以在每个页面运行加上上面的代码就能解决了。不过我们要加入的是如下的代码, 因为 require 关键字会被 webpack 给编译, webpack 会提供 require 相关的 api, 编译后 require 将不复存在。故 webpack 提供了 non_webpack_require 使得编译后能够保留 require 关键字, 即 non_webpack_require (编译前) => require (编译后)。
1 | __non_webpack_require__.extensions['.scss'] = () => {} |
错误信息2 是打包给客户端渲染时的代码遇见了发现 node_modules 中有 js 文件 require 了 *.css 等文件的校验报错。即不给这样的代码放行。
解决的办法就是遍历 webpackConfig 剔除校验的 error-loader。
1 | if (item.use && item.use.loader === "error-loader") { |
Next.js v12 已经内置了 .scss, .css 文件的支持, 但是不会处理 node_modules 中的 js 文件 require 的 *.css。
解决的办法就是遍历 webpackConfig 篡改 issuer.not 的配置使得能够支持。
1 | if ( |
问题解决
这块解决的代码 2年写到这个包中, 本次也是适配了 Next.js v12, 有需要或者想查阅完整的代码可以点击 xiaoxiaojx/with-ssr-entry
1 | // next.config.js |
以上介绍的都是如何逃避 Next.js 的检查, 适用于推动不了第三包去修改代码。如果你是这些包的负责人, 则可以像 antd 这样书写正确推荐的代码。即对于 antd 组件来说 date-picker.js 内部不引入 .css 文件, 转而由业务项目 src 下的代码去引入所需的 css。
1 | // my-app/src/** |