javascript 代码是如何被压缩的示例代码


Posted in Javascript onMay 06, 2020

随着前端的发展,特别是 ReactVue 等构造单页应用的兴起,前端的能力得以很大提升,随之而来的是项目的复杂度越来越大。 此时的前端的静态资源也越来越庞大,而毫无疑问 javascript 资源已是前端的主体资源,对于压缩它的体积至为重要。

为什么说更小的体积很重要呢:更小的体积对于用户体验来说意味着更快的加载速度以及更好的用户体验,这也能早就企业更大的利润。另外,更小的体积对于服务器来说也意味更小的带宽以及更少的服务器费用。

前端构建编译代码时,可以使用 webpack 中的 optimization.minimizer 来对代码进行压缩优化。但是我们也需要了解如何它是压缩代码的,这样当在生产环境的控制台调试代码时对它也有更深刻的理解。

如何查看资源的体积

对于我们所编写的代码,它在操作系统中是一个文件,根据文件系统中的 stat 信息我们可以查看该文件的大小。

stat 命令用来打印文件系统的信息:

$ stat config.js
 File: ‘config.js'
 Size: 3663      Blocks: 8     IO Block: 4096  regular file
Device: fd01h/64769d  Inode: 806060   Links: 1
Access: (0644/-rw-r--r--) Uid: (  0/  root)  Gid: (  0/  root)
Access: 2020-02-13 13:43:54.851381702 +0800
Modify: 2020-02-13 13:43:52.668417641 +0800
Change: 2020-02-13 13:43:52.691417262 +0800
 Birth: -

stat 打印的信息过大,如果只用来衡量体积,可以使用 wc -c

$ wc -c config.js
3663 config.js

如何压缩代码体积?

去除多余字符: 空格,换行及注释

// 对两个数求和
function sum (a, b) {
 return a + b;
}

先把一个抽象的问题给具体化,如果是以上一段代码,那如何压缩它的体积呢:

此时文件大小是 62 Byte , 一般来说中文会占用更大的空间。

多余的空白字符会占用大量的体积,如空格,换行符,另外注释也会占用文件体积。当我们把所有的空白符合注释都去掉之后,代码体积会得到减少。

去掉多余字符之后,文件大小已经变为 30 Byte 。 压缩后代码如下:

function sum(a,b){return a+b}

替换掉多余字符后会有什么问题产生呢?

有,比如多行代码压缩到一行时要注意行尾分号。这就需要通过以下介绍的 AST 来解决。

压缩变量名:变量名,函数名及属性名

function sum (first, second) {
 return first + second;
}

如以上 first 与 second 在函数的作用域中,在作用域外不会引用它,此时可以让它们的变量名称更短。但是如果这是一个 module 中, sum 这个函数也不会被导出呢?那可以把这个函数名也缩短。

// 压缩: 缩短变量名
function sum (x, y) {
 return x + y;
}

// 再压缩: 去除空余字符
function s(x,y){return a+b}

在这个示例中,当完成代码压缩 ( compress ) 时,代码的混淆 ( mangle ) 也捎带完成。 但此时缩短变量的命名也需要 AST 支持,不至于在作用域中造成命名冲突。

更简单的表达:合并声明以及布尔值简化

合并声明的示例如下:

// 压缩前
const a = 3;
const b = 4;

// 压缩后
const a = 3, b = 4;

布尔值简化的示例如下:

// 压缩前
!b && !c && !d && !e

// 压缩后
b||c||d||e

这个示例更是需要解析 AST 了

AST

AST ,抽象语法树,js 代码解析后的最小词法单元,而这个过程就是通过 Parser 来完成的。

那么 AST 可以做什么呢?

  • eslint: 校验你的代码风格
  • babel: 编译代码到 ES 低版本
  • taro/mpvue: 各种可以多端运行的小程序框架
  • GraphQL: 解析客户端查询

我们在日常工作中经常会不经意间与它打交道,如 eslint 与 babel ,都会涉及到 js 与代码中游走。不同的解析器会生成不同的 AST,司空见惯的是 babel 使用的解析器 babylon ,而 uglify 在代码压缩中使用到的解析器是 UglifyJS 。

你可以在 AST Explorer [3] 中直观感受到,如下图:

javascript 代码是如何被压缩的示例代码

那压缩代码的过程:code -> AST -> (transform)一颗更小的 AST -> code,这与 babel 和 eslint 的流程一模一样。

javascript 代码是如何被压缩的示例代码 

UglifyJS

不要重复造轮子!

于是我找了一个久负盛名的关于代码压缩的库: UglifyJS3 [4] ,一个用以代码压缩混淆的库。那它是如何完成一些压缩功能的,比如替换空白符,答案是 AST。

webpack 中内置的代码压缩插件就是使用了它,它的工作流程大致如下:

// 原始代码
const code = `const a = 3;`

// 通过 UglifyJS 把代码解析为 AST
const ast = UglifyJS.parse(code);
ast.figure_out_scope();


// 转化为一颗更小的 AST 树
compressor = UglifyJS.Compressor();
ast = ast.transform(compressor);

// 再把 AST 转化为代码
code = ast.print_to_string();

而当你真正使用它来压缩代码时,你只需要面向配置编程即可,文档参考 uglify 官方文档 [5]

{
 {
  ecma: 8,
 },
 compress: {
  ecma: 5,
  warnings: false,
  comparisons: false,
  inline: 2,
 },
 output: {
  ecma: 5,
  comments: false,
  ascii_only: true,
 }
}

在 webpack 中压缩代码

在知道代码压缩是怎么完成的之后,我们终于可以把它搬到生产环境中去压缩代码。终于到了实践的时候了,虽然它只是简单的调用 API 并且调调参数。

一切与性能优化相关的都可以在 optimization 中找到, TerserPlugin 是一个底层基于 uglifyjs 的用来压缩 JS 的插件。

optimization: {
 minimize: isEnvProduction,
 minimizer: [
  new TerserPlugin({
   terserOptions: {
    parse: {
     ecma: 8,
    },
    compress: {
     ecma: 5,
     warnings: false,
     comparisons: false,
     inline: 2,
    },
    output: {
     ecma: 5,
     comments: false,
     ascii_only: true,
    },
   },
   sourceMap: true
  })
 ]
}

参考资料 [1]

shfshanyue/blog: https://github.com/shfshanyue/blog

[2]

前端工程化系列: https://github.com/shfshanyue/blog/tree/master/frontend-engineering

[3]

AST Explorer: https://astexplorer.net/

[4]

UglifyJS3: https://github.com/mishoo/UglifyJS2

[5]

uglify 官方文档: https://github.com/mishoo/UglifyJS2#parse-options

到此这篇关于javascript 代码是如何被压缩的的文章就介绍到这了,更多相关js代码压缩内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
了解了这些才能开始发挥jQuery的威力
Oct 10 Javascript
jqGrid读取选择的多行的某个属性代码
May 18 Javascript
JQuery鼠标移到小图显示大图效果的方法
Jun 10 Javascript
jQuery实现的简单悬浮层功能完整实例
Jan 23 Javascript
javascript自执行函数
Feb 10 Javascript
Element-ui table中过滤条件变更表格内容的方法
Mar 02 Javascript
Node.js爬取豆瓣数据实例分析
Mar 05 Javascript
jQuery实现的简单对话框拖动功能示例
Jun 05 jQuery
vue的token刷新处理的方法
Jul 17 Javascript
在vue使用clipboard.js进行一键复制文本的实现示例
Jan 15 Javascript
Electron+vue从零开始打造一个本地播放器的方法示例
Oct 27 Javascript
vue+iview使用树形控件的具体使用
Nov 02 Javascript
Layui弹框中数据表格中可双击选择一条数据的实现
May 06 #Javascript
Vue SSR 即时编译技术的实现
May 06 #Javascript
深入webpack打包原理及loader和plugin的实现
May 06 #Javascript
将Vue组件库更换为按需加载的方法步骤
May 06 #Javascript
让IDE识别webpack的别名alias的实现方法
May 06 #Javascript
JS 设计模式之:工厂模式定义与实现方法浅析
May 06 #Javascript
JS 设计模式之:单例模式定义与实现方法浅析
May 06 #Javascript
You might like
centos 5.6 升级php到5.3的方法
2011/05/14 PHP
PHP操作MongoDB GridFS 存储文件的详解
2013/06/20 PHP
PHP实现无限极分类图文教程
2014/11/25 PHP
刷新PHP缓冲区为你的站点加速
2015/10/10 PHP
简单理解PHP的面向对象编程方式
2016/05/17 PHP
php中static 静态变量和普通变量的区别
2016/12/01 PHP
PHP单例模式应用示例【多次连接数据库只实例化一次】
2018/12/18 PHP
Node.js Streams文件读写操作详解
2016/07/04 Javascript
基于Vue2实现的仿手机QQ单页面应用功能(接入聊天机器人 )
2017/03/30 Javascript
Vue Spa切换页面时更改标题的实例代码
2017/07/15 Javascript
纯js实现页面返回顶部的动画(超简单)
2017/08/10 Javascript
React优化子组件render的使用
2019/05/12 Javascript
Vue router传递参数并解决刷新页面参数丢失问题
2020/12/02 Vue.js
python中实现php的var_dump函数功能
2015/01/21 Python
浅谈python数据类型及类型转换
2017/12/18 Python
python reverse反转部分数组的实例
2018/12/13 Python
使用Python快速制作可视化报表的方法
2019/02/03 Python
Pycharm简单使用教程(入门小结)
2019/07/04 Python
django 自定义过滤器(filter)处理较为复杂的变量方法
2019/08/12 Python
python协程gevent案例 爬取斗鱼图片过程解析
2019/08/27 Python
python可迭代对象去重实例
2020/05/15 Python
聊聊python中的异常嵌套
2020/09/01 Python
Java工程师面试集锦之Spring框架
2013/06/16 面试题
北京一家公司的.net开发工程师笔试题
2012/04/17 面试题
个人应聘自我评价分享
2013/11/18 职场文书
浪漫婚礼主持词
2014/03/14 职场文书
临床护理求职信
2014/04/26 职场文书
结婚保证书范文
2014/04/29 职场文书
妇联领导班子剖析材料
2014/08/21 职场文书
119消防日活动总结
2014/08/29 职场文书
社区务虚会发言材料
2014/10/20 职场文书
2014年林业工作总结
2014/12/05 职场文书
小学教师年度个人总结
2015/02/05 职场文书
mysql批量新增和存储的方法实例
2021/04/07 MySQL
基于PyQT5制作一个桌面摸鱼工具
2022/02/15 Python
HTML5之高度塌陷问题的解决
2022/06/01 HTML / CSS