Webpack已經流行好久了,但很多同學使用webpack時還是一頭霧水,一下看到那麼多文檔、各種配置、各種loader、plugin立馬就暈頭轉向了。我也不例外,以至於很長一段時間對webpack都是一知半解的狀態。但是想要繼續做好前端,webpack是必須得跨過的一道坎,其實掌握webpack並... ...
摘要: webpack入門教程。
- 原文:還學不會webpack?看這篇!
- 作者:MudOnTire
Fundebug經授權轉載,版權歸原作者所有。
Webpack已經流行好久了,但很多同學使用webpack時還是一頭霧水,一下看到那麼多文檔、各種配置、各種loader、plugin立馬就暈頭轉向了。我也不例外,以至於很長一段時間對webpack都是一知半解的狀態。但是想要繼續做好前端,webpack是必須得跨過的一道坎,其實掌握webpack並不難,只是我們沒有找到正確的方法。本文就是我自己在學習webpack時的一點心得體會,供大家參考。
什麼是webpack?
一句話概括:webpack是一個模塊打包工具(module bundler)。重點在於兩個關鍵詞“模塊”和“打包”。什麼是模塊呢?我們回顧一下曾經的前端開發方式,js文件通過script
標簽靜態引入,js文件之間由於沒有強依賴關係,如果文件1要用到文件2的某些方法或變數,則必須將文件1放到文件2後面載入。隨著項目的增大,js文件之間的依賴關係越發錯綜複雜,維護難度也越來越高。這樣的困境驅使著前端工程師們不斷探索新的開發模式,從後端、app的開發模式中我們獲得靈感,為什麼不能引入“模塊”的概念讓js文件之間可以相互引用呢?模塊1要使用模塊2的功能,只需要在該模塊1中明確引用模塊2就行了,而不用擔心它們的排列順序。基於這種理念,CommonJS和 AMD規範被創造了出來,然後有了require.js、system.js這樣的前端模塊載入工具和node的模塊系統,直到現在流行的es6 module。
模塊的引入解決了文件之間依賴引用的問題,而打包則解決了文件過多的問題。當項目規模增大,模塊的數量數以千計,瀏覽器如果要載入如此多的文件,頁面載入的速度勢必會受影響,而bundler可以把多個關聯的文件打包到一起從而大量減少文件的數量提高網頁載入性能。提供模塊化的開發方式和編譯打包功能就是webpack的核心,其他很多功能都圍繞它們展開。
核心概念
Module(模塊)
對於webpack,模塊不僅僅是javascript模塊,它包括了任何類型的源文件,不管是圖片、字體、json文件都是一個個模塊。Webpack支持以下的方式引用模塊:
- ES2015
import
方法 - CommonJs
require()
方法 - AMD
define
和require
語法 - css/sass/less文件中的 @import 語法
url(...)
和<img src=...>
中的圖片路徑
Dependency Graph(依賴關係圖)
所謂的依賴關係圖是webpack根據每個模塊之間的依賴關係遞歸生成的一張內部邏輯圖,有了這張依賴關係圖,webpack就能按圖索驥把所有需要模塊打包成一個bundle文件了。
Entry(入口)
繪製依賴關係圖的起始文件被稱為entry。預設的entry為 ./src/index.js
,或者我們可以在配置文件中配置。entry可以為一個也可以為多個。
單個entry:
module.exports = {
entry: './src/index.js'
}
或者
module.exports = {
entry: {
main: './src/index.js'
}
};
多個entry,一個chunk
我們也可以指定多個獨立的文件為entry,但將它們打包到一個chunk中,此種方法被稱為 multi-main entry,我們需要傳入文件路徑的數組:
module.exports = {
entry: ['./src/index.js', './src/index2.js', './src/index3.js']
}
但是改種方法的靈活性和擴展性有限,因此並不推薦使用。
多個entry,多個chunk
如果有多個entry,並且每個entry生成對應的chunk,我們需要傳入object:
module.exports = {
entry: {
app: './src/app.js',
admin: './src/admin.js'
}
};
這種寫法有最好的靈活性和擴展性,支持和其他的局部配置(partial configuration)進行合併。比如將開發環境和生產的配置分離,並抽離出公共的配置,在不同的環境下運行時再將環境配置和公共配置進行合併。
Output(出口)
有了入口,對應的就有出口。顧名思義,出口就是webpack打包完成的輸出,output定義了輸出的路徑和文件名稱。Webpack的預設的輸出路徑為 ./dist/main.js
。同樣,我們可以在配置文件中配置output:
module.exports = {
entry: './src/index.js',
output: {
path: __dirname + '/dist',
filename: 'bundle.js'
}
};
多個entry的情況
當有多個entry的時候,一個entry應該對應一個output,此時輸出的文件名需要使用替換符(substitutions)聲明以確保文件名的唯一性,例如使用入口模塊的名稱:
module.exports = {
entry: {
app: './src/app.js',
search: './src/search.js'
},
output: {
filename: '[name].js',
path: __dirname + '/dist'
}
}
最終在 ./dist
路徑下麵會生成 app.js
和search.js
兩個bundle文件。
Loader
Webpack自身只支持載入js和json模塊,而webpack的理念是讓所有的文件都能被引用和載入並生成依賴關係圖,所以loader出場了。Loader能讓webpack能夠去處理其他類型的文件(比如圖片、字體文件、xml)。我們可以在配置文件中這樣定義一個loader:
webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.txt$/,
use: 'raw-loader'
}
]
}
};
其中test
定義了需要被轉化的文件或者文件類型,use
定義了對該文件進行轉化的loader的類型。該條配置相當於告訴webpack當遇到一個txt文件的引用時(使用require或者import進行引用),先用raw-loader
轉換一下該文件再把它打包進bundle。
還有其他各種類型的loader,比如載入css文件的css-loader
,載入圖片和字體文件的file-loader
,載入html文件的html-loader
,將最新JS語法轉換成ES5的babel-loader
等等。完整列表請參考 webpack loaders。
Plugin(插件)
Plugin和loader是兩個比較混淆和模糊的概念。Loader是用來轉換和載入特定類型的文件,所以loader的執行層面是單個的源文件。而plugin可以實現的功能更強大,plugin可以監聽webpack處理過程中的關鍵事件,深度集成進webpack的編譯器,可以說plugin的執行層面是整個構建過程。Plugin系統是構成webpack的主幹,webpack自身也基於plugin系統搭建,webpack有豐富的內置插件和外部插件,並且允許用戶自定義插件。官方列出的插件有 這些。
與loader不同,使用plugin我們必須先引用該插件,例如:
const webpack = require('webpack'); // 用於引用webpack內置插件
const HtmlWebpackPlugin = require('html-webpack-plugin'); // 外部插件
module.exports = {
plugins: [
new webpack.HotModuleReplacementPlugin(),
new HtmlWebpackPlugin({template: './src/index.html'})
]
};
推薦大家使用Fundebug,一款很好用的BUG監控工具~
實踐
瞭解webpack的基本概念之後,我們通過實踐來加深理解。接下來,我們使用webpack搭建一個簡易的react腳手架。
創建項目
首先創建react-webpack-starter
項目並使用 npm init
初始化。
安裝依賴
安裝react
npm i react react-dom
安裝webpack相關
npm i -D webpack webpack-cli webpack-dev-server html-webpack-plugin style-loader css-loader
安裝webpack-cli
後可以在命令行中執行webpack命令;webpack-dev-server
提供了簡易的web伺服器,並且在修改文件之後自動執行webpack的編譯操作並自動刷新瀏覽器,省去了重覆的手動操作;html-webpack-plugin
用於自動生成index.html
文件,並且在index.html
中自動添加對bundle文件的引用;style-loader
和css-loader
用於載入css文件。
安裝babel相關
由於react中使用了class
, import
這樣的es6的語法,為了提高網站的瀏覽器相容性,我們需要用babel轉換一下。
npm i -D @babel/core @babel/preset-env @babel/preset-react babel-loader
其中@babel/core
是babel的核心模塊,包含了babel的核心功能;@babel/preset-env
支持轉換ES6以及更新的js語法,並且可根據需要相容的瀏覽器類型選擇載入的plugin從而精簡生成的代碼;@babel/preset-react
包含了babel轉換react所需要的plugin;babel-loader
是webpack的babel載入器。
配置webpack
在項目根目錄下麵新建webpack.config.js
,內容如下:
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_module/,
use: 'babel-loader'
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'] // 註意排列順序,執行順序與排列順序相反
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
})
]
}
其中HtmlWebpackPlugin
使用自定義的模版來生成html 文件,模版的內容如下:
./src/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>My React App</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
配置babel
在項目根目錄下麵新建.babelrc
文件,配置我們安裝的兩個babel preset:
.babelrc
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
]
}
生成react應用根節點
./src/index
import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App';
ReactDOM.render(<App />, document.getElementById('app'));
./src/component/App.js
import React, { Component } from 'react';
import './App.css';
export default class App extends Component {
render() {
return (
<div>
my react webpack starter
</div>
)
}
}
./src/components/App.css
body{
font-size: 60px;
font-weight: bolder;
color: red;
text-transform: uppercase;
}
配置 package.json
最後,在package.json
文件裡面加上兩個scripts,用來運行開發伺服器和打包:
package.json
"scripts": {
"start": "webpack-dev-server --mode development --open --hot",
"build": "webpack --mode production"
}
註意,我們啟用了webpack-dev-server
的模塊熱更新功能(HMR),進一步提高我們的開發效率。
到此一個最簡版本的react腳手架就搭建完成了,我們運行一下看看效果:
是不是沒有想象中的那麼難呢?當然webpack還有很多其他的功能和特性需要掌握,希望在參考本文之後大家進一步的學習更加順利