打个包试一试

先安装好环境

$ 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文件之间的依赖关系分析出来,再整合成一个或一组文件,让浏览器去识别,也是可行的。不过估计没人这么做,机器能做的事,为什么要人去做。

参考资料


槲栎
67 声望1 粉丝