在开发过程中,我们使用最新的语言特性,及支持最新特性的浏览器,同时,也使用sourcemap
hot reloading
, hot replacement
来提高开发效率。但在build产物时,则会开启混淆压缩,编译到es5环境。
为什么会产生这种分歧呢?是因为服务对象的不同。在开发过程中,开发者倾向于提高开发效率和面向未来编程,而在使用过程中,用户需要稳定且兼容的服务,无论他使用的什么浏览设备,浏览器及任何版本都可以正常运行。因此开发中的编译处理是十分有必要的。
但大部分编译都是有副作用的,为了避免这些问题,应该满足以下因素
- 尽可能的无副作用
在开启混淆压缩时,会对变量进行重命名,如果语句中有eval就可能会出现副作用。
在编译到es5环境中,某些特性没有完整的polyfill,也会带来副作用。
可以通过限制语法,来实现副作用的减少。
- 完备的测试
自动测试。
各个环境的测试。(有没有可能实现自动测试多个环境的工具?)
而bundless也可以是开发中的一环。在开发过程中由于资源加载速度没有限制,我们完全可以不进行打包,而选择加载分离的js文件,在对应js变化时也可只编译对应js文件。
简单实现
独立编译
每一个文件都要进行对应的处理,比如不同的模块配置。
-
使用esm模块,浏览器支持此模块
-
cjs模块,编译到esm模块
cjs在限制部分语法的情况下,可以编译到esm模块
// hello.js module.exports = function hello() { console.log('hello'); } //====> export default function hello() // world.js module.exports = function hello() { console.log('world'); } //====> export default function hello() // index.js const hello2 = () => { require('./hello.js')(); require('./world.js')(); } module.exports = hello2; // ===> 比如检测 require('xxx')的正则表达式,限制参数只能是字符串 import hash1 from './hello.js'; import hash2 from './world.js'; const hello2 = () => { hash1(); hash2(); } module.exports = hello2;
当然也可以自己不使用esm格式,像amd类似的格式也可以
wrapper('./hello.js', './world.js', function() { const hello2 = () => { require('./hello.js')(); require('./world.js')(); } module.exports = hello2; }); // 全局变量wrapper, 在加载完子依赖后再执行后续内容 // 全局变量require, 加载文件,如果已经加载则返回缓存 // module.exports 表明此模块最终加载结果。
按需编译
- node_modules里的依赖
如果有esm格式可直接使用,如果没有可以编译到esm格式,并生成对应版本的缓存。只有在更新版本时才会重新生成缓存。(之前使用snowpack过程中,更新了软件包但还是使用旧版本的缓存,需用snowpack —reload清空,较为不方便)
- 导入js文件
- 启动express服务, 最开始所有js文件都标记为dirty。
- 在访问到标记dirty的js文件时,对js文件进行编译,返回编译后的文件,清除dirty标记。
- 在访问没有dirty标记的文件时,直接返回上次编译文件。
- 通过watchFile(比如chokidar)监测js文件的更改,在更改后将此文件标记为dirty,刷新页面。
hot replacement 热重载
文件改变时,触发该文件刷新
该文件的父依赖接受到子依赖刷新事件
因此父依赖代码中应包含对应的处理代码,如果处理不了则触发父依赖进一步刷新。
总结
bundleless是未来。目前受限的一个原因是因为部分现有的软件包并没有esm格式。
bundleless的思想也可应用到别的方面,比如在游戏开发中,直接导入分离的图片素材,而在发布时可以将图片自动打包成一张或多张图片,也可按需加载,在使用到某个素材时才加载对应的打包好的图片。不过需要增加一个处理层,确保分离导入和打包导入都能获得一致的返回结果。
在这种开发模式中,测试驱动型开发(BDD)可以大展身手。因为有完备的测试逻辑,能在不合理地使用了被限制的语法时得到测试失败提示,从而可以更”冒进”地进行工程开发,在追求开发效率的路上狂奔。