前言
动态加载是网页加速的常用手段之一,你肯定不想把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.js
和 b.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
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。