vite和snowpack

之前Vue3.0生态的介绍提到了vite这个工具,借此文简单整理记录”no webpack”的相关内容。

背景

In 20192020, you should use a bundler because you want to, not because you need to.

在过去的几年中,如webpack 之类的bundle打包工具,已经从一个只限于输出优化bundle JavaScript的工具演变成一个需要为大多数web应用程序构建的必要步骤。不管你喜欢还是讨厌它,很难否认bundlers大幅增加了新web开发的复杂性。

为何会孕育出js捆绑打包的构建时代呢?个人认为有两个原因:

  • 1.http1.x的队头阻塞,使得合并请求变得很必要:http1.x的情况下,浏览器客户端在同一时间,针对同一域名下的请求有一定数量限制。超过限制数目的请求会被阻塞,从而影响性能,因此合并请求成为了普遍认同的优化原则;
  • 2.npm的运作限制。nodejs的模块体系,无论是Common.js 还是 CJS,无法在web浏览器上直接运行,我们必须依赖bundling。

这也导致如今很难看到一个前端项目没有webpack介入,由此一个项目的复杂性也大大增加。以create-react-app为例,我们单纯只想在页面中显示一个”Hello world”,但是我们需要安装200.9MB的node_modules,其中包含1,300+不同的依赖,10s~几十s的构建时间,这仅仅只是为了输出一个”Hello world”。

那么基于以上两点,现如今有没有其他更好的处理方式呢?

  • 1.http2解决对头阻塞:多路复用作为http2的一大特性,允许同时通过单一的 http2 连接发起多重的请求-响应消息,而且随着 http2 的普及,合并请求变得没那么必要。
  • 2.浏览器开始支持ESM模块功能:模块功能的核心importexport,它们在浏览器端现在的兼容情况如下:
    • import
      i-c_import.jpg
    • export
      i-c_export.jpg
    • 并且作为模块的定义,<script>标签需要添加type="module"属性,其兼容性如下:
      i-c_module.jpg
      总体来看,兼容覆盖率还算良好,特别是一些PC如CRM这类不太考虑浏览器兼容的项目可以完全抛开bundle,并且往往也就是这些项目的复杂度和构建时间的消耗更高。

在此背景下,snowpackvite应势而生。

snowpack

i-snowpack.jpg

Snowpack is a modern, lightweight toolchain for faster web development.

snowpack 项目最初时间约为18年下半年,当前(2020.08.09)最新版本为2.x,标语为不需打包的 O(1) 复杂度的构建系统。其核心特征如下:

  • 开发模式启动仅需 50ms 甚至更少。
  • 热更新速度非常快。
  • 构建时可以结合任何 bundler,比如 webpack。
  • 内置支持 TS、JSX、CSS Modules 等。
  • 支持自定义构建脚本以及三方插件。

支持情况

  • 库:React、Preact、Svelte、Vue、lit-html、lit-element、Styled Components、Tailwind CSS…
  • 工具:Babel、TypeScript、PostCSS、Sass、esbuild、11ty…

上手使用

安装

1
2
3
4
5
# using npm
npm install --save-dev snowpack

# using yarn
yarn add --dev snowpack

Snowpack 也可以通过全局安装npm install -g snowpack。但是推荐每个项目使用 --save-dev/--dev

初始化新项目

最简单的项目初始化就是使用Create Snowpack App

类似于create-react-app,通过命令初始化导入项目模板:

1
npx create-snowpack-app new-dir --template @snowpack/app-template-NAME [--use-yarn | --use-pnpm]

当前官方提供的模板:

比如初始了一个react项目,其目录结构如下:

i-demo_folder.png

其中最为核心的就是snowpack.config.json,即项目配置文件。

旧项目迁移

拷贝模板项目下“src”和“public”文件到你的旧项目下,安装snowpack并执行snowpack dev,连接并调整出现的异常即可。

配置

通过 snowpack.config.json 文件配置,并能自动读取 babel.config.json 生效 babel 插件。

详细的配置和使用可见官网使用说明),从开发的角度来说跟以往项目基本无异。

开发调试

调试

1
snowpack dev

编译

1
snowpack build

会自动以 src/index 作为应用入口进行编译。

snowpack dev 命令几乎是零耗时的,因为文件仅会在被浏览器访问时进行按需编译,因此构建速度是理想的最快速。如下实际执行的情况:

i-demo_dev.png

当浏览器访问文件时,snowpack 会将文件做如下转换:

1
2
3
4
5
6
7
// Your Code:
import * as React from "react";
import * as ReactDOM from "react-dom";

// Build Output:
import * as React from "/web_modules/react.js";
import * as ReactDOM from "/web_modules/react-dom.js";

目的就是生成一个相对路径,并启动本地服务让浏览器可以访问到这些被 import 的文件。其中 web_modules 是 snowpack 对 node_modules 构建的结果。

在这之前也会对 Typescript 文件做 tsc 编译,或者 babel 编译。

编译命令 snowpack build 默认方式与 snowpack dev 相同,也可以指定以 webpack 作为构建器:

1
2
3
4
5
6
7
8
9
10
11
12
// snowpack.config.json
{
// Optimize your production builds with Webpack
"plugins": [
[
"@snowpack/plugin-webpack",
{
/* ... */
}
]
]
}

除了默认构建方式之外,还支持自定义文件处理,通过 snowpack.config.json 配置 scripts 指定:

1
2
3
4
5
6
7
{
"extends": "@snowpack/app-scripts-react",
"scripts": {
"build:scss": "sass $FILE"
},
"plugins": []
}

比如上述语法支持了对 scss 文件编译的拓展。

生产打包

默认情况下,snowpack buildsnowpack dev效果是一样的,但是在生产环境的打包构建中,我们更希望拥有如tree-shaking等其他优化手段。这时候就需要在snowpack.config.json 中配置bundler 插件,比较常用的就是@snowpack/plugin-webpack

1
npm install @snowpack/plugin-webpack
1
2
3
4
5
// snowpack.config.json
{
// Optimize your production builds with Webpack
"plugins": [["@snowpack/plugin-webpack", {/* ... */}]]
}

基本原理

i-snowpack-unbundled-example.png

snowpack是一个TypeScript项目,主要的三方依赖是rollup
与webpack不同,snowpack独立处理文件,当文件更新时,snowpack也是只会跟新这个文件,而不是像webpack一样重新构建。这样的开发模式称为”Unbundled development”。

Unbundled development有以下优点:

  • 单文件构建更快;
  • 单文件构建更准确;
  • 单文件构建更容易debug;
  • 项目规模并不影响开发速度;
  • 单个文件缓存更好。

从它的处理流程上来看,对业务代码的模块,基本只需要把 ESM 发布(拷贝)到发布目录,再将模块导入路径从源码路径换为发布路径即可。

而对 node_modules 则通过遍历 package.json 中的依赖,按该依赖列表为粒度将 node_modules 中的依赖打包。以 node_modules 中每个包的入口作为打包 entry,使用 rollup 生成对应的 ESM 模块文件,放到 web_modules 目录中,最后替换源码的 import 路径,是得可以通过原生 JavaScript Module 来加载 node_modules 中的包。

vite

i-vite.jpg

The faster frontend build tool. Unbundled web development.

vite这个项目开始时间约为2020年初,现在(2020-08-09)处于beta试用版,

其核心特征如下:

  • 快速的冷启动;
  • 即时的模块热更新;
  • 真正的按需编译。

vite与snowpack类似,并且还有一部分依赖来自于snowpack v1。相比之下vite列出相比snowpack的优势是:

  • 1.ESM HMR热更新的支持更早;
  • 2.在需要bundle打包的时候,vite使用rollup,snowpack是以Parcel/webpack插件,vite更快更容易;
  • 3.Vue支持程度更好。

上手使用

安装及初始化

npm:

1
2
3
4
npm init vite-app <project-name>
cd <project-name>
npm install
npm run dev

yarn:

1
2
3
4
yarn create vite-app <project-name>
cd <project-name>
yarn
yarn dev

基本原理

感觉与snowpack类似,也是一个TS项目,都是对单文件进行处理,核心使用了Koa。

选型和展望

从特性的构建流程来看看,vite 和 snowpack 十分接近,从现阶段来说,snowpack更加成熟,对框架的支持更好,而vite肯定对vue3生态的支持契合度更好,但是当前建设不够成熟。
简单来说,在vite和snowpack的选型中,如果你的项目是基于Vue的,那么就选择vite,react、Svelte的话还是使用snowpack吧。

那么现在应该使用snowpack/vite吗?

个人认为在大部分业务场景中还是否定的,总的原因还是兼容问题。不过可以尝试在开发环境使用snowpack/vite以提升构建速度,在生产仍然使用bundle模式。不过配置项不同也要承担一定风险:

  • 开发与生产环境构建结果不一致的风险;
  • 项目生态存在非 ESM import 模块化包而导致大量适配成本的风险;
  • 项目存在大量 webpack 插件的 magic 魔法,导致标准化后丢失定制打包逻辑的风险。

不过未来比较明确的是,nowebpack即使不能成为主流也一定能占一席之地。

相关链接