从Vite工具看ESM模块化开发探索(一)

对于公司内部现有的Webpack打包流程认识后,发现因为WebGL,threejs,pixijs等包的自己性能、体量瓶颈,致使咱们代码进行rebuild或者HotReplacement热更新的时候会致使速度很是慢的状况javascript

场景大,业务复杂,代码多致使现有开发模式有如下业务痛点:css

  1. 打包编译速度过慢,开发效率低,有时编译甚至要8分钟,热更新最慢每次也要几分钟
  2. 热更新不太贴近业务需求,每次都要reload页面,效率低

探索起点

我最初有这个想法是看到尤雨溪尤大在Vue3.0计划直播中用到的vite打包工具,当时介绍就说这个打包工具编译速度极快,用的ESModule的规范,当时就产生了浓厚的兴趣html

当时就想到咱们公司内部的代码,在webpack加持下,那是一个“重”,每次跑项目的时候,个人16G内存的MBP就扛不住了,风扇很响,烫的能够煮鸡蛋,编译时间也是很是头疼。前端

随后在后续的开发中,我也一直持续在关注尤大仓库的vite代码更新,发现他更新得比较频繁,能够看出尤大对于这个项目仍是有必定的投入的。java

后续关注中我又了解了snowpack,esbuild等半成品的工具,了解其中的原理后,以为如今是合适的机会去推广ESModule规范下的实践,在对于前期探索阶段,到底能不能给咱们开发带来质的提高,还有待勘察。node

技术原点

对于ESModule的原理起点,能够从script标签的type="module"提及:react

<script type="module"> import foo from '../foo.js'; import bar from '../bar.js' foo(bar); //... to do </script>
复制代码

如今随着浏览器的发展和技术规范的推动,当代大部分浏览器都已经支持在type="module"script标签下直接执行解析import语句,在直接执行到这一步的时候,浏览器会自动根据目录路径去请求路径下的资源,只要触发了请求,咱们就能够“拦截”了,把请求的assets资源截取进行处理,返回给浏览器执行。webpack

依照上述的技术理论,只要有一个index.js就能够一步一步import不停地向下收集依赖,直至最后一个依赖,依次执行,前几年的打包工具(相似webpack)不就作了这个事吗?固然他作的整合更加复杂和详细,可是如今浏览器都已经支持了,这一步就能够交给浏览器作了,咱们要作的,就是对依赖进行解析。ios

技术调研

对于前期的技术调研,我看到阿里的一篇文章Webpack 打包太慢?来试试 Bundleless 吧!,当时看到这里时,以为跟咱们的业务场景的痛点很是类似:git

咱们内部也是依赖底层支撑,整合多方业务插件的业务架构,因为WebGL,Threejs,pixjs过于臃肿的包而致使总体热更新体验很差,而sourceMap的输出更加加剧了架构的负担,首次的build开发体验差劲。

对于当下ESM推广程度的调研,发现存在一些技术问题:

1.模块导入语法不支持

由于ESM规范下的全部开发体验都是强依赖于浏览器的,因此对于模块的依赖收集,咱们也是依靠浏览器直接解析下列语句而作的:

import foo from '../foo.js'
复制代码

foo文件会根据上下文去寻找路径文件,而后直接发起请求,可是问题来了,咱们原先不都是这么写的吗?

import React from 'react';
复制代码

他会按照预期,找当前目录下的react.js吗?答案固然是否认的,原来的打包器会根据require(commonJs规范)去进行映射,引入node_modules下对应的模块。

可是如今会报错,浏览器会报如下错误:

考虑到浏览器的安全性,是不支持这样直接找模块引入的,而要指定具体的路径,解决办法有吗?固然有

解决模块化引入的问题

咱们在触发import语句以前,能够经过转译成AST,重写当前import节点路径后,映射到咱们服务器的静态资源目录(vite就是这么作的)或者直接映射到node_modules:

// 转译前
import React from 'react'

// 转译后
import React from '__Module__react';

// 路径解析的时候替换 __Module__
import React from '../node_modules/react/entry.js'
复制代码

解析成__Module__react后能够替换关键字,映射到真实node_module模块,经过package.jsonmain字段(寻找主入口,此处为例)进行lookupFiles的操做,找到打包后的真实入口,而后直接根据模块路径引入。

固然,记得每一个模块引入的时候进行缓存。

2. 不少模块不支持ESModule导出

此次调研中,发现强如React,也没有支持ESModule导出,他们给出的答复是:The React team is working on ESM support,他们在dist打包中并无支持ESModule的导出,这也致使了你若是用ESM规范去引入React势必会报错,那么现有解决方案能够解决吗?答案是确定的

Rollup处理ESModule导出

rollup也崇尚ES模块:

Why are ES modules better than CommonJS Modules?

ES modules are an official standard and the clear path forward for JavaScript code structure, whereas CommonJS modules are an idiosyncratic legacy format that served as a stopgap solution before ES modules had been proposed. ES modules allow static analysis that helps with optimizations like tree-shaking and scope-hoisting, and provide advanced features like circular references and live bindings.

能够说更好的ES标准生态,更能明确将来的发展大方向,咱们能够经过rollup在服务器启动时进行预优化,能够很好地优化到每一个不支持ESM的模块。

3. 依赖过多,请求数过多致使实际开发很慢

仍是经过rollup,能够再预启动阶段,打包一些文件,有条件的话能够缓存(service-worker),优化下一次请求依赖,减小浏览器由于import语句过多而产生的请求数过多的问题。

4. Css,less等样式和其它资源如何解决?

对于如下写法

import './index.module.less'
复制代码

其实浏览器是没法解析的,这一点咱们也能够借鉴原来打包器的经验,经过import一个js模块的模式引入,经过post-css,less等处理预编译的能力,学习相似css-loader, style-loader相似的方式,进行style标签的嵌入样式,此处也能够处理一些hash值和前缀等

5.jsx,typescript?

我发现了一个很是好用的基于ESModule规范下的工具esbuild,能够支持构建和转译代码,而且速度极快(go语言并发高),以native代码直接构建,构建速度也是极快的。在加载的时候,就能够经过转译器进行翻译。

6.开发环境和生产环境?

咱们运用浏览器支持ESModule规范的原理,固然能够享受很是快速的编译和开发,可是随之而来的问题就是,你开发环境的这些配置确定没法应用到生产环境?毕竟你不能要求全部人都用ESModule支持的浏览器。还有一些语法是须要polyfill来解决兼容问题的。

rollup能够支持这个操做,同时要保证在开发环境的一些rollup打包和生产环境表现是一致的,另外要注意一些babel-runtime的操做,babel应该尚未彻底支持相似的打包解析,可能请求路径或者资源和实际会有出入。

7. Todo-热更新?

技术可探索亮点

1. services-worker

对于一些不常变更的资源,由于都是请求,咱们甚至能够在SW层面作一些缓存,在对于一些依赖的收集后,能够给出一个配置字段,来配置一些须要长时间本地缓存的打包文件,直接经过SW返回,加快传输速度,提升开发效率

2. HTTP2

对于HTTP2提供的Header 压缩和二进制传输对于咱们场景内的大模型数据传输慢的问题能够有效解决?这个问题待验证,可是不失为一种思路,能够进行探索,而多路复用对于咱们场景内大量模型的载入和资源的加载也确定有至关棒的优点,这个为技术亮点,能够进行探索发掘。

落地实践成果预测

启动体验

我本身跑了几个demo,效果仍是明显的,首先是webpack层面开发环境的:

  • webpack

  • vite

能够看到,首次打包的效率在demo里面表现仍是比较明显的,webpack的明显慢许多,vite已经作到了无感知的开发体验。

热更新体验

  • webpack

  • vite

打包体验

在最终的生产环境打包方面,二者相差无几,webpack甚至还快一点:

  • Webpack

  • Vite

SourceMap

其中比较具备诱惑的点就是SourceMap的功能,用ESM的话会自带这个功能,由于每一个请求的地址都是映射到真实的文件,就不须要一些臃肿的SourceMap了,能够直接在Source里面搜索源文件,能够说是很是香了。图中由于vite会作一层对应js文件再静态资源目录的转译,本质上是不变的。

其余

由于公司级别的项目较复杂,涉及牵扯面比较多,对于打包的迁移成本有点大,这里咱们依照阿里内部的落地实践来看,效果仍是明显的:

  • webpack

  • vite(为例)

从阿里实践来看,在启动单个 bundle 时,Webpack 须要 10s 左右的时间,用 Vite (ESModule规范下)只须要 1s 左右,提高 10 倍,热更新更是到达了毫秒级的体验,

将来

探索的目的就是为了明确将来的方向,能够说ESModule的规范化和推广度在稳固上升的场景下,利用这些特性使用在开发中是你们所须要的,从尤大每隔几天就提交一次修改记录能够看到他对这个项目也很是上心。

能够说规范化的推动有助于前端的持续发展,这次的探索成果能够说是颇有用,想象一下,你开发的时候,不须要再过多等待编译和打包,而是”毫秒级“的体验,加快你的开发效率。从前,你编译一次2分钟,调试一次3分钟,一天下来,浪费在这些无用的地方的时间可能就有小时级别的,那是低效率的表现。

将来的ESM打包本质就是把webpack低效率的依赖收集解析的工做交给浏览器去执行,加快编译转换,提升效率

下一期会从简单demo完成一个打包器,把其中涉及到的知识点巩固一遍。

相关文章
相关标签/搜索