Appearance
babel
babel 是一个 JavaScript 编译器,主要作用是 ES6 语法的代码转换成 ES5 语法的代码。
核心功能
- 语法转换:将新版本的 JavaScript 语法转换成旧版本语法
- Polyfill:polyfill 又叫做垫片/补丁,用于提供原生不支持的功能,借助
core-js
,作新API
的支持 - JSX 语法转换:将 JSX 语法转换成普通的 JavaScript 语法(React.createElement() 函数调用)
babel 和 core-js 的区别
- babel:处理新语法
- core-js:处理新 api
core-js
原理
去方法的原型上面找,如果没有找到,表示浏览器不支持,core-js 就手动写一个方法添加到原型上。
什么是 ast,有哪些应用场景
AST 是抽象语法树,用来表示源码的树形结构
应用:
- 代码转换
- babel
- less、sass
- 构建工具(将代码打包转换)
- 压缩与混淆
- 代码检查/格式化
- eslint
- prettier
babel 转换过程
babel 提供了 JavaScript 的编译链路,能够将源代码转换为目标代码。
转换过程:ast抽象语法树 -> transform转换 -> generate生成
@babel/parser
:将JavaScript代码解析成抽象语法树(AST)@babel/traverse
:遍历抽象语法树,对抽象语法树进行操作@babel/generator
:将抽象语法树重新生成为JavaScript代码
转为 ast:方便遍历
将新语法转换成旧语法
安装
bash
npm i @babel/core @babel/cli @babel/preset-env -D
例子
js
import babel from '@babel/core'
import presetEnv from '@babel/preset-env' // es6转es5的核心
import fs from 'node:fs'
const code = fs.readFileSync('./test.js', 'utf-8')
const result = babel.transform(code, {
presets: [
[
presetEnv, // 预设,做新语法转换
{
useBuiltIns: 'usage', // 按需加载,自动引入
corejs: 3 // 指定core-js版本
}
]
]
})
console.log(result.code)
js
// 新语法 babel
const fn = (num) => num + 1
const arr = [1, 2, 3]
const newArr = [...arr, 4, 5]
class Foo {
constructor(name) {
this.name = name
}
}
new Foo('foo')
// 新api core-js
const obj = Object.assign({}, { name: 1 })
js
'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.object.to-string.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.function.name.js')
require('core-js/modules/es.object.assign.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 fn = function fn(num) {
return num + 1
}
var arr = [1, 2, 3]
var newArr = [].concat(arr, [4, 5])
var Foo = /*#__PURE__*/ _createClass(function Foo(name) {
_classCallCheck(this, Foo)
this.name = name
})
new Foo('foo')
// 新api
var obj = Object.assign(
{},
{
name: 1
}
)
JSX 转 React
安装对react支持的预设
bash
npm i @babel/preset-react -D
例子
js
import babel from '@babel/core'
import presetEnv from '@babel/preset-env' // es6转es5的核心
import presetReact from '@babel/preset-react' // react语法转换预设
import fs from 'node:fs'
const code = fs.readFileSync('./App.jsx', 'utf-8')
const result = babel.transform(code, {
presets: [
[
presetEnv, // 预设,做新语法转换
{
useBuiltIns: 'usage', // 按需加载,自动引入
corejs: 3 // 指定core-js版本
}
],
presetReact
]
})
console.log(result.code)
jsx
import react from 'react';
import { createRoot } from 'react-dom/client'
const App = () => {
return (
<div>
<h1>Hello World</h1>
</div>
)
}
createRoot(document.getElementById('root')).render(<App />)
js
"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, /*#__PURE__*/React.createElement("h1", null, "Hello World"));
};
(0, _client.createRoot)(document.getElementById('root')).render(/*#__PURE__*/React.createElement(App, null));
编写 babel 插件
插件功能:将箭头函数转为普通函数
js
import babel from '@babel/core'
import fs from 'node:fs'
const code = fs.readFileSync('./test.js', 'utf-8')
// types 里包含了各种ast的方法
const transformFn = ({ types: t }) => {
return {
name: 'transform-fn',
visitor: { // 匹配
ArrowFunctionExpression(path) {
const node = path.node
// 转为普通函数
const arrowFunction = t.functionExpression(
null, // 函数名
node.params, // 参数
/** // [!code ++]
* blockStatement:ast需要的类型
*/
t.blockStatement([t.returnStatement(node.body)]), // 函数体
node.async
)
path.replaceWith(arrowFunction) // 替换原来的
},
}
}
}
const result = babel.transform(code, {
plugins: [transformFn]
})
console.log(result.code)
js
const fn = (num) => num + 1
js
const fn = function (num) {
return num + 1;
};
plugin
plugin
决定了 babel
的编译能力