原文的链接: css-tricks.com/comparing-t…
A bunch of new developer tools have landed in the past year and they are biting at the heels of the tools that have dominated front-end development over the last few years, including webpack, Babel, Rollup, Parcel, create-react-app.
These new tools aren’t designed to perform the exact same function, and each has different things they’re trying to achieve and features to get there. Despite their differences, these tools do share a common goal: improve the developer experience.
Table of contents
在过去的一年里,一系列新的开发者工具落地,它们紧随过去几年主导前端开发的工具,包括 webpack、Babel、Rollup、Parcel、create-react-app。
这些新工具并非旨在执行完全相同的功能,每个工具都有不同的目标和功能。 尽管存在差异,但这些工具确实有一个共同的目标:改善开发人员体验。
目录
Specifically, I’d like to evaluate each one, outlining what they do, why we need them, and their use cases. I realize that comparisons aren’t always fair. Again, it’s not like any of the things we’re looking at in this article are direct competitors. In fact, Snowpack and Vite actually
use
esbuild under the hood for certain tasks. Our goal is more to get a better view of the landscape of developer tools that run tasks to make our jobs easier. This way, we see what options are out there and how they stack up, so we can make the best choices when we need them.
Of course, all of this will be colored by my experience using React and Preact. I’m more familiar with these frameworks libraries, but we’ll look at their support for other front-end frameworks too.
There have been a whole lot of great articles, streams, and podcasts about these new developer tools. There are a couple of ShopTalk Show episodes I’d recommend for more context: Episode 454 discusses Vite and Episode 448 features the creators of wmr and Snowpack. Something that stands out from these episodes is that a huge amount of work has gone into building these tools to modernize our developer environments.
具体来说,我想评估每一个工具,概述它们的用途,为什么我们需要它们,以及它们的用例。我意识到比较并不总是公平的。同样,我们在本文中看到的任何东西都不是直接竞争对手。事实上,Snowpack 和 Vite 实际上在某些任务中都去使用esbuild。我们的目标是更好地了解开发人员工具的情况,这些工具运行任务,使我们的工作更容易。这样,我们就能看到有哪些选择,以及它们是如何叠加的,在需要的时候做出最好的选择。
当然,所有这些都会因为我使用React和Preact的经验而有所不同。我更熟悉这些框架库,但我们也会看看它们对其他前端框架的支持。
关于这些新的开发工具,已经有很多很棒的文章、流和播客。我推荐一些关于ShopTalk Show的剧集:第454集讨论了Vite,第448集介绍了wmr和Snowpack的创建者。从这些章节中突出的一点是,为了使我们的开发人员环境现代化,我们进行了大量的工作来构建这些工具。
Why are these tools all arriving now?
In part, I think these tools are arriving as a reaction to JavaScript tooling fatigue — something captured nicely in this article about learning JavaScript back in 2016. They also fill a missing middle ground between writing a single vanilla JavaScript file, and having to download 200 megabytes of tooling dependencies before you’ve written a line of your own code. They come batteries-included without the dependency list, and are part of a trend of collapsing layers in the JavaScript ecosystem.
Snowpack, Vite, and wmr have all been enabled by native JavaScript modules in the browser. Back in 2018, Firefox 60 was released with ECMAScript 2015 modules enabled by default. Since then, all major browser engines have supported native JavaScript modules. Node.js also shipped with native JavaScript modules in November 2019. We’re still finding out what possibilities native JavaScript modules unlock today in 2021.
为什么出现这些工具了?
在某种程度上,我认为这些工具的出现是对JavaScript工具疲劳的一种反应——这篇关于2016年学习JavaScript的文章很好地提到了这一点。它们还填补了编写单个普通JavaScript文件和在编写自己的代码之前必须下载200mb的工具依赖之间的中间地带。
它们有内置的功能,但没有依赖列表,是JavaScript生态系统中崩溃层趋势的一部分。
Snowpack、Vite和wmr都是通过浏览器中的本地JavaScript模块启用的。早在2018年,Firefox 60发布时默认启用了ECMAScript 2015模块。从那时起,所有主流浏览器引擎都支持本地JavaScript模块。Node.js也在2019年11月发布了原生JavaScript模块。我们仍在寻找原生JavaScript模块在2021年解锁的可能性。
How are these different from existing tools?
Whether we use webpack, Rollup, or Parcel for a development server, the tool bundles our entire codebase from our source code and a node_modules folder, runs these through build processes — like Babel, TypeScript, or PostCSS — then pushes the bundled code to our browser. This all takes work, and can slow development servers to a crawl in larger codebases, even after all the work that’s gone into caching and optimizing.
Snowpack, Vite, and wmr development servers don’t follow this model. Instead, they wait until the browser finds an import statement and makes an HTTP request for the module. Only after this request is made will the tool apply transforms to the requested module and any leaf nodes in the module’s import tree, then serve these to the browser. This speeds things up a lot as there’s less work in the process of pushing to a dev server.
You’ll notice esbuild missing from this picture. It’s a
bundler
first and foremost. It doesn’t side-step bundling the way the other tools do. Instead, esbuild processes code extremely fast by avoiding expensive transformations, leveraging parallelization and using the Go language.
这些工具与现有工具有何不同?
无论我们在开发服务器上使用webpack、Rollup还是Parcel,该工具都将我们的源代码和 node_modules 的整个代码打包,通过Babel、TypeScript或PostCSS等构建过程运行这些代码,然后将打包的代码推送到浏览器。这一切都需要工作,甚至在缓存和优化所有工作之后,也会使开发服务器在更大的代码库中缓慢运行。
Snowpack、Vite和wmr开发服务器不遵循这种模式。相反,它们会一直等待,直到浏览器找到 import 语句并为模块发出HTTP请求。只有在发出此请求后,工具才会将转换应用到所请求的模块和模块导入树中的任何叶子节点,然后将这些转换提供给浏览器。这大大提高了速度,因为在推送到开发服务器的过程中减少了工作。
你会注意到这张照片上的esbuild不见了。它首先是一个
bundler
。不像其他工具那样回避
bundler
。相反,esbuild通过避免昂贵的转换、利用并行化和使用Go语言来极其快速地处理代码。
The experiment
I took one of the the example apps from the React docs and rebuilt it with each tool covered in this article. The project I went with was Snap Shot by Yogita Verma. Here’s a link to the original repo, and a link to my repo with the four versions of Snap Shot, each using a different build tool. We’ll compare the output of each build step later. Rebuilding this app allowed me to test out the developer experience of pulling some pretty standard React dependencies into the tools, including React Router and axios.
这个实验
我从React文档中选取了一个示例应用,并使用本文中介绍的每个工具重新构建了它。我参与的项目是Yogita Verma的Snap Shot。这里有一个到原始仓库的链接,还有一个到我的仓库的链接,其中有四个版本的Snap Shot,每个版本都使用不同的构建工具。稍后我们将比较每个构建步骤的输出。重新构建这个应用程序让我可以测试开发人员将一些非常标准的React依赖项引入到工具中的体验,包括React Router和axios。
Comparable features
Before we get into the specifics of each individual tool, they
all
support the following features out of the box (to varying degrees):
First-class support for native JavaScript modules
TypeScript compilation (but not type checking)
JSX
Plugin API for extensibility
A built-in development server
CSS bundling and support for CSS-in-JS libraries
All of these tools can compile TypeScript into JavaScript, but will do so
even if there are type errors
. For proper type checking you would need to install TypeScript and run tsc --noEmit on your root JavaScript file, or alternatively, use editor plugins to watch for type errors.
OK, let’s take look at each tool.
类似的功能
在我们深入了解每个工具的细节之前,它们都支持以下功能(在不同程度上):
-
对原生JavaScript模块的一流支持
-
TypeScript编译(但不是类型检查)
-
JSX
-
插件API的可扩展性
-
内置开发服务器
-
CSS绑定和支持CSS-in-js库
所有这些工具都可以将TypeScript编译成JavaScript,但即使存在类型错误也会这样做。为了进行正确的类型检查,你需要安装TypeScript并在你的根JavaScript文件上运行tsc-noEmit,或者,使用编辑器插件来检查类型错误。
好的,让我们看看每一个工具。
esbuild
esbuild was created by Evan Wallace (CTO of Figma). Its main feature is that it provides a build step 10×-100× faster than Node-based bundlers (by their own benchmarks). It doesn’t provide many of the developer conveniences you might find in something like create-react-app. But there are more and more esbuild starters popping up that fills those gaps, including create-react-app-esbuild, estrella and Snowpack, which uses esbuild for its build step.
esbuild is very new. It hasn’t yet reached a 1.0 version and isn’t quite ready for production use — but it’s not far off. It gives you intuitive JavaScript and command line APIs with smart defaults.
esbuild
esbuild 由 Evan Wallace (Figma的首席技术官)创建。它的主要特性是提供了比基于 node 的 bundlers 更快的构建步骤10 -100(通过它们自己的基准测试)。它并没有提供像create-react-app那样的开发者便利。但越来越多的esbuild启动程序出现,填补了这些空白,包括create-react-app-esbuild, estrella和Snowpack,使用esbuild作为构建步骤。
esbuild是非常新的。它还没有达到1.0版本,还没有完全准备好生产使用,但它离成功不远了。它提供直观的JavaScript和带有智能默认值的命令行api。
Use cases
esbuild is a complete game-changer in the bundler world. It’s going to be most useful in large codebases where the speed difference between esbuild and node bundlers gets multiplied. When esbuild hits 1.0 it’s going to be
very
useful in big production sites and will save teams a whole lot of time waiting for builds to complete. Unfortunately, big production sites will have to wait until esbuild becomes stable. In the meantime, it’ll just be good to add some speed to your bundling in side projects.
esbuild’s lightning-fast speed will be a bonus for any kind of work that you’re doing. Less time spent waiting for builds to run is always going to be good for developer experience! This considered, if you’re prototyping quick applications you might want to start with something more high level than esbuild — otherwise, you’ll need to spend some time pulling in dependencies and configuring your environment before you get the conveniences we expect in the JavaScript ecosystem. Also, if you want to minimize the size of your bundle as much as possible you may want to use Rollup and terser, which will produce slightly smaller bundle sizes.
用例
esbuild在bundler领域是一个彻底的游戏规则改变者。它在大型代码库中最有用,因为esbuild和 node bundlers 之间的速度差距会扩大。当esbuild到达1.0版本时,它将在大型生产站点中非常有用,并将为团队节省大量等待构建完成的时间。不幸的是,大型生产站点将不得不等待esbuild变得稳定。与此同时,在你的附加项目中增加一些速度是很好的。
esbuild 闪电般的速度将是你正在做的任何工作的好处。花在等待构建运行上的时间越少,开发人员的体验就越好!考虑到这一点,如果您正在构建快速应用程序的原型,您可能希望从比esbuild更高级的东西开始,那么在获得我们在JavaScript生态系统中期望的便利之前,您将需要花一些时间引入依赖关系并配置您的环境。此外,如果你想尽可能地最小化你的包的大小,你可能会想使用Rollup和terser,可以提供更小的 bundle 体积。
Setup
I decided to start a React project in esbuild in a naïve way: npm installing esbuild, React and ReactDOM. I created a src/app.jsx file and a dist/index.html file. Then, I used the following command to compile the app into a dist/bundle.js file:
./node_modules/.bin/esbuild src/app.jsx --bundle --platform=browser --outfile=dist/bundle.js
When I hosted and opened index.html in the browser, I was met with the “white screen of death” and an “Uncaught ReferenceError: process is not defined” console error. Both the docs and the CLI explain exactly what you need to do to prevent this but it might be a bit of a “gotcha” for beginners, because it requires an extra argument when bundling React:
--define:process.env.NODE_ENV=\"production\"
Or, if you’re including esbuild in npm scripts written like this to escape the quotes:
--define:process.env.NODE_ENV=\\\"production\\\"
This define argument is needed for any library bundled for the browser that expects node environment variables. Vue 2.0 also expects these. You won’t have the same problem with Preact because it doesn’t expect any environment variables and ships ready for the browser by default.
After I ran the command with the define argument, my “Hello world” React app was working perfectly. JSX works out of the box with .jsx files. That said, React needs to be manually imported and then JSX is converted to the React.createElement. However, there are ways to add auto imports in JSX and/or configure JSX for Preact.
设置
我决定以 naïve 的方式在 esbuild 中启动一个 React 项目:
npm 安装 esbuild, React 和 ReactDOM。我创建了一个 src/app.jsx 文件和 dist/index.html 文件。
然后,我使用以下命令将应用程序编译成 dist/bundle.js 文件:
./node_modules/.bin/esbuild src/app.jsx --bundle --platform=browser --outfile=dist/bundle.js
当我在浏览器中打开 index.html 时,我遇到了“白屏”和一个“Uncaught ReferenceError: process is not defined”的控制台错误。
文档和 CLI 都准确地解释了你需要做什么来防止这种情况,但这对初学者来说可能有点“陷阱”,
因为在绑定 React 时需要一个额外的参数:
--define:process.env.NODE_ENV=\"production\"
或者,如果你在 npm 脚本中包含 esbuild 以转义引号
--define:process.env.NODE_ENV=\\\"production\\\"
在我用 define 参数运行命令后,我的“Hello world” React 应用程序运行得很完美 JSX 可以直接使用 .jsx 文件。也就是说,需要手动导入 React,然后将 JSX 转换为 React.createelement。然而,有一些方法可以在 JSX 中添加汽车导入和 / 或为 Preact 配置 JSX。
Usage
esbuild provides a --serve option for a development server. This bypasses the filesystem and serves modules straight from memory, ensuring that the browser doesn’t pull older versions of modules. However, it doesn’t include live/hot reloading, so you will find yourself refreshing the browser after saving which isn’t an ideal experience.
I decided to use the newly-released watch feature.This tells esbuild to recompile code every time a source file is saved. But we still need a server to see our saved changes. We can pull in a development server package, such as Luke Jackson’s servor:
npm install servor --save-dev
Then we can use the esbuild Javascript API to start as server and run esbuild’s watch mode at the same time. Let’s create a file at the root of our project called watch.js:
// watch.js
const esbuild = require("esbuild");
const servor = require("servor");
esbuild.build({
// pass any options to esbuild here...
entryPoints: ["src/app.jsx"],
outdir: "dist",
define: { "process.env.NODE_ENV": '"production"' },
watch: true,
});
async function serve(){
console.log("running server from: http://localhost:8080/");
await servor({
// pass any options to servor here...
browser:true,
root: "dist",
port: 8080,
});
}
serve();
Now run node watch.js in the command line. This gives us a nice dev server, though again, it doesn’t give us hot module replacement or fast refresh (i.e., your client-side state won’t be preserved). But this was enough for my testing needs.
Even though we’re rebundling our entire application every time we save a file, we’d need to have a pretty massive application before esbuild slows down. After I set up this tooling, I was getting instant feedback from changes. My computer uses an intel i7 from 2012, so it certainly isn’t a top-of-the-line machine.
If you need a preconfigured version of esbuild with live reload and some React defaults, you can clone this repo.
使用
esbuild 为开发服务器提供了一个—— serve选项。这绕过了文件系统,直接从内存中为模块提供服务,确保浏览器不会使用旧版本的模块。然而,它不包括实时/热加载,所以你会发现自己在保存后刷新浏览器,这不是一个理想的体验。
我决定使用新发布的 watch 功能。这告诉esbuild在每次保存源文件时重新编译代码。但是我们仍然需要一个服务器来查看保存的更改。我们可以引入一个开发服务器包,比如Luke Jackson的服务器:
npm install servor --save-dev
然后我们可以使用esbuild Javascript API作为服务器启动,并同时运行esbuild的监视模式。让我们在项目的根目录下创建一个名为watch.js的文件:
// watch.js
const esbuild = require("esbuild");
const servor = require("servor");
esbuild.build({
// pass any options to esbuild here...
entryPoints: ["src/app.jsx"],
outdir: "dist",
define: { "process.env.NODE_ENV": '"production"' },
watch: true,
});
async function serve(){
console.log("running server from: http://localhost:8080/");
await servor({
// pass any options to servor here...
browser:true,
root: "dist",
port: 8080,
});
}
serve();
现在在命令行中运行node watch.js。这给了我们一个很好的开发服务器,尽管它没有给我们热模块替换或快速刷新(即,您的客户端状态将不会被保留)。但这已经足够满足我的测试需求了。
即使我们每次保存一个文件时都要重新打包整个应用程序,但在esbuild减慢速度之前,我们需要有一个相当大的应用程序。在我设置了这个工具之后,我得到了来自更改的即时反馈。我的电脑使用的是2012年的英特尔i7,所以它肯定不是一台顶级的电脑。
如果你需要一个预配置的esbuild版本,带有实时加载和一些React默认值,你可以克隆这个repo。
Supported files
esbuild can import CSS in JavaScript if that’s your style. It will compile CSS into an output file with the same name as your main output JavaScript file. It can also bundle CSS @import statements by default. There is no support for CSS Modules, but there are plans for it.
There is a growing community of plugins for esbuild. For example, there are plugins available for Vue single file components, and Svelte components.
esbuild works with JSON files and can bundle them into JavaScript modules without any configuration.
It can also import images in JavaScript with the option to either convert them into data URLs or copying them into an output folder. This behavior isn’t enabled by default, but you can add the following in your esbuild config object to enable either option:
loader: { '.png': 'dataurl' } // Converts to data url in JS bundle
loader: { '.png': 'file' } // Copies to output folder
Code splitting appears to be a work in progress, but is mostly there in the ESM output format, and it does look like it is a priority for the project. It’s also worth mentioning that tree-shaking is built into esbuild by default and can’t be turned off.
支持文件
esbuild 可以在 JavaScript 中导入 CSS,如果这是你的风格。它将把 CSS 编译成与主输出 JavaScript 文件同名的输出文件。默认情况下,它也可以 bundle CSS @import语句。目前还不支持CSS模块,但已经有了相应的计划。
esbuild的插件社区正在不断壮大。例如,Vue单文件组件和Svelte组件有可用的插件。
esbuild可以使用JSON文件,并可以将它们捆绑到JavaScript模块中,而无需任何配置。
它还可以用JavaScript导入图像,并可以将其转换为数据url或将其复制到输出文件夹中。默认情况下,这个行为是不启用的,但你可以在你的esbuild配置对象中添加以下选项来启用任何一个选项:
loader: { '.png': 'dataurl' } // Converts to data url in JS bundle
loader: { '.png': 'file' } // Copies to output folder
代码分割似乎是一项正在进行的工作,但大部分是以ESM输出格式存在的,而且它看起来确实是项目的优先级。值得一提的是,tree-shaking 是esbuild默认内置的,不能关闭。
Production build
Using the “minify” and “bundle” options in your esbuild command won’t create a bundle quite as small as a Rollup/Terser pipeline. This is because esbuild sacrifices some bundle size optimization to get through your code in as few passes as possible. However, the difference may be pretty negligible, and worth it for the increase in bundling speed, depending on your project. In my clone of the Snap Shot application, esbuild created a bundle of 177 KB which isn’t a lot more than the 165KB produced by Vite, which uses rollup and terser.
生产构件
在你的 esbuild 命令中使用 "minify " 和 "bundle "选项不会创建一个像 Rollup/Terser 那么小的 bundle。这是因为 esbuild 牺牲了一些包大小的优化来尽可能少地通过你的代码。然而,这种差异可能是可以忽略不计的,这对于提高打包速度来说是值得的,这取决于您的项目。在我的 Snap Shot 应用程序的克隆版本中,esbuild 创建了一个177kb 的 bundle,这并不比 Vite 生成的 165KB 多多少,后者使用了 rollup 和 terser。
Overall
esbuild
Templates for multiple front end frameworks
❌
Hot module replacement development server
❌
Streaming imports
❌
Preconfigured production build
❌
Automatic PostCSS and preprocessor conversion
❌
HTM transform
❌
Rollup plugin support
❌
Size on disk (default install)
7.34 MB
esbuild is an extremely powerful tool. But it might be difficult if you’re used to zero-config setups. If you need more, then you might want to take a look at the next tool, Snowpack, which uses esbuild.
概览
esbuild
Templates for multiple front end frameworks
❌
Hot module replacement development server
❌
Streaming imports
❌
Preconfigured production build
❌
Automatic PostCSS and preprocessor conversion
❌
HTM transform
❌
Rollup plugin support
❌
Size on disk (default install)
7.34 MB
esbuild是一个非常强大的工具。但如果你习惯了零配置设置,这可能会很困难。如果您需要更多,那么您可能想看看下一个工具,使用esbuild的Snowpack。
Snowpack
Snowpack is a build tool by the creators of Skypack and Pika. It provides an awesome development server and was created with an “unbundled development” philosophy. To quote the documentation: “You should be able to use a bundler because you want to, and not because you need to.”
By default, Snowpack’s build step doesn’t bundle files into a single package but provides unbundled esmodules that run in the browser. esbuild is actually included in there as a dependency, but the idea is to use JavaScript modules and only bundle with esbuild when it’s needed.
Snowpack has some pretty slick documentation, including a list of guides for using it with JavaScript frameworks, and a bunch of templates for them. Some of the guides are still a work in progress, but others like the one for React are nice and clear. It also looks like Snowpack treats Svelte as a first-class citizen. I actually first heard about Snowpack from Rich Harris’s “Futuristic Web Development” talk at Svelte Summit 2020. That said, the upcoming Svelte meta-framework SvelteKit was supposed to be powered by Snowpack but has since switched to Vite (which we’ll review next).
Snowpack 是 Skypack 和 Pika 的创建的构建工具。 它提供了一个很棒的开发服务器,并以“非 bundled 开发”理念创建。 引用文档:“你应该能够使用打包器,因为你想,而不是因为你需要。”
默认情况下,Snowpack 的构建步骤不会将文件 bundle 到单个包中,而是提供在浏览器中运行的未 bundle 的 esmodules。 esbuild 实际上作为依赖项包含在其中,但其想法是使用 JavaScript 模块并且仅在需要时用 esbuild bundle。
Snowpack 有非常漂亮的文档,包括将它与 JavaScript 框架一起使用的指南列表,以及一堆它们的模板。 一些指南仍在制定中,但其他指南(例如 React 的指南)很好且清晰。 看起来 Snowpack 也将 Svelte 视为一等公民。 实际上,我第一次听说 Snowpack 是在 2020 年 Svelte 峰会上 Rich Harris 的“未来 Web 开发”演讲中。也就是说,即将推出的 Svelte 元框架 SvelteKit 应该由 Snowpack 提供支持,但此后已切换到 Vite(我们将在接下来进行回顾))。
Use cases
Snowpack is a good choice if you want to double down on unbundled deployment. You may be writing source code with a small number of modules. This would mean you’re not creating a big request waterfall with an unbundled build. If you don’t need the added complexity and technical debt of bundling, then Snowpack is a great choice. A good use case would be if you’re incrementally adopting a front-end framework into a server-rendered or static application. You’d be pulling in as little tooling as possible from the node ecosystem but you’d still be getting the benefits of declarative frontend frameworks.
如果您想在 unbundled 式部署上做一些尝试,Snowpack 是一个不错的选择。 您可能正在编写包含少量模块的源代码。 这意味着您不会使用 unbundled 的构建创建大型请求瀑布流。 如果您不需要 bundling 带来的额外复杂性和技术债务,那么 Snowpack 是一个不错的选择。 一个很好的用例是,如果您将前端框架逐步采用到服务器渲染或静态应用程序中。 您将尽可能少地从节点生态系统中引入工具,但您仍将获得声明式前端框架的好处。
Secondly, I’d argue that Snowpack is a great wrapper around esbuild. If you want to try out esbuild but also want a development server and pre-written templates for front-end frameworks, then you can’t go wrong with Snowpack. Enable esbuild in the build step of your Snowpack config and you’re good to go.
其次,我认为 Snowpack 是一个很好的 esbuild 封装。如果你想尝试 esbuild,但又想要一个开发服务器和预先为前端框架编写的模板,那么使用 Snowpack 绝对不会出错。在 Snowpack 配置的构建步骤中启用 esbuild,就可以开始了。
As things currently stand, I’d argue that Snowpack wouldn’t be the best replacement for a zero configuration tool like create-react-app because you’ll need to pull in plugins and configure them yourself if you have a big application and need a super-fancy optimized production-ready build step.
就目前的情况来看,我认为 Snowpack 并不是像 create-react-app 这样的零配置工具的最佳替代品,因为如果你有一个大的应用程序,需要一个超炫的优化生产就绪的构建步骤,你就需要引入插件并自己配置它们。
Setup
Let’s start a project with Snowpack by jumping into the command line:
mkdir snowpackproject
cd snowpackproject
npm init #fill with defaults
npm install snowpack
Now, let’s add the following to package.json:
// package.json
"scripts": {
"start": "snowpack dev",
"build": "snowpack build"
},
Next, we’ll create a configuration file:
// Mac or Linux
touch snowpack.config.js
// Windows
new-item snowpack.config.js
I think the most magical part of Snowpack comes when setting one innocent-looking key value pair in the configuration file. Paste this into the configuration file, for example:
我认为Snowpack最神奇的部分是在配置文件中设置一个看起来无关的键值对。将其粘贴到配置文件中,例如:
// snowpack.config.js
module.exports = {
packageOptions: {
"source": "remote",
}
};
source: remote enables something called streaming imports. Streaming imports enable Snowpack to bypass npm installation by converting bare imports (e.g., import React from 'react';) into CDN imports from Skypack.
source: 远程启用所谓的 streaming imports。Streaming imports 使 Snowpack 将原始的导入(例如,import React from 'react';)转换为从Skypack导入CDN,从而绕过npm安装。
Moving ahead, let’s make an index.html file:
<!--index.html-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">>
<title>Snowpack streaming imports</title>
</head>
<body>
<div id="root"></div>
<!-- Note the type="module". This is important for JavaScript module imports. -->
<script type="module" src="app.js"></script>
</body>
</html>
And, finally, we’ll add an app.jsx file:
// app.jsx
import React from 'react'
import ReactDOM from 'react-dom'
const App = ()=>{
return <h1>Welcome to Snowpack streaming imports!</h1>
}
ReactDOM.render(<App />,document.getElementById('root'));
Note that we didn’t npm install React or ReactDOM at any stage. But if we start up the Snowpack developer server like this:
./node_modules/.bin/snowpack dev
…our app still works!
Instead of pulling from a node_modules folder, Snowpack pulls the npm package down from Skypack, a CDN that hosts the npm registry, and it’s is pre-optimized to work in the browser. Snowpack then serves it in a ./_snowpack/pkg URL.
Snowpack 不是从 node_modules 文件夹下拉 npm 包,而是从 Skypack 下拉 npm 包,Skypack 是托管 npm 注册表的 CDN,它是预先优化的,可以在浏览器中工作。然后Snowpack以 ./_snowpack/pkg 的 URL 提供它。
Usage
This is a big step away from Node/npm-based workflow. What we’re actually looking at is a new CDN/JavaScript module-based workflow.
这是远离基于 Node/npm 工作流的一大步。我们实际看到的是一个新的基于 CDN/JavaScript 模块的工作流。
If, however, we run our app as it is and run a production build, Snowpack throws an error. This is because it needs to know which versions of React and ReactDOM to use when building. You can fix this by writing to a snowpack.deps.json which can automatically be created by running the following:
但是,如果我们按原样运行应用程序并运行产品构建,则 Snowpack 会抛出一个错误。这是因为它需要知道在构建时使用哪个版本的 React 和 ReactDOM。你可以通过写入 snowpack.deps.json 来修复这个问题,它可以通过运行以下命令自动创建:
./node_modules/.bin/snowpack add react
./node_modules/.bin/snowpack add react-dom
That won’t download the package from npm, but it will record the version of the packages used for Snowpack builds.
它不会从 npm 下载包,但它会记录用于 Snowpack 构建的包的版本。
One caveat is that we miss out on developer error messages, as Skypack will ship the production version of packages.
需要注意的是,我们错过了开发人员的错误消息,因为Skypack将发布生产版本的包。
Even if we aren’t using streaming imports, the Snowpack development server bundles each dependency from node_modules into one JavaScript file per dependency, converts those files to a native JavaScript module, then serves it to the browser. This means the browser can cache these scripts and only re-request them if they’ve changed. The development server automatically refreshes on save, but doesn’t preserve the client-side state. All dependencies from node seemed to work out of the box regardless of whether they were using legacy module formats or node APIs (such as the infamous process.env we had trouble with in esbuild).
即使我们没有使用 streaming imports,Snowpack 开发服务器也会将 node_modules 中的每个依赖打包为每个依赖项的一个 JavaScript 文件,将这些文件转换为本地 JavaScript 模块,然后将其提供给浏览器。
这意味着浏览器可以缓存这些脚本,只有在它们发生更改时才重新请求它们。
开发服务器在保存时自动刷新,但不保存客户端状态。
所有来自 node 的依赖项似乎都是开箱即用的,不管它们是使用传统的 module 格式还是 node api(比如臭名昭著的 process.env 我们在 esbuild 中遇到了麻烦)。
Preserving client-side state in React requires react-refresh, which requires a few Babel packages of its own as dependencies. These aren’t included by default, but are available using the more maximal React template. The template pulls in react-refresh, Prettier, Chai, and React Testing Library, for a total Node dependency package weighing in at 80 MB:
在 React 中保存客户端状态需要 react-refresh,这需要一些自己的 Babel 包作为依赖项。默认情况下不包括这些,但是可以使用更大的 React 模板。该模板引入了 React -refresh、Prettier、Chai 和 React Testing Library,以获得一个总大小为80 MB 的 Node 依赖包:
npx create-snowpack-app my-react-project --template @snowpack/app-template-react
Supported files
JSX is supported, but again, only with .jsx files by default. Snowpack automatically detects if whether React or Preact is being used, and decides accordingly which render function to use for the JSX transform. However, if we want to customize the JSX further than this, we’d need to pull in Babel via their plugin. There is also a Snowpack plugin available for Vue single file components and, of course, for Svelte components. Further, Snowpack compiles TypeScript, but for type checking we need the TypeScript plugin.
支持JSX,但同样,在默认情况下,只支持 .jsx 文件。Snowpack 自动检测是否使用 React 或 Preact,并相应地决定 jsx 转换使用哪个 render 函数。
然而,如果我们想进一步定制 JSX,我们需要通过他们的插件引入 Babel。此外,Vue 单文件组件和 Svelte 组件也可以使用 Snowpack 插件。此外,Snowpack 会编译 TypeScript,但是为了进行类型检查,我们需要TypeScript 插件。
CSS can be imported into JavaScript and are tossed into the document at runtime. CSS modules are also supported out of the box for scoping as long as they have the .module.css extension.
Imported JSON files will be cast into a JavaScript module with an object as a default export. Snowpack supports images and copies them into the production folder. Going along with its unbundled philosophy, Snowpack does not include images as data URLs in the bundle.
CSS 可以导入到 JavaScript 中,并在运行时被放入文档 。只要CSS模块有 .module.css 扩展名,就可以开箱即用地支持 CSS Module。
导入的 JSON 文件将被转换为 JavaScript 模块,并将对象作为默认导出。
Snowpack 支持图像并将它们复制到生产文件夹中。
按照其 unbundled 的理念,Snowpack 在 bundle 中不把图像作为 data urls。
Production build
The default snowpack build command basically copies the exact source file structure into an output folder. For files that compile to JavaScript (e.g. TypeScript, JSX, JSON, .vue, .svelte), it transforms each individual file into a separate browser-friendly JavaScript module.
默认的 snowpack 构建命令基本上是将准确的源文件结构复制到输出文件夹中。
对于编译成 JavaScript 的文件(例如 TypeScript, JSX, JSON, .vue, .svelte),它会将每个单独的文件转换成一个单独的浏览器友好的 JavaScript 模块。
This works fine, but isn’t great for production, as it could cause a big waterfall of requests if the source code is split into a lot of files. In the Snap Shot application I ended up with 184KB of source files which would then request another 105 KB of dependencies from Skypack which made for a pretty huge waterfall.
However, Snowpack pulls esbuild as a dependency and we can enable esbuild to bundle, minify and compile our code by adding an “optimize” object to the Snowpack config:
这可以很好地工作,但不适用于生产,因为如果源代码被分割成许多文件,可能会导致大量请求。在 Snap Shot 应用程序中,我最终得到了 184 KB 的源文件,然后需要 Skypack 提供另外 105 kb 的依赖项,这就形成了一个相当大的请求瀑布流。
然而,Snowpack 将 esbuild 作为依赖项,我们可以通过在 Snowpack 配置中添加一个“optimize”对象来启用esbuild 来打包、最小化和编译我们的代码:
// snowpack.config.js
module.exports = {
optimize: {
bundle: true,
minify: true,
target: 'es2018',
},
};
This runs the code using the optimization features provided by esbuild, so by just adding these options we could get the same build we had earlier on with esbuild.
这将使用 esbuild 提供的优化特性运行代码,因此通过添加这些选项,我们可以得到与之前使用 esbuild 时相同的构建。
Since esbuild hasn’t reached 1.0 yet, Snowpack recommends using either the webpack or Rollup plugin for production builds, both of which need to be configured.
由于 esbuild 还没有达到 1.0 版本,Snowpack 推荐使用 webpack 或 Rollup 插件进行生产版构建,这两个插件都需要配置。
Overall
Snowpack provides a lightweight developer experience with a full-featured development server, detailed documentation, and easy-to-install templates. You are left to decide whether you want to bundle your application and how you want to do so. If you want a tool that provides both a dev server and a more opinionated build step, you might want to take a look at Vite, the next tool on our list.
Snowpack 提供了一个功能齐全的开发服务器、详细的文档和易于安装的模板,为开发人员提供了轻量级的体验。您需要决定是否要打包应用程序以及如何这样做。如果你想要一个既能提供开发服务器又能提供更有主见的构建步骤的工具,你可能需要看看Vite,我们列表中的下一个工具。
Snowpack
Templates for multiple front end frameworks
✅
Hot module replacement development server
✅ (when using templates)
Streaming imports
✅
Preconfigured production build
❌
Automatic PostCSS and preprocessor conversion
❌
HTM transform
❌
Rollup plugin support
✅ (when using snowpack-plugin-rollup-bundle
for build step)
Size on disk (default install)
16 MB
Vite
Vite is developed by Vue creator (and Hades speedrunner) Evan You. Where esbuild concentrates on the build step and Snowpack concentrates on the development server, Vite provides both: a full development server
and
an optimized build command using Rollup.
Vite 是由 Vue 的创造者 Evan You 开发的。esbuild 专注于构建步骤,而 Snowpack 专注于开发服务器,而 Vite同时提供了: 完整的开发服务器和使用 Rollup 优化的构建命令。
Use cases
If you want a serious create-react-app or Vue CLI competitor, Vite is the closest one in the bunch because it comes with batteries-included features. The lightening-fast development server and zero-config optimized production build mean you can get from zero to production without any configuration. Vite is a tool that could be used in both a tiny side-project or a big production application. A good use case for Vite would be any sizeable single page app.
如果你想要一个真正的 create-react-app 或 Vue CLI 的竞争对手,Vite 是最接近的一个,因为它有包括内置功能。快速开发服务器和零配置优化的生产构建意味着无需任何配置就可以从零升级到生产。Vite 是一种既可以用于小型项目,也可以用于大型生产应用程序的工具。对于 Vite 来说,一个好的用例是任意大的单页面应用程序。
Why
wouldn’t
you use Vite? Vite is an opinionated tool and you might disagree with its opinions. You might not want to use Rollup for your build (we’ve been talking about how fast esbuild is), or you might want your tooling to give you the full power of Babel, eslint, and the ecosystem of webpack loaders out of the box.
为什么不用维特呢? Vite 是一个有主见的工具,你可能不同意它的观点。你可能不想在构建中使用 Rollup (我们一直在讨论 esbuild 有多快),或者你可能想让你的工具提供 Babel、eslint 和 webpack 加载器生态系统的全部功能。
Also, if you want zero-config server-side rendering meta-frameworks, you’d be better off staying with webpack-based frameworks, like Nuxt.js and Next.js until the story for Vite server-side rendering is more complete.
此外,如果你想要零配置的服务器端渲染元框架,你最好还是使用基于 webpack 的框架,比如 Nuxt.js 和 Next.js ,直到 Vite 服务器端渲染的功能更加完整。
Setup
Vite has more opinionated defaults than esbuild and Snowpack. Its documentation is clear and detailed. We get full support for Vue with Evan being the creator and all, so Vite is a definite happy path for Vue developers. That said, Vite can be used with any front-end framework and even provides a list of templates to get you started.
与 esbuild 和 Snowpack 相比,Vite 有更多固执己见的默认设置。它的文档是清晰和详细的。
我们得到了 Vue 的全力支持,Evan 是 Vue 的创造者,所以 Vite 无疑是 Vue 开发者的幸福之路。也就是说,Vite可以用于任何前端框架,甚至提供了一个模板列表,让你开始。
Usage
Vite’s development server is pretty powerful. Vite pre-bundles all of a project’s dependencies together into a single native JavaScript module with esbuild, then serves it up with a heavily cached HTTP header. This means no time is wasted on compiling, serving or requesting imported dependencies after the first page load. Vite also provides clear error messaging, printing the exact block of code and the line numbers to troubleshoot. Again with Vite, I didn’t have any issues pulling in dependencies that used node APIs or legacy formats. They all seemed to be shimmed into a browser-acceptable esmodule.
Vite 的开发服务器非常强大。Vite 通过 esbuild 将项目的所有依赖打包到一个本地 JavaScript 模块中,然后使用一个缓存的 HTTP 头文件提供给它。
这意味着在第一个页面加载后不会浪费时间在编译、服务或请求导入的依赖项上。Vite 还提供了清晰的错误消息,打印出精确的代码块和行号来排除故障。
同样,使用 Vite 时,我在引入使用 node api或传统格式的依赖项时没有任何问题。它们似乎都被整合到一个浏览器可接受的 esmodule 中。
Vite’s React and Vue templates both pull in plugins that enable hot module replacement. The Vue template pulls in a Vue plugin for single file components, and a Vue plugin for JSX. The React template pulls in the react-refresh plugin. Either way, both will give you hot module replacement and client-side state preservation. Sure, they add a few more dependencies, including Babel packages, but, Babel isn’t actually necessary when using JSX in Vite. By default, JSX works the same way as esbuild — it converts to React.createElement. It won’t automatically import React, but its behavior can be configured.
Vite 的React 和 Vue 模板都可以使用插件来替换热模块。
Vue 模板为单个文件组件引入一个 Vue 插件,为 JSX 引入一个 Vue 插件。
React 模板导入了 React -refresh 插件。
无论哪种方式,都将提供热模块替换和客户端状态保存。
当然,它们添加了更多的依赖项,包括 Babel 包,但是,当在 Vite 中使用 JSX 时,Babel 实际上不是必需的。默认情况下,JSX 的工作方式与 esbuild 相同——它转换为 React.createElement。它不会自动导入 React,但它的行为可以配置。
And while we’re at it, Vite doesn’t support streaming imports like Snowpack and wmr do. That means npm-installing dependencies as usual.
虽然我们在它,Vite 不支持 streaming imports 像 Snowpack 和 wmr 的做法一样。这意味着像往常一样安装npm 依赖项。
One cool thing is that Vite includes experimental support for server-side rendering. Pick your framework of choice and generate static HTML that ships directly to the client. At the moment, it looks like we need to construct this architecture on our own, but still, this looks like a good opportunity for meta-frameworks to be built on top of Vite. Evan You already has a work in progress called VitePress, a replacement for VuePress with the benefits of using Vite. And Sveltekit has also added Vite to its dependency list. It looks like CSS code splitting inclusion was part of the reason Sveltekit made the switch to Vite.
一个很酷的事情是,Vite 包含了对服务器端渲染的实验性支持。选择您所选择的框架并生成直接交付给客户端的静态 HTML。
目前看来,我们需要自己构建这个架构,但这仍然是在 Vite 上构建元框架的好机会。Evan You 已经有一个正在进行的工作叫做 VitePress,一个使用 Vite 的好处来替代 VuePress。此外,Sveltekit 还将 Vite 添加到其依赖列表中。看起来 CSS 代码拆分是 Sveltekit 切换到 Vite 的部分原因。
Supported files
For CSS, Vite provides the most features out of all of the tools that we are looking at. It supports bundling CSS imports as well as CSS modules. But we can also npm install PostCSS plugins and create a postcss.config.js file, and Vite will automatically start applying these transforms to CSS.
对于 CSS, Vite 提供了我们正在研究的所有工具中最多的特性。它支持绑定 CSS导入和 CSS模块。但是我们也可以 npm 安装 PostCSS 插件并创建一个 postcss.config.js 文件,然后 Vite 会自动开始将这些转换应用到CSS中。
We can install and use CSS preprocessors — simply npm install the preprocessor and rename the file to the right extension (e.g. .filename.scss) and Vite will start applying the corresponding preprocessor. And, as we said in the overview, Vite support CSS code-splitting.
我们可以安装和使用 CSS 预处理器——只需 npm 安装预处理器,并将文件重命名为正确的扩展名(例如.filename.scss), Vite 将开始应用相应的预处理器。而且,正如我们在概述中所说的,Vite 支持 CSS 代码分割。
Image imports default to a public URL, but we’re also able load them into the bundle as strings by using a ?raw parameter at the end of the URL string.
Image 将 default import 到一个公共 URL,但是我们也可以通过在 URL 字符串的末尾使用 ?raw 参数将它们作为字符串加载到 bundle 中。
JSON files can be imported in the source and converted into an esmodule exporting a single object. We can also provide a named import and Vite will look in the root field of the JSON file to find the import and treeshake the rest.
JSON 文件可以在源文件中导入,并转换为导出单个对象的 esmodule。
我们还可以提供一个命名导入,Vite 将在 JSON 文件的根字段中查找导入,并对其余部分进行 treeshake。
Production build
Vite uses Rollup for a preconfigured production build with a bunch of optimizations. It intentionally provides a zero-config build which should be enough for most use cases.
Vite 将 Rollup 用于预配置的产品构建,并进行了一系列优化。它有意地提供了一个零配置构建,这对于大多数用例来说已经足够了。
The build comes with the Rollup features we expect: bundling, minification and tree shaking. But we also get extras, like code-splitting dynamic imports and something called “asynchronous chunk loading” which is a fancy way to say that if we request a JavaScript module that imports another module, the build will be pre-optimized to load both at the same time (asynchronously).
该构建具有我们期望的 Rollup 特性: bundling、缩小和摇树。但我们也会得到额外的东西,比如代码分割动态导入和所谓的“异步块加载”,这是一种奇妙的方式,表示如果我们请求一个 JavaScript 模块导入另一个模块,构建将被预优化,以便同时(异步)加载两个模块。
Running Vite’s default build with the Snap Shot app I ended up with one 5KB JavaScript file and one 160KB JavaScript file (for a grand total of 165KB) and all CSS in the project was automatically minified to a tiny 2.71KB file.
使用 Snap Shot 应用程序运行 Vite 的默认构建,我最终得到了一个 5KB 的 JavaScript 文件和一个 160KB 的JavaScript 文件(总计 165KB),项目中的所有 CSS 都被自动缩小为一个 2.71KB 的文件。
Overall
The opinionated nature of Vite makes it a serious competitor with our current tooling. A lot of work has been done to make the developer experience really seamless and make production-ready builds out of the box.
Vite 固执己见的特性使其成为我们当前工具的一个强力的竞争对手。为了使开发人员体验真正无缝,并使产品准备就绪的构建开箱即用,已经做了大量工作。
Vite
Templates for multiple front end frameworks
✅
Hot module replacement development server
✅ (when using templates)
Streaming imports
❌
Preconfigured production build
✅
Automatic PostCSS and preprocessor conversion
✅
HTM transform
❌
Rollup plugin support
✅
Size on disk (default install)
17.1 MB
wmr
Like Vite, wmr is another opinionated build tool that provides both a development server and a build step. It was built by the creator of Preact, Jason Miller, so it’s definitely a happy path for Preact developers. Jason Miller explained the thinking behind wmr when he appeared as a guest on the JS Party podcast:
与 Vite 一样,wmr 也是一种固执己见的构建工具,它提供了开发服务器和构建步骤。它是由 Preact 的创建者Jason Miller 所创建的,所以对于 Preact 开发者来说这绝对是一条快乐的道路。作者作为嘉宾出现在 JS Party 播客上时,他解释了 wmr 背后的想法:
Preact is tiny and it’s really good if you want to do a lightweight project. Where is our tooling for that? We have a webpack-based tool that’s used in production by a bunch of high-profile sites, but that’s the heavyweight tool. Where’s the prototyping tool? That was the one hand. The other hand is me and a bunch of others who happened to be on the Preact team; We had been kind of on the sidelines of the bundler ecosystem for a little while, prodding people, trying to get consensus on a direction that we can move in to further this idea of writing modern code and shipping modern code.
Preact 很小,如果你想做一个轻量级的项目,它真的很好。我们的工具在哪里?我们有一个基于 webpack 的工具,很多知名网站都在生产中使用,但这是一个重量级的工具。原型制作工具在哪里? 这是一方面。另一方面是我和一群碰巧在 Preact 团队的人;有一段时间,我们一直是
bundler
生态系统的旁观者,鼓励人们,试图在一个方向上达成共识,我们可以进一步推进编写现代代码和发布现代代码的想法。
This tells us that wmr is all about writing and shipping modern code, enabling lighter tooling in a project.
这告诉我们 wmr 是关于编写和发布现代代码的,在项目中支持更轻的工具。
You might be wondering what wmr stands for? Nothing! The names “Web Modules Runtime” and “Wet Module Replacement” were floated, but it’s a fake abbreviation, similar to npm.
你可能想知道 wmr 代表什么?没有什么!“Web模块运行时”和“Wet Module 替换”的名字也曾出现过,但这是一个虚假的缩写,类似于npm。
wmr is built with the same ruthless bundle size purging as Preact, so it’s tiny — weighing a mere 2.6 MB — and contains exactly zero npm dependencies. Still, it manages to pack in a whole lot of really awesome features including a hot-module-replacing development server and an optimized production build.
wmr 是在与 Preact 相同的 bundle 大小中构建的,所以它很小——只有2.6 MB——并且完全不包含 npm 依赖项。尽管如此,它还是设法添加了许多非常棒的特性,包括热模块替换开发服务器和优化的产品构建。
Use cases
I would use wmr if I was looking to create a prototype using Preact as fast as possible. There’s no configuration and it only takes seconds to download. It feels like using a supercharged static file server. With TypeScript, an optimized-build step, and static HTML rendering, wmr offers everything needed to ship small-to-medium sized applications. Its small size is also great for quickly trying out a library or demoing an idea.
如果我想尽快使用 Preact 创建一个原型,我会使用 wmr。没有配置,下载只需要几秒钟。感觉就像使用一个超级静态文件服务器。通过 TypeScript、优化的构建步骤和静态 HTML 渲染,wmr 提供了发布中小型应用程序所需的一切。它的小尺寸也有助于快速尝试库或演示一个想法。
wmr may not be the tool for you if you’re not using Preact, React or vanilla JavaScript. The Preact team has yet to provide templates for other frameworks. The documentation also isn’t as detailed as the other tools we’ve looked at. This means the further you stray from the happy path, the more you’ll dig into the source. So, I can’t recommend it if a lot of customization is needed.
如果你不使用 Preact、React 或普通的 JavaScript, wmr 可能不是适合你的工具。Preact 团队还没有为其他框架提供模板。该文档也没有我们看过的其他工具那么详细。这意味着你离幸福的道路越远,你就会越深入到源头。所以,如果需要很多定制,我就不推荐了。
Setup
If you’re using preact there is absolutely zero setup needed except for a quick npm install. To use React with wmr instead of Preact, there are currently two steps to take. First, alias htm/preact to htm/react, and react to es-react in your package.json:
如果你正在使用 preact,除了快速安装 npm 之外,你完全不需要任何设置。要在 wmr 中使用 React 而不是Preact,目前需要采取两个步骤。首先,将 htm/preact 别名为 htm/react,并在 package.json 中对 react 更换为 es-react:
"alias": {
"htm/preact": "htm/react",
"react": "es-react"
},
Then add imports from es-react into your components:
然后将 es-react 的导入添加到组件中:
// ReactDOM only needed on root render
import { React, ReactDOM,} from 'es-react';
This means we aren’t actually using the normal React package you might be used to, but instead pulling in React from es-react. This is because wmr relies on packages being compatible with native JavaScript modules. React does not use native modules by default, instead using an older style of module called UMD modules. es-react is a package that pulls in React but provides exports that are compatible with the web platform.
这意味着我们实际上并没有使用你可能习惯的正常 React 包,而是从 es-react 中引入 React。这是因为 wmr 依赖于与本地 JavaScript 模块兼容的包。React 默认不使用本机模块,而是使用一种名为 UMD 模块的老式模块。es-react 是一个导入 React 的包,但提供与 web 平台兼容的导出。
This illustrates wmr’s philosophy of using native primitives of the web platform as opposed to using tooling to sidestep and abstract it away.
这说明了 wmr 使用 web 平台原生原语的哲学,而不是使用工具来回避和抽象它。
Another option could be to use Skypack imports in our application, which is also pre-optimized to work in the browser:
另一个选择是在我们的应用程序中使用 Skypack 导入,这也是预先优化的,以在浏览器中工作:
import React from 'https://cdn.skypack.dev/react';
import ReactDOM from 'https://cdn.skypack.dev/react-dom';
wmr expects that you’re writing modern code which runs in the browser, which may mean that you’ll need to do some configuration if you pull in dependencies that use node APIs or legacy module systems. To get the Snap Shot app working I needed to dive into node modules and convert a library or two to use native JavaScript module syntax. This might slow you down if you are using older libraries. The Preact ecosystem is all optimized to run in the browser and shouldn’t require any massaging. This is another reason to stick with the Preact happy path in wmr.
wmr 期望你写的是在浏览器中运行的现代代码,这可能意味着如果你使用 node api或 传统 module 系统的依赖项,你需要做一些配置。
为了让 Snap Shot 应用工作,我需要深入到 node 模块,并转换一两个库来使用本地 JavaScript 模块语法。如果您使用较旧的库,这可能会减慢您的速度。Preact 生态系统经过了优化,可以在浏览器中运行,不需要任何信息传递。这是在 wmr 中坚持 Preact 快乐道路的另一个原因。
There are plugins for wmr. It exposes a plugin API that supports Rollup plugins for a build step. There are more and more wmr-specific examples on the docs, including a plugin that minifies HTML, and one that features filesystem–based routing.
有针对 wmr 的插件。它公开了一个插件API,支持构建步骤的 Rollup 插件。文档中有越来越多的特定于 wmr 的示例,包括一个最小化 HTML 的插件,以及一个以基于文件系统的路由为特征的插件。
wmr supports different frameworks, but there aren’t any pre-built templates for them. And at first I found it rather difficult to configure the JSX transform. All that said, Jason has confirmed there are plans to make JSX more configurable and that wmr is intended to be framework-agnostic. JSX is planned to work out of the box in regular JavaScript files.
WMR 支持不同的框架,但是没有任何针对它们的预构建模板。首先,我发现配置 JSX 转换相当困难。综上所述,Jason 已经确认有计划使 JSX 更可配置,wmr 是框架无关的。JSX 计划在常规J avaScript 文件中开箱即用。
Usage
To get started you can either run this command in the command line:
npm init wmr your-project-name
Or alternatively, you can run these commands to manually build up your application:
npm init -y
npm install wmr
mkdir public
touch public/index.html
touch public/index.js
Then add an script import in the body of your index.html (again make sure to use type="module"):
<script type="module" src="./index.js"></script>
Now you can write a Preact hello world into your index.js file:
import { render } from 'preact';
render(<h1>Hello World!</h1>, document.body);
And finally start your development server:
node_modules/.bin/wmr
Now we have a full hot module replacement development server which will immediately respond to any changes to our source-code.
wmr uses a tool called htm when transforming JSX, which provides some awesome benefits. Let’s say we’re writing a counter using Preact in wmr and make a mistake:
现在我们有了一个完整的热模块替换开发服务器,它将立即响应对源代码的任何更改。
wmr 在转换 JSX 时使用一个名为 htm 的工具,它提供了一些令人惊叹的好处。假设我们在 wmr 中使用 Preact 编写一个计数器,并且犯了一个错误:
import { render } from 'preact';
import { useState } from 'preact/hooks';
function App() {
const [count,setCount] = useState(0)
return <>
<button onClick={()=>{setCount(cout+5)}}>Click to add 5 to count</button> // HIGHLIGHT
<p>count: {count}</p>
</>
}
render(<App />, document.body);
count is misspelled in the onClick handler function, so running this results in an error. Usually, we would have to rely on our tooling and a source map to gather information about where the bug is located, but wmr takes a different solution. With htm, this gets as close as you can get to native JSX in the browser by using tagged template literals. So, where writing React or Preact code usually looks like this:
count 在 onClick 处理函数中拼写错误,因此运行此函数将导致错误。通常,我们必须依赖我们的工具和源映射来收集关于错误所在位置的信息,但是 wmr 采用了不同的解决方案。使用 htm,您可以通过使用标记的模板字面量在浏览器中尽可能接近本机 JSX。所以,编写 React 或 Preact 代码通常是这样的:
<MyComponent>I am JSX. I am not actually valid Javascript</MyComponent>
…htm looks more like this:
html`<${MyComponent}>I am about as close as it gets to JSX as you can get while being able to run in the browser</MyComponent>`
Now, if we’re debugging our code and open the “Sources” panel in DevTools, we should see a script that’s almost identical to what the source code looks like in the editor.
This way, we can properly investigate where bugs are located in the browser without having to use sourcemaps. Sure, this specific example is pretty contrived, but you can see how this could be really useful because it means wmr doesn’t need source maps in your developer environment.
这样,我们就可以在不使用源码包的情况下正确地调查浏览器中的错误所在。当然,这个特定的示例是相当做作的,但您可以看到它是如何真正有用的,因为它意味着 wmr 在开发人员环境中不需要源映射。
wmr supports streaming imports by default, so bare imports will be pulled down from the npm registry. This is done through a complex process which examines all of the source in the npm package,
removes all the tests and metadata, and converts it to a single native JavaScript import.
WMR 默认支持 streaming imports,因此将从 npm 注册表导入。这是通过一个复杂的过程来完成的,这个过程会检查 npm 包中的所有源代码,
删除所有测试和元数据,并将其转换为单个本机 JavaScript 导入。
Similar to Snowpack, it’s possible to build a complex app without using npm to install anything. In fact, wmr is the first tool to support this idea.
类似于 Snowpack,它可以构建一个复杂的应用程序,而无需使用 npm 安装任何东西。事实上,wmr 是第一个支持这个想法的工具。
Supported files
As far as the other types of files wmr supports, CSS files can be imported in JavaScript, and CSS modules are supported as well.
对于 wmr 支持的其他类型的文件,CSS 文件可以用 JavaScript 导入,也支持 CSS 模块。
There isn’t any built-in support for either Vue single file components or Svelte components. However, wmr’s build step works with Rollup plugins and the development server can be configured with Polka/Express middleware, so it’s possible to use these to convert imports into Vue and Svelte components. In fact, I wrote a small plugin for Vue Single file Components to show how this might be done.
对于 Vue 单个文件组件或 Svelte 组件都没有任何内置支持。
但是,wmr 的构建步骤使用 Rollup 插件,并且开发服务器可以使用 Polka/Express 中间件进行配置,因此可以使用这些中间件将导入转换为 Vue 和 Svelte 组件。事实上,我为 Vue Single file Components 编写了一个小插件来演示如何实现这一点。
We can’t import images into JavaScript as data URLs in wmr without a plugin. Instead, we need to import them using a syntactically correct JavaScript method. So, if we have, say, a picture of a dog in our public folder we might include it in a Preact component like this:
在没有插件的情况下,我们无法将图像导入 JavaScript 作为 data urls。相反,我们需要使用语法正确的JavaScript 方法导入它们。所以,如果我们在公共文件夹中有一张狗的照片,我们可以像这样把它包含在 Preact 组件中:
function Dog() {
return <img src={new URL('./dog.jpg', import.meta.url)} alt="dog hanging out"></img>
}
And once the build step runs, the image is copied and accessible from the distribution folder. There is hot module replacement for images in the development server, so changes with images are immediately reflected in the browser.
一旦构建步骤运行,就可以从分发文件夹复制并访问映像。开发服务器中的图像有热模块替换,因此对图像的更改会立即反映在浏览器中。
One more note on file support: JSON can be imported, and is converted into a JavaScript object for use. But when actually building an application, we’d need the Rollup JSON plugin.
关于文件支持的另一个注意事项:
JSON可以导入,并转换成 JavaScript 对象以供使用。
但在实际构建应用程序时,我们需要 Rollup JSON 插件。
Production build
wmr provides a production build step that includes bundling, minification and tree-shaking without any additional dependencies. Having a look at the source of wmr, it looks like rollup and terser are used under the hood, and minified versions of these are included in the wmr package. The wmr bundle for the Snap Shot app was 164KB so it created a bundle just a tiny bit smaller than total size of the two JavaScript files created by Vite.
WMR 提供了一个生产构建步骤,包括 bundling、缩小和摇树,没有任何额外的依赖。查看一下 wmr 的源代码,看起来好像在幕后使用了 rollup 和 terser, wmr 包中包含了它们的简化版本。Snap Shot 应用的 wmr 包是 164KB,所以它创建的包只比 Vite 创建的两个 JavaScript 文件的总大小小一点点。
There is also a way to configure wmr is such a way that it renders an application to static HTML and hydrates on the browser using preact-iso. This means wmr can be used as a meta-framework for Preact, similar to Next.js.
还有一种配置 wmr 的方法,即使用 preact-iso 将应用程序呈现为静态 HTML 并在浏览器上进行渲染。这意味着wmr 可以用作 Preact 的元框架,类似于 Next.js。
Overall
I love the experience of using wmr to prototype both React and Preact applications. It feels great to get started with a tool that is ridiculously small but provides developer conveniences that get close to matching heavyweight bundlers.
我喜欢使用 wmr 来原型 React 和 Preact 应用程序的体验。开始使用一个小得可笑的工具,但它为开发人员提供了接近重量级绑定包的便利,这感觉很棒。
wmr
Templates for multiple front end frameworks
✅
Hot module replacement development server
✅
Streaming imports
✅
Preconfigured production build
✅
Automatic PostCSS and preprocessor conversion
❌
HTM transform
✅
Rollup plugin support
✅
Size on disk (default install)
2.57 MB
Feature comparison
We have just covered a lot of ground! Rather than scroll up and down this article to compare results, I’ve compiled everything here to see how the tools stack up when they’re side-by-side. I’ve even included additional comparisons for features that we didn’t explicitly cover.
特点比较
我们刚刚涉及了很多领域!我没有在本文中上下滚动来比较结果,而是在这里编辑了所有内容,以查看这些工具并排使用时的效果。
我甚至还对我们没有明确提到的功能进行了额外的比较。
Use cases
Tool
Use case
esbuild
Large codebases. Not ready for production yet.
Snowpack
Small applications that don’t need bundling or applications where you want to choose which bundler you use. Also good for incrementally adopting JavaScript frameworks in server-rendered apps.
Vite
Replacement for Vue CLI/Create-React-App for producing single page applications. This is the happy path for Vue.
wmr
Prototypes. Good for small- to medium-size apps and can be used for either single page or server-rendered apps. This is the happy path for Preact.
Setup
esbuild
Snowpack
Vite
wmr
Templates for multiple front end frameworks
❌
✅
✅
❌
Size on disk (default install)
7.34 MB
16 MB
17.1 MB
2.57 MB
Zero-config production build
❌
❌
✅
✅
HMR Development Server with zero config
❌
✅
✅
✅
Process.env handling for node packages
❌
✅
✅
✅
Development server
esbuild
Snowpack
Vite
wmr
Hot module replacement
❌
✅
✅
✅
CSS hot replacement
❌
✅
✅
✅
npm dependency pre-bundling
❌
✅
✅
❌
Browser error messaging
❌
✅
✅
❌
HTM tranform
❌
❌
❌
✅
Production build
esbuild
Snowpack
Vite
wmr
Output bundle size of Snap Shop app
177 KB
184 KB of multiple JavaScript files, plus 105 KB of Skypack CDN dependencies
165 KB (one 5 KB file and one 160 KB file)
164 KB
Go-based bundling
✅
✅ when using esbuild is used in build step
❌
❌
Preconfigured production build
❌
❌
✅
✅
Asynchronous chunk loading
❌
❌
✅
✅
Rollup plugin support
❌
✅
✅
✅
Other features
esbuild
Snowpack
Vite
wmr
Streaming inputs
❌
✅
❌
✅
Server-side rendering
❌
❌
✅ (experimental)
✅
CSS Modules
❌
✅
✅
✅
Automatic PostCSS and preprocessor conversion
❌
❌
✅
❌
Wrapping up
I’m super excited about building JavaScript applications with any and all of the tools we just looked at. Whether we’re writing a small side project or a big production site, all these tools speed up feedback loops and increase productivity. They’ve opened up the gates to ask what’s necessary for the JavaScript ecosystem and whether we can start losing the cruft brought by legacy modules and browsers. These tools are going to lower the barrier to entry for new developers by providing a leaner, faster developer environment with fewer abstractions between the code that gets written and the code that runs in the browser.
If you’re getting sick of waiting for your dependencies to download and your build steps to run, I’d recommend giving this new generation of tooling a try.
总结
我对使用我们刚刚介绍过的任何工具来构建 JavaScript 的应用感到非常兴奋。
无论我们是在编写一个小项目还是一个大的生产环境的站点,所有这些工具都能加速反馈循环并提高生产率。
他们对什么对JavaScript生态系统是必要的,以及我们是否可以开始丢掉传统 modules 和浏览器带来的繁琐这个问题打开了一扇门。
这些工具将为新开发人员提供一个更简洁、更快的开发环境,减少编写的代码和在浏览器中运行的代码之间的抽象。
如果您已经厌倦了等待依赖项下载和构建步骤运行,那么我建议您尝试一下这个新一代工具。
延伸阅读
Further reading
-
“Let’s Learn esbuild! (with Sunil Pai)” (Jason Lengstorf)
-
Through the pipeline: an exploration of frontend bundlers (Andrew Walpole)