webpack異步加載的原理
webpack ensure相信大家都聽(tīng)過(guò)。有人稱它為異步加載,也有人說(shuō)做代碼切割,那這
個(gè)家伙到底是用來(lái)干嘛的?其實(shí)說(shuō)白了,它就是把js模塊給獨(dú)立導(dǎo)出一個(gè).js文件的,然后使用這個(gè)
模塊的時(shí)候,webpack會(huì)構(gòu)造script dom元素,由瀏覽器發(fā)起異步請(qǐng)求這個(gè)js文件。
場(chǎng)景分析:
比如應(yīng)用的首頁(yè)里面有個(gè)按鈕,點(diǎn)擊后可以打開(kāi)某個(gè)地圖。打開(kāi)地圖的話就要利用百度地圖的js,于是
我們不得不在首頁(yè)中把百度地圖的js一起打包進(jìn)去首頁(yè),一個(gè)百度地圖的js文件是非常大的,假設(shè)為
1m,于是就造成了我們首頁(yè)打包的js非常大,用戶打開(kāi)首頁(yè)的時(shí)間就比較長(zhǎng)了。
有沒(méi)有什么好的解決方法呢?
解決1
既然打包成同一個(gè)js非常大的話,那么我們完全可以把百度地圖js分類出去,利用瀏覽器的并發(fā)請(qǐng)求
js文件處理,這樣的話,會(huì)比加載一個(gè)js文件時(shí)間小得多。嗯,這也是個(gè)不錯(cuò)的方案。為baidumap.js
配置一個(gè)新的入口就行了,這樣就能打包成兩個(gè)js文件,都插入html即可(如果baidumap.js被多個(gè)
入口文件引用的話,也可以不用將其設(shè)置為入口文件,而且直接利用CommonsChunkPlugin,導(dǎo)出到一個(gè)
公共模塊即可)可以參考我之前文章
webpack模塊打包
那還有沒(méi)有更好的解決方案呢?
解決2
當(dāng)然還是有的!我們細(xì)想,百度地圖是用戶點(diǎn)擊了才彈出來(lái)的,也就是說(shuō),這個(gè)功能是可選的。那么解決
方案就來(lái)了,能不能在用戶點(diǎn)擊的時(shí)候,我在去下載百度地圖的js.當(dāng)然可以。那如何實(shí)現(xiàn)用戶點(diǎn)擊的時(shí)候
再去下載百度地圖的js呢?于是,我們可以寫一個(gè)按鈕的監(jiān)聽(tīng)器
mapBtn.click(function() {
//獲取 文檔head對(duì)象
var head = document.getElementsByTagName('head')[0];
//構(gòu)建 <script>
var script = document.createElement('script');
//設(shè)置src屬性
script.async = true;
script.src = "http://map.baidu.com/.js"
//加入到head對(duì)象中
head.appendChild(script);
})
上面的幾行代碼對(duì)大家來(lái)說(shuō)都不難。可以在點(diǎn)擊的時(shí)候,才加載百度地圖,等百度地圖加載完成后,在
利用百度地圖的對(duì)象去執(zhí)行我們的操作。ok,講到這里webpack.ensure的原理也就講了一大半了。
它就是 把一些js模塊給獨(dú)立出一個(gè)個(gè)js文件,然后需要用到的時(shí)候,在創(chuàng)建一個(gè)script對(duì)象,加入
到document.head對(duì)象中即可,瀏覽器會(huì)自動(dòng)幫我們發(fā)起請(qǐng)求,去請(qǐng)求這個(gè)js文件,在寫個(gè)回調(diào),去
定義得到這個(gè)js文件后,需要做什么業(yè)務(wù)邏輯操作。
ok,那么我們就利用webpack的api去幫我們完成這樣一件事情。點(diǎn)擊后才進(jìn)行異步加載百度地圖js,上面
的click加載js時(shí)我們自己寫的,webpack可以輕松幫我們搞定這樣的事情,而不用我們手寫
mapBtn.click(function() {
require.ensure([], function() {
var baidumap = require('./baidumap.js') //baidumap.js放在我們當(dāng)前目錄下
})
})
搞定!當(dāng)然還是分析一下。require.ensure這個(gè)函數(shù)是一個(gè)代碼分離的分割線,表示 回調(diào)里面的require
是我們想要進(jìn)行分割出去的,即require('./baidumap.js'),把baidumap.js分割出去,形成一個(gè)
webpack打包的單獨(dú)js文件。當(dāng)然ensure里面也是可以寫一些同步的require的,比如
var sync = require('syncdemo.js') //下面ensure里面也用到
mapBtn.click(function() {
require.ensure([], function() {
var baidumap = require('./baidumap.js') //baidumap.js放在我們當(dāng)前目錄下
var sync = require('syncdemo.js') //這個(gè)不會(huì)獨(dú)立出去,因?yàn)樗呀?jīng)加載到模塊緩存中了
})
})
也就是說(shuō),ensure會(huì)把沒(méi)有使用過(guò)的require資源進(jìn)行獨(dú)立分成成一個(gè)js文件. require.ensure的
第一個(gè)參數(shù)是什么意思呢?[], 其實(shí)就是 當(dāng)前這個(gè) require.ensure所依賴的其他 異步加載的模塊。你想???如果A 和 B都是異步加載的,B中需要A,那么B下載之前,是不是先要下載A???,所以ensure的第一個(gè)參數(shù)[]
也是請(qǐng)求下載的模塊,如果想加載A require.ensure(['A.js'],function) 即可
說(shuō)完了上面的原理。下面就實(shí)踐一下
entry.js 依賴三個(gè) js。
- Abtn-work.js 是封裝了 abtn按鈕點(diǎn)擊后,才執(zhí)行的業(yè)務(wù)邏輯
- Bbtn-work.js 是封裝了 bbtn按鈕點(diǎn)擊后,才執(zhí)行的業(yè)務(wù)邏輯
- util.js 是封裝了 entry.js需要利用的工具箱
針對(duì)上面的需求,優(yōu)化方案
假設(shè) Abtn-work.js Bbtn-work.js util.js都是非常大的文件
因?yàn)?Abtn-work.js Bbtn-work.js 都不是entry.js必須有的,即可能發(fā)生的操作,那么我們把
他們利用異步加載,當(dāng)發(fā)生的時(shí)候再去加載就行了
util.js是entry.js立即馬上依賴的工具箱。但是它又非常的大,所以將其配置打包成一個(gè)公共模塊,
利用瀏覽器的并發(fā)加載,加快下載速度。ok,構(gòu)思完成,開(kāi)始實(shí)現(xiàn)
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>index</title>
</head>
<body>
<div id="aBtn">Abtn</div>
<div id="bBtn">Bbtn</div>
</body>
</html>
定義了兩個(gè)buttom
然后看看 entry.js
var util_sync = require('./util-sync.js')
alert(util_sync.data)
document.getElementById("aBtn").onclick = function() {
require.ensure([], function() {
var awork = require('./workA-async.js')
alert(awork.data)
//異步里面再導(dǎo)入同步模塊--實(shí)際是使用同步中的模塊
var util1 = require('./util-sync.js')
})
}
document.getElementById("bBtn").onclick = function() {
require.ensure([], function() {
var bwork = require('./workB-async.js')
alert(bwork.data)
})
}
可以看到,workA-async.js, workB-async.js 都是點(diǎn)擊后才ensure進(jìn)來(lái)的。什么時(shí)候加載完成呢?
就是 require.ensure() 第二個(gè)函數(shù)參數(shù),即回調(diào)函數(shù),它表示當(dāng)下載js完成后,發(fā)生的因?yàn)檫壿?/p>
webpack打包后,形成

其實(shí), 1.1.... 2.2...就是我們ensure導(dǎo)出來(lái)的js文件
我們看看代碼是如何加載的執(zhí)行的,點(diǎn)擊打包插入js后的html

可以看到,并沒(méi)有加載 ensure導(dǎo)出來(lái)的 1.1...js 2.2....js
點(diǎn)擊 abtn,

發(fā)現(xiàn)瀏覽器下載并加載了 1.1....js
點(diǎn)擊 bbtn

發(fā)現(xiàn)瀏覽器下載并加載了 2.2....js
ok 全部完成