/* hello.js */
// Default export
export default () => {
console.log('Hi from the default export!');
};
// Named export ``
export const sayHi = (user) => {
console.log('Hi from the named export!', user);
};
<script type="module">
import('./hello.js')
.then((module) => {
module.default();
// → 'Hi from the default export!'
module.doStuff('doddle');
// → 'Hi from the named export!, doddle'
});
</script>
背景
最近一直在做一个技术改进:微前端中子应用采用umd方式分包构建,取代现有的systemJs方式构建,解决子应用稍微复杂一点后构建资源过大造成应用加载缓慢的问题。
依赖umd分包,就需要依赖webpackJsonp的全局变量通信, 这个技改方案最后成功了,但这个过程让我对SystemJs有了新的认识。准确点说它差一点就成功忽悠住了我,幸好18岁的我保留了足够的好奇心,没有被表面现象懵逼。
根深蒂固的认知
作为一个工作6年的前端,虽然离牛逼还有成都地铁六号线那么远的距离,但自认为自己基础还是扎实。在我的认知里,所有的浏览器JS代码运行,都离不开script标签的引入,比如:
1.内联script
2.远程脚本加载
3.Es6 module
和前面一致,只是多一个
type="module"
标识4.动态 import()
但这个语法支持的浏览器很少,还只是一个提案,chrome也只有高版本做了支持。所以在业务开发中使用webpack打包,都对这个语法做了polyfill,其原理还是利用了script加载与webpackJsonp.push劫持做的发布订阅来实现,具体原理在去年我一篇流水账中有提到:webpack 打包的代码怎么在浏览器跑起来的?看不懂算我输
差点刷新我认知的SystemJs
这两年微前端的兴起,让SystemJs这个模块化方案也是火了一把,以前我是不知道webpack的libraryTarget配置还有
system
这一说的:webpack之libraryTarget设置对
SystemJs
还没有概念的,可以跑一下官方demo感受一下它的黑魔法
:systemjs-examplesSystemJs看起牛逼在哪呢?以demo库的示例dynamic-import为例:
Demo 我稍微改了一下,把triton.js从主动动态加载,改成点击按钮后再动态加载,只是为了加载过程更明显。点击按钮后,界面和元素长下面这样:
发现没?
triton.js
没有被加载到html中,但这个JS的内容确实是已经执行了,洋气不洋气, 惊不惊喜?!!!难道script真的可以不加入到html就能执行?但再仔细搜索,发现是有script请求下载记录的:
黑魔法解密
如果你想要快速知道答案,你可以在network直接点击script加载的触发节点:
顺着点开,你会发现黑魔法不过是一个戏法:
先把script加载到html中,加载完成后,再将这个script从html中移除,看起让人不明觉厉。
浅入SystemJs
为什么要做这种骚操作(卸磨杀驴)?留在那貌似也没有什么问题。
这种操作也不是不可以,因为script标签加载完成就会马上执行,除非加上了
defer
标识,或者采用了preload或者prefetch标签来预加载。一旦script标签中的内容被执行,其有用或者需要再次被调用的部分,就会以引用的方式存在内存中,这时script中的内容确实就是个摆设,重绘重排都没用,只有重新加载才会触发执行。简单了解一下SystemJs的原理:
当我们引入
<script src="https://cdn.net//system.js"></script>
时,就会完成以上操作,简单来讲就是生成一个System实例,遍历System相关的script标签,做一下预处理。system-module类的标签其实是唤起模块执行的一个入口,其实质是调用System.import方法。与System.import相对应的,是System.register,仔细看上面示例:
当调用
import('./triton.js')
时,System就会发起triton.js的script加载,当加载完成后,就会开始System.register的模块注册,这时只会注册模块为一个函数,并还不会执行,因为要检测模块是否还有依赖,如果有,就需要待依赖模块加载完后,再调用execute
方法执行并导出。然后通知import方法,导出已收到,resolve 执行then中内容。除了支持SystemJs模块以外,还支持
amd
和umd
模块,但其依赖扩展extras/amd.js
, 其原理就是在window上注入了amd模块依赖的define
方法,然后这个方法会把amd转化成register注入,原理还是比较易懂。但引入这个扩展前,还是有一些坑,我踩过:global.System.constructor.prototype
;externals
,但因为amd扩展的引入,这些global依赖就变成了SystemJs导入,应用会加载失效,所以有一种投机的加载方式就是:待其他js script导入完成后,再执行extras/amd
;以上只是SystemJs 浏览器相关的一些比较核心的流程,很多细节性的处理我也没深究,应该差不了多少。
欧洲杯看完了,补一个System.import的导入流程
总结
元宵也过完了,就以这一篇解(water)密(wen)开启我的2021 技术之旅吧。元宵节快乐,离5.1 还剩61天,坚持。。。