小试snowpack

415 阅读4分钟

1.前言

去年尤雨溪在发布Vue3的时候,‘顺带’还发布了打包工具vite,该工具利用现代浏览器对ESM的支持对每个更改的模块即时编译后直接提供给浏览器。当时vite使得bundless又变成了讨论的热点,在vite之前就有了基于ESM的打包工具snowpack了。前不久snowpack发布3.0版本,接下来就简单了解一下 像parcel和webpack这种bundle的打包工具会根据入口文件把相关依赖的代码和第三方库打包成一个和多个chunk,每次有代码变更时则需要重新构建和打包chunk。一个复杂项目的build+bundle时间都得按秒和分算了... snowpackdev环境下基于ESM进行即时加载和HMRsnowpack会扫描所有代码,生成依赖列表,再用@rollup/plugin-commonjs把每个依赖打包成ESM,可以直接在浏览器运行,然后进行依赖缓存和记录绝对路径。每个文件只被build一次并缓存,当有文件变更时,只对变动的文件进行rebuild。在production环境下使用bundless或许太过激进,所以snowpack同样支持使用parcel或者webpack进行打包。

相对比bundle,在dev环境使用bundless有诸多好处

  • 单个文件的build速度更快
  • 单个文件的build更易于debug
  • 单个文件的build结果是固定的
  • 项目的size不会影响dev的速度
  • 单个文件的缓存效果更好
  • 每个文件都被单独的build和cache,没有变更的文件只会被build一次和load一次
  • snowpack扫描代码,有区别的处理依赖,把所有用到的npm package打包为一个JavaScript文件,因为这些npm package一般不会有变动,所有一般不会需要重新bundle这些npm packages。经snowpack打包过的npm package是可以直接在浏览器中运行的。

2.start

先新建一个snowpack项目

mkdir snowpack-demo
cd snowpack-demo
npm init --yes
yarn add snowpack --save-dev

根目录下增加index.html文件

<!DOCTYPE html>
<html lang="en">  
    <head>    
        <meta charset="utf-8" />    
        <meta name="viewport" content="width=device-width, initial-scale=1" />    
        <meta name="description" content="Starter Snowpack App" />    
        <title>Starter Snowpack App</title>  
     </head>
     <body>
        <h1>Welcome to Snowpack!</h1>  
     </body>
</html>

package.json里添加script image 再执行

yarn dev

即可看到运行结果 dev结果.png

3. 使用JavaScript

增加两个文件

helloworld.ts
export const helloworld = (name: string) => {
    console.log(`hello ${name}`);
}

index.js
import { helloworld } from './helloworld';
helloworld('snowpack');

在项目的index.htmlhead中增加

    <script type="module" src="/src/index.js"></script>

可以看到snowpack是默认支持ts的,此外还支持CSSCSS ModuleJSONJSX

4. 使用CSS

增加css文件index.css

h1 {
    color: #16871f;
}

在项目的index.html中加载该css文件

    <link rel="stylesheet" type="text/css" href="/src/index.css" />

可以看到页面字体颜色就已经改变了。 如果想使用scss,则可以使用@snowpack/plugin-scss插件

5. 使用react

先安装reactreact-dom

yarn add react react-dom --save

然后写一个react组件Title.tsx

import React from 'react';
export const TitleComponent = () => {
    return <>this is title from react</>;
};

index.html中增加react组件的挂载点,并且把ESM的加载放在挂载点之后 react_index.pngindex.js改为index.jsx,并且调用ReactDOM.render react_render.png 页面就更新了,此时可以看到项目中使用的到ESM都是作为独立的模块文件加载的 独立加载.png

6. Build

  1. snowpack默认使用bundless进行build,在执行snowpack build后可以看到上面项目的打包结果,第三方的依赖是放在pkg文件夹下,也作为一个ESM的js文件,自定义的模块还是原有的层级。 build-1.png bundless在production环境太过激进,没关系,snowpack支持使用plugin来控制构建的工作流。可以使用@snowpack/plugin-webpack来实现bundle。只需在snowpack.config.js中配置一行即可 build-2.png 然后再执行snowpack build,发现build结果就是bundle了 build-3.png
  2. 按需加载,es module的静态分析天然对tree-shaking友好 此时build/_snowpack/pkg/react.js的内容为 build-4.png 如果在Title组件里加入reactuseState build-5.png 则打包后的react.js变成了 build-6.png

7. 结语

snowpack发布距今已有一周年了,github上的star已经有17k+,各种plugin也有100+。生态虽然比不上webpack,但是也在快速发展了,在production环境中使用或许还不成熟,但是在dev时可以节省hmr的时间。npm的臃肿,加上火热的serverless,snowpack代表的bundless还是有不错的前景的,或者webpack不讲武德,直接偷袭一个snowpack-plugin?