自动清理构建目录
避免构建前每次都需要手动删除dist,使用clean-webpack-plugin,默认删除output指定的输出目录,使用前安装和引入插件即可。
plugins:[
new CleanWebpackPlugin()
]
PostCSS插件autoprefixer自动补齐CSS3前缀
CSS3的属性为什么需要前缀
由于众多浏览器的标准并没有统一,目前来看有四种浏览器内核,IE,火狐,谷歌,欧朋
举个例子.box{
-moz-border-radius: 10px;
-webkit-border-radius:10px;
-o-border-radius:10px;
border-radius:10px;
}
在远古时期,其他浏览器的支持不是很好的情况下,需要我们手动添加前缀,因为我们平时编写的css文件很多,如果每一行样式我们都需要手动的写一些前缀,无疑是非常大的工作量,那么有没有办法自动补齐CSS3前缀?
PostCSS插件autoprefixer自动补齐CSS3前缀
使用autoprefixer插件
根据Can I Use 规则(caniuse.com/)
module:{
rules:[
{
test:/\.less$/,
use:[
'style-loader',
'css-loader',
'less-loader',
{
loader:'postcss-loader',
options:{
plugins:() => [
require('autoprefixer')({
browsers:["last 2 version", ">1%","IOS 7"]
})
]
}
}]
}]
}
browsers可以指定它兼容的版本
移动端CSS px自动转换rem
自从移动设备普及之后,设备分辨率是不断变化的,这样的话对前端开发来说会造成一个比较大的问题,需要不断的进行页面的适配。以前是怎样解决这个问题呢?
CSS媒体查询实现响应式布局
缺陷:需要写多套适配样式代码使用px2rem-loader
module:{
rules:[
{
test:/\.less$/,
use:[
'style-loader',
'css-loader',
'less-loader',
{
loader:"px2rem-loader",
options:{
remUnit:75,
remPrecision:8
}
}]
}]
}
通过这个px2rem-loader可以将px转换成rem,转换完成之后我们需要知道一个rem等于多少px,可以使用手淘的lib-flexble(github.com/amfe/lib-fl…),这个lib-flexible会自动根据当前设备的宽高来计算根元素实际font-size的值。
静态资源内联
资源内联的意义
代码层面:
- 页面框架的初始化脚本
- 上报相关打点
- css内联避免页面闪动
请求层面:
- 减少HTTP网络请求数
- 小图片或者字体内联(url-loader)
HTML和JS内联
CSS内联
- 方案一:借助style-loader
loader:'style-loader',
options:{
//将样式插入到<head>
inserAt:'top',
//将所有的style标签合并成一个
singleton:true,
}
- 方案二:html-inline-css-weboack-plugin
多页面应用打包通用方案
多页面应用(MPA)概念
每一次页面跳转的时候,后台服务器都会给返回一个新的html文档,这种类型的网站也就是多页网站,也叫多页应用。
多页面打包基本思路
每个页面对应一个entry,一个html-webpack-plugin
缺点:每次新增或删除页面需要修改webpack配置。
module.exports = {
entry:{
index:'./src/index.js',
search:'./src/search.js'
}
多页面打包通用方案
- 动态获取entry和设置html-webpack-plugin数量
module.exports = {
entry:{
index:'./src/index/index.js',
search: './src/search/index.js'
}
}
通过js脚本获取src所有的目录,就可以知道入口文件的数量,在打包的时候就可以动态的设置html-webpack-plugin。
- 利用glob.sync
entry:
glob.sync(path.join(__dirname,'./src/*/index.js'))
以这种同步的方式把文件都查询出来,获取当前的构建的文件下面,匹配src下面一级目录,然后再index.js,这样我们就把所有的entry都动态获取出来了,我们根据entry的数量再动态设置html-webpack-plugin的数量。
使用source map
作用:通过surce map定位到源代码。
- 大家可以看看阮一峰大神的souce map科普文:(www.ruanyifeng.com/blog/2013/0…)
- 开发环境开启,线上环境关闭,线上排查问题的时候可以将sourcemap上传到错误监控系统。
source map关键字
- eval:使用eval包裹模块代码
- source map:产生.map文件
- cheap:不包含列信息
- inline: 将.map作为DataURL嵌入,不单独生成.map文件
- module:包含loader的sourcemap
source map类型
提取页面公共资源
基础库分离
- 思路: 将react,react-dom基础包通过cdn引入,不打入Bundle
- 方法:使用html-webpack-externals-plugin
SplitChunksPlugin 进行公共脚本分离
webpack4内置的,替代CommonsChunkPlugin插件
chunks参数说明:
- async 异步引入的库进行分离(默认)
- initial同步引入的库进行分离
- all 所有引入的库进行分离(推荐)
SplitChunksPlugin分离基础包
test:匹配出需要分离的包
module.exports = {
optimization:{
splitChunks:{
cacheGroups:{
commons:{
test:/(react|react-dom)/,
name:'vendors',
chunks:'all'
}
}
}
}
}
利用SplitChunksPlugin分离页面公共文件
- miniChunks:设置最小引用次数为2次
- minuSize:分离的包体积的大小
module.exports = {
optimization:{
splitChunks:{
miniSize:0
cacheGroups:{
commons:{
name:'commons',
chunks:'all'
minChunks:2
}
}
}
}
}
tree shaking(摇树优化)
概念: 1个模块可能有多个方法,只要其中的某个方法使用到了,则整个文件都会被打到bundle里面去,tree shaking就是只把用到的方法打入bundle,没用到的方法会在uglify阶段被擦除掉。
使用: webpack默认支持,在.babelrc里设置modules: false即可. production mode的情况下默认开启
要求:必须是ES6的语法,CJS的方式不支持
DCE(Elimination)
- 代码不会被执行,不可到达
- 代码执行结果不会被用到
- 代码只会影响死变量(只写不读)
Tree-shaking原理
利用ES6模块的特点:
- 只能作为模块顶层的语句出现
- import 的模块只能是字符串常量
- import binding是immutable的
代码擦除:uglify阶段删除无用代码
在编译阶段,tree-shaking知道哪些代码没有用到之后,会将其注释标记,在uglify阶段将其删除掉。
ScopeHoisting使用和原理分析
没有开启scopeHoisting的会产生什么现象?
构建后代码存在大量闭包代码
会导致什么问题?
- 大量函数闭包包裹代码,导致体积增大(模块越多越明显)
- 运行代码时创建的函数作用域变多,内存开销变大
模块转换分析
结论- 被webpack转换后的模块会带上一层包裹
- import会被转换成__webpack_require
进一步分析webpack的模块机制
- 打包出来的是一个IIFE(匿名闭包)
- modules是一个数组,每一项是一个模块初始化函数
- __webpack_require用来加载模块,返回module.exports
- 通过WEBPACK_REQUIRE_METHOD(0)启动程序
scope hoisting原理
原理:将所有的模块的代码按照引用顺序放在一个函数作用域里,然后适当的重命名一些变量以防止变量名冲突
scope hoisting使用
webpack mode 为production默认开启,必须是ES6,CJS不支持。
plugins:[
new webpack.optimize.ModuleConcatenationPlugin()
]
代码分割和动态import
代码分割的意义:对于大的Web应用来讲,将所有的代码都放在一个文件中 显然是不够有效的,特别是当你的某些代码块是在某些特殊的时候才会被使用到。webpack 有一个功能就是将你的代码库分割成chunks (语块),当代码运行到需要它们的时候再进行加载。
适用的场景:
- 抽离相同代码到一个共享块
- 脚本懒加载,使得初始下载的代码更小
懒加载JS脚本的方式
- CommonJS:require.ensure
- ES6:动态import(目前还没有原生支持,需要babel转换)
如何使用动态import?
安装babel插件
npm install @babel/plugin-syntax-dynamic-import--save-dev
ES6:动态import(目前还没有原生支持,需要babel转换)
{
"pulings":["@babel/plugin-syntax-dynamic-import"],
...
}
webpack打包组件和基础库
webpack打包库和组件
webpack除了可以用来打包应用,也可以用来打包js库
实现一个大整数加法库的打包
- 需要打包压缩版和非压缩版本
- 支持AMD/CJS/ESM模块引入
库的目录结构和打包要求
打包输出的库名称
- 未压缩版 large-number.js
- 压缩版 large-number.min.js
|-/dist
|-large-number.js
|-large-number.min.js
|-webpack.config.js
|-package.json
|-index.js
|-/src
|-index.js
如何将库暴露出去?
- library:指定库的全局变量
- library:支持库引入的方式
output:{
filename:"[name].js",
library:"largeNumber",
libraryExport:"default",
libraryTarget:"umd"
}
优化构建命令行的显示日志
了解统计信息stats
如何优化命令行的构建日志
使用friendly-errors-webpack-plugin
- success:构建成功的日志提示
- warning:构建警告的日志提示
- error:构建报错的日志提示 status设置成errors-only
module.exports = {
plugins:[
new FriendlyErrorsWebpackPlugin()
],
status:'errors-only'
};
构建异常和中断处理
如何判断是否成功
每一次构建完成后输入 echo $?获取错误码,如果错误码不为0,说明这次构建是失败的,同时也可以获取到错误信息
webpack4之前的版本构建失败不会抛出错误码
如何主动捕获并处理构建错误?
- compiler在每次构建结束后会触发done这个hook
- proce.exit 主动处理构建报错
plugins:[
function(){
this.hooks.done.tap('done', (stats) => {
if (stats.compilation.errors && stats.compilation.errors.length && process.argv.indexOf('--watch') =a -1)
{
console.log('build error');
process.exit(1);
}
}
}
]
通过stats.compilation获取到是否存在errors,如果存在代表这次构建是失败的, 失败的话会抛出一个build error出来,同时通过process.exit把错误码抛出来。
写在最后
- 笔者最近在学习webpack,会像以学习笔记的方式跟大家交流分享。
- 会持续更新,欢迎点赞关注。