自從出現前端模塊化之後,咱們就能夠將本來是一坨的代碼分離到一個個的模塊中。可是由此引起了一個問題,每一個 JS 文件都須要瀏覽器經過網絡請求向服務器去獲取,這樣會致使頁面加載速度變慢。javascript
Webpack 最主要的目的就是爲了解決這個問題,將項目中全部應用的文件和模塊打包成一個或多個大文件。官網的圖片就很好的詮釋了這個事情:👇css
🗿WebPack 是一個現代 JavaScript 應用程序的靜態模塊打包器(module bundler):html
它會分析你的項目結構,找到 JavaScript 模塊以及其它的一些瀏覽器不能直接運行的擴展語言(如 ES六、TypeScript、Sass 等),將其轉換和打包爲合適的格式後供瀏覽器使用。前端
當 webpack 處理應用程序時,它會遞歸地構建一個依賴關係圖(dependency graph),其中包含應用程序須要的每一個模塊,而後將全部這些模塊打包成一個或多個 bundle。vue
構建就是把源代碼轉換成發佈到線上可執行的 JavaScript、CSS、HTML 代碼,包括如下內容:java
構建實際上是工程化、自動化思想在前端開發中的體現。把一系列流程用代碼去實現,讓代碼自動化地執行這一系列複雜的流程。😎node
入口(entry point)webpack
指示 webpack 應該使用哪一個模塊,來做爲構建其內部依賴圖的開始,webpack 會找出有哪些模塊和 library 是入口起點(直接和間接)依賴的。ios
默認值是
./src/index.js
,然而,能夠經過在 webpack 配置中配置 entry 屬性,來指定一個不一樣的入口起點(或者也能夠指定多個入口起點)。css3
告訴 webpack 在哪裏輸出它所建立的 bundles,以及如何命名這些文件。
主輸出文件默認爲
./dist/main.js
,其餘生成文件的默認輸出目錄是./dist
讓 webpack 可以去處理那些非 JavaScript 文件(webpack 自己只能加載 JavaScript/JSON 模塊)。loader 能夠將全部類型的資源文件轉換爲 webpack 可以處理的有效模塊,而後你就能夠利用 webpack 的打包能力,對它們進行處理。
loader 自己是一個函數,接受源文件做爲參數,返回轉換後的結果。注意,loader 可以 import 導入任何類型的模塊(例如 .css 文件),這是 webpack 特有的功能,其餘打包程序或任務執行器的可能並不支持。咱們認爲這種語言擴展是有很必要的,由於這可使開發人員建立出更準確的依賴關係圖。
loader 被用於轉換某些類型的模塊,而插件則能夠用於執行範圍更廣的任務。插件的範圍包括,從打包優化和壓縮,一直到從新定義環境中的變量。插件接口功能極其強大,能夠用來處理各類各樣的任務。
經常使用插件:
- CleanWebpackPlugin:自動清除指定文件夾資源
- HtmlWebopackPlugin:根據模板自動生成 html 並引入 script 腳本
- UglifyJSPlugin:壓縮 JS 代碼
經過設置mode
參數來選擇 development(開發環境) 或 production(生產環境) 生產環境之中的一個,從而啓用相應模式下的 webpack 內置的優化。
簡單來講,開發時可能須要打印 debug 信息,還有定義
sourcemap
、UglifyJSPlugin
文件,而生產環境是用於線上的即代碼都是壓縮後,運行時不打印 debug 信息等。譬如 axios、antd 等咱們的生產環境中須要使用到那麼咱們應該安裝該依賴在生產環境中,而webpack-dev-server
則是須要安裝在開發環境中。
👾 webpack 構建過程: 🏗
其實 Webpack 和另外兩個並無太多的可比性,Gulp/Grunt 是一種可以優化前端開發工做流程的工具,而 WebPack 是一種模塊化的解決方案,不過 Webpack 的優勢使它在不少場景下能夠替代 Gulp/Grunt 類的工具。
在一個配置文件中,指明對某些文件進行相似編譯,組合,壓縮等任務的具體步驟,這個工具以後能夠自動替你完成這些任務。
把你的項目當作一個總體,經過一個給定的主文件(如:index.js),Webpack 將從這個文件開始找到你的項目的全部依賴文件(如:component.js 、helper.js ),而後將這些模塊使用不一樣的 loaders(第三方包)來處理它們,最後打包爲一個瀏覽器可識別的 JavaScript 文件。(如:app.js)👇
在明白了 Webpack 的概念以後,咱們如今一步步的開始簡單的使用 Webpack4.0+ 搭建基本的前端開發環境。👨🏻💻
主要包括如下幾個方面:
# 卸載默認:
npm uninstall webpack
# 全局安裝:
npm install webpack -g
# 局部安裝:
npm install webpack webpack-cli --save-dev
# 將 webpack 和 webpack-cli 放入項目開發環境依賴中
複製代碼
- 不推薦全局安裝,它會將你項目中的 webpack 鎖定到指定版本,而且在使用不一樣的 webpack 版本的項目中,可能會致使構建失敗。 ⚠️
- webpack 即 webpack 核心庫。它提供了不少 API, 在 Node.js 腳本中經過
require('webpack')
的方式來使用 webpack。- webpack-cli 是 webpack 的命令行工具。讓咱們能夠不用寫打包腳本,只需配置打包配置文件,而後在命令行輸入
webpack-cli --config webpack.config.js
來使用 webpack, 簡單不少。webpack 4 以前命令行工具是集成在 webpack 包中的,4.0 開始 webpack 包自己再也不集成 cli。簡單來講,若是不安裝 webpack-cli 這個包,咱們就沒辦法在命令行中使用 webpack 裏的各類命令。 🤷🏻♂️- webpack-serve (非必需) 是 webpack 提供的用來開發調試的服務器,讓你能夠用 http://127.0.0.1:8080/ 這樣的 url 打開頁面來調試,有了它就不用配置 nginx 了,方便不少。 🍻
# 1. 新建一個空的練習文件夾
mkdir webpack-demo
cd webpack-demo
# 2. 建立 package.json
# npm 的說明文件,包括當前項目的基本信息,依賴模塊,自定義的腳本任務
npm init
# 這裏會問一些問題,能夠直接回車跳過或者 npm init -y
# 3. 局部安裝
npm i webpack webpack-cli -D
# 4. 建立項目代碼文件夾src
mkdir src
cd src
# 5. 建立項目入口文件
touch index.html
touch index.js
複製代碼
index.html入口頁面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Webpack 學習</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
複製代碼
index.js 入口文件:
let testStr = 'Hello World, this is a webpack-demo.'
document.getElementById('root').innerText = testStr
console.log(testStr)
複製代碼
其實 webpack 從 v4.0.0 開始已經能夠一個配置文件都不寫,實現零配置 (使用默認配置文件)。😶
好比咱們此時在終端執行npx webpack
,發現 webpack 已經把 src/index.js 文件裏的內容打包生成到了 dist (未來部署到服務器上的文件夾)目錄 mian.js 文件裏。
🤖關於如何運行webpack:
node ./node_modules/.bin/webpack
就能夠啓動 webpack。npx command
默認就是執行 ./node_modules
目錄中安裝的可執行腳本。若是這裏 webpack 未安裝,它也會自動從 npm 源下載安裝後再執行。(使用npx webapck -v
命令查看此項目安裝的 webpack 版本號) 🤠mode
屬性)而出現 WARNING⚠️ ,因此最好的方式是在 package.json 文件的 scripts 裏添加一段"build": "webpack --mode development"
(默認 production ,會壓縮打包後的代碼)後使用 npm run build
來運行 webpack。 👏實際狀況是,大多數項目都會須要咱們對 webpack 增長更多的自定義配置信息,那麼咱們就須要在根目錄下建立 webpack 的配置文件 webpack.config.js
。🙃
而後再執行 npm run build
,webpack 就會使用咱們在這個文件裏定義的配置信息了。
cd webpack-demo
touch webpack.config.js
複製代碼
webpack.config.js
中經常使用的基本配置信息:
module.exports = {
entry: '', // 打包入口:指示 webpack 應該使用哪一個模塊,來做爲構建其內部依賴圖的開始
output: '', // 出口目錄
mode: 'development', // 在這裏更改了模式,就沒必要在package.json裏再設置 --mode
resolve: {}, // 配置解析:配置別名、extensions 自動解析肯定的擴展等等
devServer: {}, // 開發服務器:run dev/start 的配置,如端口、proxy等
module: {}, // 模塊配置:配置loader(處理非 JavaScript 文件,好比 jsx、sass、vue、圖片等等)
plugins: [] // 插件配置:打包優化、資源管理和注入環境變量
}
複製代碼
注意這個文件是在 node.js 中運行的,遵循
CommonJS
規範,所以不支持 ES6 的import
語法。
// 引入 webpack 自帶的 node 核心模塊
const path = require('path')
module.exports = {
/* entry: { main: './src/index.js' } */
// 簡寫
entry: './src/index.js',
// 配置打包輸出相關
output: {
// 打包輸出目錄,必須使用絕對地址,輸出文件夾路徑
path: path.resolve(__dirname, 'dist'), // 解析路徑爲 ./dist
// 入口 js 的打包輸出文件名,默認爲 main.js
filename: "bundle.js"
}
}
複製代碼
也可使用下面的寫法:👇
const path = require('path')
// 從新配置入口文件路徑以及出口文件路徑
const PATH = {
app:path.join(__dirname, "./src/index.js"),
build:path.resolve(__dirname, "./dist")
}
module.exports = {
entry:{
//這裏面的key值決定了下面name的名字叫什麼
app:PATH.app
},
output:{
path:PATH.build,
filename:"[name].js" // 即app.js
}
}
複製代碼
⚠️要自定義 webapck 打包後的 output 路徑,必需要先引入 webpack 自帶的 node 核心模塊 path const path = require('path')
。
path.join()
將第一個參數和第二個參數進行連接(路徑鏈接),該方法的主要用途在於,會正確使用當前系統的路徑分隔符,Unix系統是 /,Windows系統是 \。path.resolve()
會把一個路徑或路徑片斷的序列參數解析爲一個絕對路徑, 也能夠變成相對路徑。__dirname
變量指的就是webpack.config.js
這個文件所在的,當前這個目錄的絕對路徑。經過上面的配置,執行npm run build
以後咱們能夠發現 webpack 已經將打包入口文件 src/index.js 打包到了 dist 目錄下的 bundle.js 文件裏。
此時在 src/index.html 文件裏引用<script src="../dist/bundle.js"></script>
文件後在瀏覽器打開就能看到咱們在 src/index.js 寫的代碼運行結果了。或者也能夠將 src/index.html 移動到 dist 文件夾下,而後引入文件<script src="./bundle.js"></script>
。
這樣作也能夠,但不必。 😏 由於手動引用打包後的 js 文件顯得一點都不智能,並且當咱們修改配置文件裏打包輸出的文件名後,index.html 裏的引用路徑就會出錯。
因此咱們可使用 html-webpack-plugin 插件以 src/index.html 爲模板來生成 dist/html 文件 ,並將 HTML 引用 JS 的路徑和咱們的構建結果自動關聯起來。👏
安裝:
npm install html-webpack-plugin -D
修改 webpack.config.js 文件:
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
//...
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html',
template: './src/index.html',
/* 由於和 webpack 4 的兼容性問題,chunksSortMode 參數須要設置爲 none https://github.com/jantimon/html-webpack-plugin/issues/870 */
chunksSortMode: 'none'
})
]
}
複製代碼
template 參數指定入口 html 文件路徑,插件會把這個文件做爲 html 模板交給 webpack 去編譯,並將構建結果儲存爲 html 文件到輸出目錄,默認文件名爲 index.html。也能夠經過 filename 參數指定輸出的文件名。
html-webpack-plugin 也能夠不指定 template 參數,它會使用插件默認的 html 模板。
從新執行 npm run build ,dist 目錄下會產生一個包含如下內容的 index.html 文件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Webpack 學習</title>
</head>
<body>
<div id="root"></div>
<script type="text/javascript" src="bundle.js"></script></body>
</html>
複製代碼
插件已經自動幫咱們把打包後的 bundle.js 文件正確地引用到根據指定模板生成的 html 裏啦。👆
接下來若是咱們但願使用 webpack 來進行構建 css 文件,那麼就須要在配置文件中引入 css-loader 和 style-loader 這兩個 loader 來解析和處理 css文件。
爲何要安裝兩個 loader,是由於前者可讓 css 文件也支持 import
,而且會解析多個 css 文件的關係,最終把它們合併成一段 css。後者能夠將解析出來的 css 經過 style
標籤的形式插入到 HTML 頁面中的 <head>
部分,因此 style-loader
依賴 css-loader
。
安裝:
npm install css-loader style-loader -D
另外,若是要處理 scss 文件,還須要引入 sass-loader
。一樣,它依賴於前兩個loader。
同時還要安裝 node-sass
,node-sass 是 sass-loader 的 peerDependency。(ps:就是安裝很慢的那個 🙄
安裝:
npm install sass-loader node-sass -D
💣 注意是 sass-loader 不是 scss-loader 哦! 🤪
添加樣式文件:
cd src
mkdir styles && cd styles
touch index.scss
複製代碼
在 index.scss 文件裏編輯樣式代碼後,而且在 src/index.js 中引入 import './styles/index.scss'
修改 webpack.config.js
文件:
module.exports = {
//...
/* 配置各類類型文件的加載器,稱之爲 loader webpack 當遇到 import ... 時,會調用這裏配置的 loader 對引用的文件進行編譯 */
module: {
/** * test: 匹配特定條件。通常是提供一個正則表達式或正則表達式的數組 * include: 匹配特定條件。通常是提供一個字符串或者字符串數組 * exclude: 排除特定條件 * and: 必須匹配數組中的全部條件 * or: 匹配數組中任何一個條件 * nor: 必須排除這個條件 * use: 指定處理該文件的 loader, 值能夠是字符串或者數組。loader 的執行順序是從最後一個到第一個。 */
rules: [
{
test: /\.(css|scss)$/, // 匹配css和scss文件
include: [path.resolve(__dirname, 'src')],
use: ['style-loader', 'css-loader', 'sass-loader']
// loader的執行順序是從右至左/從下往上。
// use: ['style-loader', 'css-loader', 'sass-loader', 'postcss-loader']
}
]
}
//...
}
複製代碼
經由上述 sass-loader 和 css-loader 的處理後,css/scss 代碼會轉變爲 JS, 若是須要單獨把 css 文件分離出來,咱們須要使用 mini-css-extract-plugin 插件。
同時,在使用 css3 裏一些須要添加廠商前綴的新特性的時候,咱們可使用 postcss-loader 配合 autoprefixer 插件來實現自動添加廠商前綴的功能。
安裝:
npm install file-loader -D
file-loader: 不僅是用來處理圖片,能夠用於處理不少類型的靜態資源文件( txt、svg、ex ),它的主要做用是直接輸出文件,並把構建後的文件路徑返回。
添加圖片資源文件夾:
cd src
mkdir assets && cd assets
mkdir images
複製代碼
編輯配置文件:
rules: [
// ...
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: 'file-loader',
options: {
outputPath: 'images/', //輸出到dist的images文件夾
}
}
]
}
]
複製代碼
在 assets/images 文件夾內放入 webpack.jpg 圖片,而後修改 index.js 文件引入這個圖片並使用它。
import './styles/index.scss';
import webpackImg from './assets/images/webpack.jpg';
// var webpackImg = require('./assets/images/webpack.jpg')
console.log(webpackImg)
let img = new Image();
img.src = webpackImg;
let testStr = 'Hello World, this is a webpack-demo.';
let root = document.getElementById('root');
root.innerText = testStr;
root.append(img);
console.log(testStr);
複製代碼
npm run build
以後,咱們會發現 file-loader 已經把圖片重命名後打包到了 dist 目錄下的 images 文件夾裏,同時會把該文件相對於 dist 目錄的文件路徑名稱做爲返回值返回。(可打開控制檯查看輸出信息 👀)
若是咱們不想被打包後的文件名稱發生改變或者是想指定打包文件的存放位置,那麼能夠經過編輯 file-loader 的 options 屬性來完成配置。
{
loader: 'file-loader',
options: {
outputPath: 'images/', //輸出到 dist 的 images文件夾
name: '[name].[ext]', // placeholder 的具體含義可查看文檔
}
}
複製代碼
url-loader: 有了file-loader
咱們就能夠對圖片進行打包,但若是圖片較多,頁面加載時就要發出不少 http 請求,這會下降性能。
而url-loader
會把引入的圖片編碼,生成 dataURl
。至關於把圖片數據翻譯成一串字符。再把這些字符串打包到一個文件中,最終只須要引入這個文件就能訪問圖片了,這能夠節省 http 請求。
But! 若是圖片較大,編碼會消耗性能。所以 url-loader
提供了一個 limit
參數,小於 limit 字節的文件會被轉爲 DataURl,大於 limit 的仍是會使用 file-loader
進行 copy。
- url-loader 能夠看做是加強版的 file-loader。
- url-loader 在文件大小(單位 byte)低於指定的限制時,能夠返回一個 DataURL。而後把圖片編碼成 base64 格式寫進頁面,從而減小服務器請求。
rules: [
// ...
{
test: /\.(png|jpg|gif)$/,
use: [
{
loader: 'url-loader', // 指定使用 url-loader 處理圖片
options: {
outputPath: 'images/',
name: '[name].[ext]',
limit: 600 //是把小於600B的文件打成Base64的格式
}
}
]
}
]
複製代碼
同時安裝 file-loader、url-loader 後,能夠只指定 url-loader 處理圖片資源文件。
當文件大於 limit 時,url-loader 會調用 file-loader, 把文件儲存到輸出目錄,並把引用的文件路徑改寫成輸出後的路徑。
當文件體積小於 limit 時,url-loader 把文件轉爲 Data URI 的格式內聯到引用的地方。(打開控制檯查看效果)
<img src="./assets/images/smallpic.png"> <!--會被編譯成--> <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAA..."> 複製代碼
接下來,爲了能讓不支持 ES6 的瀏覽器(好比萬惡的 IE )也能照常運行,咱們須要安裝 babel, 它會把咱們寫的 ES6 源代碼轉化成 ES5,這樣咱們源代碼寫 ES6,打包時生成 ES5。
安裝:
webpack 4.x | babel-loader 8.x | babel 7.x
npm i babel-loader @babel/core @babel/preset-env -D
- Babel 7.x 的相關依賴包須要加上
@babel/ scope
(babel核心庫)- 用 babel 轉換 ES6 代碼須要使用到
babel-loader
@babel/preset-env
默認狀況下是等於 ES2015 + ES2016 + ES2017,也就是說它對這三個版本的 ES 語法進行轉化- Babel 7.x 一個主要變化是 presets 設置由原來的
env
換成了@babel/preset-env
, 能夠配置targets
,useBuiltIns
等選項用於編譯出兼容目標環境的代碼- 其中
useBuiltIns
若是設爲"usage"
,Babel 會根據實際代碼中使用的 ES6/ES7 代碼,以及與你指定的 targets,按需引入對應的polyfill
,而無需在 src/index.js 代碼中直接引入import '@babel/polyfill'
,避免輸出的包過大,同時又能夠放心使用各類新語法特性。
配置webpack.config.js:
{
test: /\.m?js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
複製代碼
此時 webpack 就能夠轉義並打包咱們 ES6 的語法了,同時咱們也能夠將對 babel 的詳細配置寫到 .babelrc 文件中。 在根目錄下新建 .babelrc 文件 touch .babelrc
{
"presets": [
["@babel/preset-env", {
modules: false,
useBuiltIns: 'usage',
targets: {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
}
}]
]
}
複製代碼
這就是 babel-preset-env 的做用,幫助咱們配置 babel。咱們只須要告訴它咱們要兼容的狀況(目標運行環境),它就會自動把代碼轉換爲兼容對應環境的代碼。
以上代碼表示咱們要求代碼兼容最新兩個版本的瀏覽器,不用兼容 IE 8,另外市場份額超過 1% 的瀏覽器也必須支持。
只須要告訴 babel-preset-env 你想要兼容的環境,它就會自動轉換。
可是這種方式也並非全部場景都適用的,若是你正在開發的是一個類庫或者第三方模塊,那麼就不能使用這種配置方式。(由於 polyfill 的代碼是經過全局變量的形式注入的,這樣會污染全局環境)
此時就要安裝@babel/plugin-transform-runtime
這個插件,而後再參照文檔進行相關配置。
具體babel 在各類狀況下的配置方法能夠在官網的setup頁面查看 😋
上面的一系列操做都是研究如何打包文件,但每次編輯代碼以後咱們都須要在終端從新執行一次npm run build
從新打包,而後再去 dist 目錄下找到輸出的文件並打開查看打包編譯後的結果。
🤦🏻♂️爲了避免那麼痛苦,咱們能夠採起一些措施來提高開發效率。
最簡單的是使用 webpack 的 watch mode,在 package.json 文件的 scripts 中再添加一條:
"watch": "webpack --watch"
加入—watch
參數後,再在終端執行npm run watch
,此時 webpack 就會幫咱們 watch 監聽文件的變化並自動從新打包。
修改代碼後不用在終端從新執行命令,但要刷新瀏覽器才能查看更新的效果。🙃
更好一點的方案是使用 webpack-dev-server 在本地開啓一個簡單的本地靜態服務來進行開發。從而實現每次 src 目錄下的源代碼發生改變,dist 目錄就會自動從新打包。😄
(ps: 其實並不會生成 dist 文件夾,而是將打包後的內容放到電腦的內存中,從而提高打包速度。)
webpack-dev-server
是 webpack 官方提供的一個工具,能夠基於當前的 webpack 構建配置快速啓動一個靜態服務。當
mode
爲development
時,會具有熱加載 (hot reload) 的功能,即當源碼文件變化時,會即時更新當前頁面,以便你看到最新的效果。
安裝:
npm i webpack-dev-server -D
在 package.json 的 scripts 中添加:
"start": "webpack-dev-server --open"
--open
參數會幫助咱們自動打開瀏覽器並訪問 web server
而後執行npm start
,webapck 就會默認開啓一個 web server http://localhost:8080/ 便於咱們開發。🥳
// devtool:{}
devServer: {
contentBase: './dist', // web server 服務器的根路徑
open: 'true', // 等於 --open
port: '8086', // 服務器端口號
proxy: {
'/api': 'http://localhost:3000' // 配置代理
}
}
複製代碼
它還有不少配置選項能夠來幫助咱們提高開發效率,具體可查閱文檔使用。