webpack,異步加載,代碼分割,require.ensure

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打包后,形成

image

其實(shí), 1.1.... 2.2...就是我們ensure導(dǎo)出來(lái)的js文件

我們看看代碼是如何加載的執(zhí)行的,點(diǎn)擊打包插入js后的html

image

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

點(diǎn)擊 abtn,

image

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

點(diǎn)擊 bbtn

image

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

ok 全部完成

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容