问题背景
最近做的一个项目需要引入一个外部的第三方 JS 脚本. 由于这是一个关于渲染 3D 建筑的脚本, 所以体积比较大, 大概有 2M, 加载完成也得要个好几秒, 网速慢的时候十几秒都有可能. 之前也遇到脚本加载慢的问题, 但是没这么慢, 所以这次就特别写个文章记录一下我的解决过程.
首先上两张项目已完成的截图.
下图是通过第三方脚本渲染出来的 3D 建筑页面
下图是首页, 不需要用到第三方脚本
遇到的问题和需求
引入外部脚本太大, 加载时间太长
首页用不到外部脚本, 需要先渲染出来
用到外部脚本的页面, 要是脚本还没加载好就点进去会报错
解决问题的过程
我一开始通过 ` 这样页面是可以正常加载的, 但是页面出来的很慢, 一开始会白屏一段时间等待这个 JS 脚本加载完成. 虽然脚本体积大是事实, 但这用户体验肯定是可以优化的. 后来我又把这个脚本放到了页面底部, 也就是标签下面. 这样可以先让页面渲染出来, 再慢慢加载这个庞大的脚本, 于是首页是出来的很快, 但是从首页跳转到需要用到这个脚本的页面就会报错, 如下
这个错误原因是这个页面需要用到 Windows.DDEarth 这个对象, 但是由于此时这个脚本还没有加载完成, 所以 Windows 下并没有这个对象, 所以就报错了.
于是我又想到等脚本加载完成再执行相关方法, 这时就需要用到 onload 这个方法了, onload 这个方法在脚本加载完成的时候会执行. 我引入脚本的时候给它加了个 id, 方便以后通过 dom 找到, 代码如下:
- // 入口文件
- </body>
- <script id="ddEarthScript" src="./DDEarth.js"></script>
- // PageTwo.JS
- componentDidMount() {
- const scriptEle = document.getElementById('ddEarthScript'); // 找到脚本节点
- if (scriptEle) {
- scriptEle.onload = () => {
- // 脚本加载完成执行加载地图的操作
- this.loadEarthMap();
- };
- }
- }
有了以上代码我跳转到 PageTwo 这个页面的时候, 会等到 DDEarth.JS 这个脚本加载完成, 再执行加载建筑地图的操作, 这样就不会报错了. 但是这又有一个问题, 就是如果我跳转到 PageTwo 的之前, DDEarth.JS 已经加载完成了, onload 这个事件在 PageTwo 这个页面中就不生效了, loadEarthMap 这个方法自然也就不会执行了. 这个时候需要加一个判断, 完整代码如下:
- // PageTwo.JS
- componentDidMount() {
- if (Windows.DDEarth) { // 如果跳转到此页面之前, 脚本已加载完成
- this.loadEarthMap();
- } else {
- const scriptEle = document.getElementById('ddEarthScript');
- if (scriptEle) {
- scriptEle.onload = () => {
- this.loadEarthMap();
- };
- }
- }
- }
总结一下我以上解决问题的步骤
在入口文件的底部引入第三方脚本, 并给它加个 id. 当然也可以放在里, 但是需要额外加上 html5 新增的 async 这个属性, 这样脚本才能异步加载.
在需要用到这个脚本的页面, 先判断脚本有没有加载完成 (我这里是直接判断 Windows.DDEarth 对象是否为空). 如果已经加载完成, 就直接执行相关操作; 如果没有, 先通过 document.getElementById 找到那个脚本, 然后监听脚本的 onload 事件, 再做相关操作.
什么情况可以用我以上思路?
引入的第三方脚本较大, 加载所需时间较长
页面按需加载, 整个项目只有其中某几个页面需要用到引入的第三方脚本
第三方脚本没加载完就渲染页面导致的页面报错
来源: https://juejin.im/post/5c0d08fae51d451d97174003