<font id="zqva1"></font>
<rt id="zqva1"></rt>
  • <tt id="zqva1"></tt>
    <cite id="zqva1"></cite>

    <cite id="zqva1"><noscript id="zqva1"></noscript></cite>
      <rp id="zqva1"><meter id="zqva1"></meter></rp>

        <cite id="zqva1"></cite>
          <b id="zqva1"></b>
          <rp id="zqva1"></rp>
          <cite id="zqva1"></cite>

          <rt id="zqva1"></rt>

        1. <rp id="zqva1"></rp>

          深入理解Babel原理及其使用,babel把ES6轉成ES5的原理是什么?

          時間:?2017-11-29閱讀:?6149標簽:?babel

          前言

          半年前也寫過一篇babel的簡單使用文章,當時看了下babel的文檔,但是很多地方還不理解,所以文章里沒有怎么說道babel的一些關鍵概念,只是機械的描述如何使用(配合webstorm)。
          最近剛好遇到一個問題,發現是因為js代碼中使用的es6的新api沒有被轉義,導致拋異常了。查找了下資料,是沒有引入polyfill所致,之前一直對babel的這些概念沒有很好的理解,把這個bug作為引子,認真了解了下babel,寫出此文。
          本文的babel使用場景局限于babel配合webpack來轉譯輸出es5的js代碼,babel的命令行、以代碼形式調用或node環境下這些統統都不會涉及。

          Babel的包構成

          核心包

          • babel-core:babel轉譯器本身,提供了babel的轉譯API,如babel.transform等,用于對代碼進行轉譯。像webpack的babel-loader就是調用這些API來完成轉譯過程的。
          • babylon:js的詞法解析器
          • babel-traverse:用于對AST(抽象語法樹,想了解的請自行查詢編譯原理)的遍歷,主要給plugin用
          • babel-generator:根據AST生成代碼

          功能包

          • babel-types:用于檢驗、構建和改變AST樹的節點
          • babel-template:輔助函數,用于從字符串形式的代碼來構建AST樹節點
          • babel-helpers:一系列預制的babel-template函數,用于提供給一些plugins使用
          • babel-code-frames:用于生成錯誤信息,打印出錯誤點源代碼幀以及指出出錯位置
          • babel-plugin-xxx:babel轉譯過程中使用到的插件,其中babel-plugin-transform-xxx是transform步驟使用的
          • babel-preset-xxx:transform階段使用到的一系列的plugin
          • babel-polyfill:JS標準新增的原生對象和API的shim,實現上僅僅是core-js和regenerator-runtime兩個包的封裝
          • babel-runtime:功能類似babel-polyfill,一般用于library或plugin中,因為它不會污染全局作用域

          工具包

          babel-cli:babel的命令行工具,通過命令行對js代碼進行轉譯
          babel-register:通過綁定node.js的require來自動轉譯require引用的js代碼文件

          babel的配置

          使用形式

          如果是以命令行方式使用babel,那么babel的設置就以命令行參數的形式帶過去;
          還可以在package.json里在babel字段添加設置;
          但是建議還是使用一個單獨的.babelrc文件,把babel的設置都放置在這里,所有babel API的options(除了回調函數之外)都能夠支持,具體的options見babel的API options文檔

          常用options字段說明

          • env:指定在不同環境下使用的配置。比如production和development兩個環境使用不同的配置,就可以通過這個字段來配置。env字段的從process.env.BABEL_ENV獲取,如果BABEL_ENV不存在,則從process.env.NODE_ENV獲取,如果NODE_ENV還是不存在,則取默認值"development"
          • plugins:要加載和使用的插件列表,插件名前的babel-plugin-可省略;plugin列表按從頭到尾的順序運行
          • presets:要加載和使用的preset列表,preset名前的babel-preset-可省略;presets列表的preset按從尾到頭的逆序運行(為了兼容用戶使用習慣)
          • 同時設置了presets和plugins,那么plugins的先運行;每個preset和plugin都可以再配置自己的option

          配置文件的查找

          babel會從當前轉譯的文件所在目錄下查找配置文件,如果沒有找到,就順著文檔目錄樹一層層往上查找,一直到.babelrc文件存在或者帶babel字段的package.json文件存在為止。

          babel的工作原理

          babel是一個轉譯器,感覺相對于編譯器compiler,叫轉譯器transpiler更準確,因為它只是把同種語言的高版本規則翻譯成低版本規則,而不像編譯器那樣,輸出的是另一種更低級的語言代碼。
          但是和編譯器類似,babel的轉譯過程也分為三個階段:parsing、transforming、generating,以ES6代碼轉譯為ES5代碼為例,babel轉譯的具體過程如下:

          ES6代碼輸入 ==》 babylon進行解析 ==》 得到AST
          ==》 plugin用babel-traverse對AST樹進行遍歷轉譯 ==》 得到新的AST樹
          ==》 用babel-generator通過AST樹生成ES5代碼

          此外,還要注意很重要的一點就是,babel只是轉譯新標準引入的語法,比如ES6的箭頭函數轉譯成ES5的函數;而新標準引入的新的原生對象,部分原生對象新增的原型方法,新增的API等(如Proxy、Set等),這些babel是不會轉譯的。需要用戶自行引入polyfill來解決

          plugins

          插件應用于babel的轉譯過程,尤其是第二個階段transforming,如果這個階段不使用任何插件,那么babel會原樣輸出代碼。
          我們主要關注transforming階段使用的插件,因為transform插件會自動使用對應的詞法插件,所以parsing階段的插件不需要配置。

          presets

          如果要自行配置轉譯過程中使用的各類插件,那太痛苦了,所以babel官方幫我們做了一些預設的插件集,稱之為preset,這樣我們只需要使用對應的preset就可以了。以JS標準為例,babel提供了如下的一些preset:

          • es2015
          • es2016
          • es2017
          • env
            es20xx的preset只轉譯該年份批準的標準,而env則代指最新的標準,包括了latest和es20xx各年份
            另外,還有 stage-0到stage-4的標準成形之前的各個階段,這些都是實驗版的preset,建議不要使用。

          polyfill

          polyfill是一個針對ES2015+環境的shim,實現上來說babel-polyfill包只是簡單的把core-js和regenerator runtime包裝了下,這兩個包才是真正的實現代碼所在(后文會詳細介紹core-js)。
          使用babel-polyfill會把ES2015+環境整體引入到你的代碼環境中,讓你的代碼可以直接使用新標準所引入的新原生對象,新API等,一般來說單獨的應用和頁面都可以這樣使用。

          使用方法

          1. 先安裝包: npm install --save babel-polyfill
          2. 要確保在入口處導入polyfill,因為polyfill代碼需要在所有其他代碼前先被調用
            代碼方式:import "babel-polyfill"
          3. webpack配置:module.exports = { entry: ["babel-polyfill", "./app/js"] };

          如果只是需要引入部分新原生對象或API,那么可以按需引入,而不必導入全部的環境,具體見下文的core-js

          runtime

          polyfill和runtime的區別

          直接使用babel-polyfill對于應用或頁面等環境在你控制之中的情況來說,并沒有什么問題。但是對于在library中使用polyfill,就變得不可行了。因為library是供外部使用的,但外部的環境并不在library的可控范圍,而polyfill是會污染原來的全局環境的(因為新的原生對象、API這些都直接由polyfill引入到全局環境)。這樣就很容易會發生沖突,所以這個時候,babel-runtime就可以派上用場了。

          transform-runtime和babel-runtime

          babel-plugin-transform-runtime插件依賴babel-runtime,babel-runtime是真正提供runtime環境的包;也就是說transform-runtime插件是把js代碼中使用到的新原生對象和靜態方法轉換成對runtime實現包的引用,舉個例子如下:

          // 輸入的ES6代碼
          var sym = Symbol();
          // 通過transform-runtime轉換后的ES5+runtime代碼 
          var _symbol = require("babel-runtime/core-js/symbol");
          var sym = (0, _symbol.default)();
          

          從上面這個例子可見,原本代碼中使用的ES6新原生對象Symbol被transform-runtimec插件轉換成了babel-runtime的實現,既保持了Symbol的功能,同時又沒有像polyfill那樣污染全局環境(因為最終生成的代碼中,并沒有對Symbol的引用)
          另外,這里我們也可以隱約發現,babel-runtime其實也不是真正的實現代碼所在,真正的代碼實現是在core-js中,后面我們再說

          transform-runtime插件的功能

          1. 把代碼中的使用到的ES6引入的新原生對象和靜態方法用babel-runtime/core-js導出的對象和方法替代
          2. 當使用generators或async函數時,用babel-runtime/regenerator導出的函數取代(類似polyfill分成regenerator和core-js兩個部分)
          3. 把Babel生成的輔助函數改為用babel-runtime/helpers導出的函數來替代(babel默認會在每個文件頂部放置所需要的輔助函數,如果文件多的話,這些輔助函數就在每個文件中都重復了,通過引用babel-runtime/helpers就可以統一起來,減少代碼體積)

          上述三點就是transform-runtime插件所做的事情,由此也可見,babel-runtime就是一個提供了regenerator、core-js和helpers的運行時庫。
          建議不要直接使用babel-runtime,因為transform-runtime依賴babel-runtime,大部分情況下都可以用transform-runtime達成目的。
          此外,transform-runtime在.babelrc里配置的時候,還可以設置helpers、polyfill、regenerator這三個開關,以自行決定runtime是否要引入對應的功能。
          最后補充一點:由于runtime不會污染全局空間,所以實例方法是無法工作的(因為這必須在原型鏈上添加這個方法,這是和polyfill最大的不同) ,比如:

          var arr = ['a', 'b', 'c'];
          arr.fill(7);  // 實例方法不行
          Array.prototype.fill.apply(arr, 7);  // 用原型鏈來調用也是不行
          

          通過core-js實現按需引入polyfill或runtime

          core-js包才上述的polyfill、runtime的核心,因為polyfill和runtime其實都只是對core-js和regenerator的再封裝,方便使用而已。
          但是polyfill和runtime都是整體引入的,不能做細粒度的調整,如果我們的代碼只是用到了小部分ES6而導致需要使用polyfill和runtime的話,會造成代碼體積不必要的增大(runtime的影響較小)。所以,按需引入的需求就自然而然產生了,這個時候就得依靠core-js來實現了。

          core-js的組織結構

          首先,core-js有三種使用方式:

          • 默認方式:require('core-js')
            這種方式包括全部特性,標準的和非標準的
          • 庫的形式: var core = require('core-js/library')
            這種方式也包括全部特性,只是它不會污染全局名字空間
          • 只是shim: require('core-js/shim')或var shim = require('core-js/library/shim')
            這種方式只包括標準特性(就是只有polyfill功能,沒有擴展的特性)

          core-js的結構是高度模塊化的,它把每個特性都組織到一個小模塊里,然后再把這些小模塊組合成一個大特性,層層組織。比如:
          core-js/es6(core-js/library/es6)就包含了全部的ES6特性,而core-js/es6/array(core-js/library/es6/array)則只包含ES6的Array特性,而core-js/fn/array/from(core-js/library/fn/array/from)則只有Array.from這個實現。
          實現按需使用,就是自己選擇使用到的特性,然后導入即可。具體的每個特性和對應的路徑可以直接查看core-js的github

          core-js的按需使用

          1、類似polyfill,直接把特性添加到全局環境,這種方式體驗最完整

          require('core-js/fn/set');
          require('core-js/fn/array/from');
          require('core-js/fn/array/find-index');
          
          Array.from(new Set([1, 2, 3, 2, 1])); // => [1, 2, 3]
          [1, 2, NaN, 3, 4].findIndex(isNaN);   // => 2
          

          2、類似runtime一樣,以庫的形式來使用特性,這種方式不會污染全局名字空間,但是不能使用實例方法

          var Set       = require('core-js/library/fn/set');
          var from      = require('core-js/library/fn/array/from');
          var findIndex = require('core-js/library/fn/array/find-index');
          
          from(new Set([1, 2, 3, 2, 1]));      // => [1, 2, 3]
          findIndex([1, 2, NaN, 3, 4], isNaN); // => 2
          

          3、因為第二種庫的形式不能使用prototype方法,所以第三種方式使用了一個小技巧,通過::這個符號而不是.來調用實例方式,從而達到曲線救國的目的。這種方式的使用,路徑中都會帶有/virtual/

          import {fill, findIndex} from 'core-js/library/fn/array/virtual';
          
          Array(10)::fill(0).map((a, b) => b * b)::findIndex(it => it && !(it % 8)); // => 4
          
          // 對比下polyfill的實現 
          // Array(10).fill(0).map((a, b) => b * b).findIndex(it => it && !(it % 8));
          

          總結

          Babel使用的難點主要在于理解polyfill、runtime和core-js,通過本文,把這三者的概念和關系理清楚了,對babel的使用就不存在問題!



          作者:mercurygear
          鏈接:http://www.jianshu.com/p/e9b94b2d52e2
          來源:簡書

          站長推薦

          1.阿里云: 本站目前使用的是阿里云主機,安全/可靠/穩定。點擊領取2000元代金券、了解最新阿里云產品的各種優惠活動點擊進入

          2.騰訊云: 提供云服務器、云數據庫、云存儲、視頻與CDN、域名等服務。騰訊云各類產品的最新活動,優惠券領取點擊進入

          3.廣告聯盟: 整理了目前主流的廣告聯盟平臺,如果你有流量,可以作為參考選擇適合你的平臺點擊進入

          鏈接: http://www.modern-decoration.com.cn/article/detial/197

          babel環境安裝與編譯

          babel:將瀏覽器不支持的ES6語法轉為javascript,初始化package.json文件:描述當前項目信息,包括依賴等,創建配置文件,告訴babel用什么規則來編譯文件.babelrc

          babel polyfill 的一些理解

          為了支持業務中少量的es6+的高級特性,最近在研究了一下babel的墊片,現將此整理為文字,如下。簡單來講,babel解決語法層面的問題。用于將ES6+的高級語法轉為ES5。

          入門babel,我們需要了解些什么

          說實話,我從工作開始就一直在接觸babel,然而對于babel并沒有一個清晰的認識,只知道babel是用于編譯javascript,讓開發者能使用超前的ES6+語法進行開發。自己配置babel的時候,總是遇到很多困惑,下面我就以babel@7為例

          使用@babel/preset-typescript取代awesome-typescript-loader和ts-loader

          談@babel/preset-typescript的優越性之前,還是先說下awesome-typescript-loader方案是如何對TypeScript進行處理的。首先我們需要知道TypeScript是一個將TypeScript轉換為指定版本JS代碼的編譯器

          webpack中配置babel

          因為在webpack中,默認只能處理一部分es6的語法,一些更高級的es6和es7語法webpack不能處理,這時就需要第三方的loader即babel來幫助webpack來處理這些高級的語法

          不容錯過的 Babel7 知識

          對 Babel 的配置項的作用不那么了解,是否會影響日常開發呢?老實說,大多情況下沒有什么影響。不過呢,還是想更進一步了解下,于是最近認真閱讀了 Babel 的文檔,外加不斷編譯驗證

          如何實現一個 Babel Macros?

          可以發現實現一個 Babel macros 的過程與開發 Babel 插件的流程類似,都是對 AST 進行操作。babel-plugin-macro 只是提供一個在“外部”進行 AST 修改的方式,通過這種方式能夠靈活的對 Babel 編譯時進行拓展

          如何將ES6轉換成ES5?

          ECMAScript 6(ES6)的發展速度非常之快,但現代瀏覽器對ES6新特性支持度不高,所以要想在瀏覽器中直接使用ES6的新特性就得借助別的工具來實現。Babel是一個廣泛使用的轉碼器,babel可以將ES6代碼完美地轉換為ES5代碼

          前端中的編譯原理:從零打造一個實用的 Babel 插件

          說起編譯原理,可能我們腦海中首先浮現的就是 “編譯器” 這個詞匯。維基百科上對編譯器的定義是: 編譯器是一種計算機程序,它會將某種編程語言寫成的源代碼(原始語言)轉換成另一種編程語言(目標語言)

          Babel-loader,babel-core和babel-preset之間是什么關系

          `babel-loader` 是一個 npm 包,它使得 webpack 可以通過 babel 轉譯 JavaScript 代碼。babel 的功能在于「代碼轉譯」,具體一點,即將目標代碼轉譯為能夠符合期望語法規范的代碼。在轉譯的過程中

          內容以共享、參考、研究為目的,不存在任何商業目的。其版權屬原作者所有,如有侵權或違規,請與小編聯系!情況屬實本人將予以刪除!

          文章投稿關于web前端網站點搜索站長推薦網站地圖站長QQ:522607023

          小程序專欄: 土味情話心理測試腦筋急轉彎幽默笑話段子句子語錄成語大全運營推廣

          国产精品高清视频免费 - 视频 - 在线观看 - 影视资讯 - 唯爱网