亦敵亦友的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的身影~

但願該項目能夠從新贏回開發者們的信任,讓前端工程更完善,效率更高!

相關文章
相關標籤/搜索