banner
 Sayyiku

Sayyiku

Chaos is a ladder
telegram
twitter

Dynamic Loading of JS Files

image

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 this case, it is best to use dynamic loading.

Dynamic loading of JS scripts refers to the practice of only importing dependency files on certain special pages, rather than globally importing them. 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 you can manually create <script> tags 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 indicates to the browser that the script should be executed after the document has been parsed.
async: This Boolean attribute indicates to the browser that the script should be executed asynchronously if possible.

For defer, you can think of it 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. defer executes the JS file in the 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 case of async, the JS file will be executed as soon as it is downloaded, so it is very likely that it will not be executed in the original order. If multiple script files have mutual dependencies, using async may lead to errors.

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

defer and async

The blue line in the above diagram represents network loading, 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 and execute the JS files in a certain 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, there is a significant risk in using only defer to control the execution order of script files. But you can use the onload event to determine if the file has finished loading, and use Promise to wait for the previous script file to finish loading before loading the next file, thus achieving 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 in sequence
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.