webpack在启动后会从Entry里配置的Moudule开始,递归解析Entry依赖的所有Module,每找到一个module,就会根据配置的loader去找对应的转换规则,
对Module进行转换后,在解析当前Module依赖的Module。这些模块会以Entry为单位进行分组,一个Entry及其所有依赖Module被分到一个组,也就是一个Chunk
最后,webpack会将所有Chunk转换成文件输出。webpack会在恰当的时机执行Plugin里定义的逻辑
entry
入口,webpack执行构建的第一步将从Entry开始,可抽象成输入
配置:
方式1: 直接一个文件路径字符串:’./app/entry1’
方式2: 文件路径字符串数组:[‘./app/entry1’,’./app/entry2’]
方式3: 文件路径字符串对象:{A:’./app/entryA’,B:’./app/entryB’}
方式4: 函数,动态导出以上三种形式
方式1,2最后只会有一个文件会被导出,即导出一个Chunk,名为main
方式3导出多个Chunk,名称分别对应对象KEY值
output
输出结果,在webpack经过一系列处理并得到最终想要的代码后输出结果
配置:
filename
输出文件名
方式1:静态,名字写死,如bundle.js,适用于只有一个导出Chunk,一个输出文件
方式2:利用Chunk内置变量,动态拼接生成文件名,例如’[name].js’
Chunk内置变量有:id,name,hash(name的hash,可指定长度,例如[hash:8]),chunkhash(chunk内容的hash)
相关:ExtractTextWebpackPlugin使用contenthash表示文件内容hash,但提取的内容为代码本身,不是一组模块组成的chunck内容
chunkFilename
无具体入口的chunk输出的文件名
例如通过commonChunkPlugin生成的文件,或者使用import动态加载生成的文件等
因为都是chunk输出文件,所以内置变量同filename的chunk
path
输出文件的存放目录
配置:绝对路径的字符串
内置变量:仅有一个,hash,代表一次编译操作的hash值
publishPath
配置发布到线上资源的URL前缀
配置:字符串
内置变量:仅有一个,hash,代表一次编译操作的hash值
crossOriginloading
配置异步插入的script标签的crossorign值
配置:
anonymous,默认,不带cookie
use-credentials,加载时,带cookie
library
导出库的名称,与libraryTarget配合使用
配置:字符串
libraryTarget
以何种方式导出库
配置:以下字符串
‘var’|’commonjs’|’commonjs2’|’this’|’window’|’global’
libraryExport
在libraryTarget配置’commonjs’|’commonjs2’时,配置导出模块中哪些子模块被导出
module
模块处理规则
rules
数组,每一项是处理同一类文件的相关配置对象
test
处理哪些文件,文件名命中规则,
配置:字符串
include
在什么目录范围内匹配
配置:字符串/字符串数组(每一项是或的关系,只要满足一个条件就会命中)
exclude
排除哪些目录范围,再匹配
配置:字符串/字符串数组(每一项是或的关系,只要满足一个条件就会命中)
use
配置loader
loader可以看作具有文件转换功能的翻译员,告诉webpack在遇到哪些文件时使用哪些loader去加载和转换
配置:数组,每一项可以是是字符串,或者对象
默认执行顺序为数组倒序执行,即最末尾先执行
字符串:loader名称
对象:对于该loader的一些相关配置
loader
配置:字符串,loader名
option
配置:对象,给loader处理函数传入的参数
enforce
更改loader执行顺序
配置:
pre: 放在执行顺序最前面
post:放在执行顺序最后
noparse
功能类似于exclude,排除不需要进行接续处理的文件,
此类文件中不能采用模块化方式编写,即不应包含import,require,define等语句,
否则导致无法在浏览器下执行
配置:字符串,文件路径
parse
更细粒度地配置哪些语法被解析,哪些不被解析,精确到语法层面
{cmd:false,commonjs:false….}
resolve
配置如何寻找模块对应的文件
alias
通过将路径前缀指定别名,进行映射
配置:对象,key为别名,值为实际路径
例:{components:’./src/components’,…}
第三方模块一般会包含两套代码
一套采用comonjs规范的模块代码,这些文件都放在lib目录下,以package.json中指定的react.js为模块入口
一套是将React所有相关代码打包好的完整代码放到一个单独文件中,这些代码没有采用模块化,可以直接执行,
其中非压缩文件(dist/xxx.js)用于开发环境,里面包含检查和警告的代码,压缩的代码(dist/xxx.min.js)用于线上环境
默认情况下,webpack会从入口文件./node_module/xxx/xxx.js开始递归解析和处理依赖的几十个文件,会是一个耗时操作
可以通过配置reaolve.alias,让webpack处理第三方库时,直接使用单独完整的xxx.min.js,从而跳过耗时的递归解析操作
mainFields
决定优先使用第三方模块的哪份代码,按数组顺序查找,使用找到的第一份文件
默认值和target有关,
当target为web或者webwork时,值是[‘browser’,’module’,’main’]
当target为其他值时,值时[‘module’,’main’]
配置:第三方模块导出文件的关键字
例:[‘jsnext:main’,’brower’,’main’]
extensions
导入语句没有带文件后缀时,尝试寻找的后缀代表
配置:后缀字符串数组
例:[‘.ts’,’.js’,’json’]
modules
去哪些目录下寻找第三方模块,
默认node_modules 含义是先去当前目录的./node_module目录下找,如果没有找到,就去上一级目录../node_module中去找
再没有就去../../node_module中找,一次类推
配置:字符串数组
例:[‘.src/components’,’node_modules’],
可以直接在页面中import ‘.src/components’里面的模块,不用再写相对路径
descriptionFiles
配置第三方模块名
配置:字符串数组
enforceExtension
是否强制导入语句中必须带后缀
true:必须带
false:不用必须带
enforceModuleExtension
只对node_module文件夹下模块生效
通常设置false,与enforceExtension:true配合,兼容第三方模块,使用时不加后缀
plugin
扩展插件,在webpack构建流程中的特定时机注入扩展逻辑,来改变构建结果或者做一些自定义的操作
比如将css文件单独打包,或者抽离公共模块
不使用plugin处理css文件的原理大概是将CSS内容用JS字符串存储起来,在网页执行JS时,通过DOM操作,动态向HTML head标签里插入HTML style标签
使用plugin将css抽离,是从打包好的js文件再提取出来
配置:plugin实例数组,使用new操作符调用构造函数,同时将参数传递进去
例:[new commonsChunkPlugin({name:’common’})]
devServer
配置针对使用webpack-dev-server启动webpack时的一些配置,相当于配置webpack-dev-server
devServer 会启动一个http服务器,用于服务网页请求,
同时会启动webpack,将webpack构建的文件保存在内存中,在要访问输出的文件时,必须通过HTTP服务访问,
同时会开启webpack的监听模式,devServer会让webpack在构建出的js代码里注入一个代理客户端用于控制网页,
网页和devServer之间通过webSocket协议通信,以方便devServer主动向客户端发送命令,
devServer在收到来自webpack的文件变化通知时,通过注入的客户端控制自动刷新网页,做到实时预览。
1.由于devServer不会理会配置的output.path属性,所以获取打包文件时,应该依据HTTP获取,不再依据output.path获取
因此要注意使用devServer之后打包文件的获取路径要进行更改。
2.index.html因为脱离js模块化系统,webpack不知道它的存在,故,更改index.html,不会被监听到
3.除了通过重新刷新整个网页来实现预览,devServer还有一种被称作模块热替换的刷新技术。
模块热替换能做到在不重新加载整个网页的情况下,通过将已更新的模块替换老模块,再重新执行一次来实现实时预览。
模块热替换相对于默认的刷新机制能提供更快的响应速度和更好的开发体验。
可以在执行devServer时加上 –hot开启
4.启动webpack时,加上–devtool source-map 可生成souce-map,方便在浏览器中调试源代码
配置
hot是否开启热加载(热替换)
模块热替换原理:
类似于自动刷新,都需要向要开发的网页中注入一个代理客户端来连接DevServer和网页,区别在于热替换时会多出三个用于热替换的文件
在发生文件变化时,会重新生成一个用于替换老模块的补丁文件,补丁文件中会包含新编译的代码,页面会使用新编译的代码
当子模块发生更新时,更新事件会一层一层向上传递,会从根组件传递到main.js,直到有某层文件接收了当前变化的模块,
就会去执行自定义的逻辑,如果事件一直往上抛,到最外层都没有文件接收它,则会直接刷新网页,
最直观的就是修改main.js时,会发生整个页面刷新
而对于.css文件,在使用style-loader处理时会注入用于接收CSS的代码,所以在修改.css文件时,会触发模块热替换
inline
开启实现实时预览,自动刷新;
不开启,使用iframe方式运行开发的网页,需要去localhost:8080/webpack-dev-pack实时预览
historyApiFallback:用于H5History API单页应用开发
contentBase:devServer HTTP服务器文件根目录
headers:在HTTP响应中注入HTTP响应头
host:监听地址
port:监听端口
allowedHosts:只有HTTP请求的HOST在列表中才正常返回
disabledHostCheck:是否关闭用于DNS重新绑定的HTTP请求的HOST检查
https:是否运行在https上
clientloglevel:配置客户端日志等级
target
构建不同运行环境的代码
devtool
有很多选项可配置,选项之间可以随意组合
6个关键配置项:
eval:用eval语句包裹需要安装的模块
source-map:生成独立的Source Map文件
hidden:不在JS文件中指出 Source Map文件的位置,这样浏览器就不会自动加载Source Map
inline:将生成的Source Map转换成BASE64格式内嵌在JS文件中
cheap:在生成的Source Map文件中不会包含列信息,这样计算量更小,输出的Source Map文件更小,同时Loader输出的Source Map不会被采用
module:来自Loader的Source Map被简单的处理成每行一个模块
‘source-map’
仅设置source-map会造成以下两个问题
1.会输出质量最高且最详细的Source Map,会造成构建速度缓慢,特别是开发过程中需要频繁修改时会增加等待时间
2.会将Source Map暴露,若构建发布到线上代码的Source Map暴露等于源码被泄露
解决:
1.开发环境下将dev-tool设置成cheap-module-eval-source-map,因为生成这种Source Map的速度最快,能加速构建,
在开发环境下不会做代码压缩,所以在Source Map中即使没有列信息,也不会影响断点调试
2.生产环境下将dev-tool设置成hidden-source-map,生成最详细的Source Map,但不会将Source Map暴露出去
在生产环境会做代码压缩,一个JS文件只有一行,所以需要列信息
生产环境通常不会将Source Map上传到HTTP服务器让用户获取,而是上传到JS错误错误收集系统,
在错误收集系统上根据Source Map和收集到的JS运行错误堆栈,计算出错误所在源码位置
第三方模块
webpack是默认不会加载第三方模块附带的Source Map文件的,会在转换过程中生成Source Map,
为了让webpack加载这些第三方模块的Source Map,需要使用source-map-loader1
2
3
4
5
6
7
8
9rules:[{
test:/\.js$/,
include:[path.resolve(root,'node_module/some-component')],
//加载Source Map时计算量很大,因此要避免让该Loader处理过多的文件,不然会导致构建变慢
use:[source-map-loader],
//要将source-map-loader的执行顺序放到最前面,
//如果在source-map-loader之前有Loader转换了该JS文件,就会导致Source Map映射错误
enforce:'pre'
}]
watch
是否开启监听文件变动模式
watchOption
配置监听规则,在开启监听模式时,才有用
ignore:不监听的文件/文件夹
aggregateTimeout:监听到变化发生后,等多少ms再去执行动作,截流,防止文件更新太快而导致重新编译的频率太快,默认为300ms
poll:判断文件是否发生变化是通过不停的询问系统指定文件有没有变化实现的,这里配置每秒询问多少次
文件监听原理:
默认情况下,webpack会从配置的Entry文件出发,递归出Entry文件依赖的文件,将这些依赖的文件都加入到监听列表中,而不是监听整个项目目录下的文件
然后对列表中每个文件都定时执行检查,定时获取这个文件的最后编辑时间,每次都存下最新的最后编辑时间,如果发现当前获取的和最后编辑时间不一致,
就认为该文件发生了变化,watchOption.poll控制定时检查的周期
文件发生变化后并不会立刻告诉监听者,而是先缓存起来,收集一段时间的变化后,再一次性告诉监听者。watchOption.aggregateTimeout用于配置这个等待时间
这样做的目的就是在编辑代码过程可能会高频地输入文字,导致文件变化的时间高频发生,如果每次都重新执行构建,就会让构建卡死
externals
配置哪些模块不用被打包
resolveloader
配置如何去寻找loader