import-html-entry源码浅析

难道没人好奇qiankun是如何实现html entry的吗?

single-spa和qiankun最大的不同,我认为就是qiankun实现了html entry,而single-spa只能是js entry

之前使用single-spa的过程中,发现如果有多个chunk 插入到html中,那么他们的执行顺序会很关键。而从下图single-spa注册应用的钩子函数来看,它只暴露了一个方法来加载资源,这意味着如果你有多个chunk,你需要自己根据加载顺序依次加载。简而言之,它必须串行加载,浪费时间,白屏时间长

1
2
3
4
5
6
7
8
9
10
11
12
singleSpa.registerApplication(
'appName',
() => System.import('appName'), // 加载appName 对应的js资源
location => location.pathname.startsWith('appName'),
);

// 它需要依次加载chunk
singleSpa.registerApplication(
'appName',
() => System.import('chunk').then(()=> System.import('appName')), // 先加载依赖的chunk,再加载appName
location => location.pathname.startsWith('appName'),
);

qiankun解决了这个问题,它希望我们像使用iframe一样加载一个子应用,只需要知道其html的url就能加载到主应用中。这可能是其github star数已经超过single-spa的一个重要原因。难道就没人好奇,qiankun是如何实现了html entry吗?importHTML 方法究竟做了什么?

importHTML的几个关键方法

首先importHTML的参数为需要加载的页面url,拿到后会先通过fetch方法读取页面内容,并且返回为页面html的字符串,接下来的processTpl方法比较关键,是一个核心方法。它会解析html的内容并且删除注释,获取style样式及script代码(下图 line38-82)。用的方法很明显是正则+replace,但是每一个步骤都做了很多适配,比如获取script脚本,需要区分该script是不是entry script,type是JavaScript还是module,是行内script还是外链script,是相对路径还是绝对路径,是否需要处理协议等等。很复杂!借用Joel Denning的一句话 “Kuitos did great job”

processTpl的返回值也从上图可见,有template,script,style,entry。为什么要把entry单独出来?它不是一个普通的JavaScript脚本么难道?肯定是因为它需要等其他JavaScript都加载好才能执行啦,不然肯定会报错的。importHTML拿到这些返回值,并暴露出来几个方法。最常用的肯定是execScript、getExternalStyleSheets、getExternalScripts 等上图画五角星的三个关键方法。

execScript做了什么事

望文生义,肯定是把processTpl返回的script exec一下呗。具体流程如下图。

https://pic4.zhimg.com/80/v2-8d807e26342b1d6e7ea0387c7a95b40f_720w.jpg

execScript会先调用内部方法getExternalScript,将外部script拿到和行内script合并成一个队列按顺序执行。getExternalScript的内部就是一个promise.all 这也是我们使用qiankun后它就不必串行加载script的关键所在。getExternalScript后所有的行内script和外部script都被以text的形式获取到,接下来就是执行script了,execScript还注册了两个内部函数,schedule和exec,很显然,schedule会按照次序调度exec执行script代码,如何执行JavaScript 字符串呢? 使用eval解决的。

getExternalStyleSheets和getExternalScripts简单很多,只需要获取到style或者JavaScript文本就好了,返回给调用importHTML的开发者,自行处理。

我们一句话总结一下 import-html-entry 为 qiankun 做了那些事,”获取html的url,解析模板并暴露出来一些可以读取css、js的方法