概念
本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包工具。当 webpack 处理应用程序时,它会在内部构建一个**依赖图(dependency graph)**,此依赖图会映射项目所需的每个模块,并生成一个或多个 bundle。
上面一段话摘自官方文档,由此引申出下面几个问题
- 什么是依赖图?依赖图是怎么映射项目所需的模块?
- bundle 是什么?
依赖图
webpack 开箱即用,可以无需使用任何配置文件。然而,webpack 会假定项目的入口起点为工程目录 src/index
,然后会在 dist/main.js
输出结果,并且在生产环境开启压缩和优化。
也就是说,在没有任何配置文件和命令行传参的情况下,会有一个默认的入口起点 src/index
(如果自己配置了入口就会使用配置好的),直接执行
1 | webpack |
webpack 会将 mode
的默认值设置为 production
并开始打包,从 入口起点 开始,webpack 递归地构建一个依赖图,这个依赖图包含着应用程序所需的每个模块,然后将所有这些模块打包为少量的 bundle - 通常只有一个 - 可由浏览器加载。
所以依赖图的生成是首先取决于入口文件,当然,在入口文件中如果也引入了其它文件,那么其它文件也会变成依赖图一部分。
bundle
bundle 就是 webpack 生成的文件,bundle 里包含多个 chunk - 代码块,可能多个 bundle 会存在相同的代码块,所以需要用代码分离来共享相同代码块部分
process.env.NODE_ENV
在 wepback4 中,存在两种模式,development
、production
,webpack 自动会引入definePlugin
,这个插件会决定在开发环境与生产环境(dev-vs-prod)下,server tools(服务期工具)、build scripts(构建脚本) 和 client-side libraries(客户端库) process.env.NODE_ENV
的值,然而,在 webpack.config.js
中是不会存在这个值的,要在配置文件中使用process.env.NODE_ENV
,有以下两种方法
- 命令行中传入
--env.NODE_ENV production|development
,webpack.config.js
中导出函数
1 | // webpack.config.js |
cross-env
传参
1 | // 安装cross-env |
入口 entry
入口的写法,
1 | // webpack.config.js |
输出 output
输出主要是告诉 webpack 打包后的 bundle 放在哪里,以及如何命名这些文件
1 | const path = require("path"); |
在运行时设置 publicPath
所谓运行时,即在打包完成后运行应用程序的时候。一般在 output 中配置的 publicPath 是固定的,但是,我们可能需要在运行的时候动态加载 publicPath,webpack 暴露了一个名为 webpack_public_path 的全局变量,通过改变这个变量的值达到我们的目的。
- 创建一个文件
public_path.js
1 | __webpack_public_path__ = "http://some.cdn.com/some"; |
- 在入口文件中引入
1 | // entry.js |
如果在 entry 文件中使用 ES2015 module import,则会在 import 之后进行 webpack_public_path 赋值。在这种情况下,你必须将 public path 赋值移至一个专用模块中,然后将它的 import 语句放置到 entry.js 最上面
chunkFilename
定义非入口 chunk 文件的名称。这个在动态导入时可以设置分出来的文件名
如下,在文件中使用 import()时,webpack 会在打包时将 a.js 分离出去,成为一个新的文件,这个文件在没有设置 chunkFilename 时会自动使用[模块id].js
命名(如果注释名存在则使用注释名), 当然,使用注释命名也可以
1 | // entry.js |
crossOriginLoading | jsonpScriptType | chunkLoadTimeout
crossOriginLoading,只用于 target
是 web
,使用了通过 script 标签的 JSONP 来按需加载 chunk。通过加载资源的 origin 信息来判断是否跨域,比如在 cdn 加载 chunk 的时候肯定是跨域的,那么此设置就会生效
jsonpScriptType 设置 jsonp 中 script 的 type 属性
chunkLoadTimeout 设置 script 中超时时间,默认 120s
1 | if (script.src.indexOf(window.location.origin + "/") !== 0) { |
filename 中的 chunkhash contenthash
chunkhash 和 contenthash 的区别在于,都是 chunk 内容,不过 contenthash 是通过ExtractTextWebpackPlugin
提取出来的,如果 js 文件改变打包后 css 内容即使没变 css hash 也会改变
libraryTarget
配置如何暴露 library。
- var. (默认值)当 library 加载完成,入口起点的返回值将分配给 library 变量,会覆盖掉已经定义过的全局变量(谨慎使用)
1 | output.library = "someLibName"; |
- assign. 比
'var'
少了个 var,可以说没区别
1 | someLibName = module.exports; // 输出结果 |
- this.
- output.library 没有赋值,webpack 将把 library 对象上所有的属性挂载到浏览器的 this 上,也就是 window
1 | (function(e, a) { for(var i in a) e[i] = a[i]; }(this, module.exports) |
output.library = 'someLibName'
则会将对象挂载到this['someLibName']
1 | this["someLibName"] = module.exports; |
- window 同上
1 | window["someLibName"] = module.exports; |
- global 分配给 global 对象
1 | global["someLibName"] = module.exports; |
- commonjs 分配给 exports 对象。这个名称也意味着,模块用于 CommonJS 环境
1 | exports["someLibName"] = module.exports; |
- commonjs2 模块定义系统.用于
CommonJS
系统,入口起点的返回值将分配给module.exports
对象
与commonjs
的区别是不用指定 output.library
模块定义系统会使
bundle
带有更多的头部处理,以便兼容各种模块系统
1 | module.exports = _entry_return_; |
- amd 将 library 导出为 AMD 模块
可以由 RequireJS 或任何兼容的模块加载器加载。直接加载会报错。
1 | // MyLibrary.js |
- umd 将 library 导出为所有的模块定义下都可运行的方式。既可以在 CommonJS, AMD 环境下运行,也可以在浏览器环境下且无需 requireJS 的情况下运行。
1 | // webpack配置 |
- jsonp 将导出结果包裹在以 library 变量作为函数名的容器中
1 | library: "MyLibrary"; |
exports、module.exports 和 export、export default
require: node 和 es6 都支持的引入
export / import : 只有 es6 支持的导出引入
module.exports / exports: 只有 node 支持的导出
node 模块
- commonjs 导入导出 nodejs 支持,浏览器不支持(引用 requirejs 也可以支持)。在 webpack 打包时,如果使用了 module.exports 作为最终输出时,在浏览器中运行是获取不到模块中的变量的
Node 里面的模块系统遵循的是 CommonJS 规范。CommonJS 定义的模块分为: 模块标识(module)、模块定义(exports) 、模块引用(require)。
当 Node 执行一个文件时,会为文件生成一个 exports 和 module 对象,而 module 对象的 exports 属性和 exports 指向同一个内存地址。
1 | exports = module.exports = {}; |
当 Node 导入某个文件模块时,实际上是导入文件的 module.exports 属性。重新给 exports 属性赋一个对象会导致 exports 属性与 module.exports 断开连接。
- es6 导入导出 主要用于浏览器加载模块,当然 nodejs 也支持
- export 与 export default 均可用于导出常量、函数、文件、模块等
- 在一个文件或模块中,export、import 可以有多个,export default 仅有一个
- 通过 export 方式导出,在导入时要加{ },export default 则不需要
- export 能直接导出变量表达式,export default 不行。
1 | // testEs6Export.js |
commonjs vs commonjs2
那么 webpack 打包 library 时 commonjs 与 commonjs2 的区别就是 commonjs 必须赋值一个变量作为 exports 的属性,commonjs2 则是直接导出为 module.exports
的对象
缓存
利用缓存技术可以合理利用浏览器缓存减少请求,加快网站的加载速度。
1 | module.exports = { |
output.umdNamedDefine
当使用了 libraryTarget: "umd"
,设置:
1 | module.exports = { |
output.pathinfo
开启后多了下面的注释部分,会导致造成垃圾回收性能压力,建议还是关闭
1 | /***/ "tjUo": |
模式 mode
值有:
none
,development
,production
(默认)。设置NODE_ENV
并不会自动地设置 mode。
用法
1 | // 配置文件 |
选项 | 描述 |
---|---|
development | 会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 development。启用 NamedChunksPlugin 和 NamedModulesPlugin。 |
production | 会将 DefinePlugin 中 process.env.NODE_ENV 的值设置为 production。启用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin, NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPlugin 和 TerserPlugin。 |
none | 退出任何默认优化选项. |
mode: development
1 | // webpack.development.config.js |
optimization
webpack 4 特有的优化选项,可以进行压缩代码,分包等操作
minimize
开启后使用TerserPlugin压缩。mode:production
时自动开启
minimizer
测试后只在mode: production
时有效,可以配置 terserPlugin 的参数
splitChunks
用于分割代码块,提取出公用代码块。在多页面项目或者动态导入模块的时候非常有用,能减少初始加载代码的大小,提升网页首屏的加载速度。
splitChunks
默认只影响按需块,当然也可以通过设置 chunks: 'initial'
来拆分公用初始代码块。
splitChunks
总是会提取按需块
先看看 webpack 中默认的splitChunks
设置
1 | module.exports = { |
多页面分割代码
1 | let entry = { |
模块(module)
webpack 模块是管理各种文件资源的途径,通过 loader 能解析非.js 文件
css
普通 css
通过import './style.css'
方法引入样式文件,在没有配置 module 的情况下 webpack 是不会正常解析 css 文件的,所以我们必须引入style-loader
来将引入的 css 文件解析出来。当然也要引入css-loader
来解析 css 文件的内容,css-loader 也能解析 css 中的引入:@import
及url()
(可以配置不解析某些资源)
1 | // 安装 |
sass
1 | // 安装 |
postcss
在PostCSS官网有着这样的对 PostCSS 特性介绍,箭头后面是对应功能的插件及其 github 地址。
- increase code readability (增加代码可读性,补全) → Autoprefixer
- Use tomorrow’s CSS ,today!(使用下代 css) → postcss-cssnext
- The end of global CSS(模块化 css)→ postcss-modules
- Avoid errors in your CSS(错误提示) → stylelint
- Powerful grid CSS(栅格系统) → lost →lost
webpack 中使用 postcss
PostCSS Preset Env
可以将现代 CSS 转换为大多数浏览器可以理解的内容,并根据目标浏览器或运行时环境确定所需的 polyfill。
启用 sourceMap 支持,postcss-loader 将使用其他加载器提供的先前的 sourceMap 并进行相应的更新。除style-loader
之外的 loader 都需要配置sourceMap: true
1 | // 安装 |
images 图像
1 | // 安装 |
fonts 字体
file-loader
和 url-loader
可以接收并加载任何文件,所以也可以用来加载 fonts 字体文件
1 | module.exports = { |
数据 json xml
加载的资源还有数据,如 JSON 文件,CSV、TSV 和 XML。类似于 NodeJS,JSON 支持实际上是内置的,也就是说 import Data from './data.json'
默认将正常运行
1 | + { |