← Home

关于前端构建的碎碎念

稍微谈一下现在的前端构建学到的一些东西.

打包器

这个东西本身的功能是比较单纯的, 就是将工程的整个依赖图画出来, 然后打包到单个文件里面去.

比较主流的就是 Webpack, 其他还有 sonwnpack, rollup(这个稍微有点特别), Parcel, 最后还有最近新出现的 vite.

优化: 例如压缩 Js, CSS, HTML, 摇树, 调整图像大小, 以及分包等机制.所有的优化基本都是为了能在打开网站时更快的载入页面.

扩展: 前端的技术鱼龙混杂, 光开发语言的衍生亚种就非常多, 打包器就需要能支持这些语言的编译器. 部分代码优化也在这里完成. 扩展性基本决定了一个打包器的命运和社区发展.

这两点基本所有的打包器都支持. 接下来说一些有差异化的地方.

Webpack

究极老牌的打包器了, Webpack 在 5 版本之前是出了名的重和慢还有繁, 扩展性和社区都极佳, 基本所有的前端技术你都能在 wp 上找到对应的 loader 或者 plugin. 也是目前最流行的打包器了. Webpack5 出现了之后速度提升了很多. webpack-chainwebpack-merge使用这两个第三方包配合可以做到大部分的配置复用. 但是配置依然很重和繁琐这一点, 一直都没有很好的方案.

var webpack = require("webpack");
module.exports = {
	entry: "./entry.js", //入口文件
	output: {
		//node.js中__dirname变量获取当前模块文件所在目录的完整绝对路径
		path: "dist", //输出位置
		filename: "bundle.js", //输入文件
	},
	module: {
		loaders: [
			{
				test: /\.css$/, //支持正则
				loader: "style-loader!css-loader",
			},

			{
				test: /\.vue$/,
				loader: "vue-loader",
			},
		],
	},
	//其他解决方案配置
	resolve: {
		extensions: ["", ".js", ".json", ".css", "vue"], //添加在此的后缀所对应的文件可以省略后缀
	},
	//插件
	plugins: [new webpack.BannerPlugin("This file is created by ly")],
};

Parcel

继 Webpack 之后新出现的打包器, 解决了 Webpack 的一个非常大的痛点, Parcel 可以做到零配置启动. 自身就集成了 parcel 所有的东西, 你只需呀一个小小的 index.html 作为入口, 其他的全部解析都交给 Parcel, Parcel 内部还集成了一个 Js 编译器, 并且由于该编译器使用的是 Rust 语言编写它的执行速度非常快. 理论上应该比 esbuild 还要快. Parcel 刚出现的时候风评并不好, 因为不支持 source-map, 并且摇树还有 bug..


Sonwnpack

时代在发展, 渐渐的浏览器也开始支持 ESM 了, Sonwnpack 就是坐上了这个风口的打包器. 有了 ESM, 你所有的 JS 模块都可以懒加载的方式载入到页面中. 并且你的第一次开发服务器启动也不需要编译所有依赖的 JS 代码. 顺势的, Sonwnpack 中就出现了一个HMR(热模块替换)的概念

热模块替换 (HMR) 是一种在不触发整个页面刷新的情况下将文件更新推送到浏览器的能力。想象一下更改一些 CSS,点击保存,然后立即看到您的更改反映在页面上而无需刷新。那是 HMR。然而,Snowpack 利用 ESM 进行非捆绑开发的能力引入了近乎即时的单个文件构建,只需 10-25 毫秒即可在浏览器中加载和更新。

/** @type {import("snowpack").SnowpackUserConfig } */
module.exports = {
	mount: {
		test: { url: "/", static: true },
		public: { url: "/", static: true },
		src: { url: "/_dist_" },
	},
	plugins: [
		"@snowpack/plugin-webpack",
		"@snowpack/plugin-vue",
		"@snowpack/plugin-sass",
		"@snowpack/plugin-dotenv",
		// ["@snowpack/plugin-build-script", { "cmd": "postcss", "input": [".css"], "output": [".css"] }]//,
	],
};

Vite

vite 的起步还比较晚, 它的部分开发灵感来自 Sonwnpack, 同时 vite 也支持 HMR. 比较特别的是 Vite 自带的编译器是 ESBuild, 在官方 banner 上速度极其夸张的一个编译器. 配置文件时 vite 的优点,大部分的插件即插即用, 以及配置文件的写法修复了很多以前打包器的缺点, 可以非常灵活. 目前 Vite 的扩展现在还比较少, 以后可以期待一下.

ESBuild 的官方宣称: 基准测试通过将 three.js 库复制 10 次并从头开始构建单个包来近似大型 JavaScript 代码库,而无需任何缓存. Webpack5 = 41.53s, rollup + terser = 34.95s, parcel2 = 32.48s, esbuild = 0.33s. 这个速度是传统打包器的 100 倍.

然而在实测中, 我在当前的 Webpack 中引入了 esbuild, 来代替以前的 babel 和 tsc, 时间也只省下了 5-10s, 并没有那么夸张, 可能是 Webpack 的 loader 调用导致的瓶颈.

// vite.config.js
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

export default defineConfig({
	plugins: [react()],
});

Rollup

Rollup 是个非传统意义上的打包器, 它主要面向的是类库的前端开发者. Rollup 的配置文件的繁琐程度比较折中. Rollup 似乎没有对分包的支持, 理念就是将小块代码打包成大块的复杂代码,放在一个文件中. Rollup 自带的功能也非常少, 甚至不支持 node 模块解析. 大部分都需要依赖插件完成.

// rollup.config.js
import json from "rollup-plugin-json";

export default {
	input: "src/main.js",
	output: {
		file: "bundle.js",
		format: "cjs",
	},
	plugins: [json()],
};

关于编译器

目前大部分前端的编译器非常混杂, 基本是各做各的, JS 的编译器的 Babel 是不二之选, 所有 ES 标准以及 Typescript 甚至 Flow 和 coffeescript,Babel 都能编译. 也支持一些async do {}这种奇怪的语法糖. ESBuild 和兴起的 SWC 是针对速度进行优化. 这里也没什么好说的.