亦敌亦友的uglify

提及ugilfy,这是目前前端工程化里最重要的伙伴之一,经过对代码的压缩混淆,它给咱们带来了多种好处:前端

  1. 压缩:减小代码量,减小网络下载时间以及浏览器的解析时间
  2. 混淆:必定程度的提高代码阅读难度,有必定程度的保护代码做用

而前端工程化的象征——webpack,从1.0时期起就内置了uglify插件,简单的配置便可使用该功能。webpack

uglify-js

webpack 1.0~3.0均内置的插件,它伴随前端工程的服役时间最长,同时期也没有太多的竞争者,Closure一直被批评过于激进,使用者寥寥。git

可是,它有一个从诞生起就伴随的缺陷,仅支持ES5代码。随着浏览器的兼容性需求愈来愈低,不少非现代浏览器都已经逐步被市场淘汰,chrome近几年每一年都会升4个大版本。github

慢慢的,原生的简洁ES6代码已经能够在浏览器里运行了,这时候uglify-js就显的有点笨重。对于ES5代码再压缩,可能也比不上直接输出ES6代码呀。web

此外,还有一个危险的信号:chrome

在前端界,你们都会去研究和讨论类库的实现,但对于工程上的工具项目,却知之甚少。对于开源项目,输入越多,它越健壮;相反的,关注的少,场景输入少,项目变得黑盒化的同时,也埋下了致命bug的种子。前端工程化

uglify-es

在时代推进下,迫切须要uglify-js能够解析ES6代码,因而uglify-es诞生。webpack 4.0随着自身内部的一次大革新,将uglify-es内置,并于今年初面世。api

可是。。。我用了还不到半年就遇到一个致命bug。浏览器

诡异的是,由于没有深究uglify源码,构建出的简单示例并不会出现该问题。网络

示例代码以下:

// Parent.jsx
import Child from './Child'

class Parent extends React.PureComponent {
  onChange = () => {
	// 触发rerender
  }
  
  render() {
	  return <div><Child onChange={this.onChange} /></div> } } // Child.jsx class Child extends React.PureComponent { componentWillMount() { fetch('/api').then(this.props.onChange) } render() { return <div /> } } 复制代码

最终压缩后的代码形如:

// Parent.js的render函数
return React.createElement(
    'div', 
    null, 
    React.createElement(class Child extends PureComponent {})
)
复制代码

能够看到,Child因为只被一处引入,因此再也不是独立模块,甚至被处理成了内联。

在js语法里这样处理,彷佛没什么问题,可是当处理的是包含生命周期的组件时,就触发了严重的问题,该Child组件会被不断的销毁和建立,引发死循环。

因为部分“玄学”缘由,我没找到稳定复现该bug的写法。。。

解决方式是在uglify配置中关闭reduce_vars和reduce_funcs:

uglifyOptions: {
  compress: {
    reduce_vars: false,
    reduce_funcs: false,
  },
}
复制代码

这仅仅是我碰到的问题,搜一下uglify-es的bug,会发现,这项目确实有点哔——(消音),它已经从并肩做战的战友成功升级成4v6了。。。

terser

随着uglify-es再也不维护(我用着的时候都不知道= =!),webpack也悄悄的更改了内置uglify依赖:

uglify-es is no longer maintained

其实还有不少项目也进行了调整:

Switch minifier from uglify-es to terser

看样子terser是从uglify-esfork出来的项目,目前看来,该项目维护者很是活跃,在不少uglify-es的bug issue下都会看到他宣传terser没有bug的身影~

但愿该项目能够从新赢回开发者们的信任,让前端工程更完善,效率更高!

相关文章
相关标签/搜索