在没有构建系统的情况下编写 Javascript
当我开始一个新的前端项目时，我面临的问题是：我是否应该使用构建系统？
Javascript 构建系统
122.189.99.114
JS玩家喜欢重新发明一切，就连他们的构建工具都是一年一换。
来自湖北荆州的 Chrome Mobile 111.0|Android 10 用户
2023-03-30T14:21:18.000
当我开始一个新的前端项目时，我面临的问题是：我是否应该使用构建系统？
![](/data/attachment/album/202303/27/120324jeqlswqte9exfxhh.jpg)


嗨！这周我一直在写一些 Javascript，和往常一样，当我开始一个新的前端项目时，我面临的问题是：我是否应该使用构建系统？


我想谈谈构建系统对我有什么吸引力，为什么我（通常）仍然不使用它们，以及一些前端 Javascript 库要求你使用构建系统时，为什么我觉得这让我感到沮丧。


我写这篇文章是因为我看到的大多数关于 JS 的文章都假定你正在使用构建系统，而对于像我这样的人来说，编写非常简单的、不需要构建系统的小型 Javascript 项目时，构建系统可能反而添加了很多麻烦。


#### 什么是构建系统？


构建系统的思路是，你有一堆 Javascript 或 Typescript 代码，你想在把它放到你的网站上之前把它翻译成不同的 Javascript 代码。


构建系统可以做很多有用的事情，比如：


* （出于效率的考虑）将 100 多个 JS 文件合并成一个大的捆绑文件
* 将 Typescript 翻译成 Javascript
* 对 Typescript 进行类型检查
* 精简化
* 添加 Polyfills 以支持旧的浏览器
* 编译 JSX
* <ruby> 摇树优化 <rt>  Tree Shaking </rt></ruby>（删除不使用的 JS 代码以减少文件大小）
* 构建 CSS（像 [tailwind](https://tailwindcss.com/) 那样）
* 可能还有很多其他重要的事情


正因为如此，如果你今天正在构建一个复杂的前端项目，你可能会使用 Webpack、Rollup、Esbuild、Parcel 或 Vite 等构建系统。


很多这些功能对我很有吸引力，我过去使用构建系统也是出于这样一些原因： 例如，[Mess With DNS](https://messwithdns.net/) 使用 Esbuild 来翻译 Typescript，并将许多文件合并成一个大文件。


#### 目标：轻松地对旧的小网站进行修改


我做了很多简单的小网站（[之一](https://css-examples.wizardzines.com/)、[之二](https://questions.wizardzines.com)、[之三](https://sql-playground.wizardzines.com/)、[之四](https://nginx-playground.wizardzines.com/)），我对它们的维护精力大约为 0，而且我改变它们的频率很低。


我的目标是，如果我有一个 3、5 年前做的网站，我希望能在 20 分钟内，


* 在一台新的电脑上从 GitHub 获取源代码
* 做一些修改
* 把它放到互联网上


但我对构建系统（不仅仅是 Javascript 构建系统！）的经验是，如果你有一个 5 年历史的网站，要重新构建这个网站会非常痛苦。


因为我的大多数网站都很小，所以使用构建系统的 *优势* 很小 —— 我并不真的需要 Typescript 或 JSX。我只要有一个 400 行的 `script.js` 文件就可以了。


#### 示例：尝试构建 SQL 实验场


我的一个网站（[SQL 试验场](https://sql-playground.wizardzines.com/)）使用了一个构建系统（它使用 Vue）。我最后一次编辑该项目是在 2 年前，是在另一台机器上。


让我们看看我今天是否还能在我的机器上轻松地构建它。首先，我们要运行 `npm install`。下面是我得到的输出：



```
$ npm install
[lots of output redacted]
npm ERR! code 1
npm ERR! path /Users/bork/work/sql-playground.wizardzines.com/node_modules/grpc
npm ERR! command failed
npm ERR! command sh /var/folders/3z/g3qrs9s96mg6r4dmzryjn3mm0000gn/T/install-b52c96ad.sh
npm ERR! CXX(target) Release/obj.target/grpc/deps/grpc/src/core/lib/surface/init.o
npm ERR!   CXX(target) Release/obj.target/grpc/deps/grpc/src/core/lib/avl/avl.o
npm ERR!   CXX(target) Release/obj.target/grpc/deps/grpc/src/core/lib/backoff/backoff.o
npm ERR!   CXX(target) Release/obj.target/grpc/deps/grpc/src/core/lib/channel/channel_args.o
npm ERR!   CXX(target) Release/obj.target/grpc/deps/grpc/src/core/lib/channel/channel_stack.o
npm ERR!   CXX(target) Release/obj.target/grpc/deps/grpc/src/core/lib/channel/channel_stack_builder.o
npm ERR!   CXX(target) Release/obj.target/grpc/deps/grpc/src/core/lib/channel/channel_trace.o
npm ERR!   CXX(target) Release/obj.target/grpc/deps/grpc/src/core/lib/channel/channelz.o

```

在构建 `grpc` 时出现了某种错误。没问题。反正我也不需要这个依赖关系，所以我可以花 5 分钟把它拆下来重建。现在我可以 `npm install` 了，一切正常。


现在让我们试着构建这个项目：



```
$ npm run build
  ?  Building for production...Error: error:0308010C:digital envelope routines::unsupported
    at new Hash (node:internal/crypto/hash:71:19)
    at Object.createHash (node:crypto:130:10)
    at module.exports (/Users/bork/work/sql-playground.wizardzines.com/node_modules/webpack/lib/util/createHash.js:135:53)
    at NormalModule._initBuildHash (/Users/bork/work/sql-playground.wizardzines.com/node_modules/webpack/lib/NormalModule.js:414:16)
    at handleParseError (/Users/bork/work/sql-playground.wizardzines.com/node_modules/webpack/lib/NormalModule.js:467:10)
    at /Users/bork/work/sql-playground.wizardzines.com/node_modules/webpack/lib/NormalModule.js:499:5
    at /Users/bork/work/sql-playground.wizardzines.com/node_modules/webpack/lib/NormalModule.js:356:12
    at /Users/bork/work/sql-playground.wizardzines.com/node_modules/loader-runner/lib/LoaderRunner.js:373:3
    at iterateNormalLoaders (/Users/bork/work/sql-playground.wizardzines.com/node_modules/loader-runner/lib/LoaderRunner.js:214:10)
    at iterateNormalLoaders (/Users/bork/work/sql-playground.wizardzines.com/node_modules/loader-runner/lib/LoaderRunner.js:221:10)
    at /Users/bork/work/sql-playground.wizardzines.com/node_modules/loader-runner/lib/LoaderRunner.js:236:3
    at runSyncOrAsync (/Users/bork/work/sql-playground.wizardzines.com/node_modules/loader-runner/lib/LoaderRunner.js:130:11)
    at iterateNormalLoaders (/Users/bork/work/sql-playground.wizardzines.com/node_modules/loader-runner/lib/LoaderRunner.js:232:2)
    at Array.<anonymous> (/Users/bork/work/sql-playground.wizardzines.com/node_modules/loader-runner/lib/LoaderRunner.js:205:4)
    at Storage.finished (/Users/bork/work/sql-playground.wizardzines.com/node_modules/enhanced-resolve/lib/CachedInputFileSystem.js:43:16)
    at /Users/bork/work/sql-playground.wizardzines.com/node_modules/enhanced-resolve/lib/CachedInputFileSystem.js:79:9

```

[这个 Stack Overflow 的答案](https://stackoverflow.com/questions/69692842/error-message-error0308010cdigital-envelope-routinesunsupported) 建议运行 `export NODE_OPTIONS=--openssl-legacy-provider` 来解决这个错误。


这很有效，最后我得以 `npm run build` 来构建这个项目。


这其实并不坏（我只需要删除一个依赖关系和传递一个略显神秘的 Node 选项！），但我宁愿不被那些构建错误破坏。


#### 对我来说，对于小项目来说，构建系统并不值得


对我来说，一个复杂的 Javascript 构建系统对于 500 行的小项目来说似乎并不值得 —— 它意味着放弃了在未来能够轻松更新项目的能力，以换取一些相当微小的好处。


#### Esbuild 似乎更稳定一些


我想为 Esbuild 大声叫好： 我 [在 2021 年了解到 Esbuild](https://jvns.ca/blog/2021/11/15/esbuild-vue/)，并用于一个项目，到目前为止，它确实是一种更可靠的构建 JS 项目的方式。


我刚刚尝试在一台新电脑上构建一个我最后一次改动在 8 个月前的 Esbuild 项目，结果成功了。但我不能肯定的说，两年后我是否还能轻松的建立那个项目。也许会的，我希望如此！


#### 不使用构建系统通常是很容易的


下面是 [Nginx 实验场](https://nginx-playground.wizardzines.com/) 代码中导入所有库的部分的样子：



```
<script src="js/vue.global.prod.js"></script>
<script src="codemirror-5.63.0/lib/codemirror.js"></script>
<script src="codemirror-5.63.0/mode/nginx/nginx.js"></script>
<script src="codemirror-5.63.0/mode/shell/shell.js"></script>
<script src="codemirror-5.63.0/mode/javascript/javascript.js"></script>
<link rel="stylesheet" href="codemirror-5.63.0/lib/codemirror.css">
<script src="script.js "></script>

```

这个项目也在使用 Vue，但它只是用 `<script src` 来加载 Vue —— 前端没有构建过程。


#### 一个使用 Vue 的无构建系统模板


有几个人问如何在没有构建系统的情况下开始编写 Javascript。当然，如果你想的话，你可以写原味的 JS，但我常用的框架是 Vue 3。


[这是我做的一个小模板](https://github.com/jvns/vue3-tiny-template)，用于启动一个没有构建系统的 Vue 3 项目。它只有 2 个文件和大约 30 行的 HTML/JS。


#### 有些库需要你使用构建系统


构建系统这些事情最近盘旋在我的脑海里，因为这周我正在用 CodeMirror 5 做一个新项目，我看到有一个新版本，CodeMirror 6。


所以我想 —— 很酷，也许我应该使用 CodeMirror 6 而不是 CodeMirror 5。但是 —— 似乎没有构建系统你就不能使用 CodeMirror 6（根据 [迁移指南](https://codemirror.net/docs/migration/)），所以我打算坚持使用 CodeMirror 5。


同样地，你以前可以把 Tailwind 作为一个巨大的 CSS 文件下载，但是 [Tailwind 3](https://tailwindcss.com/docs/installation) 似乎根本不能作为一个大的 CSS 文件使用，你需要运行 Javascript 来构建它。所以我现在要继续使用 Tailwind 2。（我知道，我知道，你不应该使用大的 CSS 文件，但是它验收只有 300KB，而且我真的不希望有构建步骤）


（更正：看起来 Tailwind 在 2021 年发布了一个 [独立的命令行工具](https://tailwindcss.com/blog/standalone-cli)，这似乎是一个不错的选择）


我不完全确定为什么有些库不提供无构建系统版本 —— 也许发布无构建系统版本会给库增加很多额外的复杂性，而维护者认为这不值得。或者，库的设计意味着由于某种原因，不可能发布无构建系统版本。


#### 我希望有更多的无构建系统的 Javascript 技巧


到目前为止，我的主要策略是：


* 在某个库的网站上搜索 “CDN”，找到一个单独的 Javascript 文件
* 使用 <https://unpkg.com> 来查看该库是否有一个我可以使用的内置版本
* 托管我自己的库的版本，而不是依赖可能崩溃的 CDN
* 编写我自己的简单集成方案，而不是拉入另一个依赖关系（例如，前几天我为 Vue 编写了自己的 CodeMirror 组件）。
* 如果我想要一个构建系统，就使用 Esbuild


还有一些看起来很有趣但我还没有研究过的东西：


* 这个 [关于 Javascript 注释中类型语法的 Typescript 建议](https://devblogs.microsoft.com/typescript/a-proposal-for-type-syntax-in-javascript/)
* 一般来说，ES 模块




---


via: <https://jvns.ca/blog/2023/02/16/writing-javascript-without-a-build-system/>


作者：[Julia Evans](https://jvns.ca/) 选题：[lkxed](https://github.com/lkxed/) 译者：[wxy](https://github.com/wxy) 校对：[wxy](https://github.com/wxy)


本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出

