# Typescript ES6 + webpack 設定 ## 必要 package | 名稱 | 連結 | 用途 | | -- | -- | -- | | typescript | [link](https://www.npmjs.com/package/typescript) | | webpack | [link](https://webpack.js.org) | | webpack-cli | [link](https://www.npmjs.com/package/webpack-cli) | | @webpack-cli/generators | [link](https://www.npmjs.com/package/@webpack-cli/generators) | webpack 環境設定範本 | ts-loader | [link](https://www.npmjs.com/package/ts-loader) | ```npm npm install -D webpack webpack-cli @webpack-cli/generators typescript ts-loader ``` :::warning webpack v5 已經不需要額外安裝和設定 terser-webpack-plugin (用來壓縮發佈後的 js 內容) ::: ## 設定 * 建立 webpack 相關設定檔 ```npm npx webpack-cli init ``` 問答簡易選項 :::info ? Which of the following JS solutions do you want to use? **<font color="red">Typescript</font>** ? Do you want to use webpack-dev-server? **<font color="red">No</font>** ? Do you want to simplify the creation of HTML files for your bundle? **<font color="red">No</font>** ? Which of the following CSS solutions do you want to use? **<font color="red">none</font>** ? Do you like to install prettier to format generated configuration? **<font color="red">Yes</font>** ? Overwrite package.json? **<font color="red">overwrite</font>** ? Overwrite tsconfig.json? **<font color="red">檔案已存在才出現</font>** ::: 會產生 tsconfig.json, webpack.config.js, 複寫 package.json * 修改 webpack-cli 產生的 tsconfig.json ```json "target": "es6", "module": "ESNEXT", "moduleResolution": "node", "esModuleInterop": true ``` :::warning 此欄位可移除, 或是修改成入口檔案 ```json "files": ["src/index.ts"] ``` :::spoiler **延伸-測試&nbsp;tsconfig include 欄位是否有作用** 1. root 資料夾新增檔案 abc.ts ```typescript export class ABC {} ``` 2. 在入口檔案 src/index.ts 建立 ABC 實體 ```typescript import { ABC } from "../abc"; export class Index{ abc: ABC; constructor(){ this.abc = new ABC(); } } ``` * 測試1: 設定 **include** 指定資料夾 **src**, 測試放在外層的 abc.ts 能否正常讀取 ```json "include": ["src"] ``` ``` 通過編譯, 不會出現找不到 abc.ts 的錯誤, runtime 也正常運作 ``` <br> * 測試2: 移除 **include** 比對差異 ``` 通過編譯, runtime 正常運作 ``` *官方文件指出在沒設定 files 和 include, 預設值是 **["\*\*/\*"]** 會編譯專案目錄底下所有相關檔案* [link](https://www.typescriptlang.org/tsconfig#include) <br> * 測試3: **include** 指定一個不存在的資料夾 aaa ```json "include": ["aaa"] ``` ``` 無法通過編譯 ERROR TS18003: No inputs were found in config file 'tsconfig.json'. Specified 'include' paths were '["aaa"]' and 'exclude' paths were '[]' ``` <br> * 測試4: 建立 aaa 資料夾, 裡面無任何檔案 ``` 一樣出現 ERROR TS18003 ``` <br> * 測試5: 在 aaa 資料夾內隨意新增一個 ts 檔, 並加上 class ``` 通過編譯 ``` <br> 結論: tsconfig.json 即使不設定 include 資料夾, 也不影響 webpack 發佈 ::: :::spoiler **tsconfig.json&nbsp;範例** ```json { "compilerOptions": { "allowSyntheticDefaultImports": true, "noImplicitAny": true, "module": "ESNEXT", "target": "es6", "allowJs": true } } ``` ::: * 修改 webpack.config.js ```javascript entry: "./src/入口預設檔案.ts", output: { path: path.resolve(__dirname, "輸出資料夾"), filename: "輸出檔名.js" }, ``` :::spoiler **webpack.config.js&nbsp;範例** ```javascript const path = require("path"); const isProduction = process.env.NODE_ENV == "production"; const config = { entry: "./src/index.ts", output: { path: path.resolve(__dirname, "output"), filename: "index.js" }, plugins: [], module: { rules: [ { test: /\.(ts|tsx)$/i, loader: "ts-loader", exclude: ["/node_modules/"], }, { test: /\.(eot|svg|ttf|woff|woff2|png|jpg|gif)$/i, type: "asset", }, ], }, resolve: { extensions: [".tsx", ".ts", ".js"], }, }; module.exports = () => { if (isProduction) { config.mode = "production"; } else { config.mode = "development"; } return config; }; ``` ::: * 修改 package.json ```json "description": "My webpack project", "name": "my-webpack-project" ``` :::spoiler **package.json&nbsp;範例** ```json { "devDependencies": { "@webpack-cli/generators": "^2.1.0", "prettier": "^2.3.0", "ts-loader": "^9.2.2", "typescript": "^4.3.2", "webpack": "^5.38.1", "webpack-cli": "^4.7.0" }, "version": "1.0.0", "description": "My webpack project", "name": "my-webpack-project", "scripts": { "build": "webpack --mode=production --node-env=production", "build:dev": "webpack --mode=development", "build:prod": "webpack --mode=production --node-env=production", "watch": "webpack --watch" } } ``` ::: --- ## ES6 module * 假設 src 資料夾有 3 個 class, 其中入口是 Main ```typescript export class A {} ``` ```typescript export class B {} ``` ```typescript export class Main {} ``` * 在未使用 webpack 之前 <font color="red">匯入 module 需要含副檔名</font> ```typescript import { A } from "./A.js"; import { B } from "./B.js"; export class Main { a: A; b: B; constructor(){ this.a = new A(); this.b = new B(); } } ``` * 導入 webpack 之後 <font color="red">import module 不能加上副檔名, 否則會找不到</font> ```typescript import { A } from "./A"; import { B } from "./B"; export class Main { a: A; b: B; constructor(){ this.a = new A(); this.b = new B(); } } ``` * 使用 VS Code 總是自動加上 module 副檔名的解決方法 1. 開啟 **Preference** 2. 搜尋 **Import Module Specifier Ending** 3. 不要選 **js**, 選 **auto** ![](https://i.imgur.com/vddLMPa.png) --- ## 發佈 ```npm npm run build ``` --- ## 選用 package | 名稱 | 連結 | 用途 | | -- | -- | -- | | copy-webpack-plugin | [link](https://webpack.js.org/plugins/copy-webpack-plugin/) | 複製特定檔案到發佈資料夾 | | clean-webpack-plugin | [link](https://github.com/johnagan/clean-webpack-plugin) | 用來清空發佈資料夾 | * copy-webpack-plugin + clean-webpack-plugin :::success webpack.config.js 簡易設定 ```javascript const CopyPlugin = require('copy-webpack-plugin'); const { CleanWebpackPlugin } = require('clean-webpack-plugin'); const config = { ...略 plugins: [ new HtmlWebpackPlugin({ template: "index.html" }), new CopyPlugin({ patterns: [{ from: path.resolve(__dirname, "來源相對路徑"), to: path.resolve(__dirname, "目標相對路徑"), }] }), new CleanWebpackPlugin() ], ...略 } ``` 更多用法請參考文件 [link](https://webpack.js.org/plugins/copy-webpack-plugin/) ::: 錯誤排解 :::danger HookWebpackError: Only file and data URLs are supported by the default ESM loader :::success copy-webpack-plugin 版本不相容, 嘗試升級或降版本 ::: --- ## 導入SASS 需要安裝的 package | 名稱 | 連結 | 用途 | | -- | -- | -- | | sass-loader | [link](https://www.npmjs.com/package/sass-loader) | | | node-sass | [link](https://www.npmjs.com/package/node-sass) | | | css-loader | [link](https://www.npmjs.com/package/css-loader) | | | mini-css-extract-plugin | [link](https://www.webpack.org.cn/plugins/mini-css-extract-plugin/) | 產生css | #### 修改 webpack.config.js ```js const MiniCssExtractPlugin = require('mini-css-extract-plugin'); ... const config = { plugins: [ new MiniCssExtractPlugin({ filename: '[name].css', }) ], module: { rules: [ { test: /\.s[ac]ss$/i, use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'], }, ] } } ``` #### 在入口檔案 import scss 發佈的時候才會將 scss 編譯成 css ```typescript import "相對路徑/來源檔案.scss"; ``` #### 使用 jquery * 安裝 ```nodejs npm install --save-dev @types/jquery npm install jquery --save ``` * webpack 設定 ```js plugins: [ new webpack.ProvidePlugin({ $: "jquery", }), ] ``` #### 疑難排解 * 每次都固定發佈成 main.css, 想更換檔名 :::success webpack entry 預設名稱是 main 需要修改 webpack.config.js 的 entry 欄位 ```js entry: "./src/index.ts" ``` 改成 ```js entry: { entry自訂名稱: "./src/index.ts" } ``` ::: #### 參考文件 * [mini-css-extract-plugin說明](https://www.webpack.org.cn/plugins/mini-css-extract-plugin/) * [webpack前端打包工具](https://awdr74100.github.io/2020-03-04-webpack-sassloader/) * [Why is [name] always main in MiniCssExtractPlugin for webpack?](https://stackoverflow.com/a/60177523) --- ###### tags: `Node.js`