—— 踩坑計(jì)劃第一步
目錄一覽
webpack3.0學(xué)習(xí)筆記(一)
webpack3.0學(xué)習(xí)筆記(二)
webpack3.0學(xué)習(xí)筆記(三)
前情概要
記錄最近 webpack 的學(xué)習(xí)進(jìn)展,及使用中的注意事項(xiàng)和踩過(guò)的坑,非新手教程歡迎指正錯(cuò)誤。
PS. 當(dāng)前 webpack版本 3.6.0。
webpack
現(xiàn)今許多類(lèi)庫(kù)都支持
webpack進(jìn)行打包管理,vuereact等主流MVVM框架為其瘋狂打call。
- 模塊化,將大型項(xiàng)目進(jìn)行分割;
- 支持
TypeScript、CoffeeScript、ES6等語(yǔ)言特性開(kāi)發(fā)的程序運(yùn)行在當(dāng)下的主流瀏覽器上;scss、less、stylus等CSS預(yù)處理語(yǔ)言;- ...
webpck配置概覽
const path = require('path');
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const HtmlWebpackInlineSourcePlugin = require('html-webpack-inline-source-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin')
module.exports = {
// devtool: 'source-map',
entry: {
main: path.resolve(__dirname,'src/script/main.js'),
aa: path.resolve(__dirname,'src/script/aa.js'),
test: path.resolve(__dirname,'src/script/test.js')
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'js/[name].js'
},
module: {
rules: [
{
test: /\.js$/,
use: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.less$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
'css-loader',
'postcss-loader',
'less-loader'
]
}),
exclude: [/aa.less$/]
},
{
test: /\.less$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
'css-loader',
'postcss-loader',
'less-loader'
]
}),
include: [/aa.less$/]
},
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1
}
},
'postcss-loader',
]
}
]
},
plugins: [
new CleanWebpackPlugin(['dist']),
new ExtractTextPlugin('css/[name].css'),
new HtmlWebpackPlugin({
template: path.resolve(__dirname,'src/index.html'),
filename: 'index.html',
chunks:['main']
}),
new HtmlWebpackPlugin({
template: path.resolve(__dirname,'src/aa.html'),
filename: 'aa.html',
chunks: ['aa'],
inlineSource: '.css$'
}),
new HtmlWebpackPlugin({
template: path.resolve(__dirname,'src/test.html'),
filename: 'test.html',
chunks: ['test'],
inlineSource: '.(js|css)$'
}),
new HtmlWebpackInlineSourcePlugin(),
new webpack.HotModuleReplacementPlugin()
],
devServer: {
contentBase: './dist',
historyApiFallback: true,
hot: true,
inline: true
}
}
以上呈現(xiàn)了一個(gè)多頁(yè)面基本配置,當(dāng)然圖片資源還沒(méi)有引入,后續(xù)會(huì)進(jìn)行添加,暫時(shí)先分析一下現(xiàn)有的配置。
入口 entry 及輸出 output 配置
- 單入口可配置為:
entry: 'path.resolve(__dirname,'src/script/main.js')'
或
entry: 'path.resolve([__dirname,'src/script/main.js' , __dirname,'src/script/aa.js'])'
- 而多入口配置為:
entry: {
main: path.resolve(__dirname,'src/script/main.js'),
aa: path.resolve(__dirname,'src/script/aa.js'),
test: path.resolve(__dirname,'src/script/test.js')
},
其中 main aa test 都表示為chunk name,即可將多個(gè)入口文件合并成一個(gè) chunk 進(jìn)行合并打包。
- 多入口對(duì)應(yīng)的輸出
output為:
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'js/[name].js'
},
filename 若是單入口可設(shè)為 js/bundle.js ,多入口則需配置為 js/[name].js 保留原有文件名,也可加入hash及chunkhash進(jìn)行命名: js/[name]-[hash].js 、js/[name]-[hash].js ,方便生產(chǎn)線打包上線是對(duì)用戶(hù)文件進(jìn)行緩存更新。
以下是可配置參數(shù):
[hash]:模塊標(biāo)識(shí)符(module identifier)的 hash
[chunkhash]:chunk 內(nèi)容的 hash
[name]:模塊名稱(chēng)
[id]:模塊標(biāo)識(shí)符(module identifier)
[query]:模塊的 query,例如文件名 ? 后面的字符串
ps.單獨(dú)說(shuō)一下
entry和output這兩個(gè)設(shè)置中的path屬性,官方推薦設(shè)置為絕對(duì)路徑,相對(duì)路徑的寫(xiě)法容易引發(fā)問(wèn)題,__dirname這個(gè)參數(shù)是nodejs里自帶的,表示當(dāng)前運(yùn)行環(huán)境的絕對(duì)路徑,利用path.resolve()轉(zhuǎn)化成一個(gè)完整的路徑,這里使用path.resolve還是path.join看個(gè)人了。
模塊熱替換 Hot Module Replacement 配置
用webpack不就為了它 npm上有個(gè) live-server 模塊也能實(shí)現(xiàn)文件的熱更新,挺適合非 webpack 項(xiàng)目使用的(不是打廣告)。
這里需要兩個(gè)步驟開(kāi)啟模塊熱替換功能:
- 第一步
增加webpack.config.js配置,配置本地服務(wù)器:
module.exports = {
...
devServer: {
contentBase: './dist',
historyApiFallback: true,
hot: true,
inline: true
}
}
contentBase: 告訴服務(wù)器從哪里提供內(nèi)容。只有在你想要提供靜態(tài)文件時(shí)才需要。devServer.publicPath
將用于確定應(yīng)該從哪里提供 bundle,并且此選項(xiàng)優(yōu)先。
historyApiFallback: 當(dāng)使用 HTML5 History API 時(shí),任意的 404 響應(yīng)都可能需要被替代為 index.html。還可傳入對(duì)象進(jìn)一步控制,不多贅述。
hot: 是否開(kāi)啟熱替換,毋庸置疑質(zhì)疑的true。
inline: 將消息輸出至控制臺(tái)還是iframe的選項(xiàng),開(kāi)啟熱替換推薦true。
- 第二步
在webpack.config.js中配置熱更新插件:
const webpack = require('webpack')
module.exports = {
...
plugins: {
...
new webpack.HotModuleReplacementPlugin()
}
}
再到 package.json 中配置:
{
"scripts": {
"server": "webpack-dev-server --config webpack.config.js --open"
},
}
在項(xiàng)目根目錄執(zhí)行 npm-run-server即可,輸出如下圖并無(wú)紅字報(bào)錯(cuò)即成功了。

此時(shí)對(duì)項(xiàng)目?jī)?nèi)任何文件進(jìn)行修改均會(huì)執(zhí)行熱替換,實(shí)時(shí)展示到頁(yè)面中。

ps. 不推薦在
GitBash中使用live-serverwebpack熱替換等,結(jié)束后node進(jìn)程不會(huì)自動(dòng)關(guān)閉,一直占用端口還特別卡,得手動(dòng)到進(jìn)程管理器里關(guān)閉換macOS啊。
loader
webpack 可以使用 loader 來(lái)預(yù)處理文件。這允許你打包除 JavaScript 之外的任何靜態(tài)資源。
babel-loader
Babel 是一個(gè) JavaScript 編譯器,能將
ES2015、ES2016、ES2017的語(yǔ)法轉(zhuǎn)義為現(xiàn)代瀏覽器可執(zhí)行的代碼,所以盡情使用最新的語(yǔ)法特性書(shū)寫(xiě)你的代碼吧!
首先需要安裝babel相關(guān)依賴(lài),這里推薦使用 yarn 進(jìn)行包依賴(lài)管理:
yarn add -D babel-loader babel-core babel-preset-env
或使用NPM進(jìn)行安裝:
npm i -D babel-loader babel-core babel-preset-env
babel 的配置寫(xiě)在 webpack.config.js 中:
module.exports = {
...
module: {
rules: [
{
test: /\.js$/,
use: 'babel-loader',
exclude: /node_modules/
}
}
}
采用 loader 的普遍配置,寫(xiě)在 rules 里。
test: 書(shū)寫(xiě)匹配文件的正則表達(dá)式。
use: 對(duì)匹配到的文件使用的loader。
exclude: 需要排除不處理的文件,首要排除的就是/node_modules/文件夾,加快打包效率。
include: 指定匹配的文件或目錄。
babel 的配置可能會(huì)很多,通常都是分離出一個(gè)單文件進(jìn)行配置存儲(chǔ),不寫(xiě) cli 相關(guān)的內(nèi)容了。
根目錄下新建 .babelrc文件,內(nèi)容如下:
{
"presets": ["es2015"]
}
簡(jiǎn)單的配置一下兼容 es2015,當(dāng)然也可以加入 es2016、es2017的支持。


這樣 ES6 語(yǔ)法就被轉(zhuǎn)為 ES5 了, ES6 的 import 模塊引入已經(jīng)被 webpack 原生支持了,運(yùn)行在瀏覽器端的可以安心使用 ES6 module 模塊方法。nodejs 尚未正式支持該語(yǔ)法,僅在8.5版中測(cè)試使用。
postcss-loader
PostCSS是一個(gè)使用JavaScript插件來(lái)轉(zhuǎn)換CSS的工具,是個(gè)CSS的插件集合,暫時(shí)只用到Autoprefixer解決CSS3樣式兼容性問(wèn)題。
安裝命令,順手把要用到的 css-loader style-loader 一起裝了:
yarn add -D postcss-loader css-loader style-loader
或使用NPM進(jìn)行安裝:
npm i -D postcss-loader css-loader style-loader
postcss 的配置可能會(huì)很多,通常都是分離出一個(gè)單文件進(jìn)行配置存儲(chǔ),不寫(xiě) cli 相關(guān)的內(nèi)容了。
根目錄下新建 postcss.config.js文件,內(nèi)容如下:
module.exports={
plugins: [
require('autoprefixer')
]
}
需要注意的是在相關(guān)的 js 文件中引用 css。
import '../style/test.css'
在 webpack.config.js 配置中添加:
module.exports={
...
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader', 'postcss-loader']
}
]
}
}
loader 的加載順序是從右至左,依次 postcss-loader 添加瀏覽器前綴、css-loader轉(zhuǎn)義CSS文件 、style-loader轉(zhuǎn)義成js代碼。
html,body{
background-color: pink;
}
.div {
display: flex;
}
通過(guò)將以上三個(gè) loader 將css源碼轉(zhuǎn)化為js代碼。

js代碼執(zhí)行后將css樣式包裹在 style 標(biāo)簽內(nèi)插入至頁(yè)面中。

less-loader
less-loader能將通過(guò)less語(yǔ)法寫(xiě)的樣式轉(zhuǎn)化成瀏覽器可讀取的樣式表,sassstylus都有其對(duì)應(yīng)的loader。
安裝命令,畫(huà)一下重點(diǎn),把 less 也一塊安裝,否則依然無(wú)法解析:
yarn add -D less-loader less
或使用NPM進(jìn)行安裝:
npm i -D less-loader less
在 webpack.config.js 配置中添加:
module.exports={
...
module: {
rules: [
{
test: /\.less$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 1
}
},
'postcss-loader',
'less-loader'
]
}
]
}
}
css-loader的importLoaders參數(shù)方法解釋是:用于配置「css-loader 作用于 @import 的資源之前」有多少個(gè) loader。在模塊系統(tǒng)(即 webpack)支持原始 loader 匹配后,此功能可能在將來(lái)會(huì)發(fā)生變化,這段翻譯很難讓人理解。
重新解釋一下
importLoaders,這里設(shè)為 2:
表明在某個(gè)less文件中@import進(jìn)來(lái)的資源(其它的less文件)會(huì)被使用postcss和less這兩個(gè)loader解析,解析正確。
設(shè)為 1:
表明在某個(gè)less文件中@import進(jìn)來(lái)的資源(其它的less文件)只會(huì)被使用postcss這一個(gè)loader解析,便報(bào)錯(cuò)。
@green: green;
body,html{
background-color: green;
}
.flex {
display: flex;
}
less 文件經(jīng)過(guò) loader 轉(zhuǎn)義后:

plugins
plugins插件可以以各種方式定制webpack構(gòu)建過(guò)程。
clean-webpack-plugin
每次構(gòu)建時(shí)清理文件,避免文件名帶hash時(shí)重復(fù)生成多個(gè)文件。
神圣的rm -r XX連接著我們
yarn add -D clean-webpack-plugin
npm i -D clean-webpack-plugin
在 webpack.config.js配置中增加:
const CleanWebpackPlugin = require('clean-webpack-plugin')
module.exports = {
...
plugins: {
...
new CleanWebpackPlugin(['dist'])
}
}
傳遞的參數(shù)是需要清理的文件路徑,每次 webpack 運(yùn)行都會(huì)支持清理操作。
html-webpack-plugin
這是一個(gè)可以動(dòng)態(tài)生成
html文件的插件,配合各種loader可以將處理好的cssjsimage引入到html中。
yarn add -D html-webpack-plugin
npm i -D html-webpack-plugin
在 webpack.config.js配置中增加:
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
...
plugins: {
...
new HtmlWebpackPlugin({
template: path.resolve(__dirname,'src/index.html'),
filename: 'index.html',
chunks:['main'],
title: 'this is main.html'
}),
}
}
template: 所需讀取的index模板文件。
filename: 動(dòng)態(tài)生成后的文件名,路徑以output中path的地址為參照。
chunks:entry中多入口項(xiàng)目可以指定本次html所需依賴(lài)的js文件,如果依賴(lài)的chunk較多可配置excludeChunks來(lái)進(jìn)行排除。
title: 綁定在htmlWebpackPlugin.options上的參數(shù),可在模板內(nèi)使用。
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><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
</body>
</html>
<%= htmlWebpackPlugin.options.title %> 為 ejs 模板語(yǔ)法,可以使用 htmlWebpackPlugin 上的各種屬性定制自己的 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>this is main.html</title>
</head>
<body>
<script type="text/javascript" src="js/main.js"></script></body>
</html>
title 變?yōu)榱藗魅氲闹担?code>js 也被動(dòng)態(tài)引入了,依賴(lài)的 css 也被打包進(jìn) js 內(nèi)。
html-webpack-inline-source-plugin
這是一個(gè)依賴(lài)于
html-webpack-plugin的插件,目的是將js和css文件內(nèi)聯(lián)至html,
yarn add -D html-webpack-inline-source-plugin
npm i -D html-webpack-inline-source-plugin
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CleanWebpackPlugin = require('html-webpack-inline-source-plugin')
module.exports = {
...
plugins: {
...
new HtmlWebpackPlugin({
template: path.resolve(__dirname,'src/index.html'),
filename: 'index.html',
chunks:['main'],
inlineSource: '.(js|css)$',
title: 'this is main.html'
}),
new HtmlWebpackInlineSourcePlugin()
}
}
HtmlWebpackPlugin 配置中插入 inlineSource: '.(js|css)$' 配置,將 js 和 css 都內(nèi)聯(lián)。

注意:本以為這個(gè)插件可以單獨(dú)將
css以<style></style>的形式打包完寫(xiě)入頁(yè)面。然而并不可以,結(jié)合之前的幾個(gè)處理css的loader可知,只能將其內(nèi)聯(lián)至<script></script>中,頁(yè)面加載時(shí)動(dòng)態(tài)插入至html中。
extract-text-webpack-plugin
插件能將
js中引入的css文件分離出來(lái),不再以<style></style>的形式插入頁(yè)面,單獨(dú)以<link>的方式引入。
yarn add -D extract-text-webpack-plugin
npm i -D extract-text-webpack-plugin
在 webpack.config.js配置中增加:
const ExtractTextPlugin = require('extract-text-webpack-plugin')
module.exports = {
...
module: {
rules: [
{
test: /\.less$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
'css-loader',
'postcss-loader',
'less-loader'
]
})
}
]
},
plugins: {
...
new ExtractTextPlugin('css/[name].css'),
new HtmlWebpackPlugin({
template: path.resolve(__dirname,'src/index.html'),
filename: 'index.html',
chunks:['main'],
title: 'this is main.html'
})
}
}
重新打包后 main.css 被引入頁(yè)面。
<!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>this is main.html</title>
<link href="css/main.css" rel="stylesheet"></head>
<body>
<script type="text/javascript" src="js/main.js"></script></body>
</html>
注意:這個(gè)插件存在問(wèn)題,
chunk內(nèi)引入了多個(gè)css文件,分離時(shí)會(huì)合并為一個(gè)css文件。
將 extract-text-webpack-plugin 和 html-webpack-inline-source-plugin 聯(lián)合使用可以將 css 以 <style></style> 的形式打包完寫(xiě)入頁(yè)面。
const HtmlWebpackInlineSourcePlugin = require('html-webpack-inline-source-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
module.exports = {
...
module: {
rules: [
{
test: /\.less$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
'css-loader',
'postcss-loader',
'less-loader'
]
})
}
]
},
plugins: {
...
new ExtractTextPlugin('css/[name].css'),
new HtmlWebpackPlugin({
template: path.resolve(__dirname,'src/index.html'),
filename: 'aa.html',
chunks: ['aa'],
inlineSource: '.css$',
title: 'this is aa.html'
})
}
}
將 inlineSource 設(shè)為 .css$。
<!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>this is aa.html</title>
<style type="text/css">body,
html {
background-color: gray;
}
</style></head>
<body>
<script type="text/javascript" src="js/aa.js"></script></body>
</html>
注意: 此方法不會(huì)因?yàn)?
css的內(nèi)聯(lián)將分離生成的.css文件清除,依然會(huì)保留,只是不會(huì)引入頁(yè)面內(nèi)。
總結(jié)
webpack 對(duì)于 css 文件的處理不太友好,分離 css 的做法也有些違背 webpack 模塊化打包的初衷,有人傾向于使用 gulp 進(jìn)行 圖片 css 的資源文件的出來(lái),而使 webpack 專(zhuān)注于 js 模塊化,日后會(huì)著重研究這塊。
為了寫(xiě)博客翻了下有稍顯雜亂的
webpack文檔,發(fā)現(xiàn)extra-loader和html-loader之前被我選擇性忽略了,可以嘗試換新的方式處理css文件打包問(wèn)題。
尾巴
第一篇技術(shù)博客磕磕絆絆的寫(xiě)完了,愿自己越來(lái)越上進(jìn)吧!