深入浅出 Babel:现代 JavaScript 的编译器

wallhaven-288vd9.jpeg

在现代前端开发中,JavaScript 的版本更新速度非常快,新的语法和特性层出不穷。然而,旧版本的浏览器并不总是支持这些新特性。为了确保代码的兼容性和稳定性,我们需要一个工具来将现代 JavaScript 代码转换为旧版本的代码。Babel 就是这样一个工具。

什么是 Babel?

Babel 是一个 JavaScript 编译器,主要用于将现代 JavaScript 代码(ES6+)转换为向后兼容的 JavaScript 代码,以便在旧版本的浏览器或环境中运行。Babel 还支持转换 JSX 语法(用于 React)和 TypeScript。

// Babel 接收到的输入是: ES2015 箭头函数
[1, 2, 3].map(n => n + 1);// Babel 输出: ES5 语法实现的同等功能
[1, 2, 3].map(function(n) {return n + 1;
});

为什么需要 Babel?

  1. 向后兼容性:现代 JavaScript 特性(如箭头函数、类、模块等)在旧版本的浏览器中不被支持。Babel 可以将这些新特性转换为旧版本的 JavaScript,使代码在所有浏览器中都能运行。
  2. 代码优化:Babel 插件可以帮助优化代码,例如移除未使用的代码、压缩代码等。
  3. 扩展性:Babel 的插件系统非常强大,可以根据需要添加各种功能,如转换 JSX、TypeScript 等。

如何使用 Babel?

1. 安装与配置

安装 Node.js 和 npm

首先,你需要安装 Node.js 和 npm。你可以从 Node.js 官方网站下载并安装最新版本的 Node.js,它会自动安装 npm。

安装 Babel

在你的项目目录中,使用 npm 安装 Babel 的核心包和 CLI 工具:

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

在项目根目录下创建一个 .babelrc 文件,并添加以下内容:

{"presets": ["@babel/preset-env"]
}

这个配置文件告诉 Babel 使用 @babel/preset-env 预设来转换现代 JavaScript 代码。

2. 基本使用

编写现代 JavaScript 代码

src 目录下创建一个 index.js 文件,并编写一些现代 JavaScript 代码:

// src/index.js
const greet = (name) => {console.log(`Hello, ${name}!`);
};class Person {constructor(name) {this.name = name;}greet() {greet(this.name);}
}const john = new Person('John');
john.greet();
使用 Babel 编译代码

在终端中运行以下命令,将 src 目录下的代码编译到 dist 目录:

npx babel src --out-dir dist

编译后的代码将被输出到 dist 目录中。你可以查看 dist/index.js 文件,看到 Babel 将现代 JavaScript 代码转换为向后兼容的代码。

3. 插件与预设

Babel 的强大之处在于其插件和预设系统。你可以根据需要添加各种插件和预设。

安装 JSX 和 TypeScript 支持

如果你使用 React 和 TypeScript,可以安装相关的预设:

npm install --save-dev @babel/preset-react @babel/preset-typescript
更新 .babelrc 文件

更新 .babelrc 文件以支持 JSX 和 TypeScript:

{"presets": ["@babel/preset-env","@babel/preset-react","@babel/preset-typescript"]
}
编写 JSX 和 TypeScript 代码

src 目录下创建一个 App.tsx 文件,并编写一些 JSX 和 TypeScript 代码:

// src/App.tsx
import React from 'react';interface Props {name: string;
}const App: React.FC<Props> = ({ name }) => {return <h1>Hello, {name}!</h1>;
};export default App;
编译代码

再次运行 Babel 编译命令:

npx babel src --out-dir dist --extensions ".js,.jsx,.ts,.tsx"

4. 与 Webpack 集成

在实际项目中,Babel 通常与 Webpack 一起使用,以便更好地管理和打包代码。

安装 Webpack 和相关插件
npm install --save-dev webpack webpack-cli babel-loader
创建 Webpack 配置文件

在项目根目录下创建一个 webpack.config.js 文件,并添加以下内容:

const path = require('path');module.exports = {entry: './src/index.js',output: {filename: 'bundle.js',path: path.resolve(__dirname, 'dist')},module: {rules: [{test: /\.(js|jsx|ts|tsx)$/,exclude: /node_modules/,use: {loader: 'babel-loader'}}]},resolve: {extensions: ['.js', '.jsx', '.ts', '.tsx']}
};
更新 npm 脚本

package.json 文件中添加一个构建脚本:

"scripts": {"build": "webpack"
}
运行构建

在终端中运行以下命令:

npm run build

Webpack 将使用 Babel 编译代码,并将输出打包到 dist/bundle.js 文件中。

5. 实践项目

为了更好地理解 Babel,建议创建一个小型项目,使用 Babel 编译代码,并尝试在项目中使用不同的 Babel 插件和预设。

项目结构
my-babel-project/
├── dist/
├── node_modules/
├── src/
│   ├── App.tsx
│   └── index.js
├── .babelrc
├── package.json
└── webpack.config.js
完整代码示例
  • src/index.js

    import React from 'react';
    import ReactDOM from 'react-dom';
    import App from './App';ReactDOM.render(<App name="John" />, document.getElementById('root'));
    
  • src/App.tsx

    import React from 'react';interface Props {name: string;
    }const App: React.FC<Props> = ({ name }) => {return <h1>Hello, {name}!</h1>;
    };export default App;
    
  • .babelrc

    {"presets": ["@babel/preset-env","@babel/preset-react","@babel/preset-typescript"]
    }
    
  • webpack.config.js

    const path = require('path');module.exports = {entry: './src/index.js',output: {filename: 'bundle.js',path: path.resolve(__dirname, 'dist')},module: {rules: [{test: /\.(js|jsx|ts|tsx)$/,exclude: /node_modules/,use: {loader: 'babel-loader'}}]},resolve: {extensions: ['.js', '.jsx', '.ts', '.tsx']}
    };
    
  • package.json

    {"name": "my-babel-project","version": "1.0.0","scripts": {"build": "webpack"},"devDependencies": {"@babel/core": "^7.15.0","@babel/cli": "^7.15.0","@babel/preset-env": "^7.15.0","@babel/preset-react": "^7.14.5","@babel/preset-typescript": "^7.15.0","babel-loader": "^8.2.2","webpack": "^5.51.1","webpack-cli": "^4.8.0"},"dependencies": {"react": "^17.0.2","react-dom": "^17.0.2"}
    }
    

🌲 Babel 的工作原理

Babel 使用 AST 把不兼容的代码编译成 es5 版本,因为大多数浏览器都支持这个版本的 JavaScript 代码。

Babel 的工作流程

Babel 是一个强大的 JavaScript 编译器,它的工作流程可以分为以下几个主要步骤:

  1. 解析(Parsing):将 ES6+ 代码转换为抽象语法树(AST)。
  2. 转换(Transforming):使用插件对 AST 进行遍历和修改。
  3. 生成(Generating):将修改后的 AST 转换回 ES5 代码。

image.png

1. 解析(Parsing)

首先,Babel 使用 @babel/parser(以前称为 Babylon)将 ES6+ 代码解析成抽象语法树(AST)。AST 是代码的结构化表示,便于后续的分析和转换。

import { parse } from '@babel/parser';const code = `const greet = (name) => { console.log(\`Hello, \${name}!\`); };`;
const ast = parse(code, { sourceType: 'module' });console.log(ast);
2. 转换(Transforming)

接下来,Babel 使用 @babel/traverse 对 AST 进行遍历,并通过插件对 AST 进行修改。插件可以添加、删除或修改 AST 节点,从而实现代码的转换。

import traverse from '@babel/traverse';traverse(ast, {enter(path) {if (path.isIdentifier({ name: 'greet' })) {path.node.name = 'sayHello';}}
});console.log(ast);

在这个例子中,我们使用 @babel/traverse 遍历 AST,并将所有名为 greet 的标识符(Identifier)修改为 sayHello

3. 生成(Generating)

最后,Babel 使用 @babel/generator 将修改后的 AST 转换回 ES5 代码。

import generate from '@babel/generator';const output = generate(ast, {}, code);
console.log(output.code);

在这个例子中,@babel/generator 将修改后的 AST 转换回代码,并输出最终的 ES5 代码。

完整示例

下面是一个完整的示例,展示了 Babel 的整个工作流程:

import { parse } from '@babel/parser';
import traverse from '@babel/traverse';
import generate from '@babel/generator';// 输入的 ES6+ 代码
const code = `const greet = (name) => { console.log(\`Hello, \${name}!\`); };`;// 1. 解析:将代码转换为 AST
const ast = parse(code, { sourceType: 'module' });// 2. 转换:使用插件对 AST 进行遍历和修改
traverse(ast, {enter(path) {if (path.isIdentifier({ name: 'greet' })) {path.node.name = 'sayHello';}}
});// 3. 生成:将修改后的 AST 转换回 ES5 代码
const output = generate(ast, {}, code);console.log(output.code);
// 输出:const sayHello = (name) => { console.log(`Hello, ${name}!`); };

Babel 的性能优化

Babel 的性能优化是一个重要的主题,特别是在大型项目中,编译速度可能成为瓶颈。以下是一些常见的 Babel 性能优化技巧和策略,帮助你提高编译速度和效率。

1. 使用缓存

1.1 babel-loader 的缓存功能

在使用 Webpack 时,可以启用 babel-loader 的缓存功能,以减少重复编译的时间。

// webpack.config.js
module.exports = {// ...module: {rules: [{test: /\.(js|jsx|ts|tsx)$/,exclude: /node_modules/,use: {loader: 'babel-loader',options: {cacheDirectory: true // 启用缓存}}}]}
};
1.2 Babel 缓存插件

Babel 还提供了一个缓存插件 babel-plugin-transform-runtime,可以减少重复的辅助代码。

npm install --save-dev @babel/plugin-transform-runtime

在 Babel 配置文件中启用插件:

{"plugins": ["@babel/plugin-transform-runtime"]
}

2. 并行编译

2.1 使用 thread-loader

在 Webpack 中,可以使用 thread-loader 来启用多线程编译,从而提高编译速度。

npm install --save-dev thread-loader

在 Webpack 配置文件中添加 thread-loader

// webpack.config.js
module.exports = {// ...module: {rules: [{test: /\.(js|jsx|ts|tsx)$/,exclude: /node_modules/,use: ['thread-loader', // 启用多线程编译'babel-loader']}]}
};
2.2 使用 parallel-webpack

parallel-webpack 是一个用于并行化 Webpack 构建的工具,可以显著提高构建速度。

npm install --save-dev parallel-webpack

使用 parallel-webpack 运行 Webpack:

npx parallel-webpack --config webpack.config.js

3. 按需加载

按需加载可以减少初始加载时间和编译时间。使用 @babel/plugin-syntax-dynamic-import 插件可以实现按需加载。

npm install --save-dev @babel/plugin-syntax-dynamic-import

在 Babel 配置文件中启用插件:

{"plugins": ["@babel/plugin-syntax-dynamic-import"]
}

在代码中使用动态导入:

import(/* webpackChunkName: "my-chunk-name" */ './myModule').then(module => {// 使用模块
});

4. 减少 Babel 处理的文件数量

通过合理配置 excludeinclude 选项,可以减少 Babel 处理的文件数量,从而提高编译速度。

// webpack.config.js
module.exports = {// ...module: {rules: [{test: /\.(js|jsx|ts|tsx)$/,exclude: /node_modules/, // 排除 node_modules 目录include: /src/, // 仅处理 src 目录use: 'babel-loader'}]}
};

5. 使用更少的 Babel 插件和预设

每个 Babel 插件和预设都会增加编译时间。尽量只使用必要的插件和预设,可以显著提高编译速度。

示例:精简 Babel 配置
{"presets": [["@babel/preset-env", {"targets": {"browsers": ["last 2 versions", "ie >= 11"]}}]],"plugins": ["@babel/plugin-transform-arrow-functions","@babel/plugin-transform-classes"]
}

6. 使用 Babel 的 env 选项

Babel 的 env 选项允许你根据不同的环境(如开发、生产)配置不同的插件和预设,从而优化编译速度。

示例:使用 env 选项
{"env": {"development": {"presets": ["@babel/preset-env"],"plugins": ["@babel/plugin-transform-runtime"]},"production": {"presets": ["@babel/preset-env"],"plugins": ["@babel/plugin-transform-runtime", "@babel/plugin-transform-react-                    inline-elements"]}}
}

7. 使用 Babel 的 ignore 选项

Babel 的 ignore 选项允许你忽略特定的文件或目录,从而减少编译时间。

示例:使用 ignore 选项
{"ignore": ["node_modules", "dist"]
}

8. 使用 babel-preset-envuseBuiltIns 选项

babel-preset-envuseBuiltIns 选项可以按需引入 polyfill,从而减少编译后的代码体积。

示例:使用 useBuiltIns 选项
{"presets": [["@babel/preset-env", {"useBuiltIns": "usage","corejs": 3}]]
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/350063.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

pdf文件如何防篡改内容

PDF文件防篡改内容的方法有多种&#xff0c;以下是一些常见且有效的方法&#xff0c;它们可以帮助确保PDF文件的完整性和真实性&#xff1a; 加密PDF文档&#xff1a; 原理&#xff1a;通过设置密码来保护PDF文档&#xff0c;防止未经授权的访问和修改。注意事项&#xff1a;密…

【Linux】解锁权限的神秘面纱,让你的系统更安全、更高效!

XShell原理权限 1. Shell命令以及运行原理1.1 Shell外壳1.2 shell周边知识 2. Linux权限的概念2.1 用户2.2 用户切换2.3 sudo 3. Linux权限管理3.1 文件访问者的分类3.2 文件类型3.3 file指令3.4 文件访问权限3.5 文件权限值的表示方法 4. 文件访问权限的设置方法4.1 chmod指令…

【linux】应用程序访问百度时,操作系统内核网络接口日志

代码合入&#xff1a; 登录 - Gitee.comhttps://gitee.com/r77683962/linux-6.9.0/commit/c639573cc7c4984913d4a89884347e5a30a51eac 启动操作系统运行dmesg的日志像这样&#xff1a; dmesg_log/2024_06_14_00_40_54.txt r77683962/linux-6.9.0 - Gitee.com 注意&#xf…

SOFTS: 时间序列预测的最新模型以及Python使用示例

近年来&#xff0c;深度学习一直在时间序列预测中追赶着提升树模型&#xff0c;其中新的架构已经逐渐为最先进的性能设定了新的标准。 这一切都始于2020年的N-BEATS&#xff0c;然后是2022年的NHITS。2023年&#xff0c;PatchTST和TSMixer被提出&#xff0c;最近的iTransforme…

显著提高iOS应用中Web页面的加载速度 - 提前下载页面的关键资源(如JavaScript、CSS和图像)

手动下载并缓存资源是一种有效的方式&#xff0c;可以确保在需要时资源已经在本地存储&#xff0c;这样可以显著提高加载速度。 缓存整个 web 页面的所有资源文件 具体实现步骤 下载和缓存资源&#xff1a;包括 HTML 文件、CSS、JavaScript 和图像。在应用启动时预加载资源。…

WinForm之TCP客户端通讯

目录 一 设计界面 二 后台代码 一 设计界面 二 后台代码 using System.Net.Sockets; using System.Text;namespace TCP网络客户端通讯 {public partial class Form1 : Form{public Form1(){InitializeComponent();}TcpClient tcpClient new TcpClient();private void conne…

停止游戏中的循环扣血显示

停止游戏中循环扣血并显示的具体实现方式会依赖于你的代码结构和游戏的逻辑。通常情况下&#xff0c;你可以通过以下方式来实现停止循环扣血和显示&#xff1a; 1、问题背景 在使用 Python 代码为游戏开发一个生命值条时&#xff0c;遇到了一个问题。代码使用了循环来减少生命…

atcoder abc357

A Sanitize Hands 问题&#xff1a; 思路&#xff1a;前缀和&#xff0c;暴力&#xff0c;你想咋做就咋做 代码&#xff1a; #include <iostream>using namespace std;const int N 2e5 10;int n, m; int a[N];int main() {cin >> n >> m;for(int i 1…

四轴飞行器、无人机(STM32、NRF24L01)

一、简介 此电路由STM32为主控芯片&#xff0c;NRF24L01、MPU6050为辅,当接受到信号时&#xff0c;处理对应的指令。 二、实物图 三、部分代码 void FlightPidControl(float dt) { volatile static uint8_t statusWAITING_1; switch(status) { case WAITING_1: //等待解锁 if…

2024最新最全【AIGC】学习零基础入门到精通,看完这一篇就够了!

这个文案就是由AI生成的哦&#xff01;&#xff01;&#xff01;&#xff01; AIGC&#xff08;AI-Generated Content&#xff09; 即人工智能生成内容&#xff0c;是指利用人工智能技术来创造各种形式的内容&#xff0c;包括文字、图像、视频、音频和游戏等。与专业生成内容…

图解Transformer学习笔记

教程是来自https://github.com/datawhalechina/learn-nlp-with-transformers/blob/main/docs/ 图解Transformer Attention为RNN带来了优点&#xff0c;那么有没有一种神经网络结构直接基于Attention构造&#xff0c;而不再依赖RNN、LSTM或者CNN的结构&#xff0c;这就是Trans…

【算法专题--链表】反转链表II--高频面试题(图文详解,小白一看就会!!!)

目录 一、前言 二、题目描述 三、解题方法 ⭐迭代法 --- 带哨兵位&#xff08;头节点&#xff09; &#x1f95d; 什么是哨兵位头节点&#xff1f; &#x1f34d; 解题思路 四、总结与提炼 五、共勉 一、前言 反转链表II这道题&#xff0c;可以说是--链表专题--&am…

RAG工作流在高效信息检索中的应用

介绍 RAG&#xff08;Retrieval Augmented Generation&#xff09;是一种突破知识限制、整合外部数据并增强上下文理解的方法。 由于其高效地整合外部数据而无需持续微调&#xff0c;RAG的受欢迎程度正在飙升。 让我们来探索RAG如何克服LLM的挑战&#xff01; LLM知识限制大…

【WEB前端2024】3D智体编程:乔布斯3D纪念馆-第38课-密室逃脱-3D互动剧情

【WEB前端2024】3D智体编程&#xff1a;乔布斯3D纪念馆-第38课-密室逃脱 使用dtns.network德塔世界&#xff08;开源的智体世界引擎&#xff09;&#xff0c;策划和设计《乔布斯超大型的开源3D纪念馆》的系列教程。dtns.network是一款主要由JavaScript编写的智体世界引擎&…

Flutter - Material3适配

demo 地址: https://github.com/iotjin/jh_flutter_demo 代码不定时更新&#xff0c;请前往github查看最新代码 Flutter - Material3适配 对比图具体实现一些组件的变化 代码实现Material2的ThemeDataMaterial3的ThemeData Material3适配官方文档 flutter SDK升级到3.16.0之后 …

C# WinForm —— 35 StatusStrip 介绍

1. 简介 状态栏 StatusStrip&#xff0c;默认在软件的最下方&#xff0c;用于显示系统时间、版本、进度条、账号、角色信息、操作位置信息等 可以在状态栏中添加的控件类型有&#xff1a;StatusLabel、ProgressBar、DropDownButton、SplitButton 2. 属性 属性解释(Name)控…

utm投影

一 概述 UTM (Universal Transverse Mercator)坐标系是由美国军方在1947提出的。虽然我们仍然将其看作与“高斯&#xff0d;克吕格”相似的坐标系统&#xff0c;但实际上UTM采用了网格的分带&#xff08;或分块&#xff09;。除在美国本土采用Clarke 1866椭球体以外&#xff0c…

树莓派等Linux开发板上使用 SSD1306 OLED 屏幕,bullseye系统 ubuntu,debian

Raspberry Pi OS Bullseye 最近发布了,随之而来的是许多改进,但其中大部分都在引擎盖下。没有那么多视觉差异,最明显的可能是新的默认桌面背景,现在是大坝或湖泊上的日落。https://www.the-diy-life.com/add-an-oled-stats-display-to-raspberry-pi-os-bullseye/ 通过这次操…

【GD32】 TIMER通用定时器学习+PWM输出占空比控制LED

扩展&#xff1a;对PWM波形的输出进行捕获 目录 一、简介二、具体功能描述1、时钟源的选择&#xff1a;2、预分频器&#xff1a;3、计数模式&#xff1a;向上计数模式&#xff1a;向下计数模式&#xff1a;中央对齐模式&#xff1a; 4、捕获/比较通道 输入捕获模式 输出比…

前端问题整理

Vue vue mvvm&#xff08;Model-View-ViewModel&#xff09;架构模式原理 Model 是数据层&#xff0c;即 vue 实例中的数据View 是视图层&#xff0c; 即 domViewModel&#xff0c;即连接Model和Vue的中间层&#xff0c;Vue实例就是ViewModelViewModel 负责将 Model 的变化反映…