document.write的名声好像一直不是很好,也许是它经常出现在初学者教程中,也可能是被劫持的js文件中经常用出现这东西,导致大家都满排斥它的。
但是,无论它的长相多难看,它确实能解决同步加载的问题,比如创建一个script,然后把它appendChild到head中
//x.js alert(1)
<script> let $scp = document.createElement('script') $scp.src = './x.js' document.querySelector('head').appendChild($scp) </script> <script> alert(2) </script>
输出的消息先是2然后是1,程序并不会因为appendChild而陷入等待
接下来使用document.write在页面中塞入一个script
<script> document.write(`<script src="./x.js"></script>`) </script> <script> alert(2) </script>
发现程序按理想的顺序执行了,document.write会使程序陷入等待,输出的消息先是1然后是2
要注意的是,如果塞入的script是跨站的,在chrome中会出现下面的警告
<script> //a.com document.write(`<script src="//b.com/x.js"></script>`) </script>
就是说,在网络很差的情况下,document.write写入的script如果是跨站的会被无视掉的!
利用它同步加载资源特性就能解决一些场景下的问题
比如需要按照平台相关的代码来同步引入代码
<script> const ua = navigator.userAgent if(/android_ysdk/.test(ua)){ document.write(`<script src="./x.js"></script>`) } if(/MicroMessagenger/.test(ua)){ document.write(`<script src="./weixin.js"></script>`) } </script>
还有为了按需加载那些不支持某些特性浏览器的polyfill,需要做兼容检测处理
<script> if(!Object.keys){ document.write(`<script src="./object-keys.js"></script>`) } if(!Array.isArray){ document.write(`<script src="./array-isArray.js"></script>`) } </script>
document.write能够想到的用途就上面这儿了,总之就是为了解决同步资源加载问题
上面的两种实现方式都有能够替代的方案:
需要插入代码的检测逻辑服务器来判断
平台相关的代码由服务器端检测ua来输入script到html中
polyfill的代码同样也是由服务器获取到ua,再根据一个浏览器特性支持列表来决定加载哪些polyfill。如果需要加载,那么往页面中输入script
polyfill打包到bundle里面
在各类构建工具配置中,可以把需要的polyfill打包到bundle中,不建议在开发js中import全部polyfill,这会导致最终文件体积过大。通常会把需要用到的polyfill按需写到配置里的plugins中
打包多个页面
针对需要在多个环境下存在的页面可以构建出多个,可根据不同环境给到页面。也可打包出一个通用的页面,根据当前的环境跳到具体的页面
document.write的退出是个趋势,我们应该积极使用其他的方案解决,减少使用document.write