最近在學(xué)習(xí) Webpack,網(wǎng)上大多數(shù)入門教程都是基于 Webpack 1.x 版本的,我學(xué)習(xí) Webpack 的時候是看了 zhangwang 的 <<入門 Webpack往湿,看這篇就夠了>> 寫的非常好,不過是基于 Webpack 1.x 版本的,語法上和 Webpack 2.x 有一點不同.我學(xué)習(xí)時是使用 Webpack 2.6.1 版本,所以我就尋思著基于 zhangwang 的 <<入門 Webpack,看這篇就夠了>> 寫下這篇 Webpack 2.x 的入門實戰(zhàn),是我學(xué)習(xí) Webpack 的記錄.聽說 Webpack 3.x 版本快要出了,不得不感嘆前端領(lǐng)域發(fā)展的真是太快了!
Webpack 是什么?
Webpack 是前端資源模塊化管理和打包工具惋戏。
Webpack 可以將許多松散的模塊按照依賴和規(guī)則打包成符合生產(chǎn)環(huán)境部署的前端資源领追。
Webpack 可以將按需加載的模塊進行代碼分隔,等到實際需要的時候再異步加載响逢。
Webpack 通過 loader 的轉(zhuǎn)換绒窑,任何形式的資源都可以視作模塊,比如 CommonJs 模塊舔亭、 AMD 模塊些膨、 ES6 模塊、CSS钦铺、圖片订雾、 JSON、Coffeescript矛洞、 LESS洼哎、 SASS 等。
一圖勝千言,下圖足夠說明上面巴巴拉拉一大堆是啥了!
對于模塊的組織,通常有如下幾種方法:
- 分開寫幾個 js 文件,使用 script 標簽加載.
- CommonJS 進行同步加載, Node.js 就使用這種方式.
- AMD進行異步加載, require.js 使用這種方式.
- 新的 ES6 模塊.
Webpack 的特點
- 豐富的插件噩峦,流行的插件都有,方便進行開發(fā)工作.
- 大量的加載器锭沟,便于處理和加載各種靜態(tài)資源.
- 將按需加載的模塊進行代碼分隔,等到實際需要的時候再異步加載.
Webpack 的優(yōu)勢
- Webpack 以 commonJS 的形式來書寫腳本识补,但對 AMD / CMD / ES6 模塊 的支持也很全面族淮,方便舊項目進行代碼遷移。
- 所有資源都能模塊化凭涂。
- 開發(fā)便捷祝辣,能替代部分 Grunt / Gulp 的工作,比如打包切油、壓縮混淆较幌、圖片轉(zhuǎn) base64、SASS 解析成 CSS 等白翻。
- 擴展性強,插件機制完善绢片,特別是支持模塊熱替換(見 模塊熱替換 )的功能讓人眼前一亮。
Webpack 與 Grunt / Gulp
在沒有學(xué)習(xí) Webpack 之前我對 Webpack、Grunt雏逾、Gulp 的認識很模糊,只知道好像這三個東西都是前端自動化工具,都是用來使前端自動化惠毁、模塊化、工程化的,這三者是可以替代彼此的前端工具.
其實 Webpack 和 Gulp / Grunt 并沒有太多的可比性熙涤,Gulp / Grunt 是一種能夠優(yōu)化前端開發(fā)流程的自動化工具阁苞,而 Webpack 是一種模塊化的解決方案,不過 Webpack 的優(yōu)點使得 Webpack 可以替代 Gulp / Grunt 一部分工作祠挫。
Grunt / Gulp 的工作方式是:在一個配置文件中那槽,指明對某些文件需要進行哪些處理,例如:編譯、組合等舔、壓縮等任務(wù)的具體步驟骚灸,Grunt / Gulp 之后可以自動替你完成這些任務(wù)。Grunt / Gulp的工作流程如下圖:
Webpack 的工作方式是:把你的項目當做一個整體慌植,通過一個給定的主文件( 如:index.js )甚牲,Webpack 將從這個文件開始找到你的項目的所有依賴文件,使用 loaders 處理它們蝶柿,最后打包為一個瀏覽器可識別的 JavaScript 文件丈钙。Webpack工作方式如下圖:
如果實在要進行比較,Webpack 的處理速度更快更直接,因為 Webpack 的歷史包袱小.Webpack 還能打包更多不同類型的文件。
開始使用 Webpack
初步了解 Webpack 后交汤,就可以開始學(xué)習(xí)使用 Webpack雏赦。這里會以一個小的 Demo 為例子來一步一步進行動手學(xué)習(xí)!
新建 Webpack 項目
1. 新建一個文件夾,命名為 webpack-demo,webpack-demo 就是你的項目名,項目名建議使用小寫字母,并且不帶空格,不能含有大寫字母.
2. 安裝 Webpack,Webpack 可以使用 npm 安裝,如果你還不知道 npm 為何物,請 Google,也可以參考 Node.js 安裝配置和 NPM 使用介紹快速了解喉誊、安裝 npm.
使用終端在該文件夾中執(zhí)行下述指令就可以完成安裝,由于網(wǎng)絡(luò)原因安裝過程可能需要一些時間邀摆。
//全局安裝
npm install -g webpack
//安裝到你的項目目錄
npm install --save-dev webpack
Webpack 可以全局安裝,也可以安裝到你的項目目錄.剛開始學(xué)習(xí) Webpack 為了方便,建議同時全局安裝和安裝到你的項目目錄.
3. 在 webpack-demo 文件夾中創(chuàng)建一個 package.json 文件,這是一個標準的 npm 說明文件伍茄,里面蘊含了豐富的信息栋盹,包括當前項目的依賴模塊,自定義的腳本任務(wù)等等敷矫。在終端中使用 npm init 命令可以自動創(chuàng)建這個 package.json 文件.
npm init
輸入這個命令后例获,終端會問你一系列諸如項目名稱,項目版本,項目描述,入口文件,作者等信息,不過不用擔(dān)心曹仗,如果你不準備在 npm 中發(fā)布你的模塊榨汤,這些問題的答案都不重要,回車默認即可.這些信息今后都可以更改 package.json 來修改,所以不用擔(dān)心.
4. 在 webpack-demo 文件夾中創(chuàng)建兩個文件夾 app 文件夾和 public 文件夾, app 文件夾用來存放原始數(shù)據(jù),例如: SASS 文件怎茫、LESS 文件收壕、JavaScript 模塊等,public 文件夾用來存放經(jīng)過 Webpack 處理過的 app 文件夾數(shù)據(jù),這也是準備給瀏覽器讀取的數(shù)據(jù),其中包括使用 Webpack 打包后的 js 文件等轨蛤。在這里還需要在 public 文件夾中創(chuàng)建 index.html 文件.在 app 文件夾中創(chuàng)建 Greeter.js 和 main.js 文件蜜宪,此時項目結(jié)構(gòu)如下圖所示:
5. 在 public 文件夾中的 index.html 文件只有最基礎(chǔ)的 html 代碼,它唯一的目的就是加載打包后的 js 文件 bundle.js.
// index.html
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>webpack-demo</title>
</head>
<body>
<div id='root'>
</div>
<script type="text/javascript" src="bundle.js"></script>
</body>
</html>
6. 在 app 文件夾中的 Greeter.js 只包括一個用來返回問候信息的 html 元素的函數(shù)祥山。
// Greeter.js
module.exports = function() {
var greet = document.createElement('div');
greet.textContent = "Hi there and greetings!";
return greet;
}
7. 在 app 文件夾中的 main.js 用來把 Greeter 模塊(其實可以簡單的把它看作 Greeter.js)返回的節(jié)點插入頁面圃验。
// main.js
var greeting = require('./Greeter.js');
document.getElementById('root').appendChild(greeting());
Webpack 配置文件
Webpack 配置文件其實也是一個簡單的 JavaScript 模塊,可以把所有與項目構(gòu)建相關(guān)的信息放在里面缝呕。在 webpack-demo 文件夾根目錄下新建一個名為 webpack.config.js 的文件澳窑,并在其中進行最簡單的配置.如下所示,它包含入口文件路徑和存放打包后文件的地方路徑供常。
// webpack.config.js
module.exports = {
entry: __dirname + "/app/main.js", //入口文件路徑
output: {
path: __dirname + "/public/", //存放打包后文件的地方路徑
filename: "bundle.js" //打包后的文件名
}
}
注:__dirname 是 node.js 中的一個全局變量摊聋,它指向當前 js 文件所在的目錄.
現(xiàn)在只需要在終端里運行 webpack 命令就可以了,這條命令會自動參考 webpack.config.js 文件中的配置選項打包你的項目话侧,輸出結(jié)果如下:
此時項目的 public 文件夾下也會出現(xiàn)打包好的 bundle.js 文件.此時項目結(jié)構(gòu)如下圖所示:
可以看出 webpack 同時編譯了 main.js 和 Greeter.js,打開 public 目錄下的 index.html 文件,就可以看到最終效果,如下圖:
利用 npm 更快捷的執(zhí)行打包任務(wù)
通過 Webpack 配置文件和執(zhí)行 webpack 命令其實是比較煩人且容易出錯的栗精,不過值得慶幸的是 npm 可以引導(dǎo)任務(wù)執(zhí)行,對其進行配置后可以使用簡單的 npm start 命令來代替這些繁瑣的命令瞻鹏。在 package.json 中對 npm 的腳本部分進行相關(guān)設(shè)置悲立,相當于把 npm 的 start 命令指向 webpack 命令,設(shè)置方法如下:
// package.json
{
"name": "webpack-demo",
"version": "1.0.0",
"description": "",
"scripts": {
"start": "webpack"
},
"author": "",
"license": "ISC"
}
執(zhí)行 npm start 后命令行的輸出顯示:
現(xiàn)在只需要使用 npm start 就可以打包文件了.打開 public 目錄下的 index.html 文件,看到的最終效果是不是與之前的一樣.
利用 Webpack 生成 Source Maps
簡單說新博,Source Maps 就是一個信息文件薪夕,里面儲存著位置信息。也就是說赫悄,轉(zhuǎn)換后的代碼的每一個位置原献,所對應(yīng)的轉(zhuǎn)換前的位置.有了它馏慨,出錯的時候,除錯工具將直接顯示原始代碼姑隅,而不是轉(zhuǎn)換后的代碼写隶。這無疑給開發(fā)者帶來了很大方便.為了方便調(diào)試可以利用 Webpack 生成 Source Maps.
在 Webpack 的配置文件中配置 Source Maps,需要配置 devtool讲仰,它有以下四種不同的配置選項慕趴,各有優(yōu)缺點,描述如下:
- source-map 在一個單獨的文件中產(chǎn)生一個完整且功能完全的文件鄙陡。這個文件具有最方便調(diào)試的 Source Maps冕房,但是這個文件會比較大,會減慢打包文件的構(gòu)建速度.
- cheap-module-source-map 在一個單獨的文件中生成一個不帶列映射的 Source Maps,不帶列映射能夠提高項目構(gòu)建速度趁矾,但這也使得瀏覽器開發(fā)者工具只能對應(yīng)到具體的行耙册,不能對應(yīng)到具體的列,會對調(diào)試造成不便.
- eval-source-map 在同一個文件中生成干凈的完整的 Source Maps毫捣。這個選項可以在不影響構(gòu)建速度的前提下生成完整的 Source Maps详拙,但是對打包后輸出的 js 文件的執(zhí)行具有性能和安全的隱患。不過在開發(fā)階段這是一個非常好的選項蔓同,但是在生產(chǎn)階段一定不要用這個選項.
- cheap-module-eval-source-map 這是在打包文件時最快的生成 Source Maps 的方法溪厘,生成的Source Map 會和打包后的 js 文件同行顯示,沒有列映射牌柄,和 eval-source-map 選項具有相似的缺點,文件的執(zhí)行具有性能和安全的隱患.
上述選項由上到下打包速度越來越快,不過同時也具有越來越多的負面作用侧甫,較快的構(gòu)建速度的后果就是對打包的文件執(zhí)行有一定影響珊佣。在學(xué)習(xí)階段以及在小到中型的項目上,eval-source-map是一個很好的選項披粟,不過記得只在開發(fā)階段使用它.
編輯 webpack-demo 文件夾下的 webpack.config.js 文件配置 devtool 選項,生成 Source Maps 文件.配置 devtool 后的 webpack.config.js 文件如下:
// webpack.config.js
module.exports = {
devtool: "source-map", //配置生成 Source Maps 的選項
entry: __dirname + "/app/main.js", //入口文件路徑
output: {
path: __dirname + "/public/", //存放打包后文件的地方路徑
filename: "bundle.js" //打包后的文件名
}
}
執(zhí)行 npm start 命令后就能生成對應(yīng)的 Source Maps,終端輸出信息如下圖:
此時項目中 public 文件夾下也生成了名為 bundle.js.map 的 Source Maps 文件.此時項目結(jié)構(gòu)如下圖所示:
使用 Webpack 構(gòu)建本地服務(wù)器
想不想讓你的瀏覽器監(jiān)測你修改的代碼咒锻,并自動刷新修改后的結(jié)果.其實 Webpack 提供一個可選的本地開發(fā)服務(wù)器,這個本地服務(wù)器基于 node.js 構(gòu)建守屉,可以實現(xiàn)你想要的這些功能惑艇,不過它是一個單獨的組件,在 Webpack 中進行配置之前需要單獨安裝它作為項目依賴.在終端中輸入下面的指令安裝對應(yīng)組件.建議同時全局安裝和安裝到你的項目目錄.
//全局安裝
npm install -g webpack-dev-server
//安裝到你的項目目錄
npm install --save-dev webpack-dev-server
devserver 作為 Webpack 配置選項中的一項拇泛,具有以下配置選項
- contentBase 默認 webpack-dev-server 會為根文件夾提供本地服務(wù)器滨巴,如果想為另外一個目錄下的文件提供本地服務(wù)器,應(yīng)該在這里設(shè)置其所在目錄(本例設(shè)置到“public"文件夾下).
- port 設(shè)置默認監(jiān)聽端口俺叭,如果省略恭取,默認為"8080".
- inline 設(shè)置為 true,當源文件改變時會自動刷新頁面.
- historyApiFallback 在開發(fā)單頁應(yīng)用時非常有用熄守,它依賴于 HTML5 history API蜈垮,如果設(shè)置為 true耗跛,所有的跳轉(zhuǎn)將指向 index.html.
編輯 webpack-demo 文件夾下的 webpack.config.js 文件配置以上選項.
// webpack.config.js
module.exports = {
devtool: "source-map", //配置生成 Source Maps 的選項
entry: __dirname + "/app/main.js", //入口文件路徑
output: {
path: __dirname + "/public/", //存放打包后文件的地方路徑
filename: "bundle.js" //打包后的文件名
},
devServer: {
contentBase: "./public",
port: "9000",
inline: true,
historyApiFallback: true,
}
}
在終端中輸入如下命令,構(gòu)建本地服務(wù)器:
webpack-dev-server
終端輸出信息如下圖,表示 Webpack 構(gòu)建的本地服務(wù)器已啟動.
在瀏覽器中打開 http://localhost:9000/ 就可以看到像之前一樣的問候語頁面.
也可以使用 npm 更快捷的執(zhí)行任務(wù),編輯 webpack-demo 文件夾下的 package.json 文件 scripts 選項.
// package.json
{
"name": "webpack-demo",
"version": "1.0.0",
"description": "",
"scripts": {
"start": "webpack",
"dev": "webpack-dev-server --devtool eval --progress --content-base build"
},
"author": "",
"license": "ISC"
}
在終端中執(zhí)行 npm run dev 命令,輸出信息如下圖,一樣可以啟動的本地服務(wù)器.
按 Ctrl + C 即可退出本地服務(wù)器.
一切皆模塊
Webpack 有一個不可不說的優(yōu)點,它把所有的文件都可以當做模塊處理攒发,包括你的 JavaScript 代碼调塌,也包括 CSS 和 fonts 以及圖片等等,只要通過合適的 Loaders惠猿,它們都可以被當做模塊被處理.
Loaders
webpack 可以使用 loader 來預(yù)處理文件羔砾。這允許你打包除 JavaScript 之外的任何靜態(tài)資源.通過使用不同的 loader,Webpack 通過調(diào)用外部的腳本或工具可以對任何靜態(tài)資源進行處理紊扬,比如說分析 JSON 文件并把它轉(zhuǎn)換為 JavaScript 文件蜒茄,或者說把 ES6 / ES7 的 JS 文件轉(zhuǎn)換為現(xiàn)代瀏覽器可以識別的 JS 文件.對 React 開發(fā)而言,合適的 Loaders 可以把 React 的 JSX 文件轉(zhuǎn)換為 JS 文件.
Loaders 需要單獨安裝并且需要 在webpack.config.js 下的 modules 關(guān)鍵字下進行配置餐屎,Loaders 的配置選項包括以下幾方面:
- test:一個匹配 Loaders 所處理的文件的拓展名的正則表達式(必須)
- loader:loader 的名稱(必須)
- include/exclude: 手動添加必須處理的文件/文件夾,或屏蔽不需要處理的文件/文件夾(可選)
- query:為 Loaders 提供額外的設(shè)置選項(可選)
繼續(xù)動手實踐,修改 app 文件夾下的 Greeter.js 文件,把問候消息放在一個單獨的 JSON 文件里,通過 loader 的使 Greeter.js 可以讀取該 JSON 文件.
1. 在 app 文件夾下創(chuàng)建 config.json 文件,并輸入如下代碼:
//config.json
{
"greetText": "Hi there and greetings from JSON!"
}
2. 編輯 app 文件夾下的 Greeter.js 文件,修改后如下:
// Greeter.js
var config = require('./config.json');
module.exports = function() {
var greet = document.createElement('div');
greet.textContent = config.greetText;
return greet;
}
3. 安裝支持導(dǎo)入 JSON 文件的 json-loader .由于 webpack 2.× 默認支持導(dǎo)入 JSON 文件.如果你使用自定義文件擴展名檀葛,可能仍然需要使用此 loader.在終端中運行如下命令,安裝 json-loader 到你的項目中.
//安裝到你的項目中
npm install --save-dev json-loader
因為 json-loader 安裝到你的項目目錄里了,所以 webpack-demo 項目下會新增一個 node_modules 文件夾用于存放安裝的 json-loader.此時的項目結(jié)構(gòu)如下:
4. 編輯 webpack.config.js 文件配置 modules 選項,添加 json-loader,編輯后的文件如下:
// webpack.config.js
module.exports = {
devtool: "source-map", //配置生成 Source Maps 的選項
entry: __dirname + "/app/main.js", //入口文件路徑
output: {
path: __dirname + "/public/", //存放打包后文件的地方路徑
filename: "bundle.js" //打包后的文件名
},
devServer: {
contentBase: "./public",
port: "9000",
inline: true,
historyApiFallback: true,
},
module: {
loaders: [{
test: /\.json$/,
loader: "json-loader"
}]
}
}
在終端中輸入 npm start 重新編譯打包,再在瀏覽器中打開 public 文件夾下的 index.html 文件,如果看到和下圖一樣的,就說明 json-loader 配置成功了.
Babel
Babel 其實是一個編譯 JavaScript 的平臺,它的強大之處表現(xiàn)在可以通過編譯幫你達到以下目的:
- 把 ES6 / ES7 標準的 JavaScript 轉(zhuǎn)化為瀏覽器能夠解析的 ES5 標準的 JavaScript.
- 使用基于 JavaScript 進行了拓展的語言腹缩,比如 React 的 JSX.
Babel的安裝與配置
Babel 其實是幾個模塊化的包屿聋,其核心功能位于稱為 babel-core 的 npm 包中,不過 Webpack 把它們整合在一起使用藏鹊,但是對于每一個你需要的功能或拓展润讥,你都需要安裝單獨的包.用得最多的是解析 ES6 的 babel-preset-es2015 包和解析 JSX 的 babel-preset-react 包.
先來安裝這些依賴包,輸入如下命令,把這些依賴包安裝到你的項目中.
// 利用 npm 一次性安裝多個依賴模塊,模塊之間用空格隔開
npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-react
//安裝 React 和 React-DOM
npm install --save react react-dom
編輯 webpack.config.js 文件配置 modules 選項,添加 Babel 配置,編輯后的文件如下:
// webpack.config.js
module.exports = {
devtool: "source-map", //配置生成 Source Maps 的選項
entry: __dirname + "/app/main.js", //入口文件路徑
output: {
path: __dirname + "/public/", //存放打包后文件的地方路徑
filename: "bundle.js" //打包后的文件名
},
devServer: {
contentBase: "./public",
port: "9000",
inline: true,
historyApiFallback: true,
},
module: {
loaders: [{
test: /\.json$/,
loader: "json-loader"
}, {
test: /\.js$/,
exclude: /node_modules/, //編譯打包時需要排除 node_modules 文件夾
loader: "babel-loader",
query: {
presets: ['es2015', 'react']
}
}]
}
}
使用 ES6 的語法盘寡,更新 app 文件夾下的 Greeter.js 文件,并返回一個 React 組件,修改后的代碼如下:
// Greeter.js
import React, { Component } from 'react';
import config from './config.json';
class Greeter extends Component {
render() {
return (<div> { config.greetText } </div>);
}
}
export default Greeter;
使用 ES6 的模塊定義和渲染 Greeter 模塊,修改 app 文件夾下的 main.js 文件,修改后的代碼如下:
// main.js
import React from 'react';
import {render} from 'react-dom';
import Greeter from './Greeter';
render(<Greeter />, document.getElementById('root'));
在終端中運行 npm start 命令重新編譯打包,終端輸出信息如下:
在瀏覽器中打開 public 文件夾下的 index.html 文件,如果看到和下圖一樣的,就說明已經(jīng)成功配置了 Babel.
Babel的配置選項
Babel 其實可以完全在 webpack.config.js 文件中進行配置楚殿,但是考慮到 babel 具有非常多的配置選項,在單一的 webpack.config.js 文件中進行配置往往使得這個文件顯得太復(fù)雜竿痰,因此一些開發(fā)者支持把 babel 的配置選項放在一個單獨的名為 ".babelrc" 的配置文件中脆粥。我們現(xiàn)在的 babel 的配置并不算復(fù)雜,不過之后我們會再加一些東西影涉,因此現(xiàn)在我們就提取出相關(guān)部分变隔,分兩個配置文件進行配置, Webpack 會自動調(diào)用 .babelrc 里的 babel 配置選項.
編輯 webpack.config.js 文件配置 modules 選項,添加 Babel 配置,編輯后的文件如下:
// webpack.config.js
module.exports = {
devtool: "source-map", //配置生成 Source Maps 的選項
entry: __dirname + "/app/main.js", //入口文件路徑
output: {
path: __dirname + "/public/", //存放打包后文件的地方路徑
filename: "bundle.js" //打包后的文件名
},
devServer: {
contentBase: "./public",
port: "9000",
inline: true,
historyApiFallback: true,
},
module: {
loaders: [{
test: /\.json$/,
loader: "json-loader"
}, {
test: /\.js$/,
exclude: /node_modules/, //編譯打包時需要排除 node_modules 文件夾
loader: "babel-loader"
}]
}
}
在 webpack-demo 文件夾下新建 .babelrc 文件,添加如下代碼:
// .babelrc
{
"presets": ['es2015', 'react']
}
在終端中運行 npm start 命令重新編譯打包,終端輸出信息如下:
在瀏覽器中打開 public 文件夾下的 index.html 文件,如果看到和下圖一樣的,就說明已經(jīng)成功配置了 Babel.
CSS
Webpack 提供兩個工具處理樣式表,css-loader 和 style-loader.
- css-loader 使你能夠使用類似 @import 和 url(...) 的方法實現(xiàn) require() 的功能
- style-loader 將所有的計算后的樣式加入頁面中
二者組合在一起使你能夠把樣式表嵌入 Webpack 打包后的 JS 文件中蟹倾。
先來安裝 css-loader, style-loader,輸入如下命令,把這些依賴包安裝到你的項目中.
npm install --save-dev style-loader css-loader
編輯 webpack.config.js 文件配置 modules 選項,添加處理樣式表配置,編輯后的文件如下:
// webpack.config.js
module.exports = {
devtool: "source-map", //配置生成 Source Maps 的選項
entry: __dirname + "/app/main.js", //入口文件路徑
output: {
path: __dirname + "/public/", //存放打包后文件的地方路徑
filename: "bundle.js" //打包后的文件名
},
devServer: {
contentBase: "./public",
port: "9000",
inline: true,
historyApiFallback: true,
},
module: {
loaders: [{
test: /\.json$/,
loader: "json-loader"
}, {
test: /\.js$/,
exclude: /node_modules/, //編譯打包時需要排除 node_modules 文件夾
loader: "babel-loader"
}, {
test: /\.css$/,
loader: 'style-loader!css-loader' //添加對樣式表的處理,感嘆號的作用在于使同一文件能夠使用不同類型的 loader
}]
}
}
接下來匣缘,在 app 文件夾里創(chuàng)建一個名為 main.css 的文件,在文件中添加如下代碼,對一些元素設(shè)置樣式.
// main.css
html {
box-sizing: border-box;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
body {
margin: 0;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
h1,
h2,
h3,
h4,
h5,
h6,
p,
ul {
margin: 0;
padding: 0;
}
Webpack 只有單一的入口鲜棠,其它的模塊需要通過 import, require, url 等導(dǎo)入相關(guān)位置肌厨,為了讓 Webpack 能找到 main.css 文件,我們把它導(dǎo)入 app 文件夾下的 main.js 中.修改 app 文件夾下的 main.js 文件,修改后的代碼如下:
// main.js
import React from 'react';
import {render} from 'react-dom';
import Greeter from './Greeter';
import './main.css'; //導(dǎo)入css文件
render(<Greeter/>, document.getElementById('root'));
在終端中運行 npm start 命令重新編譯打包,終端輸出信息如下:
在瀏覽器中打開 public 文件夾下的 index.html 文件,如果看到和下圖一樣的,就說明已經(jīng)配置成功了.
通常情況下豁陆,css 會和 js 打包到同一個文件中夏哭,并不會打包為一個單獨的 css 文件,不過通過合適的配置 Webpack 也可以把 css 打包為單獨的文件的献联。
不過這也只是 Webpack 把 css 當做模塊而已竖配,繼續(xù)看一個真的 CSS 模塊的實踐.
CSS module
在過去的一些年里何址,JavaScript 通過一些新的語言特性、更好的工具进胯、更好的實踐方法(比如說模塊化)發(fā)展得非常迅速用爪。模塊使得開發(fā)者把復(fù)雜的代碼轉(zhuǎn)化為小的、干凈的胁镐、依賴聲明明確的單元偎血,且基于優(yōu)化工具,依賴管理和加載管理可以自動完成盯漂。
不過前端的另外一部分颇玷,CSS 的發(fā)展就相對慢一些,大多的樣式表卻依舊是巨大且充滿了全局類名就缆,這使得維護和修改都非常困難和復(fù)雜帖渠。
CSS modules 的技術(shù)就能夠把 JS 的模塊化思想帶入 CSS 中來,通過 CSS 模塊竭宰,所有的類名空郊,動畫名默認都只作用于當前模塊.
Webpack 從一開始就對 CSS 模塊化提供了支持,在 CSS loader 中進行配置后切揭,你所需要做的一切就是把 modules 傳遞到需要的地方狞甚,然后就可以直接把 CSS 的類名傳遞到組件的代碼中,且這樣做只對當前組件有效廓旬,不必擔(dān)心在不同的模塊中具有相同的類名可能會造成的問題哼审。
編輯 webpack.config.js 文件配置 modules 選項,添加處理樣式表配置,編輯后的文件如下:
// webpack.config.js
module.exports = {
devtool: "source-map", //配置生成 Source Maps 的選項
entry: __dirname + "/app/main.js", //入口文件路徑
output: {
path: __dirname + "/public/", //存放打包后文件的地方路徑
filename: "bundle.js" //打包后的文件名
},
devServer: {
contentBase: "./public",
port: "9000",
inline: true,
historyApiFallback: true,
},
module: {
loaders: [{
test: /\.json$/,
loader: "json-loader"
}, {
test: /\.js$/,
exclude: /node_modules/, //編譯打包時需要排除 node_modules 文件夾
loader: "babel-loader"
}, {
test: /\.css$/,
loader: 'style-loader!css-loader?modules' //跟前面相比就在后面加上了 ?modules
}]
}
}
接下來,在 app 文件夾里創(chuàng)建一個名為 Greeter.css 的文件孕豹,在文件中添加如下代碼,對一些元素設(shè)置樣式.
// Greeter.css
.root {
background-color: #eee;
padding: 10px;
border: 3px solid #ccc;
}
導(dǎo)入 .root 到 Greeter.js 中,修改 app 文件夾下的 Greeter.js 文件,修改后的代碼如下:
// Greeter.js
import React, { Component } from 'react';
import config from './config.json';
import styles from './Greeter.css'; //導(dǎo)入 .root 到 Greeter.js 中
class Greeter extends Component {
render() {
return ( <div className={styles.root}> { config.greetText } </div>);
}
}
export default Greeter;
在終端中運行 npm start 命令重新編譯打包,終端輸出信息如下:
在瀏覽器中打開 public 文件夾下的 index.html 文件,如果看到和下圖一樣的,就說明已經(jīng)配置成功了.
CSS modules 也是一個很大的主題棺蛛,有興趣的話可以去官方文檔查看更多消息.下面兩篇文章也可以看看:
CSS 預(yù)處理器
CSS 預(yù)處理器可以將 SASS、LESS 文件轉(zhuǎn)化為瀏覽器可識別的 CSS 文件,以下是常用的CSS 預(yù)處理器 loaders.
- Less Loader
- Sass Loader
- Stylus Loader
其實也存在一個 CSS 的處理平臺 PostCSS巩步,它可以幫助你的 CSS 實現(xiàn)更多的功能,可以看看<<PostCSS 是個什么鬼東西桦踊?>>.
舉例來說如何使用 PostCSS椅野,我們使用 PostCSS 來為 CSS 代碼自動添加適應(yīng)不同瀏覽器,不同版本的 CSS 前綴。首先安裝 postcss-loader 和 autoprefixer(自動添加前綴的插件),安裝到你的項目中.
npm install --save-dev postcss-loader autoprefixer
編輯 webpack.config.js 文件配置 modules 選項,添加 postcss-loader 處理樣式表配置,編輯后的文件如下:
// webpack.config.js
module.exports = {
devtool: "source-map", //配置生成 Source Maps 的選項
entry: __dirname + "/app/main.js", //入口文件路徑
output: {
path: __dirname + "/public/", //存放打包后文件的地方路徑
filename: "bundle.js" //打包后的文件名
},
devServer: {
contentBase: "./public",
port: "9000",
inline: true,
historyApiFallback: true,
},
module: {
loaders: [{
test: /\.json$/,
loader: "json-loader"
}, {
test: /\.js$/,
exclude: /node_modules/, //編譯打包時需要排除 node_modules 文件夾
loader: "babel-loader"
}, {
test: /\.css$/,
loader: 'style-loader!css-loader?modules!postcss-loader' //跟前面相比就在后面加上了 !postcss-loader
}]
}
}
在 webpack-demo 文件夾下新建 postcss.config.js 文件,添加如下代碼:
// postcss.config.js
module.exports = {
plugins: [
//調(diào)用autoprefixer插件,還可以配置選項添加需要兼容的瀏覽器版本.
require("autoprefixer")({ browsers: ['ie>=8', '>1% in CN'] })
]
}
現(xiàn)在你寫的樣式會自動根據(jù) Can i use 里的數(shù)據(jù)添加不同前綴了.在終端中運行 npm start 命令重新編譯打包,終端輸出信息如下:
在瀏覽器中打開 public 文件夾下的 index.html 文件,如果看到和下圖一樣的,就說明已經(jīng)成功配置了 PostCSS.
插件(Plugins)
插件(Plugins)是用來拓展 Webpack 功能的籍胯,它會在整個構(gòu)建過程中生效竟闪,執(zhí)行相關(guān)的任務(wù)。
Loaders 和 Plugins 常常被弄混杖狼,但是他們其實是完全不同的東西炼蛤,可以這么說,Loaders 是在打包構(gòu)建過程中用來處理源文件的(JSX蝶涩,Scss理朋,Less..)絮识,一次處理一個;插件并不直接操作單個文件,它直接對整個構(gòu)建過程其作用嗽上。
Webpack 有很多內(nèi)置插件次舌,同時也有很多第三方插件,可以讓我們完成更加豐富的功能兽愤。
使用插件的方法
要使用某個插件彼念,我們需要通過 npm 安裝它,然后要做的就是在 Webpack 配置中的 Plugins 關(guān)鍵字部分添加該插件的一個實例.
編輯 webpack.config.js 文件配置 Plugins 選項,添加一個實現(xiàn)版權(quán)聲明的 BannerPlugin 插件,BannerPlugin 是內(nèi)置插件不需要使用 npm 安裝.編輯后的文件如下:
// webpack.config.js
var webpack = require("webpack");
module.exports = {
devtool: "source-map", //配置生成 Source Maps 的選項
entry: __dirname + "/app/main.js", //入口文件路徑
output: {
path: __dirname + "/public/", //存放打包后文件的地方路徑
filename: "bundle.js" //打包后的文件名
},
devServer: {
contentBase: "./public",
port: "9000",
inline: true,
historyApiFallback: true,
},
module: {
loaders: [{
test: /\.json$/,
loader: "json-loader"
}, {
test: /\.js$/,
exclude: /node_modules/, //編譯打包時需要排除 node_modules 文件夾
loader: "babel-loader"
}, {
test: /\.css$/,
loader: 'style-loader!css-loader?modules!postcss-loader' //跟前面相比就在后面加上了 !postcss-loader
}]
},
plugins: [
new webpack.BannerPlugin("Copyright Flying Unicorns inc.")//在這個數(shù)組中new一個實例就可以了
]
}
在終端中運行 npm start 命令重新編譯打包,終端輸出信息如下:
在瀏覽器中打開 public 文件夾下的 index.html 文件,如果看到和下圖一樣的,就說明已經(jīng)成功配置了 BannerPlugin 插件.
常用插件
給大家推薦幾個常用的插件
HtmlWebpackPlugin
這個插件的作用是依據(jù)一個簡單的模板浅萧,幫你生成最終的 html 文件逐沙,這個文件中自動引用了你打包后的 JS 文件。每次編譯都在文件名中插入一個不同的哈希值洼畅。
安裝 HtmlWebpackPlugin 到你的項目中
npm install --save-dev html-webpack-plugin
在使用 HtmlWebpackPlugin 之前,需要對 webpack-demo 項目結(jié)構(gòu)做一些改變.
1. 移除 public 文件夾.
2. 在 app 目錄下吩案,創(chuàng)建一個文件名為 index.tmpl.html 模板文件,在編譯過程中,HtmlWebpackPlugin 插件會依據(jù)此模板生成最終的 html 頁面,會自動添加所依賴的 css土思、 js务热、favicon等文件.index.tmpl.html 模板文件代碼如下:
// index.tmpl.html
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>webpack-demo</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
3. 在 webpack-demo 文件夾下新建一個 build 文件夾用來存放最終的輸出文件.
4. 編輯 webpack.config.js 文件配置 Plugins 選項,添加 HtmlWebpackPlugin 插件,修改 output 選項.編輯后的文件如下:
// webpack.config.js
var webpack = require("webpack");
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
devtool: "source-map", //配置生成 Source Maps 的選項
entry: __dirname + "/app/main.js", //入口文件路徑
output: {
path: __dirname + "/build/", //存放打包后文件的地方路徑
filename: "bundle.js" //打包后的文件名
},
devServer: {
contentBase: "./public",
port: "9000",
inline: true,
historyApiFallback: true,
},
module: {
loaders: [{
test: /\.json$/,
loader: "json-loader"
}, {
test: /\.js$/,
exclude: /node_modules/, //編譯打包時需要排除 node_modules 文件夾
loader: "babel-loader"
}, {
test: /\.css$/,
loader: 'style-loader!css-loader?modules!postcss-loader' //跟前面相比就在后面加上了 !postcss-loader
}]
},
plugins: [
new webpack.BannerPlugin("Copyright Flying Unicorns inc."), //在這個數(shù)組中new一個實例就可以了
new HtmlWebpackPlugin({
template: __dirname + "/app/index.tmpl.html" //new一個插件的實例,并傳入相關(guān)的參數(shù)
})
]
}
此時項目結(jié)構(gòu)如下圖所示:
在終端中運行 npm start 命令重新編譯打包,終端輸出信息如下:
此時項目結(jié)構(gòu)已經(jīng)發(fā)生改變,build 文件夾下存放了最終的輸出的文件,項目結(jié)構(gòu)如下圖所示:
在瀏覽器中打開 build 文件夾下的 index.html 文件,如果看到和下圖一樣的,就說明已經(jīng)成功配置了 HtmlWebpackPlugin 插件.
Hot Module Replacement
Hot Module Replacement(HMR)也是 Webpack 里很有用的一個插件己儒,它允許你在修改組件代碼后崎岂,自動刷新實時預(yù)覽修改后的效果。
在 Webpack 中使用 HMR 也很簡單闪湾,只需要做兩項配置.
- 在 Webpack 配置文件中添加 HMR 插件
- 在 Webpack Dev Server 中添加 hot 參數(shù)
不過配置完這些后冲甘,JS 模塊其實還是不能自動熱加載的,還需要在你的 JS 模塊中執(zhí)行一個 Webpack 提供的 API 才能實現(xiàn)熱加載途样,雖然這個 API 不難使用江醇,但是如果是 React 模塊,使用我們已經(jīng)熟悉的 Babel 可以更方便的實現(xiàn)功能熱加載何暇。
整理下思路陶夜,具體實現(xiàn)方法如下
- Babel 和 Webpack 是獨立的工具,二者可以一起工作,二者都可以通過插件拓展功能.
- HMR 是一個 Webpack 插件,它讓你能瀏覽器中實時觀察模塊修改后的效果,但是如果你想讓它工作裆站,需要對模塊進行額外的配額.
- Babel 有一個叫做 react-transform-hrm 的插件条辟,可以在不同 React 模塊進行額外的配置下讓 HMR 正常工作.
編輯 webpack.config.js 文件配置 Plugins / devServer 選項,添加 Hot Module Replacement 插件.編輯后的文件如下:
// webpack.config.js
var webpack = require("webpack");
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
devtool: "source-map", //配置生成 Source Maps 的選項
entry: __dirname + "/app/main.js", //入口文件路徑
output: {
path: __dirname + "/build/", //存放打包后文件的地方路徑
filename: "bundle.js" //打包后的文件名
},
devServer: {
port: "9000",
inline: true,
historyApiFallback: true,
hot: true
},
module: {
loaders: [{
test: /\.json$/,
loader: "json-loader"
}, {
test: /\.js$/,
exclude: /node_modules/, //編譯打包時需要排除 node_modules 文件夾
loader: "babel-loader"
}, {
test: /\.css$/,
loader: 'style-loader!css-loader?modules!postcss-loader' //跟前面相比就在后面加上了 !postcss-loader
}]
},
plugins: [
new webpack.BannerPlugin("Copyright Flying Unicorns inc."), //在這個數(shù)組中new一個實例就可以了
new HtmlWebpackPlugin({
template: __dirname + "/app/index.tmpl.html" //new一個插件的實例,并傳入相關(guān)的參數(shù)
}),
new webpack.HotModuleReplacementPlugin() //熱加載插件
]
}
安裝 react-transform-hmr 插件
npm install --save-dev babel-plugin-react-transform react-transform-hmr
編輯在 webpack-demo 文件夾下的 .babelrc 文件,編輯后的文件如下:
// .babelrc
{
"presets": ["react", "es2015"],
"env": {
"development": {
"plugins": [["react-transform", {
"transforms": [{
"transform": "react-transform-hmr",
"imports": ["react"],
"locals": ["module"]
}]
}]]
}
}
}
編輯 webpack-demo 文件夾下的 package.json 文件 scripts 選項,添加 --hot 選項開啟代碼熱替換.
// package.json
{
"name": "webpack-demo",
"version": "1.0.0",
"description": "",
"scripts": {
"start": "webpack",
"dev": "webpack-dev-server --devtool eval --progress --content-base build --hot"
},
"author": "",
"license": "ISC"
}
在終端中執(zhí)行 npm run dev 命令,輸出信息如下圖,一樣可以啟動自動熱加載.
在瀏覽器中打開 http://localhost:9000/ 就可以看到像之前一樣的問候語頁面.
現(xiàn)在當你使用 React 時宏胯,就可以熱加載模塊了.按 Ctrl + C 即可退出自動熱加載.
產(chǎn)品階段的構(gòu)建
我們已經(jīng)使用 Webpack 構(gòu)建了一個完整的開發(fā)環(huán)境.但是在產(chǎn)品階段,可能還需要對打包的文件進行額外的處理羽嫡,比如說優(yōu)化、壓縮肩袍、緩存以及分離 CSS 和 JS.
對于復(fù)雜的項目來說杭棵,需要復(fù)雜的配置,這時候分解配置文件為多個小的文件可以使得事情井井有條氛赐,以 webpack-demo 項目來說魂爪,我們在 webpack-demo 文件夾下創(chuàng)建一個名為 webpack.production.config.js 的文件先舷,在里面加上基本的配置代碼,如下:
// webpack.production.config.js
var webpack = require("webpack");
var HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: __dirname + "/app/main.js", //入口文件路徑
output: {
path: __dirname + "/build/", //存放打包后文件的地方路徑
filename: "bundle.js" //打包后的文件名
},
module: {
loaders: [{
test: /\.json$/,
loader: "json-loader"
}, {
test: /\.js$/,
exclude: /node_modules/, //編譯打包時需要排除 node_modules 文件夾
loader: "babel-loader"
}, {
test: /\.css$/,
loader: 'style-loader!css-loader?modules!postcss-loader' //跟前面相比就在后面加上了 !postcss-loader
}]
},
plugins: [
new HtmlWebpackPlugin({
template: __dirname + "/app/index.tmpl.html" //new一個插件的實例,并傳入相關(guān)的參數(shù)
})
]
}
編輯 webpack-demo 文件夾下的 package.json 文件 scripts 選項,添加 build 選項,編輯后的文件如下:
// package.json
{
"name": "webpack-demo",
"version": "1.0.0",
"description": "",
"scripts": {
"start": "webpack",
"dev": "webpack-dev-server --devtool eval --progress --content-base build --hot",
"build": "webpack --config ./webpack.production.config.js --progress"
},
"author": "",
"license": "ISC"
}
在終端中執(zhí)行 npm run build 命令,輸出信息如下圖:
說明分解配置文件為多個小的文件成功了.
優(yōu)化插件
Webpack 提供了一些在發(fā)布階段非常有用的優(yōu)化插件甫窟,它們大多來自于 Webpack 社區(qū)密浑,可以通過 npm 安裝,通過以下插件可以完成產(chǎn)品發(fā)布階段所需的功能.
- OccurrenceOrderPlugin: 為組件分配 ID粗井,通過這個插件 Webpack 可以分析和優(yōu)先考慮使用最多的模塊尔破,并為它們分配最小的 ID.
- UglifyJsPlugin:壓縮JS代碼.
- ExtractTextPlugin:分離 CSS 和 JS 文件.
我們來看看如何使用它們,OccurrenceOrderPlugin 和 UglifyJS plugins 都是內(nèi)置插件浇衬,我們只需要安裝 ExtractTextPlugin 插件.
安裝 ExtractTextPlugin 插件
npm install --save-dev extract-text-webpack-plugin
編輯 webpack.config.js 文件配置 Plugins 選項,添加這三個插件,因為要分離 css 所以還要配置 module 下的 loaders 選項.編輯后的文件如下:
// webpack.config.js
var webpack = require("webpack");
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
devtool: "source-map", //配置生成 Source Maps 的選項
entry: __dirname + "/app/main.js", //入口文件路徑
output: {
path: __dirname + "/build/", //存放打包后文件的地方路徑
filename: "bundle.js" //打包后的文件名
},
devServer: {
port: "9000",
inline: true,
historyApiFallback: true,
hot: true
},
module: {
loaders: [{
test: /\.json$/,
loader: "json-loader"
}, {
test: /\.js$/,
exclude: /node_modules/, //編譯打包時需要排除 node_modules 文件夾
loader: "babel-loader"
}, {
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: "css-loader?modules!postcss-loader"
})
}]
},
plugins: [
new webpack.BannerPlugin("Copyright Flying Unicorns inc."), //在這個數(shù)組中new一個實例就可以了
new HtmlWebpackPlugin({
template: __dirname + "/app/index.tmpl.html" //new一個插件的實例懒构,并傳入相關(guān)的參數(shù)
}),
new webpack.HotModuleReplacementPlugin(), //熱加載插件
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.optimize.UglifyJsPlugin(),
new ExtractTextPlugin("style.css")
]
}
在終端中執(zhí)行 npm start 命令,輸出信息如下圖:
此時項目結(jié)構(gòu)已經(jīng)發(fā)生改變,build 文件夾下多出了抽離出來的 style.css 文件還有對應(yīng)的 style.css.map 文件,項目結(jié)構(gòu)如下圖所示:
如果你打開 build 文件夾下的 bundle.js 文件,就可以看到 bundle.js 文件內(nèi)容已經(jīng)被壓縮處理了.
說明這三個插件已經(jīng)配置成功了.
緩存
為了加快加載速度,合理的利用緩存是必不可少的.使用緩存的最好方法是保證文件名和文件內(nèi)容是匹配的.內(nèi)容改變,名稱也相應(yīng)改變.
Webpack 可以把一個哈希值添加到打包文件的文件名中,添加特殊的字符串混合體([name], [id] and [hash])到輸出文件名前,便于修改 BUG 以后,對應(yīng)更新用戶本地的緩存文件.
編輯 webpack.config.js 文件修改 output / plugins 選項.編輯后的文件如下:
// webpack.config.js
var webpack = require("webpack");
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
devtool: "source-map", //配置生成 Source Maps 的選項
entry: __dirname + "/app/main.js", //入口文件路徑
output: {
path: __dirname + "/build/", //存放打包后文件的地方路徑
filename: "[name]-[hash].js" //打包后的文件名
},
devServer: {
port: "9000",
inline: true,
historyApiFallback: true,
hot: true
},
module: {
loaders: [{
test: /\.json$/,
loader: "json-loader"
}, {
test: /\.js$/,
exclude: /node_modules/, //編譯打包時需要排除 node_modules 文件夾
loader: "babel-loader"
}, {
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: "style-loader",
use: "css-loader?modules!postcss-loader"
})
}]
},
plugins: [
new webpack.BannerPlugin("Copyright Flying Unicorns inc."), //在這個數(shù)組中new一個實例就可以了
new HtmlWebpackPlugin({
template: __dirname + "/app/index.tmpl.html" //new一個插件的實例耘擂,并傳入相關(guān)的參數(shù)
}),
new webpack.HotModuleReplacementPlugin(), //熱加載插件
new webpack.optimize.OccurrenceOrderPlugin(),
new webpack.optimize.UglifyJsPlugin(),
new ExtractTextPlugin("[name]-[hash].css")
]
}
在終端中執(zhí)行 npm start 命令,輸出信息如下圖:
此時項目 build 文件夾下的 main.css 和 main.js 文件都對應(yīng)的加上了哈希值.項目結(jié)構(gòu)如下圖所示:
如果你打開 build 文件夾下的 index.html 文件,就可以看到引用的 css胆剧、js 文件名也對應(yīng)發(fā)生了改變,這樣修改 BUG 以后,也能對應(yīng)更新用戶本地的緩存文件.
進階,永不止步
其實到這里我的這篇 Webpack 2.x 的入門實戰(zhàn)已經(jīng)完結(jié)了!但這也只是個入門而已!在實際項目中運用還是不夠的,還有很多細節(jié)我并沒深入講,所以大家還想進階的話建議好好去看看 webpack-china 的文檔.
另外實戰(zhàn)項目 webpack-demo 的源碼,我已經(jīng)放到 Github 上去了,歡迎大家提意見.
還有一點我覺得很重要,要學(xué)會看控制臺輸出信息,能夠看控制臺輸出信息解決的問題,就不要上 Google 搜了!
鳴謝
這篇 Webpack 2.x 的入門實戰(zhàn)是基于 zhangwang 的 <<入門Webpack,看這篇就夠了>> 寫出來的,是我學(xué)習(xí) Webpack 的實戰(zhàn)記錄.特別感謝 zhangwang 付出,如果你覺得這篇文章對你有幫助,請轉(zhuǎn)到 zhangwang 為他點個贊.