<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      [babel] babel的工作原理

      Babel是什么

      Babel 是一個通用的多功能的 JavaScript 編譯器。主要用于將采用 ECMAScript 2015+ 語法編寫的代碼轉換為向后兼容的 JavaScript 語法,以便能夠運行在當前和舊版本的瀏覽器或其他環境中。

      常見的用途有:

      • 語法轉換
      • 通過 Polyfill 方式在目標環境中添加缺失的功能(通過引入第三方 polyfill 模塊,例如 core-js
      • 源碼轉換(codemods)

      例如我們在React經常使用 JSX 語法,由于這不是JS原生語法,所以不能直接被 JS 引擎編譯執行。在代碼被執行之前,需要使用編譯器進行轉譯,轉換成 JS 代碼。

      image-20240731143915451

      Babel的工作原理

      從源代碼轉換到目標代碼的過程,也就是從源字符串轉換到目標字符串的過程。當然我們不可能直接在字符串上進行操作。在 Babel 轉換代碼的中間過程會生成抽象語法樹(Abstract Syntax Tree,簡稱AST),用樹來表示代碼的結構和語義,然后通過遍歷這棵樹,并在節點上應用一些操作來完成對代碼的轉換操作。

      Babel的主要處理步驟是:解析(parse)轉換(transform)生成(generate)

      graph LR A((CODE)) --> B[Parse] B --> C((AST)) C --> D[Transform] D --> E((AST')) E --> F[Generate] F --> G((CODE'))

      對于不了解編譯原理的前端開發人員,這里推薦一個github上面的mini級別的項目:jamiebuilds/the-super-tiny-compiler: ? Possibly the smallest compiler ever (github.com)

      這是一個使用JS編寫的超級簡單但是包含了上述三個主要步驟的編譯器。

      代碼就幾百行,加上注釋有一千多行,講解非常詳細。適合入門。

      解析 parse

      “解析”這一過程主要是通過讀取代碼字符串,構建出 AST 。

      這一過程主要包含詞法分析語法分析這兩個階段。

      詞法分析

      詞法分析部分使用tokenizer方法記錄一個tokens列表,為代碼中的每一個token標注其類型和值:

      從源碼中可以看到Token除了記錄類型和值,還記錄了這個詞在源代碼中的位置,即startendloc等屬性。

      token是指代碼中獨立的最小單元,它可以是數字字面量(NumberLiteral)、字符串字面量(StringLiteral)、操作符(operator)等等。

      export class Token {
        constructor(state: State) {
          this.type = state.type;
          this.value = state.value;
          this.start = state.start;
          this.end = state.end;
          this.loc = new SourceLocation(state.startLoc, state.endLoc);
        }
      
        declare type: TokenType;
        declare value: any;
        declare start: number;
        declare end: number;
        declare loc: SourceLocation;
      }
      

      在詞法分析完成之后,會得到一個tokens數組,記錄了代碼中每個詞的記錄。

      比如下面這個簡單的語句:

      n * n;
      

      詞法分析之后將得到如下的數組:

      [
        { type: { ... }, value: "n", start: 0, end: 1, loc: { ... } },
        { type: { ... }, value: "*", start: 2, end: 3, loc: { ... } },
        { type: { ... }, value: "n", start: 4, end: 5, loc: { ... } },
        ...
      ]
      

      type是一個對象,通過一些屬性來描述一個token

      {
        type: {
          label: 'name',
          keyword: undefined,
          beforeExpr: false,
          startsExpr: true,
          rightAssociative: false,
          isLoop: false,
          isAssign: false,
          prefix: false,
          postfix: false,
          binop: null,
          updateContext: null
        },
        ...
      }
      

      語法分析

      這一階段會根據上一階段生成的tokens構造出AST的表述結構。

      相關聯的tokens會被組合成語句,形成子樹,即子樹的根節點是描述表達式的節點,子節點是token產生的節點或者嵌套描述其它表達式的節點。

      ASTNode并不是直接復用上述Token的數據結構,而是一個新的數據結構。

      這里用AST explorer進行舉例。

      語句:n*n;

      生成的AST如下:

      {
        "type": "Program",
        "start": 0,
        "end": 6,
        "body": [
          {
            "type": "ExpressionStatement",
            "start": 0,
            "end": 6,
            "expression": {
              "type": "BinaryExpression",
              "start": 0,
              "end": 5,
              "left": {
                "type": "Identifier",
                "start": 0,
                "end": 1,
                "name": "n"
              },
              "operator": "*",
              "right": {
                "type": "Identifier",
                "start": 4,
                "end": 5,
                "name": "n"
              }
            }
          }
        ],
        "sourceType": "module"
      }
      

      語法分析完成之后就得到了抽象語法樹。

      轉換 transform

      轉換操作通過遍歷抽象語法樹,對節點進行新增、更新、刪除等操作。這是Babel工作流程中最復雜的部分,也是babel插件介入工作的主要部分。

      Visitor

      Babel使用深度優先遍歷 AST,這個過程中使用了訪問者模式。即構建一個visitor對象,遍歷過程中針對節點的類型,執行不同的方法,而方法又細分為enterexit兩個與時間相關的hook

      visitor示例:

      const MyVisitor = {
        Identifier: {
          enter() {
            ...
          },
          exit() {
            ...
          }
        },
        CallExpression: {
          enter(){
            ...
          },
          exit(){
            ...
          }
        },
        ...
      };
      

      NodePath

      在遍歷 AST 的時候,babel還會生成NodePath對象,這個對象包含了節點本身以及與節點相關的上下文信息,比如父節點、兄弟節點和作用域信息等。NodePath對象的數據結構大致如下(不止這些屬性):(摘自官方handbook

      {
        // 當前節點的父節點,表示這是一個函數聲明(FunctionDeclaration)
        "parent": {
          "type": "FunctionDeclaration",
          "id": {...},  // 函數聲明的標識符節點(具體內容省略)
          ....
        },
        
        // 當前路徑對應的 AST 節點,這是一個標識符(Identifier),其名稱是 "square"
        "node": {
          "type": "Identifier",
          "name": "square"
        },
        
        // 包含處理工具的對象,通常包括 `file` 屬性,用于訪問文件信息和其他上下文信息
        "hub": {...},
        
        // 保存路徑上下文的棧,用于處理嵌套的路徑操作
        "contexts": [],
        
        // 存儲與路徑相關的自定義數據
        "data": {},
        
        // 標記是否應跳過當前路徑的遍歷
        "shouldSkip": false,
        
        // 標記是否應停止整個遍歷過程
        "shouldStop": false,
        
        // 標記當前節點是否已被刪除
        "removed": false,
        
        // 在遍歷過程中存儲插件的狀態信息
        "state": null,
        
        // 當前路徑的選項對象,通常用于配置遍歷選項
        "opts": null,
        
        // 指示是否應跳過某些子節點
        "skipKeys": null,
        
        // 當前節點父節點的 `NodePath` 對象
        "parentPath": null,
        
        // 當前路徑的上下文信息
        "context": null,
        
        // 當前節點所在的容器(可能是父節點的屬性或數組)
        "container": null,
        
        // 如果當前節點在父節點中是一個列表的一部分,則為列表的鍵
        "listKey": null,
        
        // 布爾值,指示當前節點是否在其父節點的列表中
        "inList": false,
        
        // 當前節點在其父節點中的鍵
        "parentKey": null,
        
        // 當前節點在父節點中的位置
        "key": null,
        
        // 當前路徑的作用域(scope)對象
        "scope": null,
        
        // 當前路徑節點的類型(在某些上下文中使用)
        "type": null,
        
        // 當前路徑節點的類型注解(TypeScript 或 Flow)
        "typeAnnotation": null,
            
        ......
      }
      

      自定義插件簡單示例:

      my-plugin.js

      module.exports = function (babel) {
        const { types: t } = babel;
      
        return {
          visitor: {
            Identifier(path) {
              // `path` 是一個 `NodePath` 對象,代表當前的標識符節點
              if (path.node.name === 'oldName') {
                // 修改當前標識符節點的名稱
                path.replaceWith(t.identifier('newName'));
              }
            },
          },
        };
      };
      
      

      babel配置文件中(例如.babelrc):使用文件路徑配置目標插件

      {
        "presets": ["@babel/preset-env"],
        "plugins": ["./my-plugin"]
      }
      

      生成 generate

      babel通過babel-generator模塊深度優先遍歷 AST ,根據節點類型生成相應的代碼片段,并根據配置選項生成不同格式的代碼和源碼映射。

      這個階段的產物是:編譯后的代碼 + source-map

      結語

      babel的優點在于為JS提供了許多可能性。

      對于web前端開發人員來說,他們不再需要過度糾結語法的兼容性,babel會完成代碼降級兼容舊版本;

      對于babel插件開發人員來說,babel是一個便捷地操作抽象語法樹的工具,我們不再需要手寫編譯器,起碼不需要實現parsegenerate,只需要將注意力集中在最核心的visitor,關注如何對 AST 進行 transform

      參考資料

      [1] babel-handbook/translations/zh-Hans/plugin-handbook.md at master · jamiebuilds/babel-handbook (github.com)

      [2] EmberConf 2016: How to Build a Compiler by James Kyle - YouTube

      [3] jamiebuilds/the-super-tiny-compiler: ? Possibly the smallest compiler ever (github.com)

      [4] https://www.babeljs.cn/docs/

      [5] AST explorer

      視頻 [2]項目[3] 的作者在一個會議上的演講,視頻的內容大部分都是在講這個項目。

      posted @ 2024-07-31 18:05  feixianxing  閱讀(190)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 狠狠色噜噜狠狠狠狠蜜桃| 国产一级老熟女自拍视频| 国产女人18毛片水真多1| 亚洲男人电影天堂无码| 久久人妻国产精品| 国产精品无遮挡又爽又黄| 91亚洲免费视频| 日日噜噜夜夜爽爽| 国产精品视频一区不卡| 五月婷婷激情第四季| 孟州市| 国产一区二区三区精品综合| 亚洲经典av一区二区| 午夜不卡久久精品无码免费| 国产一区二区丰满熟女人妻| 亚洲人成小说网站色在线| 亚洲一区中文字幕第十页| 国产精品午夜av福利| 日本国产一区二区三区在线观看 | 99riav精品免费视频观看| 国产白丝jk捆绑束缚调教视频| 蜜桃视频在线免费观看一区二区| 伊人激情av一区二区三区| 精品国产午夜福利在线观看| 任我爽精品视频在线播放| 水蜜桃av导航| 无码专区 人妻系列 在线| 精品久久久久无码| 一区二区和激情视频| 中文字幕一区二区久久综合 | 不卡在线一区二区三区视频| 使劲快高潮了国语对白在线| 国产男女黄视频在线观看| 中文字幕在线精品人妻| 人妻少妇偷人精品一区| 中文字幕av无码免费一区| 日本中文一二区有码在线| 四虎影视一区二区精品| 思热99re视热频这里只精品| 99在线 | 亚洲| 国产jizzjizz视频|