停止捆绑销售——使用 Zwitterion
TLDR
网络真美
网络浏览器的局限性
捆绑商
不是捆绑器
示例代码
表现
结论
TLDR
Zwitterion是一个 Web 开发服务器,它允许你导入任何内容,这里所说的“任何内容”指的是:JavaScript ES2015+、TypeScript、JSON、JSX、TSX、AssemblyScript、Rust、C、C++、WebAssembly,以及未来任何可以编译成 JavaScript 或 WebAssembly 的内容。Zwitterion 旨在即时替代你当前的 Web 开发静态文件服务器,它并非打包工具。为了提供更简洁的使用体验,它摒弃了打包功能。
安装和使用方法如下:
npm install zwitterion
npx zwitterion
它可以让你做这样的事情:
import { add } from './index.ts';
import asModuleInit from './index.as';
import rustModuleInit from './index.rs';
import cModuleInit from './index.c';
import cppModuleInit from './index.cpp';
import watModuleInit from './index.wat';
import wasmModuleInit from './index.wasm';
import { jsxElement } from './index.jsx';
import { tsxElement } from './index.tsx';
import helloWorld from './index.json';
async function run() {
const asModule = await asModuleInit();
const rustModule = await rustModuleInit();
const cModule = await cModuleInit();
const cppModule = await cppModuleInit();
const watModule = await watModuleInit();
const wasmModule = await wasmModuleInit();
console.log('TypeScript add', add(0 , 0));
console.log('AssemblyScript add', asModule.add(0, 1));
console.log('Rust add', rustModule.add(0, 2));
console.log('C add', cModule.add(0, 3));
console.log('CPP add', cppModule.add(0, 4));
console.log('Wat add', watModule.add(0, 5));
console.log('Wasm add', wasmModule.add(0, 6));
console.log(jsxElement);
console.log(tsxElement);
console.log(helloWorld);
}
run();
那是真实的代码。现在它已经在浏览器中运行了。点击此处查看完整示例。
现在介绍一些背景信息。
网络真美
互联网真是个好东西,非常美妙。数十亿人使用网络浏览器,数以亿计的网站。它是史上最伟大的应用平台之一。但它也有一些缺点,或者说局限性。
网络浏览器的局限性
网络浏览器并非总是与时俱进,支持最新的语言标准。它们不支持最新版本的 JavaScript,也不支持任何版本的 TypeScript。它们无法运行 Rust、C、C++、AssemblyScript 以及许多其他可能在 Web 开发中用到的语言。
捆绑商
为了克服这些限制,Web 开发社区提出了一些解决方案。其中最值得一提的是打包工具。你可能听说过一些非常流行的打包工具,例如webpack、parcel和rollup。这些工具会将你的代码编译/转译成 JavaScript 或 WebAssembly,可能还会进行其他转换,并将所有内容打包成一个或几个文件,以便提供给客户端。通常情况下,这种方法效果很好。
捆绑器令人困惑
然而,有时候打包工具真的不好用。就我个人经验而言,它们往往复杂又令人困惑。就在不久前,我拥有多年的 Web 开发经验,却花了几个小时才修复好一个 webpack 配置,以便将 TypeScript 添加到项目中。几个小时!对于有经验的人来说,这简直无法接受。想想这对新手来说该有多麻烦!
打包工具还有一些其他的弊端。首先,它们会改变代码在浏览器中的运行方式,使其与你编写时的方式有所不同。例如,你可能认为你的代码使用了 ES 模块系统,但实际上这个系统几乎完全被移除了……毕竟你使用了打包工具。这会导致一些细微的行为差异,从而引发问题。我就遇到过这种情况。
其次,使用打包工具有时会要求你以不同于不使用打包工具时的方式编写源代码。最终,你必须将打包后的文件交付给客户端。打包后的文件并不存在于你的源代码中,处理它的存在有时会带来额外的复杂性(我认为 Parcel 在这方面做得很好,但 Webpack 或 Rollup 在这方面做得不够好)。
第三,打包工具的打包速度可能较慢。打包工具会将所有代码合并成一个或几个文件。在浏览器中运行代码之前,必须先完成这一步骤。对于小型项目,您可能不会注意到打包时间,但随着项目规模的扩大,打包时间必然会增加。
第四,它们的配置方式令人困惑。我不知道为什么会这样,但它们就是让人摸不着头脑。具体情况会因打包工具和任务而异,但它们确实给我留下了很差的印象。
由于这些问题以及可能存在的更多问题,总而言之,我认为打包工具过于复杂。
不是捆绑器
我提出了一种与打包截然不同的范式,实际上,它与打包完全相反。完全不要打包。我向您介绍Zwitterion,一个可以导入任何内容的 Web 开发服务器,这里所说的“任何内容”指的是:JavaScript ES2015+、TypeScript、JSON、JSX、TSX、AssemblyScript、Rust、C、C++、WebAssembly,以及未来任何可以编译成 JavaScript 或 WebAssembly 的内容。Zwitterion 旨在成为您当前 Web 开发静态文件服务器的即时替代品,它并非打包工具。它摒弃了打包,以提供更简洁的使用体验。
Zwitterion 完全依赖 ES 模块系统来共享代码,它不会试图隐藏或抽象这一过程。它还可以扩展以支持任何可以编译成 JavaScript 或 WebAssembly 且拥有自身文件扩展名的语言。例如,Vue和Svelte都有各自的组件创建文件格式。扩展 Zwitterion 以支持这些格式相当简单。
Zwitterion 的神奇之处在于它会在请求时转换文件。如果您请求一个 TypeScript 文件,您会得到一个转译后的 JavaScript 文件。如果您请求一个 Rust 文件,您会得到一个 JavaScript 文件,该文件导出了已编译的 Rust WebAssembly 模块的导出项。
Zwitterion 还会自动重新加载并缓存转换后的代码。而且由于它不打包代码,因此速度非常快,运行也很灵活。
示例代码
Zwitterion 让你可以编写这样的代码,点击刷新,享受开发生活:
TypeScript
./app.ts:
import { helloWorld } from './hello-world.ts';
console.log(helloWorld());
./hello-world.ts:
export function helloWorld(): string {
return 'Hello world!';
}
JSX
./app.js:
import { helloWorldElement } from './hello-world.jsx';
ReactDOM.render(
helloWorldElement,
document.getElementById('root')
);
./hello-world.jsx:
export const hellowWorldElement = <h1>Hello, world!</h1>;
多伦多证券交易所
./app.js:
import { helloWorldElement } from './hello-world.tsx';
ReactDOM.render(
helloWorldElement,
document.getElementById('root')
);
./hello-world.tsx:
export const hellowWorldElement: JSX.Element = <h1>Hello, world!</h1>;
AssemblyScript
./app.js:
import addModuleInit from './add.as';
runAssemblyScript();
async function runAssemblyScript() {
const adddModule = await addModuleInit();
console.log(addModule.add(1, 1));
}
./add.as:
export function add(x: i32, y: i32): i32 {
return x + y;
}
锈
./app.js
import addModuleInit from './add.rs';
runRust();
async function runRust() {
const addModule = await addModuleInit();
console.log(addModule.add(5, 5));
}
./add.rs
#![no_main]
#[no_mangle]
pub fn add(x: i32, y: i32) -> i32 {
return x + y;
}
C
./app.js
import addModuleInit from './add.c';
runC();
async function runC() {
const addModule = await addModuleInit();
console.log(addModule.add(5, 5));
}
./add.c
int add(int x, int y) {
return x + y;
}
C++
./app.js
import addModuleInit from './add.cpp';
runCPP();
async function runCPP() {
const addModule = await addModuleInit();
console.log(addModule.add(5, 5));
}
./add.cpp
extern "C" {
int add(int x, int y) {
return x + y;
}
}
WebAssembly 文本格式 (WAT)
./app.js
import addModuleInit from './add.wat';
runWat();
async function runWat() {
const addModule = await addModuleInit();
console.log(addModule.add(5, 5));
}
./add.wat
(module
(func $add (param $x i32) (param $y i32) (result i32)
(i32.add (get_local $x) (get_local $y))
)
(export "add" (func $add))
)
WebAssembly (Wasm)
./app.js
import addModuleInit from './add.wasm';
runWasm();
async function runWasm() {
const addModule = await addModuleInit();
console.log(addModule.add(5, 5));
}
./add.wasm
Imagine this is a compiled Wasm binary file with a function called `add`
表现
需要注意的是,由于 Zwitterion 目前既不打包文件也不进行 tree shaking,这可能会影响应用程序的性能。HTTP2、HTTP3、ES 模块和其他未来的技术改进或许有助于提升性能,但就目前而言,性能似乎会下降。Zwitterion 计划通过从静态构建中自动生成 HTTP2 服务器推送信息以及研究 tree shaking 来提升性能,但目前尚不清楚这些措施会产生怎样的影响。随着 Zwitterion 的不断完善,我们将持续发布更多关于性能方面的信息,敬请关注。
综上所述,这些因素对性能的影响尚不明确。请自行测试验证。
如果您感兴趣,可以阅读以下内容,了解更多关于使用 HTTP2 进行捆绑与不捆绑对性能的影响:
- https://medium.com/@asyncmax/the-right-way-to-bundle-your-assets-for-faster-sites-over-http-2-437c37efe3ff
- https://stackoverflow.com/questions/30861591/why-bundle-optimizations-are-no-longer-a-concern-in-http-2
- http://engineering.khanacademy.org/posts/js-packaging-http2.htm
- https://blog.newrelic.com/2016/02/09/http2-best-practices-web-performance/
- https://mattwilcox.net/web-development/http2-for-front-end-web-developers
- https://news.ycombinator.com/item?id=9137690
- https://www.sitepoint.com/file-bundling-and-http2/
- https://medium.freecodecamp.org/javascript-modules-part-2-module-bundling-5020383cf306
- https://css-tricks.com/musings-on-http2-and-bundling/
我相当有信心,任何性能瓶颈最终都能克服。我迫不及待地想把一些基准测试结果发给你!
结论
Zwitterion 的核心理念在于,它致力于提供尽可能简便的开发体验,让您能够使用浏览器当前不支持的额外功能。它不是打包工具,而是未来。感谢阅读。
文章来源:https://dev.to/lastmjs/stop-bundling-use-zwitterion-2l99