webpack进阶用法你都get到了么?
如何消除无用代码;打包自己的私有js库;实现代码分割和动态import提升初次加载速度;配置eslint规范团队代码规范;打包异常抓捕你都get到了么?
摇树优化:Tree Shaking
webpack借鉴了rollup构建工具,从2.0就实现支持tree shaking,其中,到webpack4.0后 通过开启mode:'production'即默认开启。
tree shaking原理
DCE(Dead code elimination),即死码消除,编译器原理中,死码消除(Dead code elimination)是一种编译最优化技术,它的用途是移除对程序运行结果没有任何影响的代码。
其中死码的特点:
代码不会被执行,不可到达
代码执行的结果不会被用到
代码只会影响死变量(只读不写)
其中,tree shaking就是借鉴了这个原理,利用了ES6模块的特点:
其中import、exports只能作为模块顶层的语句出现
import 的模块名只能是字符串常量
import 的模块名是常量不能进行修改
tree shaking就是利用ES6的这一特点,本质就是对静态的模块代码进行分析,所以需要 在构建过程中确定哪些模块的代码能利用到,哪些模块不需要进行区分。通过标识不需要 使用的代码在uglify阶段删除无用代码。
tree shaking概念和使用
顾名思义,tree shaking摇树优化其中就是类似于摇晃树,过程会使一些枯枝枯叶掉落。
tree shaking就是通过在构建过程,如果一个模块存在多个方法,如果只有其中的某个方法 使用到,则将一些没有引用到的代码在这个打包过程移除,只把用到的方法打入bundle中。
通过开启mode:'production'即可。其中,只支持ES6的语法,commonjs的方式(即require方式)不支持使用。
作用域提升:Scope Hoisting
webpack的模块机制

我们可以了解到,webpack打包出来的是一个IIFE(立即调用函数表达式,也是常说的匿名闭包), 其中,modules数组的每一项是一个模块初始化函数,通过webpackrequire的方式来加载模块, 返回module.exports,并且通过webpackrequire_(0)的方式启动程序。
scope hoisting原理及使用
在没有开启scope hoisting前,这样构建后的代码会存在大量的闭包代码。

由于模块依赖,通过webpack打包后会转换成自执行匿名函数,这样, 由于大量函数闭包包裹代码,会导致体积增大(模块越多就越明显);运行代码 时创建的函数作用域变多,导致内存开销也变大。
其中,scope hoisting的概念也是借鉴了rollup的构建原理并在webpak3.0时候引入, 其原理就是将所有模块的代码按照引入顺序放到一个函数作用域里,然后适当的重命名 一些变量以防止变量名冲突。这样就可以实现减少函数声明代码和内存花销。
在webpack4中,也只需要把mode状态调整为production即默认开启,其中,需要注意的是 只支持ES6语法,commonjs的动态引入还不支持。
而webpack3中则需要配置插件 webpack.optimize.ModuleConcatenationPlugin。
代码分割和动态import
对于一些大型的web应用,将代码打包到一个chunk是不够有效的,会导致加载的文件过大,导致页面加载慢,体验差等。所以需要通过代码分割成多个chunks,当代码运行到需要时候才进行加载。
一般通过抽离相同代码到一个共享块或者脚本懒加载使得初始下载的代码更小。
前面我们已经说到通过SplitChunksPlugin来进行通用的代码抽离,而懒加载脚本的方式我们需要条件判断等方式通过ES6的动态import的方式实现。
其中,动态import目前没有原生支持,需要babel转换。安装依赖并配置.babelrc
npm i @babel/plugin-syntax-dynamic-import -D
配置.babelrc
{
"plugins": [
"@babel/plugin-syntax-dynamic-import"
]
}
通过动态import,点击事件加载脚本,demo:

JavaScript语法规范:ESLint
ESLint 是一个插件化并且可配置的 JavaScript 语法规则和代码风格的检查工具,能够及早的发现代码错误并且帮助保持团队代码风格的统一。
其中比较有名的ESLint规范实践有:eslint-config-airbnb、eslint-config-alloy、eslint-config-ivweb等。
可通过基于eslint:recommend配置对规范进行改进。
eslint规则
| 规则名称 | 错误级别 | 说明 |
|---|---|---|
| for-direction | error | for循环的方向要求必须正确 |
| getter-return | error | getter必须有返回值,并且禁止返回值为undefined,比如return |
| no-await-in-loop | off | 允许在循环里面使用await |
| no-console | off | 允许在代码里面是有console |
| array-callback-return | error | 对于数据相关操作函数比如reduce、map、filter等,callback必须有return |
| accessor-pairs | warn | 在字符串里面出现(和)进行警告 |
更多规则可参考:https://eslint.org/docs/rules/
ESLint落地
具体ESLint落地的实施可以有两个方案,通过CI/CD(持续集成/持续交付)系统集成或者和webpack等构建工具集成。
ESLint与CI/CD系统集成
gitlab集成lint的常用做法:

本地开发时可以通过增加precommit钩子实现开发环境的检查。
安装husky并配置package
npm i husky -D
"scripts":{
"precommit":"lint-staged"
},
"lint-staged":{
"linters":{
"*.js": ["eslint-fix","git add"]
}
}
webpack与ESLint集成
通过使用eslint-loader,构建时检查JS规范。这种做法比较适合新项目的使用。因为该方案会默认在开发构建时对所有文件进行规范的检查。
安装基于react的eslint的依赖包,eslint-config-alloy
npm install --save-dev eslint babel-eslint eslint-plugin-react eslint-config-alloy
npm i eslint-loader -D
配置.eslintrc.js parser使用babel-eslint,并继承eslint-config-airbnb
npm i eslint-config-airbnb -D
// .eslintrc.js
module.exports = {
"parser": "babel-eslint",
"extends": [
'alloy',
'alloy/react',
],
"rules": {
"indent": ["error",4]
},
"env": { // 当前生效环境
"browser": true,
"node": true
}
}
打包组件和基础库
webpack不仅可以用来打包应用,也可以用来打包js库来方便我们的日常开发。
实现大整数加法库的打包demo
1、确定打包需求:
需要打包压缩版和非压缩版本
支持script标签/AMD/CJS/ESM模块引入
2、js库的目录结构
.
├── dist // 打包输出文件夹
| ├── webpack-larger-number.js // 未压缩版输出文件
| └── webpack-larger-number.min.js //压缩版
├── package.json // 依赖包配置说明
├── webpack.config.js // 打包配置
├── index.js //
├── src // 源码
└── index.js
3、配置webpack
相对于一般打包应用,我们需要配置output参数实现将库暴露出去,其中,library可以指定库的名称
module.exports = {
output: {
filename: "[name].js",
library: "WebpackLargeNumber", // 指定库的全局变量
libraryExport: "default",
libraryTarget: "umd" // 支持库引入的方式,默认以libary指定的变量名
}
}
更多详情参数可参考:https://www.webpackjs.com/configuration/output/#output-library
配置webpack,指定.min文件压缩
module.exports = {
mode: 'none',
optimization: {
minimize: true, // 默认为true,压缩js代码
minimizer: [
new TerserPlugin({ // terser-webpack-plugin支持es6语法压缩
include: /\.min\.js$/
})
]
}
更多optimization可参考:https://webpack.docschina.org/configuration/optimization/
然后设置入口文件,对于开发环境则引入未打包的js库,而生产环境则使用压缩后的库
4、设置package的入口文件并设置对应环境变量引入不同的库。
// package.js
"main": "index.js",
// index.js
if (process.env.NODE_ENV == 'production') {
module.exports = requier('./dist/webpack-large-number.min.js')
} else {
module.exports = require('./dist/webpack-large-number')
}
最后,我们也可通过npm publish发布到npm上(ps:需要npm账号),然后我们就可以通过npm下载依赖包 并且通过默认的导出名WebpackLargeNumber.add('99','2')方式直接使用函数。
大整数加法demo:https://github.com/PCAaron/blogCode/tree/master/webpack/webpack-large-number
优化构建命令行日志:stats + friendly-errors-webpack-plugin
使用webpack打包的时候,默认会将所有的打包构建信息打印出来,而stats选项则可以很好获取部分需要的bundle信息。
常见的stats值:
| Preset | Alternative | Description |
|---|---|---|
| errors-only | none | 只在发生错误时输出 |
| minimal | none | 只在发生错误或有新的编译时输出 |
| none | false | 没有输出 |
| normal | true | 标准输出 |
| verbose | none | 全部输出 |
配置webpack,其中,需要注意的是,对于webpack-dev-server,stats需要放到devServer中。
module.exports = {
stats: 'errors-only'
}
通过设置stats为errors-only,我们可以看到dev和build的日志成功的话也没有一些bundle信息,这是,我们可以借助friendly-errors-webpack-plugin对命令行的日志进行优化。
安装friendly-errors-webpack-plugin并配置。
npm i friendly-errors-webpack-plugin -D
配置webpack
const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin')
module.exports = {
plugins: [
new FriendlyErrorsWebpackPlugin()
]
}
构建异常和中断处理
如果打包时候存在一些构建异常和中断,需要捕获并做一些异常提示或者内容上报时候,我们可以通过compiler在每次构建结束后出发done的钩子实现异常的抓捕。
其中,我们需要借助node的process.exit抛出异常,默认情况下, process.exit抛出0表示成功,err为null;而非0则执行失败, 其中err为错误信息,code为对应的状态码。
配置webpack
module.exports = {
plugins: [
function () {
this.hooks.done.tap('done', stats => {
if (stats.compilation.errors && stats.compilation.errors.length &&
process.argv.indexOf('--watch') == -1) {
console.log('build err')
process.exit(1)
}
})
}
]
}
webpack系列
导读
webpack基础篇
webpack进阶篇
欢迎star关注更新:https://github.com/PCAaron/PCAaron.github.io
推荐阅读
代码demo:https://github.com/PCAaron/blogCode/tree/master/webpack/webpack-improveMore
Scope Hoisting优化Webpack输出:https://www.imweb.io/topic/5a43064fa192c3b460fce360
webpack进阶用法你都get到了么?的更多相关文章
- webpack进阶构建项目(一)
webpack进阶构建项目(一) 阅读目录 1.理解webpack加载器 2.html-webpack-plugin学习 3.压缩js与css 4.理解less-loader加载器的使用 5.理解ba ...
- Django框架学习-Model进阶用法
Model进阶用法 回顾 访问外键 访问多对多关系 更改数据库结构 当处理数据库结构改变时,需要注意到几点: 增加字段 首先在开发环境中: 再到产品环境中: 删除字段 删除多对多字段 删除model ...
- canvas图形处理和进阶用法
前面的话 上一篇博客介绍了canvas基础用法,本文将更进一步,介绍canvas的图形处理和进阶用法 图形变换 图形变换是指用数学方法调整所绘形状的物理属性,其实质是坐标变形.所有的变换都依赖于后台的 ...
- 前端自动化测试神器-Katalon进阶用法
前言 上一篇介绍了Katalon的基础用法,本篇继续介绍一些进阶的用法. Keyword 和 Method Call Statement Keyword Keyword就是自定义方法,该方法在当前项目 ...
- ASP.NET Core 6框架揭秘实例演示[14]:日志的进阶用法
为了对各种日志框架进行整合,微软创建了一个用来提供统一的日志编程模式的日志框架.<日志的基本编程模式>以实例演示的方式介绍了日志的基本编程模式,现在我们来补充几种"进阶" ...
- Spring Data JPA系列3:JPA项目中核心场景与进阶用法介绍
大家好,又见面了. 到这里呢,已经是本SpringData JPA系列文档的第三篇了,先来回顾下前面两篇: 在第1篇<Spring Data JPA系列1:JDBC.ORM.JPA.Spring ...
- webpack进阶--打包
上一片博文主要让大家了解下究竟webpack是干什么的,明显它是专注于打包的. gulp 和 webpack 的区别 gulp,要求我们一步步写task(es6编译.css压缩.图片压缩.打包. ...
- SpringBoot进阶用法-随笔
SpringBoot进阶用法 实现setApplicationContext //实现ApplicationContextAware接口,重写setApplicationContext方法 publi ...
- CocoaPods学习系列4——进阶用法
这篇文章,记录一下CocoaPods的进阶用法. 进阶用法主要体现在.podspec文件和Podfile的配置上. .podspec文件的进阶配置 以官方的一个.podspec文件示例细说: Pod: ...
随机推荐
- java 内存操作流
操作内存流的时候(从读取出来,注意一定要把真正的数据用toByteArray或者toCharArray将数据读出来) 之前的文件操作流是以文件的输入输出为主的,当输出的位置变成了内存,那么就称为内存操 ...
- 「THUPC 2019」不等式 / inequality
https://loj.ac/problem/6620 高中数学好题.. |kx+b|的函数图像很直观,直接考虑函数图像: 一定只有一段极小值点! 这个点就是最小值了 特点:斜率为0! 而且发现,如果 ...
- java的四种代码块
用{}括起来的称为代码块: 普通代码块:类中方法的方法体 构造代码块:类中{}直接括起来的语句,每次创建对象都会被调用,先于构造函数执行 静态代码块:类中static{}括起来的语句,只执行一次,先于 ...
- C# 大端小端转换
关于大端和小端,是一个有趣的问题.本文告诉大家如何在C#转换大端和小端. 这里有一个有趣的故事,请看详解大端模式和小端模式 - CSDN博客 默认的 C# 使用的是小端,如果收到的消息是大端,那么就会 ...
- dotnet 控制台读写 Sqlite 提示 no such table 找不到文件
在使用 dotnet 读写 Sqlite 可以通过 EF Core 的方法,但是在 EF Core 创建的数据库可能和读写的数据库不是相同的文件 在我运行代码的时候发现在通过迁移创建数据库,创建的文件 ...
- Java 高效代码50例
导读 世界上只有两种物质:高效率和低效率:世界上只有两种人:高效率的人和低效率的人.----萧伯纳 常量&变量 直接赋值常量,禁止声明新对象 直接赋值常量值,只是创建了一个对象引用,而这个对象 ...
- Kafka Eagle安装详情及问题解答
1.概述 最近有很多同学给笔者留言,说在安装Kafka Eagle的时候,会遇到一些问题,请教如何解决?今天笔者就在这里总结一下安装步骤,和一些安装的注意事项,以及解决方式. 2.内容 在安装Kafk ...
- HDU1166 敌兵布阵 BZOJ1012 最大数[树状数组]
一.前置知识-树状数组 树状数组(binary indexed tree)是一种简洁的代码量很小的数据结构,能够高效的处理前缀区间上的问题.在很多情况下能写树状数组解决的就不用码半天线段树了. 树状数 ...
- Elasticsearch慢查询故障诊断
最近在做ES搜索调优,看了一些lucene搜索的文档和代码,本文用于总结调优过程中学到的知识和自己的思考. 在抓到ES慢查询之后,会通过profile或者kibana的Search Profiler ...
- pg_dump
一.PostgreSQL提供的一个工具 pg_dump逻辑导出数据,生成sql文件或其他格式文件,pg_dump是一个客户端工具,可以远程或本地导出逻辑数据,恢复数据至导出时间点. 二.pg_dump ...