js file 对象_webpack源码之js代码压缩

579df153ce53c9f4088edb961827bc9e.png
基于webpack 4.x.x的版本
由于tapable类的重写,所以4.x版本和3.x版本在插件机制上有很打区别 如果你对tapable对象不熟悉,可以假装他是一个事件订阅/发布系统,虽然tapable没那么简单就是了。

webpack中的两个重要对象

  • compiler对象

其实就是webpack本身暴露出来给我们使用的一个对象。经常我们在自定义node启动webpack中的方法就可以得到compiler对象,当然一般来说该对象全局唯一的,后续再有compiler对象的创建,就是childComplier 例如:

const 

这样我们就可以得到compiler实例了,callback是可选的意思。

  • compilation实例

compilation实例是每次编译的时候就会获得的对象,在watch模式下,每次watch都会获得一个新的compilation实例,所以comppiler是每次启动webpack获得的全局唯一对象,而compilation则每次更新都会重新获取一遍。获取compilation方法如下:

// webpack4.x版本

compilation.seal方法

webpack 中处理资源生成chunks和优化压缩主要是在webpack的seal阶段,由于我们讲的是资源的压缩,所以我们主要看seal中关于压缩的代码在哪一块。 seal的代码在compilation.js中的seal方法中,重点我们要关注的方法如下:

/**

然后具体的js代码压缩的方式在uglifyjs-wepack-plugin中,如下:

compilation

uglifyjs-wepack-plugin

前一部分,我们只是简单对uglifyjs-wepack-plugin的源码开了个头,不过为什么我分析webpack的js压缩流就突然要研究uglifyjs-wepack-plugin这个三方包了呢,人生真是处处都是惊喜。 接下来我们看看uglifyjs-wepack-plugin中optimizeFn到底干了什么,uglifyjs-wepack-plugin源码传送门 首先我们看到第一段代码。

const 

taskRunner呢是一个多进程的任务执行系统,这个从名字就可以看出来,主要是来自于TaskRunner.js他也是uglifyjs-webpack-plugin的核心,taskRunner有个方法叫run,需要两个参数,第一个是tasks的对象数组,第二个是first-error类型的回调函数,表示任务执行运行完成,当然这里的任务主要是指压缩任务啦.

接下来一大串代码就是为了组织定义tasks这个参数是什么样子的。

const 

代码真长,也是真丑,咳咳 但是我们还是要继续看,直接看重点,

const task = {
      file,
      input,
      inputSourceMap,
      commentsFile,
      extractComments: this.options.extractComments,
      uglifyOptions: this.options.uglifyOptions,
      minify: this.options.minify,
    };

这里有几个重要属性,其中file就是要生成的文件名,input就是文件中的字符串的内容,inputSourceMap就是对应的sourcemap文件内容。

好了,现在我们的tasks已经组装好了,还记得前面的taskRunner我们就可以愉快执行taskRunner的run方法来压缩了。

taskRunner

TaskRunner.js

为了降低大脑负荷,我们考虑,假设taskRunner.js中没有缓存和多进程的情况。 于是整体的taskRunner.run里的代码可以简化成以下这个样子。

run

这边大概的流程就是,我们有一个专门执行js代码压缩的程序任务叫boundWorkers,然后有一个存储结果集的results,然后我们异步并行执行压缩js任务,注:这边并不是多进程js压缩。等所有压缩js的任务执行完了,就执行done函数,done函数的主要作用就是闭包index,可以使得到的结果按照顺序插入results里,这点就很想promise.all了,所以如果自己实现一个promise.all的话就可以考虑这个哟。 等所有任务都执行完了,就调用run的callbcak,也就UglifyJsPlugin的optimizeFn中的taskRunner的回调,而该回调的主要作用就是把获得的results放到compilation的assets上,然后再执行optimizeChunkAssets的callbcak,我们就继续回到了webpack的seal流程中啦。接下来我们继续看看minify.js中到底做了什么压缩操作。

minify.js

来来来,我们先不管别的,把minify的代码主要流程抽取一下,抽取之后就变成这样了。

import 

以上代码的核心在这一段

const 

这样看来,所有的所有的压缩都是uglify.minify操作的,而uglify又是来自于uglify-js,好了,我们追到现在有点追不动了。不过我们可以试试uglify-js这个三方包,比如这个样子:

const 

到现在我们的已经把整个流程梳理的差不多了,我们可以稍微尝试(臆想)着自己写一个压缩程序的demo,只实现部分功能。

让我们尝试写一段压缩程序

Javascript混淆器的原理并不复杂,其核心是对目标代码进行AST Transformation(抽象语法树改写),我们依靠现有的Javascript的AST Parser库,能比较容易的实现自己的Javascript混淆器。以下我们借助 acorn来实现一个if语句片段的改写。 假设我们存在这么一个代码片段:

for

那我们就这样操作一下:

const 

当然,我们上面的实现只是一个简单的举例,实际上的混淆器实现会比当前的实现复杂得多,需要考虑非常多的语法上的细节,此处仅抛砖引玉供大家参考学习。

压缩流程总结

  1. 执行seal事件阶段
  2. 执行compilation.hooks.optimizeChunkAssets
  3. 执行uglifyjs-webpack-plugin
  4. 执行optimizeFn
  5. 执行runner.runTasks
  6. 执行runner.runTasks的callback
  7. 执行optimizeFn的callback
  8. 执行compilation.hooks.optimizeChunkAssets的callback

如果考虑到多进程和缓存的使用的话,流程图应该长下面这个样子。

614b78196baf0cfc2178f33c30179159.png

参(chao)考(xi)资料

  • webpack原理
  • webpack群侠传(七):代码压缩和缓存
  • 前端核心代码保护技术面面观
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值