webpack和原生框架动态加载js的方法小结

前言

动态加载是网页加速的常用手段之一,你肯定不想把jquery,swiper这类库直接的引入单页面框架或者html,而是用到他们的时候再异步加载。这里总结下webpack和非webpack动态加载的方法。

webpack

webpack支持js的动态加载,但是要符合ES6或者CommonJS规范,文档见
代码分割
模块方法

ES6规范

符合es6规范的包用import()方法引入,返回一个Promise对象,打包时会添加到一个单独的chunk,名字是webpackChunkName定义的模块名+'bundle.js'。

import(/* webpackChunkName: "lodash" */ './lodash').then(_ => {
  _.defaults({ 'a': 1 }, { 'a': 3, 'b': 2 });
  // → { 'a': 1, 'b': 2 }
});

因为是Promise对象,故可以使用await语法糖

const _ = await import('./lodash')

在webpack5中import()返回的Promise不会再解析module.exports对象,而是返回一个带default的对象,上面的例子用webpack5的写法是这样

import(/* webpackChunkName: "lodash" */ './lodash').then({ default: _ } => {
  //...
});

CommonJS规范

符合CommonJS规范的包用require.ensure方法引入,语法是

require.ensure(
  dependencies: String[],
  callback: function(require),
  errorCallback: function(error),
  chunkName: String
)

这里有3个参数,第一个是数组,声明依赖的模块。第二第三是回调函数,参数require可以进一步引入其他依赖和模块。最后一个声明这个引入的chunkname。

require.ensure([], function(require){
  let $ = require('./jquery.js')
  //do something
}

这里有个坑

require.ensure(['./a.js'], function(require) {
    require('./b.js');
});

上面代码, a.jsb.js 都被打包到一起,而且从主文件束中拆分出来。但只有 b.js 的内容被执行。a.js 的内容仅仅是可被使用,但并没有被输出。

想去执行 a.js,我们需要异步地引用它,如 require('./a.js'),让它的 JavaScritp 被执行。

原生

webpack动态引入固然非常方便,但也有瑕疵

  • 引入的包不符合es6或CommonJS规范,需要改写
  • webpack会扫描引入的包,引入大量js库时浪费时间

某些场景下我们只是想引用一个成熟的库,不想关心它用的什么规范,也不介意全局对象被污染,有没有简单的办法呢
下面的方法可以在任何框架下动态加载js,返回Promise对象,且加载符合单例模式,同一个url不会加载2次。
缺点是没有了webpack的静态分析,引用的内容一般要求绑定在全局对象上。

const loadScript = (function() {
  let caches = [] //缓存列表

  return url => { //js静态地址
    if (caches.includes(url)) return Promise.resolve()

    return new Promise((resolve, reject) => {
      let script = document.createElement('script')
      script.type = 'text/javascript'

      if (script.readyState) {
        script.onreadystatechange = function() {
          if (script.readyState == 'loaded' || script.readyState == 'complete') {
            script.onreadystatechange = null
            caches.push(url)
            resolve()
          }
        }
      } else {
        script.onload = function() {
          caches.push(url)
          resolve()
        }
        script.onerror = function() {
          console.log('load js fail:' + url)
          reject()
        }
      }

      script.src = url
      document.body.appendChild(script)
    })
  }
})()

示例

loadScript('***/jquery.js').then(() => {
  $('.input').value(100)
})

总结

推荐import()方法,不建议用require.ensure,对于非webpack或体积大的常用js库可以用loadScript

你可能感兴趣的:(javascript,前端,webpack)