打個包試一試
先安裝好環境
$ node -v
v12.14.0
$ npm init -y
$ npm install -D webpack@3.6.0
項目目錄結構:
my-mall-admin-web
├── build -- 存儲打包好的文件
├── node_modules -- 安裝的node模塊
├── src
| ├── greet.js
| ├── index.js -- 無用的文件,測試用
| └── main.js
├── index.html
└── package.json
再看各個文件中的內容。main.js,引用了greet.js中的內容,在html文件中添加東西:
const greeter = require('./greet.js');
document.querySelector('#root').appendChild(greeter());
greet.js生成一個html代碼塊:
module.exports = function() {
var greet = document.createElement('div');
greet.textContent = "Hi there and greetings!";
return greet;
}
index.html,這裏引用了build/bundle.js,但是我們根本沒有寫它:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Demo</title>
</head>
<body>
<div id="root">xxx</div>
<script src="build/bundle.js"></script>
</body>
</html>
打包打包打包:
$ npx webpack src/main.js build/bundle.js
Hash: 798edb3b82b69716e2d1
Version: webpack 3.6.0
Time: 54ms
Asset Size Chunks Chunk Names
bundle.js 2.8 kB 0 [emitted] main
[0] ./src/main.js 95 bytes {0} [built]
[1] ./src/greet.js 151 bytes {0} [built]
指定把src/main.js打包成build/bundle.js,webpack識別出main.js引用了greet.js,就把這個也打包了。沒有引用的index.js根本不會理他。
這時候在瀏覽器中,輸入index.html的地址,就能看到顯示了'Hi there and greetings!'。
這樣,我們就把2個js文件打包成了一個js文件,它們之間的引用關係我們不用操心,webpack會自動處理。打包後的js文件功能和我們自己寫的原始的兩個js文件所實現的功能一樣。這樣,就能實現js文件的模塊化,每個js文件寫一個模塊,整合到一起構成複雜的功能。和java類一樣~
手寫命令太麻煩
前面我們執行打包命令時,把需要打包的文件和目標文件都寫到了命令行中,每次都寫多麻煩,文件多了還不好用。所以需要配置文件。
在目錄裏新建一個webpack.config.js文件,這就是webpack默認的配置文件:
module.exports = {
entry: __dirname + "/src/main.js", // __dirname是node全局變量,指向腳本執行時所在目錄
output: {
path: __dirname + "/build",
filename: "bundle.js"
}
}
現在目錄結構如下:
my-mall-admin-web
├── build -- 存儲打包好的文件
├── node_modules -- 安裝的node模塊
├── src
| ├── greet.js
| ├── index.js -- 無用的文件,測試用
| └── main.js
├── index.html
├── package.json
└── webpack.config.js -- webpack默認配置文件
然後打包:
$ npx webpack # 注意這裏什麼參數都沒有帶
Hash: 798edb3b82b69716e2d1
Version: webpack 3.6.0
Time: 80ms
Asset Size Chunks Chunk Names
bundle.js 2.8 kB 0 [emitted] main
[0] ./src/main.js 95 bytes {0} [built]
[1] ./src/greet.js 151 bytes {0} [built]
打包結果一摸一樣!
用node管理命令
node用package.json文件管理這個項目,提供了統一設置腳本命令的地方。我們可以把命令放到package.json中,如果腳本變更也會更方便:
{
...
"scripts": {
"build": "webpack"
}
...
}
執行npm run dev命令:
$ npm run build
> my-mall-admin-web@1.0.0 build E:\pro\web\231008-mall\dev\my-mall-admin-web
> webpack
Hash: 798edb3b82b69716e2d1
Version: webpack 3.6.0
Time: 48ms
Asset Size Chunks Chunk Names
bundle.js 2.8 kB 0 [emitted] main
[0] ./src/main.js 95 bytes {0} [built]
[1] ./src/greet.js 151 bytes {0} [built]
和直接執行webpack命令一摸一樣!
上http服務器
之前我們測試都是直接在瀏覽器中輸入index.html文件的位置,但真實環境下都是通過瀏覽器訪問一個http服務器。我們能把http服務器和webpack打包整合到一起嗎?
完全可以!webpack-dev-server就是這樣一個工具:
$ npm instalal -D webpack-dev-server@2.9.1 # 安裝
$ npx --no-install webpack-dev-server -v # 不下載遠程模塊,只執行本地安裝的webpack-dev-server
webpack-dev-server 2.9.1
webpack 3.6.0
webpack-dev-server也使用webpack.config.js作為其默認配置文件,它有自己的配置參數:
module.exports = {
entry: __dirname + "/src/main.js", // __dirname是node全局變量,指向腳本執行時所在目錄
output: {
path: __dirname + "/build",
filename: "bundle.js"
},
// webpack-dev-server的配置參數
devServer: {
inline: true // 源文件變動時自動刷新頁面
}
}
package.json中也修改一下,添加上webpack-dev-server的命令:
{
...
"scripts": {
"build": "webpack",
"server": "webpack-dev-server"
}
...
}
把build/bundle.js刪了,運行npm run server,看看會不會自動打包並啓動http服務:
$ npm run server
> my-mall-admin-web@1.0.0 server E:\pro\web\231008-mall\dev\my-mall-admin-web
> webpack-dev-server
Project is running at http://localhost:8080/
webpack output is served from /
Hash: 667b4715dc9202865caa
Version: webpack 3.6.0
Time: 641ms
Asset Size Chunks Chunk Names
bundle.js 416 kB 0 [emitted] [big] main
[41] multi (webpack)-dev-server/client?http://localhost:8080 ./src/main.js 40 bytes {0} [built]
[42] (webpack)-dev-server/client?http://localhost:8080 7.23 kB {0} [built]
[43] ./node_modules/url/url.js 23.5 kB {0} [built]
[44] ./node_modules/punycode/punycode.js 14.7 kB {0} [built]
[45] ./node_modules/qs/lib/index.js 211 bytes {0} [built]
[60] ./node_modules/strip-ansi/index.js 161 bytes {0} [built]
[61] ./node_modules/ansi-regex/index.js 135 bytes {0} [built]
[62] ./node_modules/loglevel/lib/loglevel.js 9.56 kB {0} [built]
[63] (webpack)-dev-server/client/socket.js 1.04 kB {0} [built]
[64] ./node_modules/sockjs-client/lib/entry.js 244 bytes {0} [built]
[95] (webpack)-dev-server/client/overlay.js 3.73 kB {0} [built]
[101] (webpack)/hot nonrecursive ^\.\/log$ 170 bytes {0} [built]
[103] (webpack)/hot/emitter.js 77 bytes {0} [built]
[105] ./src/main.js 95 bytes {0} [built]
[106] ./src/greet.js 151 bytes {0} [built]
+ 92 hidden modules
webpack: Compiled successfully.
看日誌,正常打包成功了,但是build目錄下沒有文件,瀏覽器中輸入“http://localhost:8080”也沒有正常顯示。為什麼?
原來webpack-dev-server打包好的bundle.js只在內存中,不會放到文件系統上,所以需要修改下index.html文件,直接從webpack-dev-server這個http服務器裏取bundle.js。注意這裏沒有指定bundle.js的路徑:
<script src="bundle.js"></script>
刷新瀏覽器,這次就好了。重啓webpack-dev-server,也能正常顯示。
總結:webpack-dev-server是一個http服務器,運行同時會使用webpack對項目進行打包。但是打包的東西只在內存中,不會存到磁盤。可以使用"http://localhost:8080/webpack-dev-server"查看內存中的文件。命令行支持--open自動打開瀏覽器。
試試打包CSS
我想寫一個css文件,webpack能給我打包嗎?
能!現在我們就寫一個src/main.css:
html {
box-sizing: border-box;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
}
*, *:before, *:after {
box-sizing: inherit;
}
body {
margin: 0;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
h1, h2, h3, h4, h5, h6, p, ul {
margin: 0;
padding: 0;
}
div {
background-color: blue;
}
然後在main.js中引入這個css文件:
const greeter = require('./greet.js');
import './main.css'; // 引入css文件
document.querySelector('#root').appendChild(greeter());
webpack默認只認識javascript文件,要讓它認識css文件,得加一些東西:
$ npm install -D style-loader@0.23.1 css-loader@0.28.0
把它們配置到webpack.config.js配置文件中,讓webpack能識別它們。這裏新增了一個module配置,表明webpack對css模塊如何處理:
module.exports = {
entry: __dirname + "/src/main.js", // __dirname是node全局變量,指向腳本執行時所在目錄
output: {
path: __dirname + "/build",
filename: "bundle.js"
},
// webpack-dev-server的配置參數
devServer: {
inline: true // 源文件變動時自動刷新頁面
},
// 處理模塊
module: {
rules: [
{ // 對於css文件,使用style-loader和css-loader進行處理
test: /\.css$/i,
use: ['style-loader', 'css-loader'] // 注意順序不能反
}
]
}
}
執行npm run server,啓動webpack-dev-server,這樣就會自動識別出css文件了:
$ npm run server
> my-mall-admin-web@1.0.0 server E:\pro\web\231008-mall\dev\my-mall-admin-web
> webpack-dev-server
Project is running at http://localhost:8080/
webpack output is served from /
Hash: 4052c51c5b4600560383
Version: webpack 3.6.0
Time: 1081ms
Asset Size Chunks Chunk Names
bundle.js 489 kB 0 [emitted] [big] main
[41] multi (webpack)-dev-server/client?http://localhost:8080 ./src/main.js 40 bytes {0} [built]
[42] (webpack)-dev-server/client?http://localhost:8080 7.23 kB {0} [built]
[43] ./node_modules/url/url.js 23.5 kB {0} [built]
[44] ./node_modules/punycode/punycode.js 14.7 kB {0} [built]
[45] ./node_modules/qs/lib/index.js 211 bytes {0} [built]
[60] ./node_modules/strip-ansi/index.js 161 bytes {0} [built]
[61] ./node_modules/ansi-regex/index.js 135 bytes {0} [built]
[62] ./node_modules/loglevel/lib/loglevel.js 9.56 kB {0} [built]
[63] (webpack)-dev-server/client/socket.js 1.04 kB {0} [built]
[95] (webpack)-dev-server/client/overlay.js 3.73 kB {0} [built]
[101] (webpack)/hot nonrecursive ^\.\/log$ 170 bytes {0} [built]
[103] (webpack)/hot/emitter.js 77 bytes {0} [built]
[105] ./src/main.js 139 bytes {0} [built]
[106] ./src/greet.js 151 bytes {0} [built]
[107] ./src/main.css 1.07 kB {0} [built]
+ 101 hidden modules
webpack: Compiled successfully.
輸出日誌中可以看到,最後增加了對./src/main.css的解析。打包好的文件統一放到了bundle.js文件中,也就是説我們的css文件被放到了js文件中。
打開瀏覽器,輸入地址,可以看到div的背景變成了藍色。
項目地址:E:\pro\web\231008-mall\dev\my-mall-admin-web,”打包css"標籤。
總結:webpack使用loader來加載處理css、png等不同的模塊:
- loader 本質上是一個函數,output=loader(input) // input可為工程源文件的字符串,也可是上一個loader轉化後的結果;
- 第一個 loader 的傳入參數只有一個:資源文件(resource file)的內容;
- loader支持鏈式調用,webpack打包時是按照數組從後往前的順序將資源交給loader處理的。例如我們配置的
use: ['style-loader', 'css-loader'],就先用css-loader加載css,然後用style-loader把css注入到js代碼中
插件:擴展webpack功能
給所有打包的文件都標記上版權
webpack支持使用插件來進行功能擴展。我們來添加一個webpack自帶的插件,如果使用其它插件,需要先使用npm install -D xxx安裝。
在webpack.config.js文件中添加插件:
const webpack = require('webpack')
module.exports = {
...
// 處理模塊
module: {
...
},
plugins: [ // 注意這裏是[]而不是{}
new webpack.BannerPlugin('這個東西是我的,你不要拿走')
]
}
這裏我們使用webpack自帶的一個插件,這個插件把打包之後的js文件,添加一些説明。
執行npm run server,瀏覽器中輸入"http://localhost:8080/webpack-dev-server",點擊進入bundle.js,可以看到第一行有我們寫的文字。
總結:插件就是用來實現一些特定的功能。
把CSS和JS分離
css和js放到了一起是不是感覺很奇怪?有沒有辦法把它們分開?
有!插件extract-text-webpack-plugin就實現了這個功能。在生產環境上,我們一般把js和css分開,這樣效率較高。
安裝插件:
$ npm install --save-dev extract-text-webpack-plugin@3.0.0
新建一個webpack.prod.conf.js,基本和之前的配置文件一樣,只是增加了這個插件:
const webpack = require('webpack');
const ExtractTextPlugin = require('extract-text-webpack-plugin'); // 使用插件分離css和js
module.exports = {
entry: __dirname + "/src/main.js",
output: {
path: __dirname + "/build",
filename: "bundle.js"
},
devServer: {
inline: true
},
module: {
rules: [
{
test: /\.css$/i,
use: ExtractTextPlugin.extract({ // 注意這裏也使用了這個插件
fallback: "style-loader",
use: "css-loader"
})
}
]
},
plugins: [
new webpack.BannerPlugin('這個東西是我的,你不要拿走'),
new ExtractTextPlugin("style.css") // 把插件添加進來
]
}
再修改一下package.json,增加兩條腳本:
{
...
"scripts": {
"build": "webpack",
"server": "webpack-dev-server",
"prod-build": "webpack --config webpack.prod.conf.js",
"prod-server": "webpack-dev-server --config webpack.prod.conf.js"
},
...
}
執行npm run prod-server:
$ npm run prod-server
> my-mall-admin-web@1.0.0 prod-server E:\pro\web\231008-mall\dev\my-mall-admin-web
> webpack-dev-server --config webpack.prod.conf.js
Project is running at http://localhost:8080/
webpack output is served from /
Hash: 508d5662a9c11d1808e2
Version: webpack 3.6.0
Time: 1141ms
Asset Size Chunks Chunk Names
bundle.js 416 kB 0 [emitted] [big] main
style.css 407 bytes 0 [emitted] main
[41] multi (webpack)-dev-server/client?http://localhost:8080 ./src/main.js 40 bytes {0} [built]
[42] (webpack)-dev-server/client?http://localhost:8080 7.23 kB {0} [built]
[43] ./node_modules/url/url.js 23.5 kB {0} [built]
[44] ./node_modules/punycode/punycode.js 14.7 kB {0} [built]
[45] ./node_modules/qs/lib/index.js 211 bytes {0} [built]
[60] ./node_modules/strip-ansi/index.js 161 bytes {0} [built]
[61] ./node_modules/ansi-regex/index.js 135 bytes {0} [built]
[62] ./node_modules/loglevel/lib/loglevel.js 9.56 kB {0} [built]
[63] (webpack)-dev-server/client/socket.js 1.04 kB {0} [built]
[95] (webpack)-dev-server/client/overlay.js 3.73 kB {0} [built]
[101] (webpack)/hot nonrecursive ^\.\/log$ 170 bytes {0} [built]
[103] (webpack)/hot/emitter.js 77 bytes {0} [built]
[105] ./src/main.js 139 bytes {0} [built]
[106] ./src/greet.js 151 bytes {0} [built]
[107] ./src/main.css 41 bytes {0} [built]
+ 101 hidden modules
Child extract-text-webpack-plugin node_modules/extract-text-webpack-plugin/dist node_modules/css-loader/index.js!src/main.css:
[0] ./node_modules/css-loader!./src/main.css 595 bytes {0} [built]
[1] ./node_modules/css-loader/lib/css-base.js 2.19 kB {0} [built]
[2] ./node_modules/buffer/index.js 48.6 kB {0} [built]
[3] (webpack)/buildin/global.js 509 bytes {0} [built]
[4] ./node_modules/base64-js/index.js 3.93 kB {0} [built]
[5] ./node_modules/ieee754/index.js 2.15 kB {0} [built]
[6] ./node_modules/isarray/index.js 132 bytes {0} [built]
webpack: Compiled successfully.
從日誌中可以看到生成了bundle.js和style.css兩個文件。瀏覽器中輸入“http://localhost:8080/webpack-dev-server”,可以看到bundle.js和sytle.css兩個文件存在於這個http服務器的內存中。
再修改下index.html,把style.css加上:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Demo</title>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div id="root">xxx</div>
<script src="bundle.js"></script>
</body>
</html>
這樣,瀏覽器中就能正常顯示了。
自動生成index.html
剛才我們打包js和css時,都需要修改index.html文件,好讓它引入我們打包的js和css文件。有辦法讓它自動生成嗎?
有!使用插件html-webpack-plugin。
安裝插件:
$ npm install -D html-webpack-plugin@2.30.1
修改webpack.config.js和webpack.prod.conf.js,添加上這個插件:
...
const HtmlWebpackkPlugin = require('html-webpack-plugin');
module.exports = {
...
plugins: [ // 注意這裏是[]而不是{}
...
new HtmlWebpackkPlugin({
filename: "index.html",
template: "index.html"
})
]
}
執行npm run prod-server:
$ npm run prod-server
> my-mall-admin-web@1.0.0 prod-server E:\pro\web\231008-mall\dev\my-mall-admin-web
> webpack-dev-server --config webpack.prod.conf.js
Project is running at http://localhost:8080/
webpack output is served from /
Hash: 452c0e7f4986ba1d6cb8
Version: webpack 3.6.0
Time: 1326ms
Asset Size Chunks Chunk Names
bundle.js 416 kB 0 [emitted] [big] main
style.css 407 bytes 0 [emitted] main
index.html 374 bytes [emitted]
[41] multi (webpack)-dev-server/client?http://localhost:8080 ./src/main.js 40 bytes {0} [built]
[42] (webpack)-dev-server/client?http://localhost:8080 7.23 kB {0} [built]
[43] ./node_modules/url/url.js 23.5 kB {0} [built]
[44] ./node_modules/punycode/punycode.js 14.7 kB {0} [built]
[45] ./node_modules/qs/lib/index.js 211 bytes {0} [built]
[60] ./node_modules/strip-ansi/index.js 161 bytes {0} [built]
[61] ./node_modules/ansi-regex/index.js 135 bytes {0} [built]
[62] ./node_modules/loglevel/lib/loglevel.js 9.56 kB {0} [built]
[63] (webpack)-dev-server/client/socket.js 1.04 kB {0} [built]
[95] (webpack)-dev-server/client/overlay.js 3.73 kB {0} [built]
[101] (webpack)/hot nonrecursive ^\.\/log$ 170 bytes {0} [built]
[103] (webpack)/hot/emitter.js 77 bytes {0} [built]
[105] ./src/main.js 139 bytes {0} [built]
[106] ./src/greet.js 151 bytes {0} [built]
[107] ./src/main.css 41 bytes {0} [built]
+ 101 hidden modules
Child html-webpack-plugin for "index.html":
1 asset
[0] ./node_modules/html-webpack-plugin/lib/loader.js!./index.html 655 bytes {0} [built]
[1] ./node_modules/lodash/lodash.js 544 kB {0} [built]
[2] (webpack)/buildin/global.js 509 bytes {0} [built]
[3] (webpack)/buildin/module.js 517 bytes {0} [built]
Child extract-text-webpack-plugin node_modules/extract-text-webpack-plugin/dist node_modules/css-loader/index.js!src/main.css:
[0] ./node_modules/css-loader!./src/main.css 595 bytes {0} [built]
[1] ./node_modules/css-loader/lib/css-base.js 2.19 kB {0} [built]
[2] ./node_modules/buffer/index.js 48.6 kB {0} [built]
[3] (webpack)/buildin/global.js 509 bytes {0} [built]
[4] ./node_modules/base64-js/index.js 3.93 kB {0} [built]
[5] ./node_modules/ieee754/index.js 2.15 kB {0} [built]
[6] ./node_modules/isarray/index.js 132 bytes {0} [built]
webpack: Compiled successfully.
從日誌中看出,打包生成了3個文件。從瀏覽器中看也正常。
總結
webpack是一個編譯和打包工具,用來把具有相互引用關係的javascript文件編譯打包成一個文件。
loader提高了webpack的打包範圍,使其不僅可以處理javascript文件,還可以處理css、json、vue等各種各樣的文件。
plugin提高了webpack的處理能力,使其不僅可以完成打包,還可以完成自定義寫入、分離css和js、自動生成html文件等各種各樣的能力。
webpack處理的所有文件,都是從“入口”處開始查找的,沒有被入口文件及其相關依賴文件引用的文件,是不會被處理的。比如我們的src/index.js,就永遠不會被webpack處理。webpack的入口在webpack.config.js中使用entry指定。
webpack只是一個自動化工具,減輕了我們的工作。如果沒有webpack,我們自己手工把各個javascript、css、json、vue文件之間的依賴關係分析出來,再整合成一個或一組文件,讓瀏覽器去識別,也是可行的。不過估計沒人這麼做,機器能做的事,為什麼要人去做。
參考資料
- 入門 Webpack,看這篇就夠了 ,這個文檔的主要參考資料
- npx 使用教程
- webpack-dev-server編譯文件寫入磁盤
- DevServer ,這是webpack-dev-server官方配置文檔
- webpack-v3官網文檔鏡像 ,不要轉成中文,中文就不是v3版本了
- 快速簡明教程: webpack ,介紹了一些實用插件
- Webpack 教程 ,一個比較詳細的教程,有介紹require和import
- webpack 入門教程 ,介紹了一些實用loader和插件
- 吐血整理的webpack入門知識及常用loader和plugin
- webpack 的 loader 和 plugin 你真的弄懂了嗎 ,簡要介紹了loader和plugin的原理和如何開發
- Webpack入門:常用loader和plugin配置 ,介紹了一些常用loader和plugin,不過不全