Webpack 2 in Action

webpack is a module bundler. Its main purpose is to bundle JavaScript files for usage in a browser, yet it is also capable of transforming, bundling, or packaging just about any resource or asset.

传统工具中,流程是,将 js, css, html 各自合并,压缩,形式一个或多个文件,分别在各处引用。

但 webpack 相对于之前的工具区别于,所有的东西都是资源,像 css, js, image 亦或 json 等,可以通过 loader 来进行加载。
同时,它有模块机制,会根据依赖,去把代码自动放在一起,而不用开发的配置。
接下来,一起来走一个流程,来熟悉下配置。


安装使用 Webpack

1
2
3
4
# 更推荐使用 yarn 来进行依赖库的操作
$ yarn add --dev webpack webpack-dev-server
# 为了更方便使用 webpack,建议全局安装
$ yarn global add --dev webpack webpack-dev-server

在项目中创建 webpack.config.js 即可开启使用。

可以依据不同环境,增设不同的配置文件 webpack.production.config.js


一切都在配置文件

Webpack 所有的配置都在 webpack.config.js 文件中。其中包括以下几个大的类别:

Context/Entry/Output

这是几个必备基础配置,我们来一一讲解:

  • context: 配置 entryloader 工作所在的根目录。
  • entry: 应用的入口文件,该文件和其依赖,最终会被编译到一个文件。(可以配置为多个对象,编译出对应个数的结果文件。)
  • output: 入口文件编译后的配置项,可以是一个固定字符串,也可是以一个 [name].[hash].bundle.js 的变量配置。
1
2
3
4
5
6
7
8
9
10
11
12
13
let config = {
context: path.resolve(__dirname, 'src'),
entry: {
app: './main.jsx'
},
output: {
path: path.resolve(__dirname, 'dist'),
publicPath: '/',
filename: "[name].[hash].bundle.js"
}
}

Module

Webpack 是设计为处理 JavaScript 文件的,但其也可以处理任何的文件类型,这归功于强大的 Loaders 机制。

Loader 的使用方法是,通过正则匹配到一些类型的文件,然后对其依次经过配置 loader 的处理。Loader 可以配置多个,倒序依次处理。

示例中,是对 scss 文件的处理,先由 sass-loader 进行编译,然后交由 css-loader,最后由 style-loader 来处理。

1
2
3
4
5
6
7
8
9
10
11
let config = {
module: {
rules: [{
test: /\.scss$/,
use: [
'style-loader',
'css-loader',
'sass-loader',
]}
]}
}

Plugins

和 Module 针对文件类型不同的是 Plugin 处理的是:下面,主要讲几个常用的 Plugin 和配置:

- webpack.HotModuleReplacementPlugin

webpack 内置 Plugin,自动热加载,就是在开发时,一旦有文件改动,浏览器会自动刷新。

- webpack.optimize.CommonsChunkPlugin

正如其名,它会让引用模块中重复的地方取出,做成一个单独的文件,这样可以将一些公共的库取出,降低改动带来的更新成本。

如果你正在将应用拆解,打包成多个 output 的话(如果应用的某部分有大量不需要提前加载的 JS 的话,这样做会很有用),那么在这些文件里就有可能出现重复的代码,因为在解决依赖问题的时候它们是互相不干预的。幸好,Webpack 有一个内建插件 CommonsChunk 来处理这个问题:

现在,在 output 的文件里,如果有任意模块加载了两次或更多(通过 minChunks 设置该值),它就会被打包进一个叫 commons.js 的文件里,后面你就可以在客户端缓存这个文件了。当然,这肯定会造成一次额外的请求,但是却避免了客户端多次下载相同库的问题。所以在很多场景下,这都是提升速度的举措。

- webpack.optimize.UglifyJsPlugin

代码压缩,用于生产环境。

- webpack.ProvidePlugin

全局挂载插件,用于挂载老的代码,如 jQuery 之类的。

- Extract Text Plugin

把 CSS 从 JS 中的 import/reuqre 中取出来,放到一个单独的 CSS 文件中。

- HtmlWebpackPlugin

不用开发人员自己在 index.html 中加 bundle.js, bundle.css 等 output 文件,因为这可能是一个 hash 值,这个插件会帮你搞定。

效果:

1
2
3
<link href="/app.4683be2.css" rel="stylesheet"></head>
...
<script type="text/javascript" src="/app.2e17047c719b4525dc77.bundle.js"></script>

- CopyWebpackPlugin

拷贝资源插件
像用了 ant.design,这里也会产生一些文件。

Resolve

最常用的,就是 alias,给一个模块路径起上一个别名,这样在每次 import 时,可以少写很多代码。

Webpack DevServer

DevServer 是一个内建的 HTTP 服务器的独立程序,可通过 webpack-dev-server 命令启动,为开发时提供方便。

它也可以使用 webpack.config 文件来进行配置,或通过命令行参数来进行配置,但有点参数只能通过命令行参数。

一种是命令行配置:

1
$ webpack-dev-server --open --host 0.0.0.0 --port 5000

另一种是在 webpack.config.js 中加入配置项目

1
2
3
4
5
6
7
let config = {
devServer: {
contentBase: path.resolve(__dirname, 'src'),
historyApiFallback: true,
hot: true
}
}

Other Devtool/Externals

Devtool 主要是配置生成何种方式的 Source Mpas。


其它事项

ECMAScript 2015 new syntax

一个有着代码洁癖的人,是不会让整个项目中再出现 ES 2015 前风格的代码的,如何解决 webpack.config 呢?

其实有个很简单的办法,把 webpack.config.js 改名成 webpack.config.babel.js 就可以了。

Webpack 在执行时会先用 Babel 把配置文件转成 ES5 代码再继续处理。一切 Babel 支持的语言特性都可以用。


REF::