在专栏课程里,有位同学提到过一个很有意思的问题:“我没装 babel,js 入口里写了个箭头函数,运行 webpack 构建命令后,也成功编译了。这是为什么?”。今天就带领大家一起去探讨下这个话题。

在使用 webpack 的时候,很常见的一个构建优化手段就是缩小构建目标。比如在构建阶段只构建 src 里面的模块代码,对于 node_modules 里面所引入的三方包不进行构建操作。

发现问题

如果使用的是 webpack 3.x 版本,编写的构建脚本类似这样的,我们通过设置loader 里面的 exclude 字段避免由于解析 node_modules 里面的模块造成的构建耗时:

const path = require('path');
const webpack = require('webpack');
​
module.exports = {entry: './src/index.js',output: {path: path.join(__dirname, 'dist'),filename: 'bundle.js'},module: {rules: [{test: /.js$/,loader: 'happypack/loader',exclude: path.join(__dirname, 'node_modules')}]}plugins: [new webpack.optimize.UglifyJsPlugin()]
};

我们经常会遇到一个问题,假设引入的 npm 包质量不够高,比如 node_modules 里面有 ES6 的语法,那么 webpack 在 uglify 阶段会报错!下面给出两种常见的出错场景:

ES6 的模板字符串

假设 node_modules 里面存在 ES6 的模板字符串语法,那么在生产环境打包的代码压缩阶段,UglifyJs 会抛出错误。

ES6 的箭头函数

同样的,你使用 ES6 的箭头函数也是无法正常的压缩代码的。

细心的你一定会发现如果使用的是 webpack 4,这个场景描述的问题将不再出现。webpack 4默认支持 ES6 代码的压缩,这个是什么原因呢?

初步分析

如果你有对 webpack 4 的依赖包进行过相关分析,比如直接查阅 package.json 文件或者通过 http://npm.broofa.com/ 网站上进行 webpack 依赖图分析。不难发现 webpack 4 里面使用了 terser-webpack-plugin 插件替代了之前一直使用的 uglifyjs-webpack-plugin 作为它的内置插件。

以 4.39.3 这个版本为例,可以看到它的 package.json 文件的依赖包括了terser-webpack-plugin。

我们进一步分析发现 webpack 的 4.26.0 这个版本有一次提交,它的提交内容是对 webpack 内置插件进行了一次切换。

经过这么一次分析,我们可以知道 webpack 4 之所以具备默认压缩 ES6 代码的能力,离不开 terser-webpack-plugin 所起的作用!

进一步分析

在探究 terser-webpack-plugin 插件的原理前,我们先系统的回顾一下代码压缩插件的历史:

  • 当 uglifyjs-webpack-plugin 版本小于 v1.0 时,它使用的是 uglify-js 依赖
  • 但是 uglify-js 并不支持 ES6, 因此在 uglify-js 仓库的 harmony 分支 Fork 了一个 uglify-es
  • uglifyjs-webpack-plugin 的 v1.x 为了支持 ES6 的压缩语法,将 uglify-js 依赖切换到了 uglify-es
  • 但是 uglify-es 停止维护了: mishoo/UglifyJS2#3156 (comment)
  • uglify-es 的停止维护导致了 terser 被 fork 出来了,并且 terser 处理了没有合入的 PRs,最终创建了一个独立的仓库: https://github.com/fabiosantoscode/terser
  • 随后,terser-webpack-plugin 被创建出来, 它基于 terser,并且具备uglifyjs-webpack-plugin 的同等功能 : https://github.com/webpack-contrib/terser-webpack-plugin
  • 由于 uglifyjs-webpack-plugin v2.x 回退到了 uglify-js, 不再支持 ES6。 因此那些希望支持 ES6 语法压缩的项目必须切换到 terser-webpack-plugin

备注:压缩插件历史的来源 https://github.com/webpack/webpack/commit/311a7285d36b38bada46102967c431e93ff48a89

到这里,我们可以得出一个基本的结论:terser-webpack-plugin 基于 terser 因此它具备 ES6 的压缩能力,uglifyjs-webpack-plugin v2.x 版本基于 uglify-js,无法支持 ES6 的压缩。

插件依赖是否支持 ES6(Y/N)terser-webpack-pluginterserYuglifyjs-webpack-plugin v1.xuglify-esNuglifyjs-webpack-plugin v2.xuglify-jsN

原理探究

代码压缩原理其实挺简单的,也是 AST 的一个经典的应用案例。它的压缩过程通常是:

     JS 源代码 -> AST -> 美化、压缩 -> 新的 AST -> 压缩后的代码

了解了代码压缩的基本流程后,接下来我们看看源码包含了哪些内容,由于 terser 是从 uglify-es Fork 出来进行修改的,因此它的代码结构和 uglify-js 基本一致,只不过 terser 使用了 ES6 模块的静态分析功能。我们以 terser 的源码为例分析下:

  • ast.js:JS 的抽象语法树的描述信息
  • parse.js:Parser,用于从 JS 源代码分析出 AST
  • minify.js:用于将 AST 优化成更简短的结构
  • output.js:代码生成器,从 AST 输出 压缩后的代码,支持 sourcemap 的生成
  • propmangle.js:对变量的长度进行压缩,通常是单个字符
  • scope.js:分析变量定义/引用位置的信息
  • transform.js:节点遍历

然后,我们来一探 terser 和 uglify-js 的差异。对比了之后,发现一个很大的差异是 AST 的支持上面不同。

分析AST的差异发现,下面是两个文件 diff 对比只在 terser 中才有,而这些刚好对应 ES6 的语法。

AST_Arrow,
AST_Await,
AST_BigInt,
AST_Class,
AST_ClassExpression,
AST_ConciseMethod,
AST_Const,
AST_DefaultAssign,
AST_Destructuring,
AST_Expansion,
AST_Export,
AST_ForOf,
AST_Import,
AST_Let,
AST_NameMapping,
AST_NewTarget,
AST_PrefixedTemplateString,
AST_Super,
AST_SymbolMethod,
AST_TemplateSegment,
AST_TemplateString,
AST_Yield

join为什么每个字符都分割了 js_为什么 webpack4 默认支持 ES6 语法的压缩?相关推荐

  1. join为什么每个字符都分割了 js_js的join()与 split() (转)

    数组的一些方法: 1.join()和split()方法 var x; var a=new Array(); a[0]="XHTML"; a[1]="CSS"; ...

  2. join为什么每个字符都分割了 js_JS截取与分割字符串常用技巧总结

    本文实例讲述了JS截取与分割字符串的常用方法.分享给大家供大家参考,具体如下: JS截取字符串可使用 substring()或者slice() 函数:substring() 定义:substring( ...

  3. Java将String字符串里的每个字符都分割取出来

    Java将String字符串里的每个字符都分割取出来 方法一 String str = "play with style"; char[] strArray = str.toCha ...

  4. 字符图片分割研究小结(搬运、整理、分类、汇总)(自用,持续更新中)

    目录 一.定义 二.分类 三.分割难点 四.常用算法 五.相关论文研读 六.参考所用到的文献和博客等 一.定义 文字图像识别近年来应用广泛 包括传统OCR和自然场景下的文字识别 图1 印刷文档中的文本 ...

  5. java 按符号分割字符串_如何根据java根据指定的字符,分割字符串?

    展开全部 根据java根据指定的字符,分割字符串的方法是:e69da5e887aa62616964757a686964616f31333365633866String temp[]=result.sp ...

  6. 为什么说在KMP算法中文本串中的每个字符都是需要进行比较操作的?

     KMP算法需要计算一个shift或者next表,这个表是一个部分匹配表,通过这个next表来计算当字符不匹配的时候移动的位数,这个移动位数的计算公式为 移动位数 = 已匹配的字符数 - 对应的n ...

  7. while0表示什么意思_轮胎上的各种字符都表示什么意思,家用车的轮胎又该如何选择呢?...

    普通家用车的轮胎在使用四年或者六万公里左右就需要更换了.广大车主朋友在更换轮胎时往往都很纠结,一方面是不知道换什么样的轮胎好,另一方面看不懂轮胎上密密麻麻的数字.字母和符号.今天我们就来说说汽车轮胎上 ...

  8. python的正则表达式 re-------可以在字符串前加上 r 这个前缀来避免部分疑惑,因为 r 开头的python字符串是 raw 字符串,所以里面的所有字符都不会被转义...

    正则表达式使用反斜杆(\)来转义特殊字符,使其可以匹配字符本身,而不是指定其他特殊的含义.这可能会和python字面意义上的字符串转义相冲突,这也许有些令人费解.比如,要匹配一个反斜杆本身,你也许要用 ...

  9. 字符串之字符数组种是否所有的字符都只出现过一次

    字符串之字符数组种是否所有的字符都只出现过一次 例子: chas=['a','b','c'],return true,chas=['1','2','1'],return false 解题思路: 定义b ...

最新文章

  1. 数字对 (长乐一中模拟赛day2T2)
  2. c++大文本比较_Excel – 将文本转换为数值,第二种方法会的请举左手
  3. php图片上传 thinkphp5,thinkphp5图片上传接口
  4. 【数字信号处理】分贝的概念及其日常使用中常见的错误
  5. c# 从地址拷贝byte_面试必备的 “零拷贝” 问题!从头给你说!
  6. python中index方法详解_Python中的index()方法使用教程
  7. 特斯拉AI Day首秀:FSD终极进化?AI超算Dojo、D1芯片、人形机器人亮相!
  8. Django之admin
  9. Python3.WRF的投影转换
  10. 对称加密密码 对称加密算法
  11. 读书笔记——《一个人的朝圣》
  12. 如何避免字节流读取文本乱码
  13. 超详细讲解OSPF中如何选举DR和BDR
  14. linux安装压缩文件的工具,Linux上安装rar解压工具
  15. 蚪侠-远程泛目录[泛域名+泛目录+泛内页]-[代码+汉字]干扰-字体繁简切换-蜘蛛欺骗-主动推送_镜像版-V25版
  16. 智慧图书馆中的自助借还系统
  17. 《海边的卡夫卡》摘抄
  18. Github Page绑定至个人域名
  19. PyTorch实战6:好莱坞明星识别--VGG16
  20. v3S移植NES游戏

热门文章

  1. JS中浮点数运算误差处理
  2. Linux下SHELL的PS1变量简介
  3. 对于PHP大型开发框架的看法
  4. excel空值读不到java里_第一列中的空值是否阻止Pentaho Spoon中的Excel文件导入?
  5. python变量域名_想尝试使用python进行域名分析,可是没有接触过python,想请教请教。...
  6. DOCTYPE 声明
  7. C++ const对成员函数的修饰 及 取地址及const取地址操作符重载
  8. JVM Class 类文件结构 (系列号2)
  9. 怎样在hdfs上创建多级目录文件夹_【HDFS API编程】第一个应用程序的开发-创建文件夹...
  10. 计算机五大逻辑部件数据流,211计算机基础知识综合篇.ppt