babel 每次學習都有新的理解,哪怕是其配置都與咱們前端生態中的各類概念息息相關。近期再次複習babel知識從而更好的編寫js類庫,本文是學習過程所作的記錄。前端
那麼,咱們使用 webpack 來打包js代碼時,是使用的什麼呢?答案是 babel-loader。node
babel-lodaer 是適配 webpack 的一個模塊,其內部就是調起 babel/core 來實現代碼轉譯。webpack
記住:babel自身惟一能作的事情叫:代碼轉譯。
至於新增api那些,那是須要引入polyfill墊片的事情(下文會講)。es6
babel自己所作的事情,通常都叫他代碼轉譯。那麼所謂的轉譯都有哪些種類呢?以下:web
一、單純的語法解析和轉換。例如箭頭函數轉成function
二、須要一點輔助的語法解析和轉換,例如 class語法,給你轉成prototype原型語法chrome
解釋下第二點:咱們知道,一種新的語法意味着新的一些關鍵字和符號等,那麼對於一些特殊符號如箭頭函數,那麼他剛好有es5對應的 function
關鍵字,因此babel只需簡單換一下字符。npm
然而有些卻不是簡單的換一下,例如 class 語法,babel給你轉成 es5 以後,他必須改形成函數和prototype的寫法,在這種狀況下,babel編譯後的代碼中會加入一些輔助函數(也就是下文說的babel/runtime所幹的事兒)以協助來用es5的方式實現 class。api
舉個栗子:
promise
看到了嗎,babel生成的原型式寫法中,須要在構造函數中對調用者的實例化方式進行檢測,這些都封裝成了 helper函數 _classCallCheck。瀏覽器
除此以外,還有更復雜的場景 babel轉譯以後甚至須要依賴polyfill墊片:例如 async 轉換。 babel 會把 async 語法代碼轉成 generator 輔助函數,而 generator 輔助函數的功能須要依賴 regenerator-runtime
這個polyfill墊片。以下圖,babel給你把async語法轉換後,多了不少輔助函數;甚至其中有一個 regeneratorRuntime 的函數是沒有找到定義的,而這個函數其實就是依賴全局須要引入 regenerator-runtime
這個 polyfill 才行。
首先,咱們要理解什麼是 polyfill 什麼是輔助函數。
像上文所說的2種babel轉譯,其中轉換以後所出現的那些輔助函數,叫作運行時所須要的helper,他們其實就是 runtime。 而這些運行時函數,有一個單獨的 npm 包去實現他們,叫作 babel/runtime。
而像那些 es6 新增的 API,例如 Promise、Map、WeakMap。數據新方法 flat、includes 等等 api,這些東西不屬於 babel語法編譯的範疇,是屬於一些新的api。這些叫作 墊片,英文名叫 polyfill。在社區裏,polyfill墊片經過另外2個npm包來實現:一個叫作 corejs(用來實現除了generator以外的墊片,如今要使用他的版本3),另外一個叫作 regenerator-runtime(用來實現generator墊片)。
其實最佳實踐的前提就是正確的理解上文中兩個概念。從而正確的使用 babel/runtime 和 corejs3.
若是比喻成作飯,babel/runtime 和 corejs3 就是咱們的食材。有了食材,咱們就要用一個配套的刀具來加工他們。
咱們接下來所講的實踐,其背景是在 webpack + babel 下的實踐。由於,畢竟咱們一般不會單據來用 babel 操做一個 js 文件。
這種場景的要求是:
解決方法:
一、 爲了能按需轉譯語法。在最新babel7以後,咱們只需使用preset/env預設就很簡單了。
// babel.config.js module.exports = { "presets": [ [ "@babel/env", { "targets": "last 50 Chrome versions", "useBuiltIns": false, // polyfill配置先關掉,後面再講 } ] ] }
咱們能夠經過修改 targets ,來決定babel編譯哪些語法。例如你把chrome版本號調到最新1個版本,那麼箭頭函數必然是不會轉換的。
二、 三、按需加載polyfill
按需加載polyfill有2種方式,一種是把 useBuiltIns配置成 entry,另外一種是配置成 usage.
使用上的區別是:
以下是usage的方式:
module.exports = { "presets": [ [ "@babel/env", { "targets": "last 50 Chrome versions", "useBuiltIns": 'usage', "corejs": 3 // 寫死就好!當前階段就該用3 } ] ] }
以下是entry方式
//babel.config.js module.exports = { "presets": [ [ "@babel/env", { "targets": "last 50 Chrome versions", "useBuiltIns": 'entry', "corejs": 3 } ] ] }
entry模式時,須要在代碼裏手工引入一下。
// index.js // 手工引入 import "core-js/stable"; import "regenerator-runtime/runtime";
既然usage這麼好,咱們何苦要用entry呢? 因此,就用usage模式吧!
四、問題來了:怎麼解決輔助函數的冗餘問題。
babel/runtime是幹啥的,要雜用?
上文的配置,咱們解決了polyfill的問題。下一步,咱們要解決輔助函數在每一個js裏都有冗餘的問題。由於如今的配置下,若是你有 a.js 和 b.js,那麼兩個js中都會被babel放入那些 helper 輔助函數。當webpack打包完成一個 bundle。學過webpack原理的應該指到:最終的bundle裏也會有 a.js 和 b.js模塊,每一個模塊中必然也存在重複的輔助函數。
怎麼解決? 那就用 babel/plugin-transform-runtime 插件。
這個插件的工做就是:在babel編譯每一個js時,把裏面的輔助函數給換成 對 babel/runtime 的 require 引用。
當每一個js裏的輔助函數,都變成 babel/runtime的引用。那麼webpack打包後的bundle,必然這些輔助函數就變成一個公共模塊了,解決了冗餘問題。
配置方式:
module.exports = { "presets": [ [ "@babel/env", { "targets": "last 50 Chrome versions", "useBuiltIns": 'usage', "corejs": 3 } ] ], "plugins": [ ["@babel/plugin-transform-runtime", { "helpers": true, // 這就是抽離helper 的配置 "corejs": false, // 先設置false,後面再講 "regenerator": false // 先設置false,後面再講 }] ] }
上面把 helper配置爲true,就能夠實現helper函數抽到 babel/runtime。
至此,網站打包講解配置完成!開發網站時就用上述配置便可。
開發jssdk跟開發網站對bundle.js有不一樣的要求。
能夠顯而易見,對於開發jssdk,咱們第一要作的,就是先把全局polyfill給他關掉。好比關掉那個 usage:
module.exports = { "presets": [ [ "@babel/env", { "targets": "last 50 Chrome versions", "useBuiltIns": false } ] ] }
幸運的是,jssdk的另外幾個要求,靠 babel/plugin-transform-runtime 均可以搞定。以下配置便可:
module.exports = { "presets": [ [ "@babel/env", { "targets": "last 50 Chrome versions", "useBuiltIns": false } ] ], "plugins": [ ["@babel/plugin-transform-runtime", { "helpers": true, // 這就是抽離helper 的配置 "corejs": 3, // 這是局部polyfill的配置 "regenerator": true // 這是局部polyfill generator的配置 }] ] }
咱們只需把該插件的配置全開。把 preset/env 的polyfill配置關掉,就是一個適配jssdk開發的配置了。