Webpack是JavaScript的模組建置工具,運行在Node.js上,它可以將零散的JavaScript檔案用各式工具優化並打包起來,加快網頁的載入時間。Webpack也並不限於用在JavaScript上,它除了還能打包網頁有用到的靜態資源(如JS、CSS、圖片檔等)外,也還能透過TypeScript的載入器(loader)來編譯並打包TypeScript程式碼。



背景知識

在開始閱讀這篇文章之前,您應該要先對TypeScript程式語言和Webpack打包檔案的方式有所了解。可以先閱讀以下幾篇文章:

TypeScript 學習之路系列文章:

https://magiclen.org/tag/typescript-學習之路/

用Webpack來打包JavaScript、SCSS/CSS、HTML網頁和任意檔案:

https://magiclen.org/webpack/

用了TypeScript還需要用Webpack嗎?

TypeScript是一種能用來編譯出JavaScript程式碼的程式語言,它「本身」並沒有將多個TypeScript模組編譯為一個JavaScript的功能,必須要依靠System(SystemJS)或是AMD(Asynchronous Module Definition)模組系統來整合到同一個JavaScript檔案上。

例如以下的TypeScript程式:

console.log('A');
export default function print() {
    console.log('B');
}
import './a';
import print from './b';

print();

我們可以在tsconfig.json設定檔中的compilerOptions欄位內加上outFile欄位來讓TypeScript在編譯TypeScript時將JavaScript程式碼輸出到同一個JavaScript檔案中。此時compilerOptions欄位內的module欄位要設定為System或是AMD,也就是說,編譯出來的JavaScript檔案必須要運作在System和AMD的Runtime上(如果是要把該JavaScript檔案放在網頁瀏覽器上執行,就需要引用額外的JavaScript套件)。但如果我們是在Webpack下用TypeScript撰寫相同的程式,就不用受到模組系統的限制,自在地使用ES6的模組系統。

另外還有一點就是,TypeScript的編譯器並無法幫我們最小化並混淆編譯出來的JavaScript程式,但Webpack可以做到這件事。

所以,如果您的TypeScript程式所編譯出來的JavaScript程式是要用於網頁瀏覽器上的,最好還是用Webpack來打包。

建立TypeScript的Webpack專案

加入Webpack

Webpack可以被加進現有的TypeScript的Node.js專案中,在終端機執行以下指令來安裝webpackwebpack-cli這兩個套件。

npm i -D webpack webpack-cli

將原本存在於package.json中的build腳本和start-dev腳本刪除,然後加入新的腳本指令:

{
  ...

  "scripts": {
    ...

    "build": "webpack --mode production",
    "dev": "webpack --mode development",
    "start-dev": "webpack --mode development --watch"

    ...
  }
  
  ...
}

執行npm run start-dev可以持續監看打包的檔案有沒有變動,如果有的話就自動重新打包。

再來就是在Node.js專案根目錄中,新增Webpack的設定檔webpack.config.js,內容可以初始如下:

module.exports = {
    plugins: [],
    module: {
        rules: []
    }
};

其它常用外掛,如clean-webpack-pluginterser-webpack-plugin可以參考這篇文章加至目前的Webpack專案中。如果還是有JavaScript的需求,可以再加上Babel。

加入TypeScript載入器

TypeScript的官方載入器為ts-loader,可以在終端機執行以下指令來安裝:

npm i -D ts-loader

webpack.config.js設定檔中,於module欄位的rules陣列內,加入TypeScript的載入器規則,如下:

module.exports = {
    ...

    module: {
        rules: [
            ...

            {
                test: /\.ts$/,
                loader: 'ts-loader'
            }

            ...
        ]
    }

    ...
};

設定Webpack的進入點(Entry Point)

直接使用TypeScript檔案作為Webpack的進入點。例如:

module.exports = {
    ...

    entry: './src/index.ts'

    ...
};

Webpack下的tsconfig.json

tsconfig.json檔案中compilerOptions欄位內的outDirmodule欄位刪除,因為它們的功能已經被Webpack取代了。另外,編譯目標也要根據預計在哪個網頁瀏覽器來運行JavaScript程式來設定。

Webpack下的TypeScript程式

Webpack下的TypeScript程式,或者說被ts-loader載入的TypeScript程式,其import關鍵字原先是用來引用TypeScrict檔案和JavaScript檔案,就會變成能引用任意檔案。且若不撰寫副檔名的話,預設會是.js,而不是.ts。不過,importfrom關鍵字還是只能用來引用TypeScript模組。

例如:

console.log('A');
export default function print() {
    console.log('B');
}
import './a.ts';
import print from './b.ts';

print();

以上的src/index.ts檔案,第一行不能寫成import './a';,因為我們沒有src/a.js檔案。同理,第二行不能寫成import print from './b';,因為我們沒有src/b.js檔案。順帶一提,如果是直接用tsc來編譯TypeScript程式,而沒有用Webpack的話,from關鍵字後的檔案路徑不能有副檔名(連.ts都不能有)。

當然,如果不想要手動撰寫.ts副檔名的話,我們可以在webpack.config.js設定檔中,加上resolve設定項目,其值為一個物件,將該物件加上extensions屬性,屬性值設定為['.ts', '.js']。如下:

module.exports = {
    ...

    resolve: {
        extensions: ['.ts', '.js']
    }

    ...
};

如此一來,import的路徑就算沒有寫副檔名,也會先以.ts副檔名來嘗試,如果沒有這個檔案,就會以.js副檔名來嘗試。