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

此時對項目內(nèi)任何文件進行修改均會執(zhí)行熱替換,實時展示到頁面中。

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


這樣 ES6 語法就被轉(zhuǎn)為 ES5 了, ES6 的 import 模塊引入已經(jīng)被 webpack 原生支持了,運行在瀏覽器端的可以安心使用 ES6 module 模塊方法。nodejs 尚未正式支持該語法,僅在8.5版中測試使用。
postcss-loader
PostCSS是一個使用JavaScript插件來轉(zhuǎn)換CSS的工具,是個CSS的插件集合,暫時只用到Autoprefixer解決CSS3樣式兼容性問題。
安裝命令,順手把要用到的 css-loader style-loader 一起裝了:
yarn add -D postcss-loader css-loader style-loader
或使用NPM進行安裝:
npm i -D postcss-loader css-loader style-loader
postcss 的配置可能會很多,通常都是分離出一個單文件進行配置存儲,不寫 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;
}
通過將以上三個 loader 將css源碼轉(zhuǎn)化為js代碼。

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

less-loader
less-loader能將通過less語法寫的樣式轉(zhuǎn)化成瀏覽器可讀取的樣式表,sassstylus都有其對應的loader。
安裝命令,畫一下重點,把 less 也一塊安裝,否則依然無法解析:
yarn add -D less-loader less
或使用NPM進行安裝:
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 的資源之前」有多少個 loader。在模塊系統(tǒng)(即 webpack)支持原始 loader 匹配后,此功能可能在將來會發(fā)生變化,這段翻譯很難讓人理解。
重新解釋一下
importLoaders,這里設為 2:
表明在某個less文件中@import進來的資源(其它的less文件)會被使用postcss和less這兩個loader解析,解析正確。
設為 1:
表明在某個less文件中@import進來的資源(其它的less文件)只會被使用postcss這一個loader解析,便報錯。
@green: green;
body,html{
background-color: green;
}
.flex {
display: flex;
}
less 文件經(jīng)過 loader 轉(zhuǎn)義后:

plugins
plugins插件可以以各種方式定制webpack構(gòu)建過程。
clean-webpack-plugin
每次構(gòu)建時清理文件,避免文件名帶hash時重復生成多個文件。
神圣的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 運行都會支持清理操作。
html-webpack-plugin
這是一個可以動態(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: 動態(tài)生成后的文件名,路徑以output中path的地址為參照。
chunks:entry中多入口項目可以指定本次html所需依賴的js文件,如果依賴的chunk較多可配置excludeChunks來進行排除。
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 模板語法,可以使用 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 也被動態(tài)引入了,依賴的 css 也被打包進 js 內(nèi)。
html-webpack-inline-source-plugin
這是一個依賴于
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)。

注意:本以為這個插件可以單獨將
css以<style></style>的形式打包完寫入頁面。然而并不可以,結(jié)合之前的幾個處理css的loader可知,只能將其內(nèi)聯(lián)至<script></script>中,頁面加載時動態(tài)插入至html中。
extract-text-webpack-plugin
插件能將
js中引入的css文件分離出來,不再以<style></style>的形式插入頁面,單獨以<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 被引入頁面。
<!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>
注意:這個插件存在問題,
chunk內(nèi)引入了多個css文件,分離時會合并為一個css文件。
將 extract-text-webpack-plugin 和 html-webpack-inline-source-plugin 聯(lián)合使用可以將 css 以 <style></style> 的形式打包完寫入頁面。
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 設為 .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>
注意: 此方法不會因為
css的內(nèi)聯(lián)將分離生成的.css文件清除,依然會保留,只是不會引入頁面內(nèi)。
總結(jié)
webpack 對于 css 文件的處理不太友好,分離 css 的做法也有些違背 webpack 模塊化打包的初衷,有人傾向于使用 gulp 進行 圖片 css 的資源文件的出來,而使 webpack 專注于 js 模塊化,日后會著重研究這塊。
為了寫博客翻了下有稍顯雜亂的
webpack文檔,發(fā)現(xiàn)extra-loader和html-loader之前被我選擇性忽略了,可以嘗試換新的方式處理css文件打包問題。
尾巴
第一篇技術(shù)博客磕磕絆絆的寫完了,愿自己越來越上進吧!