You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
/* hello.js */// Default exportexportdefault()=>{console.log('Hi from the default export!');};// Named export ``exportconstsayHi=(user)=>{console.log('Hi from the named export!',user);};
<scripttype="module">import('./hello.js').then((module)=>{module.default();// → 'Hi from the default export!'module.doStuff('doddle');// → 'Hi from the named export!, doddle'});</script>
// neptune.jsSystem.register([],function(_export,_context){return{execute: function(){document.body.appendChild(Object.assign(document.createElement('p'),{textContent: 'Neptune is a planet that revolves around the Sun'}));// 点击按钮后 加载triton.jsdocument.querySelector('#load').addEventListener('click',()=>{console.log('start debug');_context.import('./triton.js').then(function(triton){console.log("Triton was discovered on",triton.discoveryDate);});});}};});
// triton.jsSystem.register([],function(_export,_context){return{execute: function(){document.body.appendChild(Object.assign(document.createElement('p'),{textContent: 'Triton is a moon that revolves around Neptune.'}));_export("discoveryDate","Oct. 10, 1846");}};});
// _context 意指实例与System_context.import('./triton.js').then(function(triton){console.log("Triton was discovered on",triton.discoveryDate);});System.register([/*依赖项*/],function(_export,_context){return{execute: function(){document.body.appendChild(Object.assign(document.createElement('p'),{textContent: 'Triton is a moon that revolves around Neptune.'}));_export("discoveryDate","Oct. 10, 1846");}};});
背景
最近一直在做一个技术改进:微前端中子应用采用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天,坚持。。。
The text was updated successfully, but these errors were encountered: