接上一次模块化爬坑经历,这里只围绕一个关注点:

esm模式的web端项目下,如何polyfill nodejs生态(主要是服务端包或者服务端api),使网页在浏览器运行时不报错。

这里我使用打包器的是vite(esmodule的拥趸,esm的天堂),当然webpack和rollup也有相应的解决方案,而且可能更容易找到,所以我只尝试了vite。

遇到的问题和chat-gpt

之前说过,遇到的问题都是浏览器运行时找不到nodejs相关包的报错。

适逢chat GPT-4o上线,免费使用一下。

这里抽出几个关键的问答,(回答有些小纰漏我已去掉):

1. nodejs包:process

jstt使用到了process.cwd,问了chat-gpt之后,得到答案:@esbuild-plugins/node-globals-polyfill可以解决。实操验证可行。附问答:

问:你说到的webpack或rollup插件排除特定依赖,能详细介绍一下vite相关的吗?

答:

安装@esbuild-plugins/node-globals-polyfill

首先,需要安装这个插件:

npm install @esbuild-plugins/node-globals-polyfill --save-dev

或者使用Yarn:

yarn add @esbuild-plugins/node-globals-polyfill --dev

配置Vite

安装完成后,按照以下步骤配置你的 vite.config.js

import { defineConfig } from 'vite';
import { NodeGlobalsPolyfillPlugin } from '@esbuild-plugins/node-globals-polyfill';
 
export default defineConfig({
  plugins: [
    // 其他插件...
  ],
  optimizeDeps: {
    esbuildOptions: {
      // Node.js global to browser globalThis
      define: {
        global: 'globalThis'
      },
      // Enable esbuild polyfill plugins
      plugins: [
        NodeGlobalsPolyfillPlugin({
          process: true,
          buffer: true,
        })
      ]
    }
  },
});
 

2. nodejs 包:path

jstt使用了path.resolve之类的path相关的api。问了chat-gpt之后,得到答案里依旧有@esbuild-plugins/node-globals-polyfill,但是这里是错的:@esbuild-plugins/node-globals-polyfill不能解决path的问题(此插件并没有path选项)。

不过在回答里的其他方案是可行的,即path-browserify,参考之后问题实操可解决。附问答:

问:继续报错path.basename is not a function,怎么解决

答:

使用path-browserify 替代 path

安装 path-browserify,它是一个可以在浏览器环境中使用的 path 模块的替代品。

npm install path-browserify --save-dev

或者使用 Yarn:

yarn add path-browserify --dev

然后在你的代码中引入 path-browserify,并使用其提供的 basename 方法:

import path from 'path-browserify';
 
const filename = '/path/to/your/file.txt';
const base = path.basename(filename);
console.log(base); // 输出: 'file.txt'
 

第二个问题手动修正

第二问GPT没有答完整,不过也提供了解决方案了,即path-browserify这个库。我尝试在vite.config.js中给它配置成path的别名,path找不到的问题就解决了,即可实现构建时替换。

// vite.config.js
defineConfig({
	// 这里省略其他配置
    resolve: {
      alias: {
        // 添加path的别名
        path: 'path-browserify'
      }
    }
}

3 nodejs包:url (暂未解决,可升级绕过)

其实process和path搞定后,这个json-schema-to-typescript插件就可以在web上正常运行了,但是10.x版本还会有url的相关报错,比如找不到url.parse等等。

尝试找到一个polyfill:url-polyfill,按照path的处理方式再处理一遍也不行,调用compile时还是报错。

json-schema-to-typescript最新版本14.x中,不再有url的报错了

但是使用到了prettier,也会产生报错:module.createRequire找不到(开发时没问题,构建还是会报错)。

这里就不再去修复module.createRequire相关问题了,解决思路一样,找polyfill,找不到就mock或者替换别名(麻烦)。

所以放弃转成esm了。

为什么browserify打包的产物就能直接用

懒得搜资料了,GPT的答案是:

Browserify 和 Vite 是两个不同的工具,它们在处理模块和构建过程中采取了不同的策略。

Browserify

Browserify 是一个流行的构建工具,用于在浏览器中使用 CommonJS 模块规范。它会将所有的 JavaScript 文件打包成一个或多个文件,使得它们可以在浏览器环境中运行。

由于 Browserify 专注于处理 CommonJS 模块,并且会模拟 Node.js 的全局变量和内置模块,因此它在打包时会自动处理对于 Node.js 特定 API 的调用,比如 process.cwd()path.basename()。这意味着你不需要额外的插件或配置来处理这些问题。

Vite

Vite 是一个现代化的构建工具,用于快速开发现代 Web 应用。它使用了 ES 模块(ESM)作为默认的模块系统,并利用浏览器原生的 ES 模块加载器进行开发和构建。

由于 Vite 使用了原生的 ES 模块加载器,并且不会自动模拟 Node.js 的全局变量和内置模块,因此在引入一些包含 Node.js 特定 API 的模块时,可能会出现问题,比如 process.cwd()path.basename()。为了解决这些问题,你需要手动配置 Polyfill 或使用相应的插件来处理。

结论

虽然 Browserify 和 Vite 都是用于构建 JavaScript 应用的工具,但它们的工作方式和目标不同,因此在处理模块和构建过程中需要不同的策略。在选择使用哪个工具时,你可以根据项目的需求和特点来进行权衡,并选择最适合的工具。

至此,在esm下使用cjs的包,这个命题算是放弃了。

再次验证了一句话:万能的工具不存在,只有合适的工具。这种特殊场景下,旧世界里的工具(browserify)还是会趁手的。以前只是了解browserify能打包,是以前的打包工具,忽略了它确实是解决过很多问题。

彩蛋

2024-04-03 CommonJS 和 ES Module 终于要互相兼容了???: 其实是cjs单方面兼容ESM

2024-04-28 Node.js 22 正式发布,支持 Require() ESM 模块!