首先將npm更改成cnpm,由於國內的npm有時下載速度很慢,能夠安裝cnpm,從國內淘寶鏡像下載,執行如下命令:javascript
npm install -g cnpm --registry=https://registry.npm.taobao.org
複製代碼
之後npm直接替換成cnpm使用。css
找個喜歡的目錄,執行如下命令:html
mkdir webpack-demo
cd webpack-demo
npm init -y
npm install webpack webpack-cli --save-dev
複製代碼
※注:本文代碼區域每行開頭的 「+」表示新增,「-或者D」表示刪除,「M」表示修改,「...」表示省略。 dist爲最終編譯出來的生產環境代碼,src爲開發環境代碼。java
/- webpack-demo
|- package.json
+ |- /dist
+ |- index.html
+ |- /src
+ |- index.js
複製代碼
調整 package.json 文件,以便確保咱們安裝包是私有的(private),而且移除 main 入口。這能夠防止意外發布你的代碼。 同時加入script命令,讓執行npx webpack 等同於執行npm run build。node
"name": "webpack-demo",
"version": "1.0.0",
"description": "",
+ "private": true,
- "main": "index.js",
"scripts": {
- "test": "echo \"Error: no test specified\" && exit 1",
+ "build": "webpack --mode production",
+ "dev": "webpack --mode development"
},
...
複製代碼
編寫dist/index.html,注意引入bundle.js(打包時會自動生成bundle.js)jquery
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Webpack Demo</title>
<script src="bundle.js"></script>
</head>
<body>
</body>
</html>
複製代碼
編寫src/index.js,隨便寫點。webpack
document.write('hello world');
複製代碼
項目根目錄建立webpack.config.js,用來配置webpack。git
|- package.json
|- /dist
|- index.html
|- /src
|- index.js
+ |- webpack.config.js
複製代碼
編寫webpack.config.js:github
const path = require('path');
module.exports = {
// 入口js路徑
entry: './src/index.js',
// 編譯輸出的js及路徑
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
複製代碼
執行npm run build,生成的dist以下:web
|- /dist
|- index.html
+ |- bundle.js
複製代碼
瀏覽器運行index.html,顯示「hello world」。 以上是最原始的方式,須要在dist配置html,這樣並不科學。咱們須要實現的是在src裏開發,而後所有生成到dist中。請繼續後面的章節。
安裝clean-webpack-plugin,設置代碼見2.3
npm install clean-webpack-plugin --save-dev
複製代碼
安裝html-webpack-plugin,設置代碼見2.3
npm install html-webpack-plugin --save-dev
複製代碼
構建目錄以下
|- package.json
|- webpack.config.js
|- /dist
|- /src
|- /html
|- index.html
|- login.html
|- /js
|- index.js
|- login.js
複製代碼
從新編寫webpack.config.js
const path = require('path');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
// 入口js路徑
entry: {
index: './src/js/index.js',
login: './src/js/login.js'
},
plugins: [
// 自動清空dist目錄
new CleanWebpackPlugin(),
// 設置html模板生成路徑
new HtmlWebpackPlugin({
filename: 'index.html',
template: './src/html/index.html',
chunks: ['index']
}),
new HtmlWebpackPlugin({
filename: 'login.html',
template: './src/html/login.html',
chunks: ['login']
}),
],
// 編譯輸出配置
output: {
// js生成到dist/js,[name]表示保留原js文件名
filename: 'js/[name].js',
// 輸出路徑爲dist
path: path.resolve(__dirname, 'dist')
}
};
複製代碼
如今執行npm run build,生成dist以下:
|- /dist
|- index.html
|- login.html
|- /js
|- index.js
|- login.js
複製代碼
查看html代碼,發現對應的js已經被引入了。
如今咱們搭建一個基於nodejs的服務器,讓代碼運行在開發環境,而且實如今修改代碼時不用刷新便可自動熱更新頁面。
自動監測代碼變化並實時刷新瀏覽器
npm install webpack-dev-server --save-dev
複製代碼
修改package.json:
"scripts": {
"build": "webpack --mode production",
- "dev": "webpack --mode development"
+ "serve": "webpack-dev-server --open --mode development"
},
複製代碼
修改webpack.config.js:
module.exports = {
entry: {...},
+ // 動態監測並實時更新頁面
+ devServer: {
+ contentBase: './dist',
+ // 默認8080,可不寫
+ port: 8080,
+ // 熱更新,無需刷新
+ hot: true
+ },
plugins: [...],
...
};
複製代碼
執行npm run serve,會在本地啓動一個nodejs的web服務,瀏覽器會自動打開http://localhost:8080/。 經過http://localhost:8080/login.html能夠訪問login頁面。 這時修改src下的js或者html文件,頁面會自動更新。
module.exports = {
...
devServer: {...},
+ // 方便追蹤源代碼錯誤
+ devtool: 'source-map',
plugins: [...],
..
};
複製代碼
webpack.config.js包含了生產環境和開發環境的配置,分離後,有利於代碼的維護。
|- package.json
- |- webpack.config.js
+ |- webpack.common.js
+ |- webpack.dev.js
+ |- webpack.prod.js
|- /dist
|- /src
複製代碼
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const {CleanWebpackPlugin} = require('clean-webpack-plugin');
const webpack = require('webpack');
module.exports = {
entry: {...},
plugins: [...],
output: {...}
};
複製代碼
webpack.dev.js保留開發環境配置的代碼
const merge = require('webpack-merge');
const common = require('./webpack.common');
module.exports = merge(common, {
// 動態監測並實時更新頁面
devServer: {
contentBase: './dist',
// 默認端口8080,可不填
port: 8080,
// 熱更新,無需刷新
hot: true
}
});
複製代碼
webpack.prod.js保留生產環境配置的代碼
const merge = require('webpack-merge');
const common = require('./webpack.common');
module.exports = merge(common, {
// 方便追蹤源代碼錯誤
//(若是不須要3.2小節的追蹤功能,能夠註釋掉下行代碼)
devtool: 'source-map'
});
複製代碼
修改package.json:
"scripts": {
M "build": "webpack --config webpack.prod.js --mode production",
M "serve": "webpack-dev-server --open --config webpack.dev.js --mode development"
},
複製代碼
本節以jQuery爲例,講解如何引入公用JS庫。若是使用AngularJS、React或者Vue,建議使用官方的CLI工具。
安裝jQuery:
npm install jquery --save
複製代碼
jQuery是每一個頁面都要用的,在每一個頁面import太過於繁瑣,修改webpack.common.js進行全局配置:
...
+ const webpack = require('webpack');
module.exports = {
...
plugins: [
+ new webpack.ProvidePlugin({
+ $: 'jquery',
+ jQuery: 'jquery'
+ }),
new CleanWebpackPlugin(),
...
複製代碼
修改src/html/index.html,加入div,用於測試:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Webpack Demo</title>
</head>
<body>
<div id="app">
<div id="info"></div>
</div>
</body>
</html>
複製代碼
修改src/js/index.js:
$('#info').text('jQuery正常使用');
複製代碼
執行npm run build,生成dist以下:
|- /dist
|- index.html
|- login.html
|- /js
|- index.js <--jQuery代碼已集成到這裏
|- login.js
複製代碼
使用webpack的splitChunks來實現單獨打包。
plugins: [
...
// 設置html模板生成路徑
new HtmlWebpackPlugin({
filename: 'index.html',
template: './src/html/index.html',
M chunks: ['jquery', 'index']
}),
new HtmlWebpackPlugin({
filename: 'login.html',
template: './src/html/login.html',
M chunks: ['jquery', 'login']
}),
],
+ optimization: {
+ splitChunks: {
+ cacheGroups: {
+ commons: {
+ test: /jquery/,
+ name: 'jquery',
+ chunks: 'all'
+ }
+ }
+ }
+ },
// 編譯輸出配置
output: {...}
複製代碼
執行npm run build,頁面運行正常,生成dist以下:
|- /dist
|- index.html
|- login.html
|- /js
|- index.js
|- login.js
+ |- jquery.js <--jQuery已獨立出來
複製代碼
若是要兼容IE8等低版本瀏覽器,在開發過程當中使用ES6等高版本js語法,會導低版本瀏覽器沒法運行。另外,jQuery只能使用1.x,要安裝低版本jQuery。(若是不須要兼容IE8,請直接跳過本章節)
npm install jquery-1x --save
複製代碼
修改webpack.common.js:
...
plugins: [
new webpack.ProvidePlugin({
M $: 'jquery-1x',
M jQuery: 'jquery-1x'
}),
...
複製代碼
如今build出來的代碼,IE8會在e.default代碼處報錯「缺乏標識符」。須要安裝uglifyjs-webpack-plugin。
npm install uglifyjs-webpack-plugin --save-dev
複製代碼
修改webpack.common.js
...
+ const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
module.exports = {
...
optimization: {
splitChunks: { ... },
+ minimizer: [
+ new UglifyJsPlugin({
+ uglifyOptions: {
+ ie8: true
+ }
+ })
+ ]
},
複製代碼
若是使用ES6語法,例如箭頭函數,build會報錯不經過。要把ES6轉爲ES5,須要安裝如下插件(較多):
npm install babel-loader @babel/core @babel/preset-env --save-dev
npm install @babel/plugin-transform-runtime @babel/plugin-transform-modules-commonjs --save-dev
npm install @babel/runtime --save
複製代碼
修改webpack.common.js,這裏代碼的做用是,在編譯時把js文件中ES6轉成ES5:
optimization: {...},
+ module: {
+ rules: [
+ {
+ test: /\.js$/,
+ exclude: /(node_modules|bower_components)/,
+ use: {
+ loader: 'babel-loader',
+ options: {
+ presets: ['@babel/preset-env'],
+ plugins: [
+ '@babel/plugin-transform-runtime',
+ '@babel/plugin-transform-modules-commonjs'
+ ]
+ }
+ }
+ }
+ ]
+ },
// 編譯輸出配置
output: {...}
複製代碼
如今執行npm run build,編譯經過。可是IE8下報錯「對象不支持bind屬性方法」,這是由於IE8不支持ES5,還須要引入es5-shim和es5-sham(見下節)。
嘗試了不少網上的方法,都沒有嘗試成功,最後乾脆用直接引用的方法來解決。
|- /src
|- /css
(略)
|- /html
|- index.html
|- login.html
|- /js
(略)
|- /static
|- /js
|- es5-shim.min.js
|- es5-sham.min.js
複製代碼
而後,在src/html/index.html和src/html/login.html的裏直接引入
<head>
<meta charset="UTF-8">
<title>>Webpack Demo</title>
+ <script type="text/javascript" src="static/js/es5-shim.min.js"></script>
</head>
複製代碼
...
+ const CopyWebpackPlugin = require('copy-webpack-plugin');
module.exports = {
...
plugins: [
...
new HtmlWebpackPlugin({
filename: 'login.html',
template: './src/html/login.html',
chunks: ['jquery', 'login']
}),
+ new CopyWebpackPlugin([
+ { from: './src/static', to: 'static' }
+ ])
複製代碼
執行npm run build,在IE8下能夠愉快的運行啦。
本節以Stylus爲例,若是使用Sass/Less,能夠參考本方法。混用CSS是爲了同時掌握CSS使用方法。 *安裝相關依賴包:
npm install style-loader css-loader --save-dev
npm install stylus-loader stylus --save-dev
複製代碼
如今src目錄以下:
|- /src
+ |- /css
+ |- /common
+ |- common.css
+ |- frame.css (這個文件@import common.css和reset.css)
+ |- reset.css
+ |- /pages
+ |- index.styl
+ |- login.styl
|- /html
|- index.html
|- login.html
|- /js
(略)
複製代碼
css及styl內的樣式代碼,請自行補充,這裏再也不展現了。
import '../css/common/frame.css';
import '../css/pages/index.styl';
login.js中引入樣式:
import '../css/common/frame.css';
import '../css/pages/login.styl';
複製代碼
module.exports = {
...
module: {
rules: [
+ {
+ test: /\.css$/,
+ use: [
+ 'style-loader',
+ 'css-loader'
+ ]
+ },
+ {
+ test: /\.styl$/,
+ use: [
+ 'style-loader',
+ 'css-loader',
+ 'stylus-loader'
+ ]
+ }
...
複製代碼
執行build後,樣式代碼會直接打包插入到html文件中。
如今咱們想把樣式經過link方式引入。
npm install mini-css-extract-plugin --save-dev
複製代碼
而後修改webpack.common.js
···
+ const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
plugins: [
...
+ new MiniCssExtractPlugin({
+ filename: 'css/[name].css'
+ }),
new CopyWebpackPlugin([
{ from: './src/static', to: 'static' }
])
],
module: {
rules: [
{
test: /\.css$/,
use: [
+ // 將原來的style-loader替換
M MiniCssExtractPlugin.loader,
'css-loader'
]
},
{
test: /\.styl$/,
use: [
+ // 將原來的style-loader替換
M MiniCssExtractPlugin.loader,
'css-loader',
'stylus-loader'
]
}
···
複製代碼
執行npm run build,生成的dist以下:
|- /dist
|- index.html
|- login.html
+ |- /css
+ |- index.css
+ |- login.css
|- /js
(略)
複製代碼
index.css和login.css都包含了公用樣式,還能夠進一步優化,把公用樣式分離出來。
修改webpack.common.js,使用正則,把src/css/common/下的css單獨打包並引用。
...
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html',
template: './src/html/index.html',
M chunks: ['style', 'jquery', 'index']
}),
new HtmlWebpackPlugin({
filename: 'login.html',
template: './src/html/login.html',
M chunks: ['style', 'jquery', 'login']
}),
new MiniCssExtractPlugin({
filename: 'css/[name].css'
})
],
optimization: {
splitChunks: {
cacheGroups: {
commons: {
test: /jquery/,
name: 'jquery',
chunks: 'all'
},
+ styles: {
+ test: /[\\/]common[\\/].+\.css$/,
+ name: 'style',
+ chunks: 'all',
+ enforce: true
+ }
}
}
},
複製代碼
執行npm run build,生成的dist以下:
|- /dist
|- index.html
|- login.html
+ |- /css
+ |- index.css
+ |- login.css
|- style.css <-- 公用css
|- /js
|- index.js
|- login.js
|- jquery.js
|- style.js <-- 怎麼會有這個?
複製代碼
發如今dist/js/目錄下會生成一個多餘的style.js,而且html也會引用這個多餘的js。這是webpack4的bug,從2016年到如今處於open狀態未解決,第三方插件都試過了,目前尚未解決,期待在webpack5中能夠解決。
若是每一個頁面都有一個header,就能夠把header抽成html模塊,而後在須要的頁面引入。這裏經過ejs來實現。ejs本質仍是個html,只不過多了些模板語法。
npm install ejs-loader --save-dev
複製代碼
以index.html爲例,把index.html重命名爲index.ejs
plugins: [
...
new HtmlWebpackPlugin({
filename: 'index.html',
+ // 這裏將html改爲ejs
M template: './src/html/index.ejs',
chunks: ['style', 'jquery', 'index']
}),
...
module: {
rules: [
...
+ {
+ test: /\.ejs/,
+ use: ['ejs-loader'],
+ }
...
複製代碼
建立src/html/components/header/header.ejs
<div id="header" class="G-header">這是公用頭部</div>
複製代碼
在src/html/index.ejs引入便可。
...
<div id="app">
+ <%= require('./components/header/header.ejs')() %>
<div id="info"></div>
</div>
...
複製代碼
本章節介紹如何在css和html中引用圖片。
npm install file-loader url-loader --save-dev
複製代碼
修改webpack.common.js
module: {
rules: [
...
+ {
+ test: /\.(png|svg|jpg|gif|webp)$/,
+ use: [
+ {
+ loader: 'url-loader',
+ options: {
+ // 最終生成的css代碼中,圖片url前綴
+ publicPath: '../images',
+ // 圖片輸出的實際路徑(相對於dist)
+ outputPath: 'images',
+ // 當小於某KB時轉爲base64
+ limit: 0
+ }
+ }
+ ]
+ }
...
]
},
複製代碼
在src/images里加入圖片1.jpg
|- /src
|- /css
(略)
|- /html
(略)
+ |- /images
+ |- 1.jpg
|- /js
(略)
|- /static
(略)
複製代碼
在src/css/pages/index.styl加入代碼:
.bg-pic
width: 200px
height: 200px
background: url(../../images/1.jpg) no-repeat
複製代碼
在src/html/index.ejs加入代碼:
<div id="app">
<%= require('./components/header/header.ejs')() %>
<div id="info"></div>
+ <div class="bg-pic"></div>
</div>
複製代碼
執行npm run build,圖片可正常訪問,生成dist目錄以下:
|- /dist
|- /css
(略)
|- /images
+ |- f0a89ff457b237711f8306a000204128.jpg
|- /js
(略)
|- /static
(略)
|- index.html
|- login.html
複製代碼
html加載圖片的方式就是img加載圖片,須要提取html中的圖片地址,須要安裝插件html-loader
npm install html-loader --save-dev
複製代碼
在src/images里加入圖片2.jpg
|- /src
|- /css
(略)
|- /html
(略)
|- /images
|- 1.jpg
+ |- 2.jpg
|- /js
(略)
|- /static
(略)
複製代碼
修改webpack.common.js,這樣能夠把html中的圖片提取並打包。
module: {
rules: [
...
+ {
+ test: /\.(html)$/,
+ use: {
+ loader: 'html-loader',
+ options: {
+ attrs: ['img:src', 'img:data-src', 'audio:src'],
+ minimize: true
+ }
+ }
+ }
...
複製代碼
在src/html/index.ejs加入代碼:
<div id="app">
<%= require('./components/header/header.ejs')() %>
<div id="info"></div>
<div class="bg-pic"></div>
+ <img src="${require('../images/2.jpg')}" alt="">
</div>
複製代碼
執行npm run build,圖片已可正常訪問,生成dist目錄以下
|- /dist
|- /css
(略)
|- /images
|- f0a89ff457b237711f8306a000204128.jpg
+ |- dab63db25d48455007edc5d81c476076.jpg
|- /js
(略)
|- /static
(略)
|- index.html
|- login.html
複製代碼
可是發現html中img的圖片不能顯示。查看生成的代碼發現:
修改webpack.common.js,將MiniCssExtractPlugin.loader改成對象的方式:
module: {
rules: [
...
{
test: /\.css$/,
use: [
M {
M loader: MiniCssExtractPlugin.loader,
M options: {
M // css中的圖片路徑增長前綴
M publicPath: '../'
M }
M },
'css-loader'
]
},
{
test: /\.styl$/,
use: [
M {
M loader: MiniCssExtractPlugin.loader,
M options: {
M // css中的圖片路徑增長前綴
M publicPath: '../'
M }
M },
'css-loader',
'stylus-loader'
]
},
...
{
test: /\.(png|svg|jpg|gif|webp)$/,
use: [
{
loader: 'url-loader',
options: {
D // 最終生成的圖片路徑代碼 (刪除)
D // publicPath: '../images', (刪除)
// 圖片輸出的實際路徑
outputPath: 'images',
// 當小於某KB時轉爲base64
limit: 0
}
}
]
},
...
複製代碼
再執行npm run build,路徑顯示正常了。
從阿里巴巴圖標網站 www.iconfont.cn/ 下載字體和樣式文件,導入到項目中,結構以下:
|- /src
|- /css
|- /common
|- common.css
|- frame.css
+ |- iconfont.css
|- reset.css
|- /pages
|- index.styl
|- login.styl
+ |- /fonts
+ |- iconfont.eot
+ |- iconfont.svg
+ |- iconfont.ttf
+ |- iconfont.woff
+ |- iconfont.woff2
|- /html
(略)
|- /images
(略)
|- /js
(略)
|- /static
(略)
複製代碼
src/css/common/frame.css裏要@import "iconfont.css",而後修改iconfont.css中每一個字體的路徑。
<i class="G-iconfont G-ficon-cart"></i>
複製代碼
修改webpack.common.js,
module: {
rules: [
...
+ {
+ test: /\.(woff|woff2|eot|ttf|svg)$/,
+ use: {
+ loader: 'file-loader',
+ options: {
+ // 保留原文件名和後綴名
+ name: '[name].[ext]',
+ // 輸出到dist/fonts/目錄
+ outputPath: 'fonts',
+ }
+ }
+ }
]
},
複製代碼
執行npm run build, 生成dist目錄以下:
|- /dist
|- /css
(略)
|- /images
(略)
+ |- /fonts
|- iconfont.eot
|- iconfont.svg
|- iconfont.ttf
|- iconfont.woff
|- /js
(略)
|- /static
(略)
|- index.html
|- login.html
複製代碼
沒有生成woff2文件,是由於css中沒有引用。