妙用 script 的 nomodule 属性实现优雅降级和性能优化

865 阅读2分钟

什么是 module/nomodule 技术

我们在打包时,如果打包目标是中底版本的浏览器,就需要插入许多兼容函数和语法转化。

而用高版本浏览器访问,其实并不需要这些变换。如果用高版本浏览器访问,能够直接运行高版本浏览器的语法,就可以太大减小在高版本浏览器下的包体积。

使用 <script type="module"> 可以在让支持 esmodule 的浏览器运行,不支持 esmodule 的浏览器忽略。

使用 <script nomodule> 可以让支持 esmodule 的浏览器忽略,不支持 esmodule 的浏览器运行。

我们可以利用以上特性写个打包方案输出 2 个版本,用不同的配置进行使用。

比较特殊的一点是 Safari 10 两个都会运行,我们提前运行个 polyfill 就能解决。


处理高版本

高版本转化为 es2015,但是有 2 个常用的重要特性是 es2015 不支持的。

一个是 async await 一个是动态 import。

async await Babel 可以直接转,没有什么特殊处理。

动态 import 比较特殊,要先在全局变量里保存 promise 的 resolve,然后动态 import 需要动态创建 script 标签,内容大致如下

<script type="module">
import * as namespace from "模块";
__import_promise_resolve__["模块"](namespace)
</script>

我们可以把动态 import 和资源预加载和 nomodule polyfill 写成基础库放在最前面运行。

这样我们就在高版本省去了 Promises、Map、Set 的打入和 Class 、arrow functions 的转化等。


处理中低版本

中低版本,我们写个 amd 或 systemjs 的加载器放在最前面就行了。

然后我们看看效果。

Nice


处理低版本

中低版本我们也可以按照这个思路进一步细分

IE8 到 IE9 有巨大的差异,我们用条件注释在分为低中 2 个版本。

低版本不会打入其不支持的功能,中版本也不会打入已支持的功能。

还可以用别名让低版本使用低版本实现的相关库(如 anujs)

我们引入个 React 看看效果

Nice


CSS

使用这种方式 CSS 的使用我们也可以通过不同版本的 PostCSS 配置打出不同版本的 CSS 可以减少加载的代码。


打更多版本

是否能够再更包版本上进行区分?让浏览器原生运行 async await 和动态 import?

按道理是可以的,但是需要打更多版本的包。而且需要 js 判断。

我认为目前 3 个版本最佳,因为差异够大,且有 html 层的判断方式。

今后也许会有差异够大的特性需要再打一个版本的包