banner
 Sayyiku

Sayyiku

Chaos is a ladder
telegram
twitter

Dynamic loading of JS files

For single-page applications developed using frameworks like Vue and React, it is often necessary to rely on third-party JS files for special functionality on certain pages. If CDN resources are globally imported, redundant files may be loaded. In such cases, it is best to use dynamic loading.

Dynamic loading of JS scripts refers to the practice of only importing dependency files on specific pages, rather than globally. This avoids loading unnecessary resources on these pages, improves page loading speed, and makes the entire project more modular.

The Document Object Model (DOM) allows for the dynamic creation of HTML using JavaScript. The <script> element is no different from other elements on the page, so it can be manually created to load JS files.

Defer and Async#

The <script> element has two attributes, defer and async, which represent two different loading and execution modes for JS scripts.

Defer: This boolean attribute is set to indicate to the browser that the script should be executed after the document has been parsed.
Async: This boolean attribute is set to indicate to the browser that the script should be executed asynchronously if possible.

For defer, it can be thought of as placing the external JS file at the bottom of the page. The loading of the JS file does not block the rendering and loading of resources on the page. The defer attribute executes the scripts in their original order.

For async, its purpose is to load and execute scripts asynchronously, also without blocking the rendering and loading of resources on the page. Once the script is loaded, it will be executed immediately. In the presence of async, the scripts may not be executed in their original order. If multiple script files have interdependencies, using async may lead to errors.

In simple terms, the browser first requests the HTML document, then asynchronously requests various resources using the corresponding resource loaders, while performing DOM rendering. It continues this process until it encounters a <script> tag. At this point, the main process stops rendering and waits for the resource to finish loading before executing it, and then continues with DOM parsing. If the async attribute is added, it is equivalent to loading and executing the script in a separate process, while defer has the same effect as placing the <script> tag at the bottom of the <body>.

Defer and Async

In the above diagram, the blue line represents network requests, the red line represents execution time, and the green line represents HTML parsing.

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('Loading complete', url)
  }
  document.head.appendChild(recaptchaScript)
}

Here's an example. We are loading five JS scripts, where jquery-ui and fullcalendar depend on jquery, and locale depends on fullcalendar. In this case, we need to load the JS files in a specific order based on their dependencies.

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))

The Reality is Harsh#

However, in real-world scenarios, browsers do not necessarily execute deferred scripts in order, nor do they necessarily execute them before the DOMContentLoaded event is triggered. Therefore, relying solely on defer to control the execution order of script files carries significant risks. However, by listening for the onload event and using Promise to wait for the previous script file to finish loading before loading the next one, we can achieve sequential loading and execution of scripts.

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)
}

// Load JS files sequentially
const loadAssets = async () => {
  for (const url of assets) {
    await loadJS(url, true)
  }
}

Just enjoy it ฅ●ω●ฅ

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.