摘要:和類似的預處理器還有等。的用處非常多,包括給自動加前綴使用下一代語法等,目前越來越多的人開始用它,它很可能會成為預處理器的最終贏家。
webpack實戰
查看所有文檔頁面:全棧開發,獲取更多信息。快馬加鞭,加班加點,終于把這個文檔整理出來了,順便深入地學習一番,鞏固知識,就是太累人,影響睡眠時間和質量。極客就是想要把事情做到極致,開始了就必須到達終點。
原文鏈接:webpack實戰,原文廣告模態框遮擋,閱讀體驗不好,所以整理成本文,方便查找。
本章教你如何用 Webpack 去解決實際項目中常見的場景。
按照不同場景劃分成以下幾類:
使用新語言來開發項目:
使用 ES6 語言
使用 TypeScript 語言
使用 Flow 檢查器
使用 SCSS 語言
使用 PostCSS
使用新框架來開發項目:
使用 React 框架
使用 Vue 框架
使用 Angular2 框架
用 Webpack 構建單頁應用:
為單頁應用生成 HTML
管理多個單頁應用
用 Webpack 構建不同運行環境的項目:
構建同構應用
構建 Electron 應用
構建 Npm 模塊
構建離線應用
Webpack 結合其它工具搭配使用,各取所長:
搭配 Npm Script
檢查代碼
通過 Node.js API 啟動 Webpack
使用 Webpack Dev Middleware
用 Webpack 加載特殊類型的資源:
加載圖片
加載SVG
加載 Source Map
使用 TypeScript 語言由于本文不推薦使用TypeScript,ES6就足夠完成大部分任務。原文鏈接:使用 TypeScript 語言
使用 Angular2 框架Angular2不在我的技術棧范圍,所以這一章不加入,有興趣的查看原文:使用 Angular2 框架
使用ES6語言通常我們需要把采用 ES6 編寫的代碼轉換成目前已經支持良好的 ES5 代碼,這包含2件事:
把新的 ES6 語法用 ES5 實現,例如 ES6 的 class 語法用 ES5 的 prototype 實現。
給新的 API 注入 polyfill ,例如使用新的 fetch API 時注入對應的 polyfill 后才能讓低端瀏覽器正常運行。
BabelBabel 可以方便的完成以上2件事。
Babel 是一個 JavaScript 編譯器,能將 ES6 代碼轉為 ES5 代碼,讓你使用最新的語言特性而不用擔心兼容性問題,并且可以通過插件機制根據需求靈活的擴展。
在 Babel 執行編譯的過程中,會從項目根目錄下的 .babelrc 文件讀取配置。.babelrc 是一個 JSON 格式的文件,內容大致如下:
{ "plugins": [ [ "transform-runtime", { "polyfill": false } ] ], "presets": [ [ "es2015", { "modules": false } ], "stage-2", "react" ] }Plugins
plugins 屬性告訴 Babel 要使用哪些插件,插件可以控制如何轉換代碼。
以上配置文件里的 transform-runtime 對應的插件全名叫做 babel-plugin-transform-runtime,即在前面加上了 babel-plugin-,要讓 Babel 正常運行我們必須先安裝它:
npm i -D babel-plugin-transform-runtime
babel-plugin-transform-runtime 是 Babel 官方提供的一個插件,作用是減少冗余代碼。
Babel 在把 ES6 代碼轉換成 ES5 代碼時通常需要一些 ES5 寫的輔助函數來完成新語法的實現,例如在轉換 class extent 語法時會在轉換后的 ES5 代碼里注入 _extent 輔助函數用于實現繼承:
function _extent(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }
這會導致每個使用了 class extent 語法的文件都被注入重復的 _extent 輔助函數代碼,babel-plugin-transform-runtime 的作用在于不把輔助函數內容注入到文件里,而是注入一條導入語句:
var _extent = require("babel-runtime/helpers/_extent");
這樣能減小 Babel 編譯出來的代碼的文件大小。
同時需要注意的是由于 babel-plugin-transform-runtime 注入了 require("babel-runtime/helpers/_extent") 語句到編譯后的代碼里,需要安裝 babel-runtime 依賴到你的項目后,代碼才能正常運行。 也就是說 babel-plugin-transform-runtime 和 babel-runtime 需要配套使用,使用了 babel-plugin-transform-runtime 后一定需要 babel-runtime。
Presetspresets 屬性告訴 Babel 要轉換的源碼使用了哪些新的語法特性,一個 Presets 對一組新語法特性提供支持,多個 Presets 可以疊加。
Presets 其實是一組 Plugins 的集合,每一個 Plugin 完成一個新語法的轉換工作。Presets 是按照 ECMAScript 草案來組織的,通常可以分為以下三大類:
已經被寫入 ECMAScript 標準里的特性,由于之前每年都有新特性被加入到標準里;
env 包含當前所有 ECMAScript 標準里的最新特性。
被社區提出來的但還未被寫入 ECMAScript 標準里特性,這其中又分為以下四種:
stage0 只是一個美好激進的想法,有 Babel 插件實現了對這些特性的支持,但是不確定是否會被定為標準;
stage1 值得被納入標準的特性;
stage2 該特性規范已經被起草,將會被納入標準里;
stage3 該特性規范已經定稿,各大瀏覽器廠商和 Node.js 社區開始著手實現;
stage4 在接下來的一年將會加入到標準里去。
為了支持一些特定應用場景下的語法,和 ECMAScript 標準沒有關系,例如 babel-preset-react 是為了支持 React 開發中的 JSX 語法。
在實際應用中,你需要根據項目源碼所使用的語法去安裝對應的 Plugins 或 Presets。
接入 Babel由于 Babel 所做的事情是轉換代碼,所以應該通過 Loader 去接入 Babel,Webpack 配置如下:
module.exports = { module: { rules: [ { test: /.js$/, use: ["babel-loader"], }, ] }, // 輸出 source-map 方便直接調試 ES6 源碼 devtool: "source-map" };
配置命中了項目目錄下所有的 JavaScript 文件,通過 babel-loader 去調用 Babel 完成轉換工作。 在重新執行構建前,需要先安裝新引入的依賴:
# Webpack 接入 Babel 必須依賴的模塊 npm i -D babel-core babel-loader # 根據你的需求選擇不同的 Plugins 或 Presets npm i -D babel-preset-env使用SCSS語言
SCSS 可以讓你用更靈活的方式寫 CSS。 它是一種 CSS 預處理器,語法和 CSS 相似,但加入了變量、邏輯等編程元素,代碼類似這樣:
$blue: #1875e7; div { color: $blue; }
SCSS 又叫 SASS,區別在于 SASS 語法類似 Ruby,而 SCSS 語法類似 CSS,對于熟悉 CSS 的前端工程師來說會更喜歡 SCSS。
采用 SCSS 去寫 CSS 的好處在于可以方便地管理代碼,抽離公共的部分,通過邏輯寫出更靈活的代碼。 和 SCSS 類似的 CSS 預處理器還有 LESS 等。
使用 SCSS 可以提升編碼效率,但是必須把 SCSS 源代碼編譯成可以直接在瀏覽器環境下運行的 CSS 代碼。
node-sass 核心模塊是由 C++ 編寫,再用 Node.js 封裝了一層,以供給其它 Node.js 調用。 node-sass 還支持通過命令行調用,先安裝它到全局:
npm i -g node-sass
再執行編譯命令:
# 把 main.scss 源文件編譯成 main.css node-sass main.scss main.css
你就能在源碼同目錄下看到編譯后的 main.css 文件。
接入 WebpackWebpack 接入 sass-loader 相關配置如下:
module.exports = { module: { rules: [ { // 增加對 SCSS 文件的支持 test: /.scss/, // SCSS 文件的處理順序為先 sass-loader 再 css-loader 再 style-loader use: ["style-loader", "css-loader", "sass-loader"], }, ] }, };
以上配置通過正則 /.scss/ 匹配所有以 .scss 為后綴的 SCSS 文件,再分別使用3個 Loader 去處理。具體處理流程如下:
通過 sass-loader 把 SCSS 源碼轉換為 CSS 代碼,再把 CSS 代碼交給 css-loader 去處理。
css-loader 會找出 CSS 代碼中的 @import 和 url() 這樣的導入語句,告訴 Webpack 依賴這些資源。同時還支持 CSS Modules、壓縮 CSS 等功能。處理完后再把結果交給 style-loader 去處理。
style-loader 會把 CSS 代碼轉換成字符串后,注入到 JavaScript 代碼中去,通過 JavaScript 去給 DOM 增加樣式。如果你想把 CSS 代碼提取到一個多帶帶的文件而不是和 JavaScript 混在一起,可以使用1-5 使用Plugin 中介紹過的 ExtractTextPlugin。
由于接入 sass-loader,項目需要安裝這些新的依賴:
# 安裝 Webpack Loader 依賴 npm i -D sass-loader css-loader style-loader # sass-loader 依賴 node-sass npm i -D node-sass使用Flow檢查器
Flow 是一個 Facebook 開源的 JavaScript 靜態類型檢測器,它是 JavaScript 語言的超集。
你所需要做的就是在需要的地方加上類型檢查,例如在兩個由不同人開發的模塊對接的接口出加上靜態類型檢查,能在編譯階段就指出部分模塊使用不當的問題。 同時 Flow 也能通過類型推斷檢查出 JavaScript 代碼中潛在的 Bug。
Flow 使用效果如下:
// @flow // 靜態類型檢查 function square1(n: number): number { return n * n; } square1("2"); // Error: square1 需要傳入 number 作為參數 // 類型推斷檢查 function square2(n) { return n * n; // Error: 傳入的 string 類型不能做乘法運算 } square2("2");
需要注意的時代碼中的第一行 // @flow 告訴 Flow 檢查器這個文件需要被檢查。使用 Flow
Flow 檢測器由高性能跨平臺的 OCaml 語言編寫,它的可執行文件可以通過:
npm i -D flow-bin
安裝,安裝完成后通過先配置 Npm Script:
"scripts": { "flow": "flow" }
再通過 npm run flow 去調用 Flow 執行代碼檢查。
除此之外你還可以通過:
npm i -g flow-bin
把 Flow 安裝到全局后,再直接通過 flow 命令去執行代碼檢查。
安裝成功后,在項目根目錄下執行 Flow 后,Flow 會遍歷出所有需要檢查的文件并對其進行檢查,輸出錯誤結果到控制臺。
采用了 Flow 靜態類型語法的 JavaScript 是無法直接在目前已有的 JavaScript 引擎中運行的,要讓代碼可以運行需要把這些靜態類型語法去掉。
// 采用 Flow 的源代碼 function foo(one: any, two: number, three?): string {} // 去掉靜態類型語法后輸出代碼 function foo(one, two, three) {}
有兩種方式可以做到這點:
flow-remove-types 可多帶帶使用,速度快。
babel-preset-flow 與 Babel 集成。
集成 Webpack由于使用了 Flow 項目一般都會使用 ES6 語法,所以把 Flow 集成到使用 Webpack 構建的項目里最方便的方法是借助 Babel。
安裝 npm i -D babel-preset-flow 依賴到項目。
修改 .babelrc 配置文件,加入 Flow Preset:
"presets": [ ...[], "flow" ]
往源碼里加入靜態類型后重新構建項目,你會發現采用了 Flow 的源碼還是能正常在瀏覽器中運行。
要明確構建的目的只是為了去除源碼中的 Flow 靜態類型語法,而代碼檢查和構建無關。 許多編輯器已經整合 Flow,可以實時在代碼中高亮指出 Flow 檢查出的問題。使用PostCSS
PostCSS 是一個 CSS 處理工具,和 SCSS 不同的地方在于它通過插件機制可以靈活的擴展其支持的特性,而不是像 SCSS 那樣語法是固定的。 PostCSS 的用處非常多,包括給 CSS 自動加前綴、使用下一代 CSS 語法等,目前越來越多的人開始用它,它很可能會成為 CSS 預處理器的最終贏家。
PostCSS 和 CSS 的關系就像 Babel 和 JavaScript 的關系,它們解除了語法上的禁錮,通過插件機制來擴展語言本身,用工程化手段給語言帶來了更多的可能性。PostCSS 和 SCSS 的關系就像 Babel 和 TypeScript 的關系,PostCSS 更加靈活、可擴張性強,而 SCSS 內置了大量功能而不能擴展。
給 CSS 自動加前綴,增加各瀏覽器的兼容性:
/*輸入*/ h1 { display: flex; } /*輸出*/ h1 { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; }
使用下一代 CSS 語法:
/*輸入*/ :root { --red: #d33; } h1 { color: var(--red); } /*輸出*/ h1 { color: #d33; }
PostCSS 全部采用 JavaScript 編寫,運行在 Node.js 之上,即提供了給 JavaScript 代碼調用的模塊,也提供了可執行的文件。
在 PostCSS 啟動時,會從目錄下的 postcss.config.js 文件中讀取所需配置,所以需要新建該文件,文件內容大致如下:
module.exports = { plugins: [ // 需要使用的插件列表 require("postcss-cssnext") ] }
其中的 postcss-cssnext 插件可以讓你使用下一代 CSS 語法編寫代碼,再通過 PostCSS 轉換成目前的瀏覽器可識別的 CSS,并且該插件還包含給 CSS 自動加前綴的功能。
目前 Chrome 等現代瀏覽器已經能完全支持 cssnext 中的所有語法,也就是說按照 cssnext 語法寫的 CSS 在不經過轉換的情況下也能在瀏覽器中直接運行。接入 Webpack
雖然使用 PostCSS 后文件后綴還是 .css 但這些文件必須先交給 postcss-loader 處理一遍后再交給 css-loader。
接入 PostCSS 相關的 Webpack 配置如下:
module.exports = { module: { rules: [ { // 使用 PostCSS 處理 CSS 文件 test: /.css/, use: ["style-loader", "css-loader", "postcss-loader"], }, ] }, };
接入 PostCSS 給項目帶來了新的依賴需要安裝,如下:
# 安裝 Webpack Loader 依賴 npm i -D postcss-loader css-loader style-loader # 根據你使用的特性安裝對應的 PostCSS 插件依賴 npm i -D postcss-cssnext使用React框架 React 語法特征
使用了 React 項目的代碼特征有 JSX 和 Class 語法,例如:
class Button extends Component { render() { returnHello,Webpack
} }
在使用了 React 的項目里 JSX 和 Class 語法并不是必須的,但使用新語法寫出的代碼看上去更優雅。
其中 JSX 語法是無法在任何現有的 JavaScript 引擎中運行的,所以在構建過程中需要把源碼轉換成可以運行的代碼,例如:
// 原 JSX 語法代碼 returnReact 與 BabelHello,Webpack
// 被轉換成正常的 JavaScript 代碼 return React.createElement("h1", null, "Hello,Webpack")
要在使用 Babel 的項目中接入 React 框架是很簡單的,只需要加入 React 所依賴的 Presets babel-preset-react。
通過以下命令:
# 安裝 React 基礎依賴 npm i -D react react-dom # 安裝 babel 完成語法轉換所需依賴 npm i -D babel-preset-react
安裝新的依賴后,再修改 .babelrc 配置文件加入 React Presets
"presets": [ "react" ],
就完成了一切準備工作。
再修改 main.js 文件如下:
import * as React from "react"; import { Component } from "react"; import { render } from "react-dom"; class Button extends Component { render() { returnHello,Webpack
} } render(, window.document.getElementById("app"));
重新執行構建打開網頁你將會發現由 React 渲染出來的 Hello,Webpack。
React 與 TypeScriptTypeScript 相比于 Babel 的優點在于它原生支持 JSX 語法,你不需要重新安裝新的依賴,只需修改一行配置。 但 TypeScript 的不同在于:
使用了 JSX 語法的文件后綴必須是 tsx。
由于 React 不是采用 TypeScript 編寫的,需要安裝 react 和 react-dom 對應的 TypeScript 接口描述模塊 @types/react 和 @types/react-dom 后才能通過編譯。
修改 TypeScript 編譯器配置文件 tsconfig.json 增加對 JSX 語法的支持,如下:
{ "compilerOptions": { "jsx": "react" // 開啟 jsx ,支持 React } }
由于 main.js 文件中存在 JSX 語法,再把 main.js 文件重命名為 main.tsx,同時修改文件內容為在上面 React 與 Babel 里所采用的 React 代碼。 同時為了讓 Webpack 對項目里的 ts 與 tsx 原文件都采用 awesome-typescript-loader 去轉換, 需要注意的是 Webpack Loader 配置的 test 選項需要匹配到 tsx 類型的文件,并且 extensions 中也要加上 .tsx,配置如下:
module.exports = { // TS 執行入口文件 entry: "./main", output: { filename: "bundle.js", path: path.resolve(__dirname, "./dist"), }, resolve: { // 先嘗試 ts,tsx 后綴的 TypeScript 源碼文件 extensions: [".ts", ".tsx", ".js",] }, module: { rules: [ { // 同時匹配 ts,tsx 后綴的 TypeScript 源碼文件 test: /.tsx?$/, loader: "awesome-typescript-loader" } ] }, devtool: "source-map",// 輸出 Source Map 方便在瀏覽器里調試 TypeScript 代碼 };
通過npm i react react-dom @types/react @types/react-dom安裝新的依賴后重啟構建,重新打開網頁你將會發現由 React 渲染出來的 Hello,Webpack。
使用Vue框架Vue是一個漸進式的 MVVM 框架,相比于 React、Angular 它更靈活輕量。 它不會強制性地內置一些功能和語法,你可以根據自己的需要一點點地添加功能。 雖然采用 Vue 的項目能用可直接運行在瀏覽器環境里的代碼編寫,但為了方便編碼大多數項目都會采用 Vue 官方的單文件組件的寫法去編寫項目。
Vue 的單文件組件通過一個類似 HTML 文件的 .vue 文件就能描述清楚一個組件所需的模版、樣式、邏輯。
main.js 入口文件:
import Vue from "vue" import App from "./App.vue" new Vue({ el: "#app", render: h => h(App) });
入口文件創建一個 Vue 的根實例,在 ID 為 app 的 DOM 節點上渲染出上面定義的 App 組件。
接入 Webpack目前最成熟和流行的開發 Vue 項目的方式是采用 ES6 加 Babel 轉換,這和基本的采用 ES6 開發的項目很相似,差別在于要解析 .vue 格式的單文件組件。 好在 Vue 官方提供了對應的 vue-loader 可以非常方便的完成單文件組件的轉換。
修改 Webpack 相關配置如下:
module: { rules: [ { test: /.vue$/, use: ["vue-loader"], }, ] }
安裝新引入的依賴:
# Vue 框架運行需要的庫 npm i -S vue # 構建所需的依賴 npm i -D vue-loader css-loader vue-template-compiler
在這些依賴中,它們的作用分別是:
vue-loader:解析和轉換 .vue 文件,提取出其中的邏輯代碼 script、樣式代碼 style、以及 HTML 模版 template,再分別把它們交給對應的 Loader 去處理。
css-loader:加載由 vue-loader 提取出的 CSS 代碼。
vue-template-compiler:把 vue-loader 提取出的 HTML 模版編譯成對應的可執行的 JavaScript 代碼,這和 React 中的 JSX 語法被編譯成 JavaScript 代碼類似。預先編譯好 HTML 模版相對于在瀏覽器中再去編譯 HTML 模版的好處在于性能更好。
使用 TypeScript 編寫 Vue 應用從 Vue 2.5.0+ 版本開始,提供了對 TypeScript 的良好支持,使用 TypeScript 編寫 Vue 是一個很好的選擇,因為 TypeScript 能檢查出一些潛在的錯誤。
新增 tsconfig.json 配置文件,內容如下:
{ "compilerOptions": { // 構建出 ES5 版本的 JavaScript,與 Vue 的瀏覽器支持保持一致 "target": "es5", // 開啟嚴格模式,這可以對 `this` 上的數據屬性進行更嚴格的推斷 "strict": true, // TypeScript 編譯器輸出的 JavaScript 采用 es2015 模塊化,使 Tree Shaking 生效 "module": "es2015", "moduleResolution": "node" } }
修改 App.vue 腳本部分內容如下:
注意 script 標簽中的 lang="ts" 是為了指明代碼的語法是 TypeScript。
修改 main.ts 執行入口文件為如下:
import Vue from "vue" import App from "./App.vue" new Vue({ el: "#app", render: h => h(App) });
由于 TypeScript 不認識 .vue 結尾的文件,為了讓其支持 import App from "./App.vue" 導入語句,還需要以下文件 vue-shims.d.ts 去定義 .vue 的類型:
// 告訴 TypeScript 編譯器 .vue 文件其實是一個 Vue declare module "*.vue" { import Vue from "vue"; export default Vue; }
Webpack 配置需要修改兩個地方,如下:
const path = require("path"); module.exports = { resolve: { // 增加對 TypeScript 的 .ts 和 .vue 文件的支持 extensions: [".ts", ".js", ".vue", ".json"], }, module: { rules: [ // 加載 .ts 文件 { test: /.ts$/, loader: "ts-loader", exclude: /node_modules/, options: { // 讓 tsc 把 vue 文件當成一個 TypeScript 模塊去處理,以解決 moudle not found 的問題,tsc 本身不會處理 .vue 結尾的文件 appendTsSuffixTo: [/.vue$/], } }, ] }, };
除此之外還需要安裝新引入的依賴:npm i -D ts-loader typescript
為單頁應用生成HTML 引入問題在使用 React 框架中,是用最簡單的 Hello,Webpack 作為例子讓大家理解, 這個例子里因為只輸出了一個 bundle.js 文件,所以手寫了一個 index.html 文件去引入這個 bundle.js,才能讓應用在瀏覽器中運行起來。
在實際項目中遠比這復雜,一個頁面常常有很多資源要加載。接下來舉一個實戰中的例子,要求如下:
項目采用 ES6 語言加 React 框架。
給頁面加入 Google Analytics,這部分代碼需要內嵌進 HEAD 標簽里去。
給頁面加入 Disqus 用戶評論,這部分代碼需要異步加載以提升首屏加載速度。
壓縮和分離 JavaScript 和 CSS 代碼,提升加載速度。
在開始前先來看看該應用最終發布到線上的代碼。
可以看到部分代碼被內嵌進了 HTML 的 HEAD 標簽中,部分文件的文件名稱被打上根據文件內容算出的 Hash 值,并且加載這些文件的 URL 地址也被正常的注入到了 HTML 中。
解決方案推薦一個用于方便地解決以上問題的 Webpack 插件 web-webpack-plugin。 該插件已經被社區上許多人使用和驗證,解決了大家的痛點獲得了很多好評,下面具體介紹如何用它來解決上面的問題。
首先,修改 Webpack 配置。
以上配置中,大多數都是按照前面已經講過的內容增加的配置,例如:
增加對 CSS 文件的支持,提取出 Chunk 中的 CSS 代碼到多帶帶的文件中,壓縮 CSS 文件;
定義 NODE_ENV 環境變量為 production,以去除源碼中只有開發時才需要的部分;
給輸出的文件名稱加上 Hash 值;
壓縮輸出的 JavaScript 代碼。
但最核心的部分在于 plugins 里的:
new WebPlugin({ template: "./template.html", // HTML 模版文件所在的文件路徑 filename: "index.html" // 輸出的 HTML 的文件名稱 })
其中 template: "./template.html" 所指的模版文件 template.html 的內容是:
該文件描述了哪些資源需要被以何種方式加入到輸出的 HTML 文件中。
以 為例,按照正常引入 CSS 文件一樣的語法來引入 Webpack 生產的代碼。href 屬性中的 app?_inline 可以分為兩部分,前面的 app 表示 CSS 代碼來自名叫 app 的 Chunk 中,后面的 _inline 表示這些代碼需要被內嵌到這個標簽所在的位置。
同樣的 表示 JavaScript 代碼來自相對于當前模版文件 template.html 的本地文件 ./google_analytics.js, 而且文件中的 JavaScript 代碼也需要被內嵌到這個標簽所在的位置。
也就是說資源鏈接 URL 字符串里問號前面的部分表示資源內容來自哪里,后面的 querystring 表示這些資源注入的方式。
除了 _inline 表示內嵌外,還支持以下屬性:
_dist 只有在生產環境下才引入該資源;
_dev 只有在開發環境下才引入該資源;
_ie 只有IE瀏覽器才需要引入的資源,通過 [if IE]>resource 注釋實現。
這些屬性之間可以搭配使用,互不沖突。例如 app?_inline&_dist 表示只在生產環境下才引入該資源,并且需要內嵌到 HTML 里去。
WebPlugin 插件還支持一些其它更高級的用法,詳情可以訪問該項目主頁閱讀文檔。
管理多個單頁應用 引入問題在開始前先來看看該應用最終發布到線上的代碼。
構建出的目錄結構為:
dist ├── common_029086ff.js ├── common_7cc98ad0.css ├── index.html ├── index_04c08fbf.css ├── index_b3d3761c.js ├── login.html ├── login_0a3feca9.js └── login_e31e214b.css
如果按照上節的思路,可能需要為每個單頁應用配置一段如下代碼:
new WebPlugin({ template: "./template.html", // HTML 模版文件所在的文件路徑 filename: "login.html" // 輸出的 HTML 的文件名稱 })
并且把頁面對應的入口加入到 enrty 配置項中,就像這樣:
entry: { index: "./pages/index/index.js",// 頁面 index.html 的入口文件 login: "./pages/login/index.js",// 頁面 login.html 的入口文件 }
當有新頁面加入時就需要修改 Webpack 配置文件,新插入一段以上代碼,這會導致構建代碼難以維護而且易錯。
解決方案項目源碼目錄結構如下:
├── pages │ ├── index │ │ ├── index.css // 該頁面多帶帶需要的 CSS 樣式 │ │ └── index.js // 該頁面的入口文件 │ └── login │ ├── index.css │ └── index.js ├── common.css // 所有頁面都需要的公共 CSS 樣式 ├── google_analytics.js ├── template.html └── webpack.config.js
從目錄結構中可以看成出下幾點要求:
所有單頁應用的代碼都需要放到一個目錄下,例如都放在 pages 目錄下;
一個單頁應用一個多帶帶的文件夾,例如最后生成的 index.html 相關的代碼都在 index 目錄下,login.html 同理;
每個單頁應用的目錄下都有一個 index.js 文件作為入口執行文件。
雖然 AutoWebPlugin 強制性的規定了項目部分的目錄結構,但從實戰經驗來看這是一種優雅的目錄規范,合理的拆分了代碼,又能讓新人快速的看懂項目結構,也方便日后的維護。
Webpack 配置文件修改如下:
See the Pen webpack管理多個單頁應用 by whjin (@whjin) on CodePen.