上一篇介绍的模块化打包是将我们的前端所有依赖的文件封装成一个个零散的包文件,而Webpack的真正功能并不止于此,将各种包文件组装成一个完整的工程才是Webpack真正功能所在,Webpack处理也是前端工程化的体现。那么组装成一个完整的工程,从哪个文件开始呢? 要经过怎样的处理流程呢?处理完成要输出到哪个文件呢?这就是本篇要探讨的问题。
一、资源处理流程
在介绍Webpack配置之前,我们先了解几个概念
module:模块,整个工程所依赖的各种文件、npm包、等
entry:webpack打包的入口文件,可以有一个或者多个
chunk:代码块,存在依赖关系的模块在打包后会形成一个chunk文件,每个chunk对应一个entry
bundle:由chunk经过处理(ES6转ES5、SCSS\LESS转CSS等)后会生成一个bundle文件,每个bundle对应一个chunk
entry、chunk、module、bundle关系图
二、资源输入
Webpack通过context、entry这两个配置项决定文件入口,在配置入口时,实际做了两件事:
- 确定模块入口位置:告诉webpack从哪里打包
- 定义chunk name:如果工程只有一个入口,默认chunk name为**
main**
1、Context
context 可以理解为资源入口的前缀,只能为字符串,必须为绝对路径;如下面案例入口文件都为:/src/scripts/index.js
module.exports = {
context: path.join(__dirname, './src'),
entry: './scripts/index.js'
}
module.exports = {
context: path.join(__dirname, './src/scripts'),
entry: './index.js'
}
context的目的就是在多入口的情况下让entry的编写更加简洁;同时也可以省略,默认为根目录。
2、Entry
entry webpack打包入口文件,值可以为字符串、数组、对象、函数
-
字符串形式入口
module.exports = { entry: './src/index.js', output: [ filename: 'bundle.js' ], mode: 'development' }
-
数组形式入口
使用数组形式的入口打包时,webpack会将数组的最后一个元素作为实际的入口,其余元素会在入口文件导入
module.exports = {
entry: ['babel-polyfill', './src/index.js']
}
// webpack.config.js 上面配置相当于
module.exports = {
entry: './src/index.js'
}
// index.js
import 'babel-polyfill'
- 对象形式入口
对象应用于配置多入口时,对象属性是chunk name,属性值为每个入口的文件路径(可以为数组或者字符串)
module.exports = {
entry: {
index: './src/index.js',
lib: './src/lib.js'
}
}
- 函数形式入口
用函数定义入口,路径为其返回值,只要返回字符串、数组、对象都可以;也可以返回一个promise
module.exports = {
entry: _ => ({
index: './src/index.js',
lib: './src/lib.js'
})
}
根据不同工程模式资源入口的配置
- 单页面应用:单一入口即可,如果工程很大,也会需要注意wepack产出一个bundle如果大于250KB会发生
[big]警告 - 多页面应用:为了尽可能的让资源体积小,可以配置多个入口
其实无论是单页面应用、多页面应用,在如今这个重视用户体验的环境下,都会使打包体积超过250KB,那么我们应该怎样解决呢?很幸运,webpack给我们提供了**vendor**
提取Vendor:
意为供应商,在webpack中一般指工程使用的库、框架、第三方库等,我们可以通过提取vendor的方式减小对于某个入口的打包体积,如:
module.exports = {
entry: {
app: './src/app.js',
vendor: ['react', 'react-dom', 'react-router', 'redux']
}
}
上面配置的意思是我们提取了一个叫vendor的chunk name,并且以数组的形式将第三方库房里进去,那么问题来了,我们没有设置入口路径,webpack如何打包的呢?我们可以使用**optimization.splitChunks(webpack4.0之后的插件)**将app与vendor这两个chunk中的公共部分提取出来,那么app.js产生的bundle.js只包含业务逻辑模块,第三方模块将会生成一个全新的bundle,这就是提取vendor过程。
那它是如何提升渲染速度的呢?
由于vendor只包含第三方模块,这部分不会经常变动,因此可以利用客户端缓存,在用户后续请求页面时会加快整体渲染速度。
三、资源输出
所有资源的输出配置都在output这个对象里,下面我们一一介绍
1、Filename
控制输出的文件名,字符串形式,如:
module.exports = {
entry: './src/app.js',
output: {
filename: 'app-bundle.js'
}
}
//当然也可以是一个路径,如果没有这个路径,webpack会自动创建
module.exports = {
entry: './src/app.js',
output: {
filename: './js/bundle.js'
}
}
最重要也是最实用的,webpack还支持模板语言对filename的配置-动态生成文件名
module.exports = {
entry: {
app: './src/app.js',
vendor: './src/vendor.js'
},
output: {
filename: '[name].js'
}
}
filename中的[name]会被chunk name替换(很适合多入口的情形)
下面是其他的Webpack支持的模板变量
[hash] 指代此次webpack打包所有资源生成的hash
[chunkhash] 指代当前chunk内容的hash
[id] 指代当前chunk的id
[query] 指代filename配置项中的query
模板变量的作用
- 区分chunk:根据id、name、chunkhash
- 控制客户端缓存:根据hash、chunkhash的改变与否,判断是否重新请求资源
2、path
指定资源的输出位置,必须为绝对路径,默认路径为dist目录
module.exports = {
entry: './src/index.js',
output: {
filename: '[name].js',
path: path.join(__dirname, 'dist')
}
}
3、publicPath
指定资源的请求位置,可能有的小伙伴会迷惑了,什么是资源的请求位置,什么是输出位置呢?
- 资源的输出位置:打包之后资源产生的目录,一般为dist
- 资源的请求位置:由js、css请求的间接资源路径;页面的资源分两种,一是html直接请求的,比如script标签的js,另一种是间接请求的,如异步加载的js、从css请求的图片字体等;
publicPath就是指定这部分请求资源的路径。
publicPath的三种形式
-
HTML相关
html地址:code.com/app/index.h… 异步资源地址:0.js
publicPath: '' // 实际地址:code.com/app/0.js
publicPath: './src' // 实际地址:code.com/app/src/0.j…
-
HOST相关
publicPath若以‘/’,表示当前异步请求的路径的路径是以hostname为根路径开始的
html地址:https://code.com/app/index.html
异步资源名:0.js
hostname:https://code.com
publicPath: '/' // 实际地址:https://code.com/0.js
publicPath: '/src/' // 实际地址:https://code.com/src/0.js
- CDN相关
如果publicPath的路径为绝对路径,代表当前路径是cdn相关
html地址:https://code.com/app/index.html
异步资源名:0.js
cdn资源存放地址:http://cdn.com/
publicPath: 'http://cdn.com/' // 实际请求路径地址:http://cdn.com/0.js
关于wepack的资源输入输出配置就介绍到这里啦,接下来就是loader、plugin相关,再到原理,由浅入深彻底搞懂Webpack.