教你用正確的方式在webpack裡引入jQuery、eCharts等lib

qq20004604發表於2018-02-07

前注:

關於webpack的文件全文(我自己寫的教程)請檢視 根目錄的文件說明

如果可以,請給本專案加【Star】和【Fork】持續關注。

有疑義請點選這裡,發【Issues】。

點選這裡檢視DEMO

9、外部擴充套件(Externals)

9.0、DEMO使用說明

安裝:

npm i
複製程式碼

執行:

npm run dev
複製程式碼

打包(生產模式):

npm run build
複製程式碼

9.1、應用場景

當我們載入一個外部庫,比如 jQuery,比如 echarts 等,為了節約流量,我們往往會使用 CDN 來載入這些東西。

這和我們平常進行模組化開發就有了一定的矛盾:

  1. 需求:我們需要引入一個全域性變數(實質是引入一個模組);
  2. 常規做法:所以正常來說,我們應該通過 import 來引入這個模組;
  3. 問題:但由於通過CDN來載入,顯然我們不能這麼做;

解決方案:

1、一般解決方案:

我們可以直接通過使用這個全域性變數來使用這個變數,例如:

// globalVariableName 通過CDN引入js檔案,這個變數名其該js檔案暴露出來的全域性變數名(型別是一個函式)
document.getElementById("root").innerText = globalVariableName(1, 2, 3)
複製程式碼

這個不是不行,但容易造成一個問題就是,容易在一不小心的情況下,篡改了原有的變數。

並且,這種方式是高耦合度的,不推薦使用。

2、模組化解決方案:

按照正常的開發方式,我們通常是使用 外部擴充套件 來實現。

他具有以下特點:

示例程式碼:

// ``webpack.config.js`` 檔案裡的配置程式碼

// 指定別名
externals: {
    // 後面是原本使用的全域性變數名,前面的是引入的包名(就是import xx from 'echart'),然後我們實際寫程式碼時候,用的是xx這個變數名。
    "moduleName": 'globalVariableName'
}

// app.js 裡的業務程式碼
import add from 'moduleName'
document.getElementById("root").innerText = add(1, 2, 3)
複製程式碼
  1. webpack.config.js 裡進行配置;
  2. 通常使用 kv 模式,並且 v 一般是字串(就像 "moduleName": 'globalVariableName' 這樣);
  3. 效果是當引入某個模組時(k 決定),將不會像常規處理那樣去載入他,而是排除掉(就像 import add from 'moduleName' 這段程式碼,不會去找 moduleName 這個模組);
  4. 排除掉後怎麼處理呢,執行時從外部獲取這個擴充套件;
  5. 具體做法是(需要一定程度上知曉 webpack 打包後如何載入模組,才能理解以下內容):獲取時,像正常匯入一個模組一樣,載入一個模組;
  6. 但這個模組做的事情,是將一個外部變數,通過 AMD 規範賦值給 module.exports(我們使用 require 載入模組時,獲取的值,就是 AMD 規範的模組,通過這個屬性匯出的值);
  7. 從而讓載入 moduleName 模組時,實際載入到的是這個全域性變數(5-7這個過程,實際體現的就是以下這段程式碼)。
// 其他程式碼略
"moduleName": (function (module, exports) {
    module.exports = globalVariableName;
})
複製程式碼

9.2、簡單來說(使用說明)

【需求】

假如我需要通過 CDN 載入一個我自定義的庫,示例我用的是:<script src="http://www.jianwangsan.cn/looksLikeCDN.js"></script>

這個庫就做了一件事情,暴露了一個全域性變數 window.globalVariableName,這個變數是一個函式,他會將所有引數的和相加,並返回。(當然實際應用中,這個可能是一個物件,有 N 個屬性,非常複雜,但原理是一樣的)

【第一步,修改html原始檔】

我現在通過 CDN 來載入這個庫,因此我的 html 原始檔是這樣的(當然實際上我不是CDN,因此用的是我個人伺服器上的一個js檔案,但道理也是一樣的):

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <script src="http://www.jianwangsan.cn/looksLikeCDN.js"></script>
</head>
<body>
<div id="root"></div>
</body>
</html>
複製程式碼

【第二步,配置webpack】

我現在需要使用這個方法,又不想通過模組化引入(因為這個是固定不變的,沒有必要每次重複打包,這樣不利於快取,就像 jQuery 庫一樣)。

於是我首先編輯 webpack.config.js 檔案,給打包配置物件新增一個屬性,具體配置如下:

// 指定別名
externals: {
    // 後面是原本使用的全域性變數名,前面的是引入的包名(就是import xx from 'echart'),然後我們實際寫程式碼時候,用的是xx這個變數名。
    "moduleName": 'globalVariableName'
}
複製程式碼

將你想使用的全域性變數,作為 kv 鍵值對的 v,而 k 作為在 js 檔案中引入的模組名使用(參考第三步)。

不想看解釋的跳過下面這段話,直接看第三步

以上配置意味著:

  1. moduleName 不會被正常載入:當我需要載入 import add from 'moduleName' 這個模組時,webpack 不會像之前去找對應模組那樣而處理(找到對應模組,載入對應模組,將模組的返回值賦值給 add),而是採用一種新的機制來處理;
  2. 創造出的模組:新的機制意味著,將創造一個 'moduleName' 模組(注意,實際上這個模組在工程裡並不存在);
  3. globalVariableName 全域性變數:創造出來的模組做了一件事情,他返回了一個值,這個值是 globalVariableName 這個全域性變數的值。(想想 AMD 規範中,module.exports = globalVariableName; 這段程式碼表示什麼?);

於是體現的效果,就相當於以下程式碼:

// app.js
import add from './moduleName'

// moduleName.js
export default window.globalVariableName
複製程式碼

【第三步,在工程中引入】

上面我們獲得一個 k(moduleName),作為模組名,因此在 app.js 這個工程中檢視:

// app.js
import add from 'moduleName'

// 這裡的 moduleName 模組,返回 window.globalVariableName 這個值
// 所以就意味著 add 的值等同於 globalVariableName 的值
document.getElementById("root").innerText = add(1, 2, 3)
複製程式碼

【結束】

這樣就可以了,現在可以嘗試進入命令列,輸入 npm run build 打包,並檢視一下效果啦。

相關文章