当前位置: 首页 > news >正文

02 什么是Babel

什么是Babel?

Babel 是一个 JavaScript 编译器,提供了JavaScript的编译过程,能够将源代码转换为目标代码。AST -> Transform -> Generate

官网 Babel · Babel

查看AST https://astexplorer.net/

Babel所有的包 @babel/traverse · Babel

Babel 是一个广泛使用的 JavaScript 编译器,它主要用于将现代的 JavaScript 代码转换成向后兼容的版本,以便可以在旧版浏览器中运行。Babel 的编译过程可以分为三个主要阶段:解析(Parse)、转换(Transform)和生成(Generate)。这三个阶段分别对应于上文提到的 AST -> Transform -> Generate。

  1. 解析 (Parse) - 生成抽象语法树 (AST) 在这个阶段,Babel 将源代码作为输入,并通过解析器将其转换为抽象语法树 (Abstract Syntax Tree, AST)。AST 是一种表示代码结构的树形数据结构,其中每个节点代表源代码中的一个构造,比如变量声明、函数调用等。这个阶段的主要工作是读取源代码并创建相应的 AST 结构,这样就可以以编程方式来理解和操作代码了。

  2. 转换 (Transform) 一旦有了 AST,Babel 就可以通过遍历这棵树来应用各种转换插件。这些插件能够修改 AST,从而实现从一种 JavaScript 版本到另一种版本的转换。例如,一个插件可能将 ES6 的箭头函数转换为 ES5 中的普通函数。转换阶段是 Babel 功能的核心部分,因为它允许开发者根据需要添加或移除特定的功能,或者执行其他形式的代码转换。Babel 使用访问者模式来遍历 AST,并在适当的地方插入或删除节点,或者改变节点属性。

  3. 生成 (Generate) 最后,在所有必要的转换完成后,Babel 需要将更新后的 AST 转换回实际的 JavaScript 代码。这个阶段被称为代码生成。在这个过程中,Babel 会遍历修改过的 AST 并输出新的源代码。生成的代码应该与原始代码具有相同的行为,但可能使用了不同的语言特性或语法结构。最终的结果就是转换后的代码,它可以被更广泛的环境所支持。

 AST抽象语法树

核心功能​

  1. 语法转换:将新版本的 JavaScript 语法转换为旧版本的语法
  2. Polyfill:通过引入额外的代码,使新功能在旧浏览器中可用如filter这些新特性在旧浏览器中无法使用需要处理
  3. JSX: 将JSX语法转换成普通的JavaScript语法
  4. 插件: 为Babel提供自定义功能

案例

案例1 es6转es5 

语法转换:将新版本的 JavaScript 语法转换为旧版本的语法

npm install --save-dev @babel/core @babel/cli @babel/preset-env

//如何支持新特性例如 Object.assign Array.prototype.find 等

npm i core-js -D

目录结构:

test.js 测试用例  

//语法
const a = (params = 2) => 1 + params;
const b = [1, 2, 3]
const c = [...b, 4, 5]
class Babel {}
new Babel()
//API
const x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].filter((x) => x % 2 === 0)
const y = Object.assign({}, { name: 1 })

index.js 核心转换代码

///记得设置package.json的type为module
import Babel from '@babel/core'
import presetEnv from '@babel/preset-env'
import fs from 'node:fs'
const file = fs.readFileSync('./test.js', 'utf8')
const result = Babel.transform(file, {//usage 会根据配置的浏览器兼容,以及你代码中用到的 API 来进行 polyfill,实现了按需添加//corejs 3 是corejs的版本presets: [// useBuiltIns参数有很多entry,usage等,corejs是版本// entry手动引入 usage按需引入[presetEnv, { useBuiltIns: "usage", corejs: 3 }]]
})
console.log(result.code)

转换之后的代码

"use strict";require("core-js/modules/es.symbol.js");
require("core-js/modules/es.symbol.description.js");
require("core-js/modules/es.symbol.iterator.js");
require("core-js/modules/es.symbol.to-primitive.js");
require("core-js/modules/es.array.iterator.js");
require("core-js/modules/es.date.to-primitive.js");
require("core-js/modules/es.number.constructor.js");
require("core-js/modules/es.object.define-property.js");
require("core-js/modules/es.string.iterator.js");
require("core-js/modules/web.dom-collections.iterator.js");
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
require("core-js/modules/es.array.concat.js");
require("core-js/modules/es.array.filter.js");
require("core-js/modules/es.object.assign.js");
require("core-js/modules/es.object.to-string.js");
function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } 
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
//语法
var a = function a() {var params = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 2;return 1 + params;
};
var b = [1, 2, 3];
var c = [].concat(b, [4, 5]);
var Babel = /*#__PURE__*/_createClass(function Babel() {_classCallCheck(this, Babel);
});
new Babel();
//API
var x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].filter(function (x) {return x % 2 === 0;
});
var y = Object.assign({}, {name: 1
});

案例2jsx代码转换react

npm i  react react-dom -D

npm install @babel/preset-react -D  //之前的babel预设也需要

测试用例 test.jsx

import react from 'react'
import { createRoot } from 'react-dom/client'const App = () => {return <div>是谁?????</div>
}createRoot(document.getElementById('root')).render(<App />)

index.js

//记得设置package.json的type为module
import Babel from '@babel/core'
import presetEnv from '@babel/preset-env'
import fs from 'node:fs'
import react from '@babel/preset-react' //支持jsx语法
// const file = fs.readFileSync('./test.js', 'utf8')
const file = fs.readFileSync('./test.jsx', 'utf8')
const result = Babel.transform(file, {//usage 会根据配置的浏览器兼容,以及你代码中用到的 API 来进行 polyfill,实现了按需添加//corejs 3 是corejs的版本presets: [// useBuiltIns参数有很多entry,usage等,corejs是版本// entry手动引入 usage按需引入[presetEnv, { useBuiltIns: "usage", corejs: 3 }],react]
})
console.log(result.code)

转换的结果

其实也就是调用了React.createElement去创建元素

"use strict";var _react = _interopRequireDefault(require("react"));
var _client = require("react-dom/client");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
var App = function App() {return /*#__PURE__*/React.createElement("div", null, "\u5C0F\u6EE1\u662F\u8C01\uFF1F\uFF1F\uFF1F\uFF1F\uFF1F");
};
(0, _client.createRoot)(document.getElementById('root')).render(/*#__PURE__*/React.createElement(App, null));

手写babel插件

import Babel from '@babel/core'
import fs from 'node:fs'
const file = fs.readFileSync('./test.js', 'utf8')
//babel会注入一个types对象里面包含了各种ast节点的方法
const transformFunction = ({ types: t }) => {return {name: 'babel-transform-function',//visitor 是一个对象,它包含了一组方法,这些方法对应于 AST 中的不同节点类型。每当 Babel 遇到某种类型的节点时,都会调用 visitor 中对应的方法。visitor: {//匹配 箭头函数 当然也可以匹配别的东西 这儿只是案例ArrowFunctionExpression(path) {const node = path.nodeconst arrowFunction = t.functionExpression(null, //node.id 是一个 Identifier 节点,表示函数名node.params, //node.params 是一个数组,表示函数的参数// BlockStatement 是 JavaScript 抽象语法树(AST)中的一种节点类型,表示一个由大括号 {} 包围的语句块。它是函数体、循环体、条件分支(如 if 语句)等代码块的基础结构t.blockStatement([t.returnStatement(node.body)]),  //node.body 是函数的主体,通常是一个 BlockStatement 节点node.async //node.async 是一个布尔值,表示函数是否是异步的 (async 函数))path.replaceWith(arrowFunction) //替换当前节点}}}
}
const result = Babel.transform(file, {plugins: [transformFunction]
})
console.log(result.code)

很多插件的写法都是一样的,如vite rollup esbuild都是差不多的 除了webpack插件不同,

const test =(params) =>{

  //都是返回一个对象,含各种配置信息

  return {

    //插件包名

    //插件的生命周期

    name: '',

    visitor: {

    }

  }

}

创建基本的 Vite 插件

1.定义插件: 你需要定义一个对象或函数作为你的插件。这个对象可以包含多个钩子(hooks),这些钩子会在构建过程中被调用

// my-plugin.js
export default function myPlugin(options) {return {name: 'vite-plugin-my-plugin', // 插件名称,用于调试和日志记录// 在这里定义各种钩子config(config, env) {// 修改 Vite 配置console.log('config hook');return {...config,define: {__APP_VERSION__: JSON.stringify(process.env.npm_package_version)}};},configureServer(server) {// 自定义 dev serverconsole.log('configureServer hook');// 可以在这里添加中间件、监听事件等},transform(code, id) {// 转换代码if (id.endsWith('.js')) {console.log(`transforming ${id}`);code = code.replace(/console\.log/g, 'console.info');}return code;},buildStart() {// 构建开始时执行console.log('buildStart hook');},closeBundle() {// 打包结束时执行console.log('closeBundle hook');}};
}

2.在 Vite 配置中使用插件: 一旦你有了插件,你可以在 vite.config.js 中引入并使用它。

import { defineConfig } from 'vite';
import myPlugin from './my-plugin';export default defineConfig({plugins: [myPlugin({ /* 传递给插件的选项 */ })]
});

使用现有的 Vite 插件接口

Vite 提供了许多标准的插件钩子,比如 config, configureServer, transform, buildStart, closeBundle 等等。你可以根据需要实现相应的钩子来扩展 Vite 的功能。


http://www.mrgr.cn/news/60997.html

相关文章:

  • 揭秘程序员薪资密码:10K 与 20K 的思维 “分水岭”
  • 【C++】多态与虚函数:深入理解对象的动态行为(万字长文详解)
  • 设计资讯 | 塑造数字交互未来的 Sol Reader
  • 快捷回复软件助力客服高效工作
  • 基于SSM(spring+springmvc+mybatis)+MySQL开发的新闻推荐系统
  • 用低配置的轻薄本玩《黑神话》是一种什么样的体验?
  • Sci Adv项目文章|ChIP-seq助力解析巨噬细胞关键调节因子AhR在黑色素瘤的进展和免疫治疗的耐药性作用
  • 苏打水奋斗过非与7656要
  • 《SMO算法 公式推导》拉格朗日乘子上界和下界
  • 什么是POJO类?
  • 关于InternVL2的环境安装
  • 等级保护测评与风险评估:企业信息安全的双剑合璧
  • vue底层原理
  • 基于微信小程序的图书馆座位预约系统+LW示例参考
  • ifuse挂载后,在python代码中访问iOS沙盒目录获取app日志
  • MySQL中的索引失效问题
  • .NET Core WebApi第6讲:WebApi的前端怎么派人去拿数据?(区别MVC)
  • 【形态学 - 击中-击不中变换(很多都讲得不直观不清楚,甚至是错的,我来个通俗易懂的)】
  • java项目分层开发中,真的有必要定义 VO 吗?
  • 除了`ROW_NUMBER()`,还有哪些SQL函数适合处理大型数据集?