前言
說是解決方案實際上更像是webpack的插件索引。
寫這一篇的目的是為了形成一個索引,將來要用時直接來查找即可。
索引目錄
- 自動構建HTML,可壓縮空格,可給引用的js加版本號或隨機數:html-webpack-plugin
- 處理CSS:css-loader與style-loader
- 處理LESS:less-loade與less
- 提取css代碼到css文件中: extract-text-webpack-plugin
- 開發環境下的服務器搭建:webpack-dev-server
- 解析ES6代碼:babel-core babel-preset-env babel-loader
- 解析ES6新增的對象函數:babel-polyfill
- 解析react的jsx語法:babel-preset-react
- 轉換相對路徑到絕度路徑:nodejs的path模塊
- 給文件加上hash值:[chunkhash],[hash]
- 清空輸出文件夾之前的輸出文件:clean-webpack-plugin
- 模塊熱替換:NamedModulesPlugin和HotModuleReplacementPlugin
- 環境變量
- 跨平台使用環境變量: cross-env
- 處理圖片路徑: file-loader和html-loader
- 圖片壓縮:image-webpack-loader
- 定位源文件代碼:source-map
- 分離生產環境和開發環境的配置文件
- webpack輸出文件體積與交互關系的可視化:webpack-bundle-analyzer
1.自動構建HTML,可壓縮空格,可給引用的js加版本號或隨機數:html-webpack-plugin
解決方案:使用插件 html-webpack-plugin
webpack.config.js如下:
module.exports = {
entry: './src/app.js',
output: {
path: __dirname + '/dist',
filename: 'app.bundle.js'
},
plugins: [new HtmlWebpackPlugin({
template: './src/模板文件.html',
filename: '構建的.html',
minify: {
collapseWhitespace: true,
},
hash: true,
})]
};
注意要有path,因為這個輸出的html需要知道輸出目錄
2.處理CSS:css-loader與style-loader
loader用於對模塊的源代碼進行預處理轉換。
解決方案:使用css-loader,style-loader
看一下項目結構:
此時運行webpack命令會拋出錯誤:
接下來安裝 css-loader 和 style-loader
npm install --save-dev css-loader style-loader
再修改webpack.config.js為:
這其中rules數組就是loader用來的匹配和轉換資源的規則數組。
test代表匹配需轉換文件的正則表達式,而圖中表示匹配所有以css結尾的文件。
而use數組代表用哪些loader去處理這些匹配到的文件。
此時再運行webpack,打包后的文件bundle.js就包含了css代碼。
其中css-loader負責加載css,打包css到js中。
而style-loader負責生成:在js運行時,將css代碼通過style標簽注入到dom中。
3.處理LESS:less-loade與less
解決方案:使用less-loader
但是用less-loader只是將LESS代碼轉換為css代碼。如果要打包文件到js中,還是需要用到上面提到的css-loader和style-loader。
看一下項目結構:
然后app.js的代碼為:
import styles from './app.less';
console.info('我是一個js文件123')
為了解決這種情況,首先要安裝 less-loader,而less-loader是基於less的,所以也要安裝less。
npm i --save-dev less less-loader
修改webpack.config.js為:
module: {
rules: [
{
test: /\.less$/,
use: [ 'style-loader', 'css-loader', 'less-loader' ]
}
]
}
4.提取css代碼到css文件中: extract-text-webpack-plugin
很多時候我們想要的效果並不是想要把幾個LESS或者CSS處理好后,打包到一個js中,而是想要把它打包到一個css文件中。
此時就有了插件 extract-text-webpack-plugin。
首先進行安裝
npm i --save-dev extract-text-webpack-plugin
然后修改webpack.config.js為:
與原配置對比可以發現,比html-webpack-plugin這個插件多做了一步,就是在匹配和轉換規則里面的use中使用了ExtractTextPlugin.extract。
注意這里的fallback表示,在提取文件失敗后,將繼續使用style-loader去打包到js中。
此時運行webpack
可以發現輸出目錄build下生成了一個style.css文件,也就是我們在webpack.config.js中期望生成的文件,並且在生成的demo.html中被引用了。
5.開發環境下的服務器搭建:webpack-dev-server
webpack-dev-server可以在本地搭建一個簡單的開發環境用的服務器,自動打開瀏覽器,而且還可以達到webpack -watch的效果。
首先安裝一下:
npm i -g webpack-dev-server
npm i --save-dev webpack-dev-server
這里不需要改動webpack.config.js,直接運行命令
webpack-dev-server
查看控制台輸出:
顯示項目運行在http://localhost:8080/
webpack的輸出目錄的路徑在/下面
並且這個服務器會自動識別輸出目錄下名為index的HTML文件,而我們之前輸出的文件名為demo.html。
所以還需要將之前html-webpack-plugin中配置的filename改為index.html,或者直接用http://localhost:8080/demo.html也行。
當我們修改了源代碼后,打開的網頁還會自動更新。
為了更靈活的應用開發環境的服務器,也可以在webpack.config.js中加入如下代碼:
devServer配置 | 功能 |
---|---|
port | 修改端口為8787,而不是默認的8080。 |
open | 為true表示會自動打開瀏覽器,而不是需要我們再手動打開瀏覽器並在里面輸入http://localhost:8080。 |
compress | 對本地server返回的文件提供gzip壓縮 |
index | 指定網站首頁映射的文件,默認為index.html |
6.解析ES6代碼:babel-core babel-preset-env babel-loader
這里說是ES6,實際上可以認為是ECMAScript的高版本代碼,只是代指而已。
babel的作用是將瀏覽器還未支持的這些高版本js代碼轉換成可以被指定瀏覽器支持的js代碼。
這里列出可以轉換的大致語法:
那么首先就需要安裝babel
npm install babel-core babel-preset-env --save-dev
然后,為了和webpack結合起來,要用到babel-loader
npm install babel-loader --save-dev
然后在webpack.config.js的rules數組中增加以下代碼:
{
test: /\.js$/,
exclude: /(node_modules)/,
use: {
loader: 'babel-loader',
options: {
presets: ['env']
}
}
}
這行代碼的意思是用babel-loader解析除了node_modules文件下的所有js文件。
而babel-loader就是用babel去解析js代碼。
options的內容類似於.babelrc文件的配置,有了這個就不需要.babelrc文件了。
presets表示預處理器,現在的babel不像以前需要很多預處理器了,只需要env這一個就夠了。
修改之前的app.js中的代碼為:
console.info('我是一個js文件123')
const doSomething=() => {
console.info('do do do')
}
使用webpack命令后,可以看到我們最后的打包js文件中代碼變成了這樣:
7.解析ES6新增的對象函數:babel-polyfill
以下為這些新增函數:
安裝:
npm install --save-dev babel-polyfill
為了確保babel-polyfill被最先加載和解析,所以一般都是講babel-polyfill在最開始的腳本中引入。
而在webpack中,就是在放到entry中,所以需要修改webpack.config.js中的配置為:
8.解析react的jsx語法:babel-preset-react
安裝
npm install --save-dev babel-preset-react
配置:
這里是匹配所有以js或者jsx結尾的文件,並用 babel-preset-env和babel-preset-react進行解析
9.轉換相對路徑到絕度路徑:nodejs的path模塊
這里首先介紹一下nodejs的path模塊的一個功能:resolve。
將相對路徑轉換為絕對路徑。
在最開始引用path模塊
var path = require('path');
然后可以在輸出設置那里修改代碼為:
output: {
path: path.resolve(__dirname, 'build'),
filename: 'bundle.js'
},
和我們原來的代碼沒有任何區別。
10.給文件加上hash值:[chunkhash],[hash]
hash和chunkhash有區別,hash的話輸出的文件用的都是同一個hash值,而chunkhash的話是根據模塊來計算的,每個輸出文件的hash值都不一樣。
直接將輸出文件改為
output: {
path: path.resolve(__dirname, 'build'),
filename: 'bundle.[chunkhash].js'
},
[chunkhash]就代表一串隨機的hash值
11.清空輸出文件夾之前的輸出文件:clean-webpack-plugin
當我們像上面一樣不斷改變輸出文件時,之前的輸出文件並沒有去掉。
為了解決這個問題就需要clean-webpack-plugin。
首先安裝
npm i clean-webpack-plugin --save-dev
然后引用插件,並聲明每次生成輸出需要清空的文件夾
var CleanWebpackPlugin = require('clean-webpack-plugin');
var pathsToClean = [
'build',
]
再在插件配置中加入:
new CleanWebpackPlugin(pathsToClean)
12.模塊熱替換:NamedModulesPlugin和HotModuleReplacementPlugin
之前的webpack-dev-server提供了監聽功能,只要代碼改變,瀏覽器就會刷新。
但是模塊熱替換是不會刷新瀏覽器,只刷新修改到的那部分模塊。
模塊熱替換無需安裝。
首先需要引入模塊
var webpack = require('webpack')
其實插件中加入:
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin()
此時運行webpack可能會報錯,我們需要把之前在輸出環境中寫的[chunkhash]改為[hash]
13.環境變量
可以在腳本中這么寫:
"scripts": {
"dev": "webpack-dev-server",
"prod": "set NODE_ENV=production && webpack -p"
},
這樣在webpack.config.js中這樣修改上面的東西:
if (isProduction) {
config.output.filename = 'bundle.[chunkhash].js'
} else {
config.plugins.push(new webpack.NamedModulesPlugin())
config.plugins.push(new webpack.HotModuleReplacementPlugin())
}
這樣就可以根據環境的不同來運行不同的配置
14.跨平台使用環境變量: cross-env
上述設置環境變量的腳本中只有在window下才有效,在linux和mac上需要使用
"prod": "NODE_ENV=production webpack -p"
為了解決這個問題,使得不同平台的人能公用一套代碼,我們可以使用cross-env。
首先進行安裝:
npm i --save-dev cross-env
然后命令直接使用類似於mac上的用法即可
"prod": "cross-env NODE_ENV=production webpack -p"
15.處理圖片路徑: file-loader和html-loader
file-loader可以用來處理圖片和字體文件在css文件中的路徑問題,輸出的css文件中會引用輸出的文件地址。
html-loader可以用來處理html中,比如img元素的圖片路徑問題。
首先安裝
npm i --save-dev file-loader html-loader
配置:
{
test: /\.(gif|png|jpe?g|svg)$/i,
use: {
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'src/images/'
}
}
},
{
test: /\.html$/,
use: [{
loader: 'html-loader',
options: {
minimize: true
}
}],
}
16.圖片壓縮:image-webpack-loader
安裝:
npm i --save-dev image-webpack-loader
配置:
{
test: /\.(gif|png|jpe?g|svg)$/i,
use: [{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/'
}
},
{
loader: 'image-webpack-loader',
options: {
bypassOnDebug: true,
}
}
]
},
這里的options中也可以具體配置各個圖片類型的壓縮質量
17.定位源文件代碼:source-map
如果我們用web-dev-server運行我們的輸出文件,發現其中有些BUG,然后打開開發者工具取定位文件的時候,只會定位到我們的輸出文件。
而這些輸出文件是經過處理的,我們只有找到我們的源文件代碼,然后進行相應的修改才能解決問題。
於是這里我們需要用到source-map。
很簡單,在webpack.config.js中加入如下配置即可:
devtool: 'source-map',
就這么簡單,還不需要安裝什么插件。
但是這只對js有效,如果我們的css出現錯誤了呢,答案就是如下配置:
18.分離生產環境和開發環境的配置文件
之前我們通過在命令中設置環境變量,並且通過環境變量來判斷環境來進行不同的配置。
現在我們用官方推薦的方法來分離生產環境和開發環境的配置文件。
我們將webpack.config.js分為三個文件
- webpack.common.js
- webpack.dev.js
- webpack.prod.js
其中webpack.common.config.js為生產環境和開發環境共有的配置,dev為開發環境獨有的配置,prod為生成環境獨有的配置。
而想要合成真正的配置文件,還需要一個工具:webpack-merge。
npm install --save-dev webpack-merge
以下是我們之前的webpack.config.js代碼:
var ExtractTextPlugin = require('extract-text-webpack-plugin')
var HtmlWebpackPlugin = require('html-webpack-plugin')
var CleanWebpackPlugin = require('clean-webpack-plugin')
var path = require('path')
var webpack = require('webpack')
var pathsToClean = [
'build',
]
var isProduction = process.env.NODE_ENV === 'production'
var config = {
entry: ['babel-polyfill', './src/app.js'],
output: {
path: path.resolve(__dirname, 'build'),
filename: '[name].[hash].js'
},
devtool: 'source-map',
devServer: {
port: 8787,
open: true,
compress: true,
index: 'demo.html'
},
plugins: [
new HtmlWebpackPlugin({
template: './template/index.html',
filename: 'demo.html',
minify: {
collapseWhitespace: true,
},
hash: true
}),
new ExtractTextPlugin({ filename: 'style.css', allChunks: false }),
new CleanWebpackPlugin(pathsToClean)
],
module: {
rules: [{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: ['css-loader?sourceMap']
})
},
{
test: /\.less$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: ['css-loader?sourceMap', 'less-loader?sourceMap']
})
},
{
test: /\.jsx?$/,
exclude: /(node_modules)/,
use: {
loader: 'babel-loader',
options: {
presets: ['env', 'react']
}
}
},
{
test: /\.(gif|png|jpe?g|svg)$/i,
use: [{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/'
}
},
{
loader: 'image-webpack-loader',
options: {
bypassOnDebug: true,
}
}
]
},
{
test: /\.html$/,
use: [{
loader: 'html-loader',
options: {
minimize: true
}
}],
}
]
}
};
if (isProduction) {
config.output.filename = '[name].[chunkhash].js'
} else {
config.plugins.push(new webpack.NamedModulesPlugin())
config.plugins.push(new webpack.HotModuleReplacementPlugin())
}
module.exports = config
接下來分為三個文件,webpack.common.js:
var ExtractTextPlugin = require('extract-text-webpack-plugin')
var HtmlWebpackPlugin = require('html-webpack-plugin')
var CleanWebpackPlugin = require('clean-webpack-plugin')
var path = require('path')
var webpack = require('webpack')
var pathsToClean = [
'build',
]
var isProduction = process.env.NODE_ENV === 'production'
module.exports = {
entry: ['babel-polyfill', './src/app.js'],
output: {
path: path.resolve(__dirname, 'build'),
filename: '[name].[chunkhash].js'
},
plugins: [
new HtmlWebpackPlugin({
template: './template/index.html',
filename: 'demo.html',
minify: {
collapseWhitespace: true,
},
hash: isProduction
}),
new ExtractTextPlugin({ filename: '[name].[contenthash].css', allChunks: false }),
new CleanWebpackPlugin(pathsToClean)
],
module: {
rules: [{
test: /\.jsx?$/,
exclude: /(node_modules)/,
use: {
loader: 'babel-loader',
options: {
presets: ['env', 'react']
}
}
},
{
test: /\.(gif|png|jpe?g|svg)$/i,
use: [{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'images/'
}
},
{
loader: 'image-webpack-loader',
options: {
bypassOnDebug: true,
}
}
]
},
{
test: /\.html$/,
use: [{
loader: 'html-loader',
options: {
minimize: true
}
}],
}
]
}
};
然后是webpack.dev.js:
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin')
module.exports = merge(common, {
output: {
filename: '[name].[hash].js'
},
devtool: 'source-map',
devServer: {
port: 8787,
open: true,
compress: true,
index: 'demo.html'
},
plugins: [
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin()
],
module: {
rules: [{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: ['css-loader?sourceMap']
})
},
{
test: /\.less$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: ['css-loader?sourceMap', 'less-loader?sourceMap']
})
}
]
}
});
最后是webpack.prod.js:
const merge = require('webpack-merge');
const common = require('./webpack.common.js');
const ExtractTextPlugin = require('extract-text-webpack-plugin')
module.exports = merge(common, {
module: {
rules: [{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: ['css-loader']
})
},
{
test: /\.less$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: ['css-loader', 'less-loader']
})
}
]
}
});
然后修改一下package.json中的腳本即可
"scripts": {
"dev": "webpack-dev-server --config webpack.dev.js",
"prod": "cross-env NODE_ENV=production webpack -p --config webpack.prod.js"
},
19. webpack輸出文件體積與交互關系的可視化:webpack-bundle-analyzer
安裝:
npm install --save-dev webpack-bundle-analyzer
然后修改webpack.config.js:
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
module.exports = merge(common, {
// ...
plugins: [
new BundleAnalyzerPlugin({ analyzerPort: 8919 })
],
});
這里的analyzerPort為打包后的本地展示網頁的端口,默認是8888。
然后webpack即可。
效果圖如下:
總結
各個插件以及loader的玩法還有很多,這里不具體介紹。