—— 踩坑計劃第一步
目錄一覽
webpack3.0學習筆記(一)
webpack3.0學習筆記(二)
webpack3.0學習筆記(三)
前情概要
記錄最近 webpack
的學習進展,及使用中的注意事項和踩過的坑,非新手教程歡迎指正錯誤。
PS. 當前 webpack
版本 3.6.0
。
webpack
現今許多類庫都支持
webpack
進行打包管理,vue
react
等主流MVVM
框架為其瘋狂打call。
- 模塊化,將大型項目進行分割;
- 支持
TypeScript
、CoffeeScript
、ES6
等語言特性開發的程序運行在當下的主流瀏覽器上;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
}
}
以上呈現了一個多頁面基本配置,當然圖片資源還沒有引入,后續會進行添加,暫時先分析一下現有的配置。
入口 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
,方便生產線打包上線是對用戶文件進行緩存更新。
以下是可配置參數:
[hash]
:模塊標識符(module identifier)的 hash
[chunkhash]
:chunk 內容的 hash
[name]
:模塊名稱
[id]
:模塊標識符(module identifier)
[query]
:模塊的 query,例如文件名 ? 后面的字符串
ps.單獨說一下
entry
和output
這兩個設置中的path
屬性,官方推薦設置為絕對路徑,相對路徑的寫法容易引發問題,__dirname
這個參數是nodejs
里自帶的,表示當前運行環境的絕對路徑,利用path.resolve()
轉化成一個完整的路徑,這里使用path.resolve
還是path.join
看個人了。
模塊熱替換 Hot Module Replacement 配置
用webpack不就為了它 npm上有個 live-server
模塊也能實現文件的熱更新,挺適合非 webpack
項目使用的(不是打廣告)。
這里需要兩個步驟開啟模塊熱替換功能:
- 第一步
增加webpack.config.js
配置,配置本地服務器:
module.exports = {
...
devServer: {
contentBase: './dist',
historyApiFallback: true,
hot: true,
inline: true
}
}
contentBase
: 告訴服務器從哪里提供內容。只有在你想要提供靜態文件時才需要。devServer.publicPath
將用于確定應該從哪里提供 bundle,并且此選項優先。
historyApiFallback
: 當使用 HTML5 History API 時,任意的 404 響應都可能需要被替代為 index.html。還可傳入對象進一步控制,不多贅述。
hot
: 是否開啟熱替換,毋庸置疑質疑的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"
},
}
在項目根目錄執行 npm-run-server
即可,輸出如下圖并無紅字報錯即成功了。
此時對項目內任何文件進行修改均會執行熱替換,實時展示到頁面中。
ps. 不推薦在
GitBash
中使用live-server
webpack熱替換
等,結束后node進程不會自動關閉,一直占用端口還特別卡,得手動到進程管理器里關閉換macOS啊。
loader
webpack 可以使用 loader 來預處理文件。這允許你打包除 JavaScript 之外的任何靜態資源。
babel-loader
Babel 是一個 JavaScript 編譯器,能將
ES2015
、ES2016
、ES2017
的語法轉義為現代瀏覽器可執行的代碼,所以盡情使用最新的語法特性書寫你的代碼吧!
首先需要安裝babel相關依賴,這里推薦使用 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
相關的內容了。
根目錄下新建 .babelrc
文件,內容如下:
{
"presets": ["es2015"]
}
簡單的配置一下兼容 es2015
,當然也可以加入 es2016
、es2017
的支持。
這樣 ES6
語法就被轉為 ES5
了, ES6
的 import
模塊引入已經被 webpack
原生支持了,運行在瀏覽器端的可以安心使用 ES6
module
模塊方法。nodejs
尚未正式支持該語法,僅在8.5版中測試使用。
postcss-loader
PostCSS
是一個使用JavaScript插件來轉換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
相關的內容了。
根目錄下新建 postcss.config.js
文件,內容如下:
module.exports={
plugins: [
require('autoprefixer')
]
}
需要注意的是在相關的 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
轉義CSS文件 、style-loader
轉義成js代碼。
html,body{
background-color: pink;
}
.div {
display: flex;
}
通過將以上三個 loader
將css源碼轉化為js代碼。
js代碼執行后將css樣式包裹在 style
標簽內插入至頁面中。
less-loader
less-loader
能將通過less
語法寫的樣式轉化成瀏覽器可讀取的樣式表,sass
stylus
都有其對應的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
參數方法解釋是:用于配置「css-loader 作用于 @import 的資源之前」有多少個 loader。在模塊系統(即 webpack)支持原始 loader 匹配后,此功能可能在將來會發生變化,這段翻譯很難讓人理解。
重新解釋一下
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
文件經過 loader
轉義后:
plugins
plugins
插件可以以各種方式定制webpack
構建過程。
clean-webpack-plugin
每次構建時清理文件,避免文件名帶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'])
}
}
傳遞的參數是需要清理的文件路徑,每次 webpack
運行都會支持清理操作。
html-webpack-plugin
這是一個可以動態生成
html
文件的插件,配合各種loader
可以將處理好的css
js
image
引入到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
: 動態生成后的文件名,路徑以output
中path
的地址為參照。
chunks
:entry
中多入口項目可以指定本次html
所需依賴的js
文件,如果依賴的chunk
較多可配置excludeChunks
來進行排除。
title
: 綁定在htmlWebpackPlugin.options
上的參數,可在模板內使用。
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
變為了傳入的值,js
也被動態引入了,依賴的 css
也被打包進 js
內。
html-webpack-inline-source-plugin
這是一個依賴于
html-webpack-plugin
的插件,目的是將js
和css
文件內聯至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
都內聯。
注意:本以為這個插件可以單獨將
css
以<style></style>
的形式打包完寫入頁面。然而并不可以,結合之前的幾個處理css
的loader
可知,只能將其內聯至<script></script>
中,頁面加載時動態插入至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
內引入了多個css
文件,分離時會合并為一個css
文件。
將 extract-text-webpack-plugin
和 html-webpack-inline-source-plugin
聯合使用可以將 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
的內聯將分離生成的.css
文件清除,依然會保留,只是不會引入頁面內。
總結
webpack
對于 css
文件的處理不太友好,分離 css
的做法也有些違背 webpack
模塊化打包的初衷,有人傾向于使用 gulp
進行 圖片
css
的資源文件的出來,而使 webpack
專注于 js
模塊化,日后會著重研究這塊。
為了寫博客翻了下有稍顯雜亂的
webpack
文檔,發現extra-loader
和html-loader
之前被我選擇性忽略了,可以嘗試換新的方式處理css
文件打包問題。
尾巴
第一篇技術博客磕磕絆絆的寫完了,愿自己越來越上進吧!