美文网首页
异步第三库加载

异步第三库加载

作者: 小俊的世界 | 来源:发表于2021-06-23 13:54 被阅读0次

起因

由于项目中直接通过script引入了,大量资源导致了首次渲染特别慢,在进行发布没有办法利用本地缓存的情况下,超过半分钟。


1.png

异步资源加载

其实涉及到的第三方库,是没有必要一开始就加载的,至少是不能影响到dom渲染的。
直接能想到的script defer 但是因为script是在index.html文件下,因此采用的脚本化的方式处理更为合适。

关于异步加载资源直接想到的是AMD,CMD模块化加载的方式,其最为底层的基理还是动态添加script。

动态添加script

    const script = document.createElement('script')
    script.type = 'text/javascript'
    script.src = url
    script.async = 'async'
    const head = document.getElementsByTagName('head')[0];
    (head || document.body).appendChild(script)

利用装饰器保证脚本确实被加载完成

利用装饰器将会使代码的侵入变得更少,在webpack中需要添加上babel的transform-decorators-legacy来保证对装饰器语法的支持

// .babelrc
{
  "presets": [
    [
      "env",
      {
        "modules": false,
        "targets": {
          "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
        }
      }
    ],
    "stage-0"
  ],
  "plugins": [
    "transform-vue-jsx",
    "transform-runtime",
    "transform-decorators-legacy"
  ],
  "env": {
    "development": {
      "plugins": ["dynamic-import-node"]
    }
  }
}

代码

class LoadScripts {
/** 创建脚本元素
 * @function createScript
 * @param {*} id
 * @param {*} url
 */
createScript({ id, url }) {
  if (LoadScripts.loadInfo && LoadScripts.loadInfo[id] && LoadScripts.loadInfo[id]['created']) {
    return LoadScripts.loadInfo[id]['script']
  }
  if (!url) {
    throw new Error('必须要有url参数')
  }
  const script = document.createElement('script')
  script.type = 'text/javascript'
  script.src = url
  script.async = 'async'
  const head = document.getElementsByTagName('head')[0];
  (head || document.body).appendChild(script)
  LoadScripts.loadInfo = LoadScripts.loadInfo ? LoadScripts.loadInfo : {}
  LoadScripts.loadInfo[id] = {
    created: true,
    script
  } // 标识script已经创建
  return script
}
/** 加载静态资源
 * @function load
 * @param {*} id
 * @param {*} url
 * @param {*} callBack
 */
load({ id, url, callBack }) {
  if (!id) {
    throw new Error('必须要有id参数')
  }
  // 当标识已经加载完成  直接执行回调
  if (LoadScripts.loadInfo && LoadScripts.loadInfo[id] && LoadScripts.loadInfo[id]['loaded']) {
    callBack && callBack({
      loaded: true,
      success: true
    })
    return
  }
  const baseUrl = process.env.NODE_ENV === 'development' ? '../static' : './static'
  if (!url) {
    url = `${baseUrl}/${id}`
  }
  const script = this.createScript({ id, url, callBack })
  script.onload = () => {
    LoadScripts.loadInfo[id] && (LoadScripts.loadInfo[id]['loaded'] = true)
    callBack && callBack({
      loaded: true,
      success: true
    })
  }
  script.onerror = () => {
    LoadScripts.loadInfo[id] && (LoadScripts.loadInfo[id]['loaded'] = false)
    callBack && callBack({
      loaded: true,
      success: false
    })
  }
}
/** 多静态资源加载
 * @function loads
 * @param {*} arr
 */
loads(arr) {
  if (!Array.isArray(arr)) {
    throw new Error('need a array!')
  }
  arr.forEach(item => this.load(item))
}
/** 加载有相互依赖的资源 比如 bimrun.config.js bimrun.js
 * @function loadsAsync
 * @param {*} id
 * @param {*} ids
 */
async loadsAsync({ fullPath, id, ids, callBack }) {
  if (!id) throw new Error('need a id!')
      // 当标识已经加载完成  直接执行回调

  if (LoadScripts.asyncloadInfo && LoadScripts.asyncloadInfo[id] && LoadScripts.asyncloadInfo[id]['asyncLoaded']) {
     callBack && callBack({
          loaded: true,
          success: true
     })
     return
  }
  // 处理id与ids的映射
  if (!LoadScripts.aysncIdMap) {
    LoadScripts.aysncIdMap = {}
  }
  if (ids) {
    LoadScripts.aysncIdMap[id] = ids
  } else {
    ids = LoadScripts.aysncIdMap[id]
  }

  if (!Array.isArray(ids)) {
    throw new Error('need a array!')
  }
  const promiseRes = []
  for (let index = 0; index < ids.length; index++) {
    const tpId = ids[index]
    const promise = new Promise((resolve, reject) => {
      this.load({ id: tpId, url: (fullPath ? tpId : null), callBack({ loaded, success }) {
        resolve({ loaded, success })
      } })
    })
    promiseRes.push(await promise)
  }
  // Promise.all(promiseAll).then(res => {
  const success = promiseRes.every(item => item.success)
  const loaded = promiseRes.every(item => item.loaded)
  if (!LoadScripts.asyncloadInfo) {
    LoadScripts.asyncloadInfo = {}
  }
  if (!(LoadScripts.asyncloadInfo && LoadScripts.asyncloadInfo[id])) LoadScripts.asyncloadInfo[id] = {}
  LoadScripts.asyncloadInfo[id] && (LoadScripts.asyncloadInfo[id]['asyncloaded'] = true)
  callBack && callBack({
    loaded,
    success
  })
}

/** 资源加载装饰器 如果资源正在加载 等待资源加载完成 如果加载完成 直接执行
 * @function loadDecorator
 * @param {*} id
 * @param {*} isAsync
 */
loadDecorator(id, isAsync) {
  return (target, name, descriptor) => {
    const loadScript = new LoadScripts()
    const oldValue = descriptor.value
    descriptor.value = async function fn(...args) {
    const promise = new Promise((resolve, reject) => {
      if (isAsync) {
        loadScript.loadsAsync({
          id,
          callBack({ load, success }) {
            resolve({ load, success })
          }
        })
      } else {
        loadScript.load({
          id,
          callBack({ load, success }) {
            resolve({ load, success })
          }
        })
      }
    })
    await promise
    return oldValue.call(this, ...args)
    }
    return descriptor
  }
}
}

export default new LoadScripts()

使用姿势

提前加载资源

// loadResource.js
import loadScripts from '@/utils/loadScripts'

// 加载资源
try {
  loadScripts.loads([
    { id: 'Cesium/Cesium.js' },
    { id: 'tinymce4.7.5/tinymce.min.js' },
    { id: 'BIMRUN/bimrun-engine.js' }
  ])
} catch (err) {
console.log(err)
}

// gantt图加载
try {
loadScripts.loadsAsync({
  id: 'gantt',
  ids: [
    'dhtmlx/dhtmlxgantt.js',
    'codebase/dhtmlxscheduler.js',
    'dhtmlx/ext/dhtmlxgantt_keyboard_navigation.js'
  ]
})
} catch (err) {
 console.log(err)
} 

// main.js
import './loadResource'

保证资源加载完成后使用

// cesium.js的加载
 @loadResource('Cesium/Cesium.js')

// gantt的加载 
 @loadResource('gantt', true)

相关文章

  • Swift 常用库

    异步加载库 AsyncTaskQueueAsyncTask

  • 异步第三库加载

    起因 由于项目中直接通过script引入了,大量资源导致了首次渲染特别慢,在进行发布没有办法利用本地缓存的情况下,...

  • 按需加载指定文件名(require.ensure、import(

    上篇帖子说到Vue中的异步路由、异步组件、懒加载第三方类库,但是怎么给异步加载的文件指定文件名呢?接下来将为你解答...

  • 面试题收集(1)

    一、 SDWebImage的实现原理 SDWebImage是用来加载网络图片的第三方库,实现了异步请求数据,并且实...

  • iOS面试题

    1. SDWebImage的实现原理: SDWebImage是用来加载网络图片的第三方库,实现了异步请求数据,并且...

  • SDWebImage源码(一)——SDWebImage概览

    SDWebImage是我们经常使用的一个异步图片加载库,使用时只需一行代码就能实现网络图片的异步加载、缓存(内存+...

  • iOS开发常用库

    框架类 网络AFNetworking轻量级网络库。 网络-图像SDWebImage支持缓存的异步图片加载库。 图像...

  • React组建实现新闻下拉刷新加载

    整体布局: 首先需要引入React基础库,dom库,jsx解析库和移动端Jquery库(用于动态请求异步加载数据)...

  • iOS面试(五)-网络及第三方框架

    目录: 网络 AFNetworking SDWebImage:异步图片加载库+缓存 1. 网络 使用TCP/IP四...

  • iOS 功能性

    EGOImageLoading 下载使用网络图片的库 SDWebImage 异步加载网络图片 (UIImage...

网友评论

      本文标题:异步第三库加载

      本文链接:https://www.haomeiwen.com/subject/nxrbyltx.html