浏览器实时构建方案探索

1. 为什么需要实时构建

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

**打包工具(Bundler) ≠ 构建工具(Build Tools) ≠ 工程化 **

  • 前端包依赖的庞大,当项目增长时依赖关系和体积指数级增长
  • 传统构建速度慢,需要不断优化
  • 复杂的构建工具,学习曲线,Webpack5??
  • 在 ESM 出现之前,JavaScript 的模块化就有各式各样的规范,主要有 CommonJS, AMD, CMD, UMD 等规范,最为广泛的就是 Node.js 的 CommonJS,使用 module.exports 和 require 来导出导入模块,它是 npm 中的模块最主要提供的格式。由于浏览器并不直接支持这些模块,因此打包工具(Webpack,Browserify,Parcel 等)出现了。

Webpack 一方面解决了前端构建大型项目的问题,另一方面提供了插件优化 Web 前端性能。例如从资源合并以及压缩上解决了 HTTP 请求过程的问题,把小图片打包为 base64 以减少请求量,提供 Tree-Shaking 以及动态导入。伴随着 HTTP 协议的升级以及网速的不断加快,这些工作慢慢变成负担。

以 CRA 为例,只是为了运行一个 React 应用,我们居然还需要附加如此复杂的依赖。

image.png

前端的依赖比黑洞造成的时间扭曲还要大。

image.png

ESM 允许进行静态分析,从而实现像 tree-shaking 的优化,并提供诸如循环引用和动态绑定等高级功能。同时在 Node v8.5.0 之后也可以在实现标准的情况下使用 ESM,随着Node 13.9.0 版本的发布,ESM 已经可以在没有实验性标志的情况下使用。

ESM在浏览器中使用了大约5年的时间,现在在所有现代浏览器中都受支持(可追溯到2018年初)。使用ESM,不再需要打包工具。您可以在没有 Webpack 的情况下构建一个现代化,高性能,可用于生产的Web应用程序!

  • 兼容性可接受:基本主流的浏览器版本都支持直接使用 JavaScript Module 了(当然,IE 一如既往除外)。
  • 性能问题的改善:之前打包的一个重要原因是 HTTP/1.1 的特性导致,我们合并请求来优化性能;而如今 HTTP/2 普及之后,这个性能问题不像以前那么突出了。
  • 打包的必要性:打包工具的存在主要就是为了处理模块化与合并请求,而以上两点基本解决这两个问题;再加之打包工具越来越复杂,此消彼长,其存在的必要性自然被作者所质疑。
image.png

2. 实时构建有什么解决方案——Bunless

  • 什么是Bundless

  • webpack 之所以慢,主要的原因还是在于他将各个资源打包整合在一起形成 bundle,如果我们不需要 bundle 打包的过程,直接让浏览器去加载对应的资源,我们将有可能可以跳出这个循环,实现质的提升。

  • image.png

在 Bundleless 的架构下,我们不再需要构建一个完整的 bundle,同时在修改文件时,浏览器也只需要重新加载单个文件即可。由于没有了构建这一层我们将能够实现以下的目标:

  • 极快的本地启动速度,只需要启动本地服务。

  • 极快的代码编译速度,每次只需要处理单个文件。

  • 项目开发构建的时间复杂度始终为 O(1),使得项目能够持续保持高效的构建。

  • 更加简单的调试体验,不再强依赖 sourcemaps 即可实现稳定的单文件的 debug。

  • bundleless解决了什么问题?

bundleless 解决了大部分前端开发者 的一大痛点,就是心流。当一个程序员被打搅后,他需要 10 - 15 分钟的时间才能重新恢复到之前的编程状态。当你在完成一小块功能时候,想要去查看结果时候,发现当前的项目还在构建状态,这个时候很有可能就会中止心流。

  • Bundless实现

  • System.js 之类的 ES 模块加载器,好处是具有较高的兼容性。

  • 直接利用 Web 标准的 Native-ESModule,面向未来,同时整体架构也更加简单。

3. 从资源加载看 Bundle 和 Bundleless 的不同

下面以大家最熟悉的 create-react-app 默认项目为例,从实际的页面渲染资源的加载过程对比 Bundle 和 Bundleless 的区别。

  • 整体代码解构
  • image.png

    image.png
  • 加载机制
image.png
image.png
image.png

在项目启动和有文件变化时重新进行打包,这使得项目的启动和二次构建都需要做较多的事情,相应的耗时也会增长。从上图可以看到,Vite项目已经不再有一个构建好的 bundle、chunk 之类的文件,而是直接加载本地对应的文件。

image.png

从上图可以看到,在 Bundleless 的机制下,项目的启动只需要启动一个服务器承接浏览器的请求即可,同时在文件变更时,也只需要额外处理变更的文件即可,其他文件可直接在缓存中读取。

对比总结

image.png

4. Bundless实现

实现 Bundleless 的第一步是要让浏览器自主加载对应的模块。

使用 type="module" 开启 ESModule

如果type属性为module,代码会被当作JavaScript模块 。请参见ES6 in Depth: Modules

利用 import-maps 支持 bare import

分享一个在 chrome 中已经实现了的 import-maps 的标准 ,可以让我们直接用 import React from 'react' 这样的写法,未来我们可以利用此能力实现线上的 Bundleless 部署。

面向本地开发的场景,我们只需要启动一个本地的 devServer 承载浏览器的请求映射到对应的本地文件,同时动态地将项目中 import 的资源路径指向我们的本地地址,即可让浏览器直接加载本地的文件,比如可以使用下面的写法,将入口 JS 文件直接指向本地的路径,然后 devServer 再拦截相应的请求返回对应的文件。

如何加载非 JS 的文件资源

浏览器在处理文件时是依据 Content-Type 的,不关心具体的文件类型,所以我们需要在浏览器发起请求时,将对应的资源转化为 ESModule 格式,同时设置对应的 Content-Type 为 JS,返回给浏览器执行,浏览器就会按照 JS 的语法进行解析处理

  1. Bundless框架介绍
  • snowpack

snowpack解决了什么问题?

出于对主流浏览器的判断,SnowPack 大胆采用 ESM,其原理也很简单,内部帮助我们将 node_modules 的代码整理并且安装到 一个叫做 web_modules 的文件夹中,需要的时候直接到该文件夹中引入即可。其目标也是为了解决第三方代码的引入问题。

  • 目前对 node_modules 的处理是做了 bundle 的

  • 它仍然提供了 @snowpack/plugin-webpack / @snowpack/plugin-parcel 这样的插件来让你能为生产环境做打包。所以,配合 module/nomodule 技术,它将会有更强的抵御兼容性问题的能力,这也算是一种渐进式营销手段

  • vite

  • Vite was created to tackle native ESM-based HMR. When Vite was first released with working ESM-based HMR, there was no other project actively trying to bring native ESM based HMR to production.
    Snowpack v2 initially did not offer HMR support but added it in a later release, making the scope of two projects much closer. Vite and Snowpack has collaborated on a common API spec for ESM HMR, but due to the constraints of different implementation strategies, the two projects still ship slightly different APIs.

  • Both solutions can also bundle the app for production, but Vite uses Rollup with built-in config while Snowpack delegates it to Parcel/webpack via additional plugins. Vite will in most cases build faster and produce smaller bundles. In addition, a tighter integration with the bundler makes it easier to author Vite transforms and plugins that modify dev/build configs at the same.

  • Vue support is a first-class feature in Vite. For example, Vite provides a much more fine-grained HMR integration with Vue, and the build config is fined tuned to produce the most efficient bundle.

5. 扩展-Cloud IDE

未来趋势:Cloud + Browser Based Bundless + Web NPM

  • 把服务端的能力进行输出。这种方案的优势是服务端拥有和本地研发环境一致化的环境;缺点是即时性较差、效率较差、无法离线、成本高昂。
  • 把客户端的能力释放出来。这种方案的优势是无服务端依赖、即时性、高效率、可离线运行;但缺点也比较明显,所有能力建设都必须围绕着浏览器技术。

Gravity 架构大图

image.png
image.png

其实,Clever就是一个Cloud IDE

6. Reference

  • 探讨不需要打包的构建工具 Snowpack
  • Webpack 打包太慢?来试试 Bundleless
  • Vite
  • 链接

你可能感兴趣的:(浏览器实时构建方案探索)