博客 / 詳情

返回

webpack實戰

打個包試一試

先安裝好環境

$ 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,不過不全
user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.