webpack 安装和使用

webpack 是什么

webpack 是一款模块加载器兼打包工具,它能把各种资源,例如JS(含JSX)、coffee、样式(含 less / sass)、图片等都作为模块来使用和处理。 webpack 的核心是 依赖分析,把依赖分析出来了,其他都是细枝末节了。

安装 webpack

前提,你的电脑需要安装 node 和 npm.

使用 npm install webpack 安装 webpack.

入门示例

我们创建两个 js 文件:

src/bar.js

1
2
3
export default function bar() {
  //
}

src/index.js

1
2
3
import bar from './bar';

bar();

安装完 webpack 之后的目录如下:

接下来我们在根目录创建一个 webpack 的默认配置文件 webpack.config.js.

1
2
3
4
5
6
7
8
9
const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js'
  }
};

入口起点(entry point)指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。

entry 属性,来指定一个入口起点(或多个入口起点)。默认值为 ./src.

output 属性告诉 webpack 在哪里输出它所创建的 bundles,以及如何命名这些文件,默认值为 ./dist.

接下来我们只需要在命令行运行 webpack 就会创建 bundle.js

dist/bundle.js

1
!function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}}; //...省略

入口起点

上面已经使用了 entry 属性设置了一个入口起点,事实上它是下面这个语法的简写(只有一个入口):

1
2
3
4
5
const config = {
    entry: {
        main: './src/index.js'
    }
}

实时上,我们完全可以不创建 webpack.config.js 这个文件,因为 webpack 有一个默认的配置,默认入口起点为 ./src 默认输出 ./dist/main.js.

可以修改上面的 webpack.config.js 如下,执行结果一致:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
const path = require('path');

const config = {
    entry: {
        main: './src'
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js'
    }
};
  
module.exports = config;

上面的 ./src 实际上默认找的是 src 目录下的 index.js 文件。

多个入口起点

我们接下来配置一个多入口起点的,例如我有两个库 lib1lib2,目录结构如下:

1
2
3
4
5
6
7
8
9
src
|
├─lib1
│      bar.js
│      index.js
│
└─lib2
       bar.js
       index.js

修改 webpack.config.js 如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
const path = require('path');

const config = {
    entry: {
        lib1: './src/lib1',
        lib2: './src/lib2'
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js'
    }
};
  
module.exports = config;

此时你执行 webpack 命令就会发现出错了,错误(Error)如下:

大致意思是,两个入口起点的输出文件是同一个名字,这样是不允许的,我们在使用多个入口起点的时候必须使用占位符 [name] 来确保不同的输出文件名。

修改后的 webpack.config.js:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
const path = require('path');

const config = {
    entry: {
        lib1: './src/lib1',
        lib2: './src/lib2'
    },
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: '[name].js'
    }
};
  
module.exports = config;

执行后会在 dist 目录下产生两个文件 lib1.jslib2.js.

可以使用的占位符(由 webpack 内部提供)如下:

模板描述
[hash]模块标识符(module identifier) 的 hash
[chunkhash]chunk 内容的 hash
[name]模块名称
[id]模块标识符(module identifier)
[query]模块的 query,例如,文件名 ? 后面的字符串

入口集合

事实上我们还可以将多个文件夹或者库,例如上面的 lib1lib2 作为一个入口集合,然后输出到一个文件内:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
const path = require('path');

const config = {
    entry: [
        './src/lib1',
        './src/lib2'
    ],
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js'
    }
};
  
module.exports = config;

执行 webpack 命令后就会在 dist 目录下生成一个 bundle.js 文件。

插件

webpack 有着丰富的插件接口,webpack 自身的多数功能都使用这个插件接口。这个插件接口使 webpack 变得极其灵活,例如下面使用 html-webpack-plugin 插件。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
const HtmlWebpackPlugin = require('html-webpack-plugin'); //通过 npm 安装

module.exports = {

    //省略...

    plugins: [
        new webpack.optimize.UglifyJsPlugin(),
        new HtmlWebpackPlugin({template: './src/index.html'})
    ]
}

当然上面的插件需要使用 npm install 先进行安装的,然后查看对应插件的 API 文档。当然 webpack 自身也依赖于一些内置插件,内置插件请参考文档:https://webpack.docschina.org/plugins/

例如 ProvidePlugin 插件就是一个内置插件:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
const webpack = require('webpack'); //访问内置的插件

module.exports = {

    //省略...

    plugins: [
        new webpack.ProvidePlugin({
            dayjs: 'dayjs',
            PIXI: 'pixi.js',
        })
    ]
}

ProvidePlugin 是一个自动加载模块插件,不用你到处使用 import 或者 require 导入。

loader

上面我们使用的 webpack 都是针对 js 文件的打包处理,事实上 webpack 可以使用插件来实现对其他静态资源或者模板的打包处理。loader 可以将文件从不同的语言(如 TypeScript)转换为 JavaScript,或将内联图像转换为 data URL。loader 甚至允许你直接在 JavaScript 模块中 import CSS文件!

例如,你可以使用 loader 告诉 webpack 加载 CSS 文件,或者将 TypeScript 转为 JavaScript。为此,首先安装相对应的 loader:

1
2
npm install --save-dev css-loader
npm install --save-dev ts-loader

然后指示 webpack 对每个 .css 使用 css-loader,以及对所有 .ts 文件使用 ts-loader:

1
2
3
4
5
6
7
8
module.exports = {
    module: {
        rules: [
            { test: /\.css$/, use: 'css-loader' },
            { test: /\.ts$/, use: 'ts-loader' }
        ]
    }
};

在你的应用程序中,有三种使用 loader 的方式:

  • 配置(推荐):在 webpack.config.js 文件中指定 loader。
  • 内联:在每个 import 语句中显式指定 loader。
  • CLI:在 shell 命令中指定它们。

module.rules 允许你在 webpack 配置中指定多个 loader。 这是展示 loader 的一种简明方式,并且有助于使代码变得简洁。同时让你对各个 loader 有个全局概览:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
module: {
    rules: [
        {
            test: /\.js$/,
            use: ['babel-loader'],
            exclude: /node_modules/
        },
        {
            test: /\.(vert|frag)$/,
            use: ['raw-loader']
        }
    ]
}

可以在 import 语句或任何等效于 “import” 的方式中指定 loader。使用 ! 将资源中的 loader 分开。分开的每个部分都相对于当前目录解析。

1
import Styles from 'style-loader!css-loader?modules!./styles.css';

通过前置所有规则及使用 !,可以对应覆盖到配置中的任意 loader。选项可以传递查询参数,例如 ?key=value&foo=bar,或者一个 JSON 对象,例如 ?{"key":"value","foo":"bar"}

你也可以通过 CLI 使用 loader:

1
webpack --module-bind jade-loader --module-bind 'css=style-loader!css-loader'

这会对 .jade 文件使用 jade-loader,对 .css 文件使用 style-loader 和 css-loader。

总结

上面是 webpack 的入门级用法,其实还有很多其他配置,例如我们可以使用 webpack -watch 命令来执行热打包更新,一般情况下我们会配置到 package.json 文件中,例如:

1
2
3
4
5
6
"scripts": {
    "build": "cross-env NODE_ENV=production webpack",
    "build:ctx": "cross-env NODE_ENV=production webpack --config webpack.ctx.js",
    "dev": "webpack -w",
    "dev:ctx": "webpack -w --config webpack.ctx.js"
}

上面的 webpack -w 就是 webpack -watch 的缩写,当然我们也可以更改配置文件 webpack.config.js 的名字,但是需要使用 --config 来指定对应的配置文件。

更多的配置可以参考官方文档,或者使用 webpack --help 命令查看帮助。