前端 实战项目·动态加载 JS 文件

作者 : jamin 本文共2135个字,预计阅读时间需要6分钟 发布时间: 2020-10-18 共1087人阅读

动态加载 JS 文件

对于 Vue、React 等框架开发的单页面应用,在某些页面开发特殊功能时经常需要依赖第三方 JS 文件,如果在全局引入 CDN 资源可能会加载冗余文件,此时最好使用动态加载方式。

动态加载 JS 脚本指仅在某些特殊页面引入依赖文件,而非全局引入,这样可以避免在这些页面并未打开时造成加载无用的资源,提高页面加载速度的同时,也让整个项目更加模块化。

文档对象模型(DOM)允许使用 JavaScript 动态创建 HTML。<script> 元素也是如此,它与页面其他元素没有什么不同,所以可以手动创建 <script> 来加载 JS 文件。

defer 与 async

<script> 元素有两个属性 deferasync 分别代表两种 JS 脚本的加载执行模式。

defer:此布尔属性被设置为向浏览器指示脚本在文档被解析后执行。
async:设置此布尔属性,以指示浏览器如果可能的话,应异步执行脚本。

对于 defer,可以认为是将外链的 js 放在了页面底部。js 的加载不会阻塞页面的渲染和资源的加载。defer 会按照原本的 js 的顺序执行。

对于 async,它的作用是能够异步的加载和执行脚本,同样不会阻塞页面的渲染和资源的加载,一旦加载到就会立刻执行。在有 async 的情况下,js 一旦下载好了就会执行,所以很有可能不是按照原本的顺序来执行的。如果多个脚本文件前后具有相互依赖性,用 async 就很有可能出错。

所以通俗来讲,浏览器首先会请求 HTML 文档,然后对其中的各种资源调用相应的资源加载器进行异步网络请求,同时进行 DOM 渲染,直到遇 <script> 到标签的时候,主进程才会停止渲染等待此资源加载完毕然后执行,继而继续进行 DOM 解析。如果加了 async 属性就相当于单独开了一个进程去独立加载和执行,而defer是和将 <script> 放到 <body> 底部一样的效果。

defer 与 async

上图蓝色线代表网络读取,红色线代表执行时间,绿色线代表 HTML 解析。

const loadJS = (url, defer) => {
  const recaptchaScript = document.createElement('script')
  recaptchaScript.setAttribute('src', url)
  if (defer) {
    recaptchaScript.defer = true
  } else {
    recaptchaScript.async = true
  }
  recaptchaScript.onload = () => {
    console.log('加载完成', url)
  }
  document.head.appendChild(recaptchaScript)
}

下面举个栗子,这里加载五个 JS 脚本,其中 jquery-uifullcalendar 都依赖 jquery,而 locale 依赖 fullcalendar,这种情况需要让 JS 文件按照一定的依赖关系按次序加载资源。

const assets = [
  'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.0/jquery.min.js',
  'https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js',
  'https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.9.0/moment.min.js',
  'https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.10.0/fullcalendar.min.js',
  'https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.10.0/locale/zh-cn.js'
]

assets.forEach(url => loadJS(url, true))

现实很骨感

然而在现实环境当中,浏览器对于延迟脚本并不一定会按照顺序执行,也不一定会在 DOMContentLoaded 事件触发前执行,因此仅使用 defer 来控制脚本文件的执行顺序有很大的风险,但可以通过监听 onload 事件来判断文件是否加载完成,配合 Promise 等待上一个脚本文件加载完成后再加载下一个文件,从而实现按次序加载执行脚本。

const loadJS = url => {
  return new Promise(resolve => {
    const recaptchaScript = document.createElement('script')
    recaptchaScript.setAttribute('src', url)
    recaptchaScript.defer = true
    recaptchaScript.onload = () => {
      resolve()
    }
    document.head.appendChild(recaptchaScript)
  }).catch(console.error)
}

// 按次序加载 JS 文件
const loadAssets = async () => {
  for (const url of assets) {
    await loadJS(url, true)
  }
}

Just enjoy it ฅ●ω●ฅ

本站所提供的部分资源来自于网络,版权争议与本站无关,版权归原创者所有!仅限用于学习和研究目的,不得将上述内容资源用于商业或者非法用途,否则,一切后果请用户自负。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容资源。如果上述内容资对您的版权或者利益造成损害,请提供相应的资质证明,我们将于3个工作日内予以删除。本站不保证所提供下载的资源的准确性、安全性和完整性,源码仅供下载学习之用!如用于商业或者非法用途,与本站无关,一切后果请用户自负!本站也不承担用户因使用这些下载资源对自己和他人造成任何形式的损失或伤害。如有侵权、不妥之处,请联系站长以便删除!
金点网络 » 前端 实战项目·动态加载 JS 文件

常见问题FAQ

免费下载或者VIP会员专享资源能否直接商用?
本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。
是否提供免费更新服务?
持续更新,永久免费
是否经过安全检测?
安全无毒,放心食用

提供最优质的资源集合

立即加入 友好社区
×