- 全局安裝webpack 和 webpack-cli
npm install -g webpack webpack-cli
- 創(chuàng)建并進入項目文件
mkdir webpack-demo && cd $_
- 創(chuàng)建package.json
npm init
我這里 webpack 版本為 4.20.2 ,webpack-cli 版本為 3.1.1
一、命令行打包
假設(shè)hello.js是我們需要打包的文件:
(1)不配置打包后文件的名稱和路徑,則默認在根目錄下生成dist/main.js,
--mode的可選值為development和production,默認值為development。
webpack hello.js --mode=development
(2)配置打包后文件的名稱和路徑
webpack hello.js -o ./dist/hello.min.js --mode=production
(3)wepack支持AMD,CommonJS,ES6
以下為CommonJS模塊示例:
根目錄下 hello.js, 導(dǎo)出一個函數(shù):
// hello.js:
module.exports=function () {
alert("hello")
}
根目錄下 index.js, 使用require加載了hello.js,并執(zhí)行了hello()函數(shù)
// index.js
var hello=require("./hello")
hello()
執(zhí)行命令 webpack index.js --mode=development, 打包結(jié)果如下:

(4)一些參數(shù)
--watch : 當文件變動時,自動打包
--progress: 看到打包的百分比進程
--colors : 有顏色
--display-modules: 列出引用的所有模塊
--display-reasons: 顯示打包原因(哪里引用了被打包的文件)
(5)如何打包css文件
若直接在 index.js中 require ("./style.css"),然后打包,會報錯 “You may need an appropriate loader to handle this file type.”, 提示我們需要有合適的loader來處理這種類型的文件。
所以,我們安裝兩個loader ——css-loader 和 style-loader :
// 安裝css-loader 和 style-loader
npm install css-loader style-loader --save-dev
方式一:
在 index.js中 添加require("style-loader!css-loader!./style.css"),意思是說,先通過css-loader處理style.css, 然后再將處理結(jié)果交給style-loader處理。css-loader 的作用是使得webpack可以處理css文件,而style-loader是將處理過后的css通過新建style標簽的方式插入html中
// index.js
require("style-loader!css-loader!./style.css") // 注意兩個loader的順序
執(zhí)行打包命令 webpack index.js --mode=development 即可成功打包css文件。這種方式的缺點是require 每個css文件都要加上loader, 不免麻煩。
方式二:
// index.js
require ("./style.css")
執(zhí)行webpack index.js --mode=development --module-bind "css=style-loader!css-loader"。
這種方式是通過--module-bind指定打包css文件時需要的 loader,打包結(jié)果和方式一一樣。
二、webpack 配置文件
(1)簡單示例
一個最簡單的webpack 配置:
// webpack.config.js
const path = require('path');
module.exports={
// mode:"development", // 模式,放在package.json中配置更好
entry: './index.js', // 打包入口文件
output:{
path:path.resolve(__dirname, 'dist'), // 打包輸出路徑
filename:"dist.min.js" // 打包輸出文件名稱
}
}
直接運行命令 webpack 即可打包, 這是因為 webpack默認會去查找名稱為webpack.config.js的配置文件。若我們的配置文件不叫webpack.config.js,比如叫 webpack.config.dev.js, 則需通過--config參數(shù)指定配置文件:
webpack --config webpack.dev.config.js
更簡單的方式是在 package.json 中配置我們的打包命令:
// package.json
"scripts":{
"webpack-dev":"webpack --config webpack.config.js --mode=development --progress --colors --display-modules --display-reasons",
"webpack-prod":"webpack --config webpack.config.js --mode=production --progress --colors --display-modules --display-reasons"
}
然后運行npm run webpack-dev 或 npm run webpack-prod 即可完成對應(yīng)配置的打包。
(2)entry 的 3 種配置方式
-
entry:"./index.js", 簡單示例中的方式 -
entry:["./entry1.js","./entry2.js"], 適用于兩個互不依賴的文件想打包到一起 - 多chunk方式(會打包生成多個文件)
以下為多chunk 配置示例:
// 文件目錄
webpack-demo
|- index.js
|- webpack.config.js
|- package.json
|- js
|- a.js
|- b.js
|- c.js
//webpack.config.js
const path = require('path');
module.exports={
entry: {
index:"./index.js",
ab:["./js/a.js","./js/b.js"],
c:"./js/c.js",
},
output:{
path:path.resolve(__dirname, 'dist'),
filename:"[name]-[hash].js", // [name], [hash]為占位符, 還有[chunk-hash]
}
}
打包結(jié)果如下:

三、自動生成html頁面(html-webpack-plugin插件的使用)
若使用 [hash] 或 [chunk-hash] 占位符的方式生成文件名稱,每次生成的文件名稱都是不一樣的,那么在html中 如何通過外鏈的方式(<script src=" "></script>)引入打包后的文件呢?或者我們有時候需要在不同的頁面引入不同的 js文件,又該如何做呢?
我們可以使用 html-webpack-plugin 插件來做到這一切。
npm install html-webpack-plugin --save-dev
(1)按模板自動生成html頁面
// webpack.config.js
const path = require('path');
var htmlWebpackPlugin=require("html-webpack-plugin")
module.exports={
entry: {
index:"./index.js",
ab:["./js/a.js","./js/b.js"],
c:"./js/c.js",
},
output:{
path:path.resolve(__dirname, 'dist'),
filename:"[name]-[hash].js",
},
plugins:[
new htmlWebpackPlugin({
filename:"index-[hash].html", // 生成的html文件名稱
template:"index.html", // 以當前目錄下的的 index.html 為模板來生成html
inject:"head", // script標簽插入到 html中的位置,head/body/false, 默認為插入body, 若設(shè)為false,則生成的html中不會自動插入script標簽
minify:{
removeComments:true, // 刪除 html 中的注釋
collapseWhitespace:true, // 刪除 html 中空格
}
})
]
}
以上配置的打包結(jié)果是,以 index.html為模板生成一個新的 index-[hash].html,新的html 的 <header></header> 標簽中以外鏈的方式引入了[index]-[hash].js, [ab]-[hash].js, [c]-[hash].js 三個打包后的js文件。
注:若 require("html-webpack-plugin") 時報錯 Cannot find module 'webpack/lib/node/NodeTemplatePlugin,嘗試本地安裝webpack 或者 運行npm link webpack --save-dev (該命令的作用是將一個任意位置的npm包鏈接到全局執(zhí)行環(huán)境) 。
(2)生成多頁面
可以在 plugins 中 new 若干個htmlWebpackPlugin實例以生成多頁面,htmlWebpackPlugin還支持ejs語法自定義一些內(nèi)容插入到html模板中。
webpack.config.js配置:
// webpack.config.js
const path = require('path');
var htmlWebpackPlugin=require("html-webpack-plugin")
module.exports={
entry: {
index:"./index.js",
ab:["./js/a.js","./js/b.js"],
c:"./js/c.js",
},
output:{
filename:"[name]-[hash].js",
path:path.resolve(__dirname, 'dist'),
publicPath:"http://cdn", // publicPath為項目中的所有資源指定一個基礎(chǔ)路徑
},
plugins:[
new htmlWebpackPlugin({
filename:"a.html",
template:"index.html" ,
title:"this is a.html", // title 將替換到模板中
chunks:["index","c"], // a.html 引入打包后的index.js 和 c.js
}) ,
new htmlWebpackPlugin({
filename:"b.html",
template:"index.html" ,
title:"this is b.html", // title 將替換到模板中
chunks:["ab"], // b.html 引入打包后的 ab.js
}),
new htmlWebpackPlugin({
filename:"c.html",
template:"index.html" ,
title:"this is c.html", // title 將替換到模板中
excludeChunks:["c"], // c.html 頁面引入除c.js之外的其它js
})
]
}
模板index.html:
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!-- ejs 語法,渲染title -->
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
</body>
</html>
以上配置的打包結(jié)果是,生成了a.html 、 b.html 和 c.html 文件,a.html的<body></body>標簽中外鏈了index-[hash].js 和 c-[hash].js, 如下圖所示:

(3)inline 的方式引入js
inline方式引入腳本可以減少http請求,以下演示如何實現(xiàn)部分腳本inline方式引入,部分腳本外鏈方式引入。
在“生成多頁面”的例子上做修改,我們現(xiàn)在的目的是,讓a.html, b.html,c.html 都以inline的方式引入index.js 腳本,其它腳本各頁面以外鏈的方式按需引入。
webpack.config.js配置:
// webpack.config.js
const path = require('path');
var htmlWebpackPlugin=require("html-webpack-plugin")
module.exports={
...
plugins:[
new htmlWebpackPlugin({
filename:"a.html",
template:"index.html" ,
title:"this is a.html",
inject:false, // 設(shè)為 false,表示不自動插入腳本
chunks:["index","c"],
}) ,
new htmlWebpackPlugin({
filename:"b.html",
template:"index.html" ,
title:"this is b.html",
inject:false, // 設(shè)為 false
chunks:["index"], // 因為要演示inline方式引入index-[hash].js腳本,所以這里chunks中必須包含index
}),
new htmlWebpackPlugin({
filename:"c.html",
template:"index.html" ,
title:"this is c.html",
inject:false, // 設(shè)為 false
excludeChunks:["c"],
})
]
}
模板index.html:
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title><%= htmlWebpackPlugin.options.title %></title>
<script type="text/javascript">
<%= compilation.assets[htmlWebpackPlugin.files.chunks.index.entry.substr(htmlWebpackPlugin.files.publicPath.length)].source() %>
</script>
</head>
<body>
<% for (var k in htmlWebpackPlugin.files.chunks){%>
<% if(k !=="index") { %>
<script type="text/javascript" src="
<%= htmlWebpackPlugin.files.chunks[k].entry %>
"></script>
<% }%>
<% }%>
</body>
</html>
解釋一下上圖模板index.html的含義:
首先,compilation.assets拿到的是所有的chunks,如下圖所示:

其次,htmlWebpackPlugin.files 拿到的是對應(yīng)頁面的publicPath,chunks等信息,以下是a.html頁面的htmlWebpackPlugin.files數(shù)據(jù),a.html 使用了index 和 c 兩個chunks:
// a.html頁面 htmlWebpackPlugin.files 取到的值
{
"publicPath":"http://cdn/",
"chunks":{
"index":{
"size":154,
"entry":"http://cdn/index-6ab81e3153cfd7fb16c1.js",
"hash":"630b3fd517102652c275",
"css":[]
},
"c":{
"size":35,
"entry":"http://cdn/c-6ab81e3153cfd7fb16c1.js",
"hash":"a172ef412ae0af3b89b9",
"css":[]
}
},
"js":["http://cdn/index-6ab81e3153cfd7fb16c1.js", "http://cdn/c-6ab81e3153cfd7fb16c1.js"],
"css":[]
}
最后,通過.source()拿到chuankName 為 index的 js代碼,直接插入 <script>標簽中。而外鏈方式的腳本直接通過for循環(huán)htmlWebpackPlugin.files.chunks,排除chuankName為index的chunk就可以了。
下圖為最終生成 的a.html(inline的script內(nèi)容過長,已折疊):

四、 loader的配置和使用
webpack本身只能打包Javascript文件,對于其他資源例如 css,圖片,或者其他的語法集比如jsx,是沒有辦法加載的。 這就需要對應(yīng)的loader將資源轉(zhuǎn)化后再加載進來。
(1)css loader
// 文件目錄
webpack-demo
|- app.js
|- webpack.config.js
|- package.json
|- src
|- style
|- base.css
|- common.css
app.js中引入base.css:
// src/app.js
import "./style/base.css"
base.css內(nèi)容(其中又引入了common.css):
// src/style/base.css
@import "./common.css";
html{
background: yellow;
}
.box1{
display: flex;
}
common.css內(nèi)容:
// src/style/common.css
.box2{
display: flex;
}
由于css中包含了一些可能導(dǎo)致瀏覽器樣式差異的屬性,除了安裝依賴css-loader和style-loader外,通常還需要安裝postcss-loader 和 autoprefixer,用于自動補充css前綴。
// 安裝 postcss-loader 和 autoprefixer
npm install postcss-loader autoprefixer --save-dev
webpack.config.js配置如下:
const path = require('path');
var htmlWebpackPlugin = require("html-webpack-plugin")
module.exports = {
entry: "./src/app.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "bundle.js",
},
plugins: [
new htmlWebpackPlugin({
filename: "index.html",
inject: "body",
})
],
module: {
rules: [
{
test: /\.css$/,
use:["style-loader","css-loader",{
loader: 'postcss-loader',
options: {
plugins: [
require("autoprefixer")({browsers: ["last 2 versions"]})
]
}
}],
},
]
}
}
打包完成后審查元素,發(fā)現(xiàn)base.css中的flex已自動加上前綴,但是通過@import方式引入的common.css中的flex并未加上前綴,如下圖所示:

解決方法,給css-loader添加額外配置 importLoaders:
// webpack.config.js
module.exports = {
...
module: {
rules: [
{
test: /\.css$/,
use:["style-loader",
{
loader: 'css-loader',
options: {
importLoaders: 1, // 表示在css-loader處理之前,指定相應(yīng)數(shù)量的loader來先處理import進來的資源,即需要先經(jīng)過postcss-loader的處理
}
},{
loader: 'postcss-loader',
options: {
plugins: [
require("autoprefixer")({browsers: ["last 2 versions"]})
]
}
}],
},
],
}
}
若整個項目中使用的都是預(yù)編譯sass或less,則這里css-loader 不需要配置importLoaders=1,因為less和sass都會自動補全前綴,接下來會講到。
(2)less 和sass loader
安裝less-loader,如果沒有l(wèi)ess, 還需要安裝less:
// 安裝less-loader
npm install less-loader --save-dev
// 如果用的是sass,則安裝sass-loader
// npm install sass-loader --save-dev
app.js中引入less文件:
// app.js
import "./style/cover.less" // 或者使用 require ("./style/cover.less")
webpack.config.js中相應(yīng)配置即可:
// webpack.config.js
module.exports = {
...
module: {
rules: [
{
test: /\.less$/,
// 由于less會自動補全前綴,故而這里的 css-loader 不需要配置importLoaders參數(shù)
use:["style-loader","css-loader",{
loader: 'postcss-loader',
options: {
plugins: [
require("autoprefixer")({browsers: ["last 2 versions"]})
]
}
},"less-loader"], // 若用的是sass, 這里對應(yīng)修改為sass-loader
},
],
}
}
(3)處理模板文件
這里只講html和ejs模板文件的處理,更為詳細的可移步這里。
1. html-loader
// 安裝 html-loader
npm install html-loader --save-dev
模板文件 test.html:
<!-- test.html-->
<div>
This is test.html
</div>
app.js:
// app.js
import layer from "./xxx/test.html"
document.getElementById("app")
app.innerHTML=layer
// console.log(layer)
webpack.config.js:
// webpack.config.js
module.exports = {
...
module: {
rules: [
{
test: /\.html$/,
use:["html-loader"],
},
],
}
}
打包結(jié)果如下圖所示,可見html-loader將html資源處理成了字符串。

2. ejs-loader
模板文件 test2.ejs:
// 模板文件 test2.ejs
<div class="test2">
<div>this is <%= name %></div>
<% for(var i=0;i<arr.length;i++){ %>
<%= arr[i] %>
<% } %>
</div>
layer.js:
// layer.js
import tpl2 from "../xxx/test2.ejs"
export default function () {
return {
name:"Test",
tpl:tpl2
}
}
app.js:
// app.js
import Layer from "./js/layer"
var app=document.getElementById("app")
var layer=new Layer()
app.innerHTML=layer.tpl(
{
name:"ejs",
arr:["apple","banana","pear"]
}
)
// console.log(layer)
webpack.config.js
// webpack.config.js
module.exports = {
...
module: {
rules: [
{
test: /\.ejs$/,
use:["ejs-loader"],
},
],
}
}
打包結(jié)果如下圖所示:
