阅读 2590

webpack(2)——配置项详解

引子

昨天有关 Webpack 笔记也是有许多人喜爱的,所以今天再接再厉汇总了第二篇笔记,欢迎各位指点不足之处。
之后的笔记应该会记录一些实际开发了,希望在这个周末内彻底入门 Webapck

  1. webpack (1)——核心概念的理解
  2. webpack(2)——配置项详解
  3. ...

1. mode

mode 配置项是 webpack4 新增的配置项,这个配置项是必须的。

如果使用的不是 webpack4 ,该配置项了解一下即可,使用了 webpack4 以上版本,也可以了解一下即可。
事实上它并非必须的,但是如果忽略它, webpack 会有个警告。

该配置项的属性值类型是 String,并有三个可选项:

  • production
  • development
  • none

1.1 production 默认值

production 默认值会给你提供一系列有效的默认值以便部署你的应用,它注重:

  • 小的输出体积
  • 运行快速的代码
  • 忽略仅在开发时需要的代码
  • 不暴露源码和文件路径
  • 易于使用的输出产物

1.2 development 默认值

该默认顾名思义是注重最好的开发体验,它注重:

  • 浏览器调试工具
  • 快速开发周期中的快速增量编译
  • 在运行过程中提供有效的错误信息

当人对于初学者来说,对于 webpack 的优化还有一段距离,首先我们大体了解一下即可。

它还有很多其他的优化默认值,但是效果可能并不美好,因为它针对的某些特定的大型项目,而我们只是入门,所以了解即可。

2. Module

module 中配置处理模块的规则。

2.1 配置 Loader

rules 配置模块的读取和解析规则,通常用来配置 Loader。其类型是一个数组,数组里每一项都描述了如何去处理部分文件。配置一项 rules 时大致可通过以下方式来实现:

  1. 条件匹配:通过 testincludeexclude 三个配置项来命中 Loader 要应用规则的文件。
  2. 应用规则:对选中后的文件通过 use 配置项来应用 Loader,可以只应用一个 Loader 或者按照从后往前的顺序应用一组 Loader,同时还可以分别给 Loader 传入参数。
  3. 重置顺序:一组 Loader 的执行顺序默认是 从右到左 执行,通过 enforce 选项可以让其中一个 Loader 的执行顺序放到最前或者最后。
module: {
  rules: [
    {
      // 命中 JavaScript 文件
      test: /\.js$/,
      // 用 babel-loader 转换 JavaScript 文件
      // ?cacheDirectory 表示传给 babel-loader 的参数,用于缓存 babel 编译结果加快重新编译速度
      use: ['babel-loader?cacheDirectory'],
      // 只命中src目录里的js文件,加快 Webpack 搜索速度
      include: path.resolve(__dirname, 'src')
    },
    {
      // 命中 SCSS 文件
      test: /\.scss$/,
      // 使用一组 Loader 去处理 SCSS 文件。
      // 处理顺序为从后到前,即先交给 sass-loader 处理,再把结果交给 css-loader 最后再给 style-loader。
      use: ['style-loader', 'css-loader', 'sass-loader'],
      // 排除 node_modules 目录下的文件
      exclude: path.resolve(__dirname, 'node_modules'),
    },
    {
      // 对非文本文件采用 file-loader 加载
      test: /\.(gif|png|jpe?g|eot|woff|ttf|svg|pdf)$/,
      use: ['file-loader'],
    },
  ]
}
复制代码

上面的例子基本将配置 loader 的大部分配置项都涵盖了,非常清晰简洁。

// 使用一组 Loader 去处理 SCSS 文件。
// 处理顺序为从后到前,即先交给 sass-loader 处理,再把结果交给 css-loader 最后再给 style-loader。
use: ['style-loader', 'css-loader', 'sass-loader']
复制代码

在上面的例子中,对于多 Loader 处理方式中,采用的是数组,执行数序为从左到右

除此之外,我们还可以通过一个 Object 方式来写这一块的代码

use: [
  {
    loader:'babel-loader',
    options:{
      cacheDirectory:true,
    },
    // enforce:'post' 的含义是把该 Loader 的执行顺序放到最后
    // enforce 的值还可以是 pre,代表把 Loader 的执行顺序放到最前面
    enforce:'post'
  },
  // 省略其它 Loader
]
复制代码

对于 Loader 的最后一点补充就是关于命中文件的三个配置项:textincludeexclude

在上述例子中,这三兄弟的配置只传入了一个字符串或者是正则,其实他们还都可以支持数组,数组之间的关系为 “或”的关系,即文件满足数组中的任何一个条件,都会被命中。

2.2 noParse

noParse 配置项可以让 Webpack 忽略对部分没采用模块化的文件的递归解析和处理,这样做的好处是能提高构建性能。 原因是一些库例如 jQueryChartJS 它们庞大又没有采用模块化标准,让 Webpack 去解析这些文件耗时又没有意义。

noParse 是可选配置项,类型需要是 RegExp、[RegExp]、function 其中一个。

// 使用正则表达式
noParse: /jquery|chartjs/

// 使用函数,从 Webpack 3.0.0 开始支持
noParse: (content)=> {
  // content 代表一个模块的文件路径
  // 返回 true or false
  return /jquery|chartjs/.test(content);
}
复制代码

注意被忽略掉的文件里不应该包含 import 、 require 、 define 等模块化语句,不然会导致构建出的代码中包含无法在浏览器环境下执行的模块化语句。

2.3 parse

因为 Webpack 是以模块化的 JavaScript 文件为入口,所以内置了对模块化 JavaScript 的解析功能,支持 AMD、CommonJS、SystemJS、ES6。 parser 属性可以更细粒度的配置哪些模块语法要解析哪些不解析,和 noParse 配置项的区别在于 parser 可以精确到语法层面, 而 noParse 只能控制哪些文件不被解析。 parser 使用如下:

module: {
  rules: [
    {
      test: /\.js$/,
      use: ['babel-loader'],
      parser: {
      amd: false, // 禁用 AMD
      commonjs: false, // 禁用 CommonJS
      system: false, // 禁用 SystemJS
      harmony: false, // 禁用 ES6 import/export
      requireInclude: false, // 禁用 require.include
      requireEnsure: false, // 禁用 require.ensure
      requireContext: false, // 禁用 require.context
      browserify: false, // 禁用 browserify
      requireJs: false, // 禁用 requirejs
      }
    },
  ]
}
复制代码

3. Resolve

该配置项主要是配置 Webapck 如何寻找模块所对应的文件。
Webpack 内置 JavaScript 模块化语法解析功能,默认会采用模块化标准里约定好的规则去寻找,但你也可以根据自己的需要修改默认的规则。

该配置项的属性蛮多的,但是我觉得我能用得到的也就几个=。=

3.1 alias

resolve.alias 配置项通过别名来把原导入路径映射成一个新的导入路径。例如使用以下配置:

// Webpack alias 配置
resolve:{
  alias:{
    components: './src/components/'
  }
}
复制代码

当你通过 import Button from 'components/button' 导入时,实际上被 alias 等价替换成了 import Button from './src/components/button'

以上 alias 配置的含义是把导入语句里的 components 关键字替换成 ./src/components/

这样做可能会命中太多的导入语句,alias 还支持 $ 符号来缩小范围到只命中以关键字结尾的导入语句:

resolve:{
  alias:{
    'react$': '/path/to/react.min.js'
  }
}
复制代码

react$ 只会命中以 react 结尾的导入语句,即只会把 import 'react' 关键字替换成 import '/path/to/react.min.js'

该方法的应用场景有待我去探索,真不太清楚这样替换的原因。
但是我觉得是蛮有用的。

3.2 extensions

在导入语句没带文件后缀时,Webpack 会自动带上后缀后去尝试访问文件是否存在。 resolve.extensions 用于配置在尝试过程中用到的后缀列表,默认是:

extensions: ['.js', '.json']

这个配置项应该有用,在处理 TypeScriptvue 这些文件的时候,可以设置一下

3.3 enforceExtension

resolve.enforceExtension 如果配置为 true 所有导入语句都必须要带文件后缀, 例如开启前 import './foo' 能正常工作,开启后就必须写成* import './foo.js'。*

对于 Resolve 的其他配置项,大体了解一下即可,我觉得可能用的不是特别多。

3.4 Plugins

对于 Plugins 的使用,上一节也写到过,还是那句话吧:

使用 Plugin 的难点在于掌握 Plugin 本身提供的配置项,而不是如何在 Webpack 中接入 Plugin。

4. DevServer

使用 DevServer 能干啥?

  • 提供 HTTP 服务来预览本地文件;
  • 监听文件的变化并自动刷新页面;
  • 支持 Source Map,以方便调试。

DevServer 会启动一个 HTTP 服务器 用于服务网页请求,同时会帮助启动 Webpack,并接收 Webpack 发出的文件更新信号,通过 webSocket 协议自动刷新网页做到实时预览。

4.1 开启一个DevServer 服务

集成 DevServer ,首先需要安装一下:

npm i -D webpack-dev-server
复制代码

如果不是通过 npm run 启动,直接将 webpack-dev-server 安装到全局,直接使用,会报: webpack-dev-server: command not found

安装成功后,执行 webpack-dev-server 命令,接着一连串日志输出:

i 「wds」: Project is running at http://localhost:8080/
i 「wds」: webpack output is served from /
i 「wdm」: Hash: 5cece8a0d7243a4cf1ba
Version: webpack 4.8.3
Time: 1627ms
Built at: 2018-05-18 14:18:57

......
复制代码

4.2 ./dist/bundle.js 加载 404 问题

用浏览器直接打开这个地址你会发现页面空白,错误原因是 ./dist/bundle.js 加载404了。 同时你会发现并没有文件输出到 dist 目录,原因是 DevServer 会把 Webpack 构建出的文件保存在内存中,在要访问输出的文件时,必须通过 HTTP 服务访问。 由于 DevServer 不会理会 webpack.config.js 里配置的 output.path 属性,所以要获取 bundle.js 的正确 URLhttp://localhost:8080/bundle.js

4.3 实时预览

webpack --watch 会开启监听模式,而 webpack 启动默认是关闭的,开启 DevServer 默认开启监听模式,所以修改需要被打包的文件浏览器会被自动刷新,这里主要应用到了 Websocket ,有需要的童鞋可以去了解一下。

4.4 修改index.html并不会被刷新的原因

如果尝试修改 index.html 文件并保存,你会发现这并不会触发以上机制,导致这个问题的原因是 Webpack 在启动时会以配置里的 entry 为入口去递归解析出 entry 所依赖的文件,只有 entry 本身和依赖的文件才会被 Webpack 添加到监听列表里。 而 index.html 文件是脱离了 JavaScript 模块化系统的,所以 Webpack 不知道它的存在。

可以尝试用 raw-loader 来处理一下 html

4.5 热更新 --hot

当然上述实现页面更改都是基于页面刷新实现的,DevServer 还有一种被称作模块热替换的刷新技术——热更新。

DevServer 默认是关闭热更新的,可以通过

webpack-dev-server --hot
复制代码

来开启

4.6 Source Map

webpack-dev-server --devtool source-map
复制代码

这个功能就不多说了。

4.7 devserver 配置项

plugins: [
    new webpack.HotModuleReplacementPlugin()
],
devServer: {
    hot: true,
    open: 'http://127.0.0.1:9000',
    // inline: true,
    // contentBase: path.join(__dirname, "dist"),
    compress: true,
    port: 9000
}
复制代码

devServer 的配置项也是蛮多的,看了好久,发现有用的也就是这几个吧:

  • hot: 开启热更新;
  • open:自动打开要开发的网页;
  • inline:实时预览功能,即页面刷新,用热更新替代即可,没必要管他;
  • compress: 开启 Gzip 压缩;
  • port:服务器监听的端口号,指定即可,如果不指定,默认是 8080 ,冲突了会自动顺位 8081、8082,挺赞的。

该配置项还有很多其他的属性,我觉得目前我是用不上,但不妨碍我们了解一下,在此我就不一一记录了。

写在最后

webpack 的基本操作到目前为止基本结束,他能完成的功能还是有很多的,从它的 N 多配置项就能看出来,但我们不需要全部记住,记住之前的核心概念和它的基本原理就可以,之后再根据自己的经验去判断它属于哪一块再去查文档就没问题。

  • 让源文件加入到构建流程中去被 Webpack 控制,配置 entry。
  • 自定义输出文件的位置和名称,配置 output。
  • 自定义寻找依赖模块时的策略,配置 resolve。
  • 自定义解析和转换文件的策略,配置 module,通常是配置 module.rules 里的 Loader。
  • 其它的大部分需求可能要通过 Plugin 去实现,配置 plugin。

参考

webpack4:模式与优化
深入浅出 Webpack
webpack 配置项
[HMR] Hot Module Replacement is disabled
Gzip