# babel
Babel是一个 工具链 ,主要用于旧浏览器或者环境中将ECMAScript 2015+代码转换为向后兼容版本的JavaScript;包括:语法转换、源代码转换等;
在 7.0 版本中,对 Babel 的包做了一次大调整,统一改成域级包,将原先以“babel-”为前缀的包迁移到@babel 的命名空间,如@babel/core、@babel/cli 等。这种模块化的设计,既能区分是否为官方发布的,也能避免命名冲突。
域级包是在包的基础上增加命名空间的概念,其形式如下所示。在@符号和斜杠之间的所有内容(不能包含点号和下划线)就是命名空间。
@scope/package_name
# 单独命令行使用babel
babel本身可以作为一个独立的工具(和postcss一样),不和webpack等构建工具配置来单独使用。
如果我们希望在命令行尝试使用babel,需要安装如下库:
- @babel/core:babel的核心代码,必须安装;
- @babel/cli:可以让我们在命令行使用babel;如果在webpack中可以不使用
npm install @babel/cli @babel/core -D
使用babel来处理我们的源代码:
- src:是源文件的目录;
- --out-dir:指定要输出的文件夹dist;
npx babel src --out-dir dist //dist目录
npx babel src --out-file dist.js //dist文件
npx babel demo.js --out-file dist.js //dist文件
# babel插件
比如我们需要转换箭头函数,那么我们就可以使用箭头函数转换相关的插件:
cnpm install @babel/plugin-transform-arrow-functions -D
npx babel src --out-dir dist --plugins=@babel/plugin-transform-arrow-functions
再增加一个转化作用域
npm install @babel/plugin-transform-block-scoping -D
npx babel src --out-dir dist --plugins=@babel/plugin-transform-block-scoping,@babel/plugin-transform-arrow-functions
# babel预设
但是如果要转换的内容过多,一个个设置是比较麻烦的,我们可以使用预设(preset)
cnpm i --save-dev babel-loader @babel/core @babel/preset-env
//也可以类似上面用逗号隔开进行多个预设
在命令行中使用:
npx babel src --out-dir dist --presets=@babel/preset-env
在webpack配置中配置规则:
rules: [
{
test: /\.js$/,
exclude:/node_modules/,
loader:"babel-loader",
options:{
presets:["@babel/preset-env"]
}
},
]
- 既可以使用options中配置单独的babel解析es6的插件,也可以使用presets预设的方法。如果预设的方法需要额外的操作,可以改成数组的形式。
{
test: /\.js$/,
use: {
loader: "babel-loader",
options: {
// plugins: [
// "@babel/plugin-transform-arrow-functions",
// "@babel/plugin-transform-block-scoping",
// ]
presets: [
"@babel/preset-env"
]
//也可以是数组形式
// presets: [
// ["@babel/preset-env",{
// }]
// ]
}
}
}
这种语法转换有局限性,因为低版本对于某些转换后的js还是不认识,如map,promise等
# Babel的配置文件
像之前一样,我们可以将babel的配置信息放到一个独立的文件中,babel给我们提供了两种配置文件的编写:
babel.config.json(或者.js,.cjs,.mjs)文件;
.babelrc.json(或者.babelrc,.js,.cjs,.mjs)文件;
.babelrc.json:早期使用较多的配置方式,但是对于配置Monorepos项目是比较麻烦的;
babel.config.json(babel7):可以直接作用于Monorepos项目的子包,更加推荐;
抽离出babel,放到单独文件babel.config.js
//webpack中就不需要继续配置babel的具体操作
{
test: /\.js$/,
loader: "babel-loader"
}
module.exports = {
presets: [
"@babel/preset-env"
]
}
babel.config.js 这是 Babel 7 最新引入的配置文件,存在于根目录中(即 package.json文件所在的目录)。它不仅能以编程的方式声明全局生效的配置参数,还能利用 overrides 字段对不同的子目录进行针对性的配置,从而就能避免为相关目录创建一个.babelrc 文件了。在下面的示例中,为 test 目录单独配置了预设(presets)。
module.exports = {
presets: [...],
overrides: [{
test: ["./test"],
presets: [...]
}]
};
当运行下面两条命令进行编译时,第一条读取的是最外层的预设,第二条读取的是overrides 中的预设。
npx babel src.js
npx babel ./test/src.js
.babelrc:babel.config.js 并不是.babelrc 的替代品,.babelrc 文件用于局部配置(代码如下所示),可放置于所有目录中。如果当前目录没有.babelrc 文件,那么就会往上查找直至找到为止。
{
"presets": [...]
}
注意,如果.babelrc 的扩展名是“.js”(即.babelrc.js),那么在文件中可以通过 JavaScript配置参数。
在 package.json 文件中,可以声明一个 babel 字段,其值就是.babelrc文件中的配置参数,如下所示。
{
"babel": {
"presets": [...]
}
}
注意,不能让.babelrc 和声明过 babel 字段的 package.json 处在相同的目录中。
# 配置函数或方法
前面 3 种都是用单独的文件来配置 Babel 的参数,其实还可以通过相关的函数或方法来达到相同的目的。在下面的示例中,引入 gulp-babel 包后就能通过得到的babel()函数来配置 Babel 的参数。
let babel = require("gulp-babel");
babel({
presets: [...]
});
# babel支持Vue的jsx插件
npm install @vue/babel-plugin-jsx -D
module.exports = {
presets: [
"@babel/preset-env"
],
plugins:[
"@vue/babel-plugin-jsx"
]
}
# polyfill
Polyfill是一个js库,主要抚平不同浏览器之间对js实现的差异。
cnpm i --save @babel/polyfill
在原先的业务代码前加上import '@babel/polyfill';不过在最新版本的webpack中只要配置了useBuiltIns:'usage', import '@babel/polyfill';会自动被引入
而且可以采用合适的方法来减少体积
{
test: /\.js$/,
exclude:/node_modules/,
loader:"babel-loader",
options:{
presets:[["@babel/preset-env",
{
targets: {
chrome: "67",//目标浏览器版本
},
useBuiltIns:'usage',//使用了哪些ES6语法然后只转换用到的ES6
}
]]
}
},
# 另外一种方法 不污染全局
cnpm install --save-dev @babel/plugin-transform-runtime
cnpm install --save @babel/runtime
cnpm install --save @babel/runtime-corejs2
//如果设置corejs为2时需要安装
# .babelrc
可把options里的配置单独抽出来放在.babelrc 中
{
test: /\.js$/,
exclude:/node_modules/,
loader:"babel-loader",
},
{
"plugins": [["@babel/plugin-transform-runtime",{
"corejs": 2,
"helpers": true,
"regenerator": true,
"useESModules": false
}]]
}
# 降低babel编译后体积
Babel 为编译的每个文件都插入了辅助代码,使代码体积过大!
Babel 对一些公共方法使用了非常小的辅助代码,比如 _extend。默认情况下会被添加到每一个需要它的文件中。
可以将这些辅助代码作为一个独立模块,来避免重复引入。
@babel/plugin-transform-runtime: 禁用了 Babel 自动对每个文件的 runtime 注入,而是引入 @babel/plugin-transform-runtime 并且使所有辅助代码从这里引用。
npm i @babel/plugin-transform-runtime -D
const os = require("os");
const path = require("path");
const ESLintWebpackPlugin = require("eslint-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
// cpu核数
const threads = os.cpus().length;
module.exports = {
module: {
rules: [
{
oneOf: [
{
test: /\.js$/,
// exclude: /node_modules/, // 排除node_modules代码不编译
include: path.resolve(__dirname, "../src"), // 也可以用包含
use: [
{
loader: "thread-loader", // 开启多进程
options: {
workers: threads, // 数量
},
},
{
loader: "babel-loader",
options: {
cacheDirectory: true, // 开启babel编译缓存
cacheCompression: false, // 缓存文件不要压缩
plugins: ["@babel/plugin-transform-runtime"], // 减少代码体积
},
},
],
},
],
},
],
},
plugins: [
new ESLintWebpackPlugin({
// 指定检查文件的根目录
context: path.resolve(__dirname, "../src"),
exclude: "node_modules", // 默认值
cache: true, // 开启缓存
// 缓存目录
cacheLocation: path.resolve(
__dirname,
"../node_modules/.cache/.eslintcache"
),
threads, // 开启多进程
})
],
optimization: {
minimizer: [
new CssMinimizerPlugin(),
// 当生产模式会默认开启TerserPlugin,但是如果需要进行其他配置,就要重新写了
new TerserPlugin({
parallel: threads, // 开启多进程
}),
]
],
mode: "production",
devtool: "source-map",
};
# core-js处理es6+ 代码兼容性处理
babel 对 js 代码进行了兼容性处理,其中使用@babel/preset-env 智能预设来处理兼容性问题。它能将 ES6 的一些语法进行编译转换,比如箭头函数、点点点运算符等。但是如果是 async 函数、promise 对象、数组的一些方法(includes)等,它没办法处理。
core-js 是专门用来做 ES6 以及以上 API 的 polyfill。
npm i core-js
// 会将未用到的es6+代码也会处理,这样会造成代码体积过大。
// 如果配置了splitChunks:all,那么会单独生成一个很大的文件
import "core-js";
// 按需加载,不过这样如果es6+内容过多,也是很不方便
//import "core-js/es/promise";
// 添加promise代码
const promise = Promise.resolve();
promise.then(() => {
console.log("hello promise");
});
- 自动按需引入
babel.config.js
module.exports = {
// 智能预设:能够编译ES6语法
presets: [
[
"@babel/preset-env",
// 按需加载core-js的polyfill
{ useBuiltIns: "usage", corejs: { version: "3", proposals: true } },
],
],
};
# babel+corejs单独使用
不在webpack中使用,安装@babel/core,@babel/preset-env,core-js
import babel from "@babel/core"
import fs from 'node:fs'
import presetEnv from "@babel/preset-env"
const code = fs.readFileSync('./demo.js', 'utf-8') // 不加utf-8是字符串
// console.log(code)
// 同步转换 第二个参数是一个预设
babel.transformAsync(code, {
presets: [[
presetEnv,
{
useBuiltIns: 'usage', // 新特性会按需引入 垫片
corejs: 3
}
]]
}).then((res) => {
console.log(res.code) // es6 => es5
})
需要处理的demojs
//demo.js
const x = [1, 2, 3]
const y = x.filter(item => item > 1)
const promise = new Promise()
package.json中type修改成module
{
"devDependencies": {
"@babel/core": "^7.24.5",
"@babel/preset-env": "^7.24.5",
"core-js": "^3.37.0"
},
"type": "module"
}
打印出的结果
"use strict";
require("core-js/modules/es.array.filter.js");
require("core-js/modules/es.object.to-string.js");
require("core-js/modules/es.promise.js");
var x = [1, 2, 3];
var y = x.filter(function (item) {
return item > 1;
});
var promise = new Promise();
# babel对应的插件
- @babel/core
var babel = require("@babel/core");
// 编译代码
babel.transform(code, options, function(err, result) {
console.log(result); // => { code, map, ast }
});
babel.transformSync(code, options) // => { code, map, ast }
babel.transformAsync(code, options) // => Promise<{ code, map, ast }>
如果要编译文件或 AST,那么也有类似的 3 个方法可供选择。除了能编译之外,利用这个包还能把代码解析成 AST(即传入一段代码,得到一个 AST)以便其他插件对其进行语法分析,可使用的方法有 parse()、parseSync()和 parseAsync()。
- @babel/cli
@babel/cli 是一个 Babel 内置的命令行工具,可通过命令来编译文件
假设有一个 src.js 文件,代码如下所示,用到了 ES6 中的箭头函数。
let fn = () => true;
在运行下面的命令后,可以看到输出和输入的代码相同,这是因为此时还未指定任何转译插件。
./node_modules/.bin/babel src.js
如果当前的 npm 版本是 5.2 以上,那么可将./node_modules/.bin 缩短为 npx,如下所示。
npx babel src.js
在 babel 中,有多个可用的参数,下面只列举其中的 4 个,其余参数的用法可参考官方文档。
npx babel src --out-dir dist
npx babel src.js --out-file dist.js
npx babel src.js --out-file dist.js --plugins=@babel/plugin-transform-classes,@babel/transformmodules-amd
npx babel src.js --out-file dist.js --presets=@babel/preset-env,@babel/flow
1)“--out-dir”参数可编译整个 src 目录下的文件并输出到 dist 目录中。
2)“--out-file”参数可编译 src.js 文件并输出到 dist.js 文件中。
3)“--plugins”参数可指定编译过程中要使用的插件,多个插件用逗号隔开。
4)“--presets”参数可指定编译过程中要使用的预设,多个预设用逗号隔开。
在 package.json 文件中,可以通过 scripts 字段声明脚本命令。如果想要简化 babel 命令,那么可以将它们迁移到 scripts 字段中,如下所示。
{
"scripts": {
"compile": "npx babel src.js --out-file dist.js"
}
}
现在要编译目录,只要执行下面的这条 npm 命令即可。
npm run compile
← 模块热更新 treeshaking →