du blog
Hello, welcome to my blog
babel笔记(二)-配置babel
created: Jan 18 21updated: Jan 20 21

babel的配置有多种形式,在这里我尝试梳理一下babel的各种配置.

全局配置文件与局部配置文件

区别

babel在7版本引入了根的概念,与之对应的根配置文件为 babel.config.json/js/cjs/mjs 对应的局部配置文件为 babelrc.json/js/cjs/mjs

项目中统一使用一套babel配置、 需要转译node_modules 使用babel.config.json

如果配置文件仅使用于项目中的某一部分 使用babelrc.json

babel.config.json 必须放置在项目根目录下(默认为当前执行目录),或者使用configFile指定文件位置

如何配置

两种配置文件都可以是json形式 也可以是js形式的,

1 { 2 "presets": [...], 3 "plugins": [...] 4 } 5
1 module.exports = function (api) { 2 3 const presets = [ ... ]; 4 const plugins = [ ... ]; 5 6 return { 7 presets, 8 plugins 9 }; 10 } 11

此外可以将 babelrc.json 作为 package.json中babel的值

1 { 2 "name": "my-package", 3 "version": "1.0.0", 4 "babel": { 5 "presets": [ ... ], 6 "plugins": [ ... ], 7 } 8 } 9

json与js形式的区别:

1. 某些参数需要使用到js代码

2. 使用js的方式可以根据环境进行动态配置

1 const presets = [ ... ]; 2 const plugins = [ ... ]; 3 4 if (process.env["ENV"] === "prod") { 5 plugins.push(...); 6 } 7 8 module.exports = { presets, plugins }; 9

3. js形式不利于babel进行缓存, json形式更加利于babel进行缓存

使用命令行

1 babel --plugins @babel/plugin-transform-arrow-functions script.js 2

使用 API (@babel/core)

1 require("@babel/core").transform("code", { 2 plugins: ["@babel/plugin-transform-arrow-functions"] 3 }); 4

打印有效配置

使用BABEL_SHOW_CONFIG_FOR环境变量,告诉babel输出有效的配置

1 BABEL_SHOW_CONFIG_FOR=./src/index.js npx babel src/index.js 2

output:

1 Babel configs on "/Users/**/Documents/my/babel/src/index.js" (ascending priority): 2 config /Users/**/Documents/my/babel/babel.config.json 3 { 4 "presets": [ 5 [ 6 "@babel/preset-env", 7 { 8 "targets": { 9 "chrome": "50", 10 "ie": 11 11 }, 12 "bugfixes": true, 13 "useBuiltIns": "entry", 14 "corejs": { 15 "version": 3.8, 16 "proposals": true 17 } 18 } 19 ] 20 ], 21 "plugins": [ 22 "@babel/plugin-transform-runtime" 23 ] 24 } 25 26 config /Users/**/Documents/my/babel/.babelrc.js 27 { 28 "plugins": [ 29 [ 30 "@babel/plugin-proposal-decorators", 31 { 32 "legacy": true 33 } 34 ] 35 ] 36 } 37 38 programmatic options from @babel/cli 39 { 40 "sourceFileName": "src/index.js", 41 "caller": { 42 "name": "@babel/cli" 43 }, 44 "filename": "src/index.js" 45 } 46

BABEL_SHOW_CONFIG_FOR接收绝对路径和相对路径,如果是相对路径则根据 命令行参数的cwd 解析

babel会根据优先级输出配置

1 babel.config.json < .babelrc < programmatic options from @babel/cli 2

babel.config.json被babelrc覆盖 babelrc被编程选项覆盖

如果配置中存在overrides或者env,并且匹配, 则会单独打印例如

1 BABEL_SHOW_CONFIG_FOR=./src/index.js NODE_ENV=production npx babel src/index.js 2
1 Babel configs on "/Users/**/Documents/my/babel/src/index.js" (ascending priority): 2 config /Users/**/Documents/my/babel/babel.config.json 3 { 4 "presets": [ 5 [ 6 "@babel/preset-env", 7 { 8 "targets": { 9 "chrome": "50", 10 "ie": 11 11 }, 12 "bugfixes": true, 13 "useBuiltIns": "entry", 14 "corejs": { 15 "version": 3.8, 16 "proposals": true 17 } 18 } 19 ] 20 ], 21 "plugins": [ 22 "@babel/plugin-transform-runtime" 23 ] 24 } 25 26 config /Users/**/Documents/my/babel/babel.config.json .env["production"] 27 { 28 "compact": false 29 } 30 31 config /Users/**/Documents/my/babel/babel.config.json .overrides[0] 32 { 33 "test": "./src/index.js", 34 "compact": true 35 } 36 37 config /Users/**/Documents/my/babel/.babelrc.js 38 { 39 "plugins": [ 40 [ 41 "@babel/plugin-proposal-decorators", 42 { 43 "legacy": true 44 } 45 ] 46 ] 47 } 48 49 programmatic options from @babel/cli 50 { 51 "sourceFileName": "src/index.js", 52 "caller": { 53 "name": "@babel/cli" 54 }, 55 "filename": "src/index.js" 56 } 57

如果输出文件被 ignore 或者 only 则会输出文件被忽略

babel如何合并配置项

babel使用 Object.assign 合并除了presets和plugins之外的的配置, 使用concat连接 presets 和 plugins

1 const config = { 2 plugins: [["plugin-1a", { loose: true }], "plugin-1b"], 3 presets: ["preset-1a"], 4 sourceType: "script" 5 } 6 7 const newConfigItem = { 8 plugins: [["plugin-1a", { loose: false }], "plugin-2b"], 9 presets: ["preset-1a", "preset-2a"], 10 sourceType: "module" 11 } 12 13 BabelConfigMerge(config, newConfigItem); 14 // returns 15 ({ 16 plugins: [ 17 ["plugin-1a", { loose: false }], 18 "plugin-1b", 19 ["plugin-1a", { loose: false }], 20 "plugin-2b" 21 ], // new plugins are pushed 22 presets: [ 23 "preset-1a", 24 "preset-1a", 25 "preset-2b" 26 ], // new presets are pushed 27 sourceType: "module" // sourceType: "script" is overwritten 28 }) 29

插件/预设

插件

babel是一个编译器(输入源码 => 输出编译后的代码), 编译分为三个阶段: 解析 => 转换 => 打印输出.

babel 虽然开箱即用,但是什么动作都不做。它基本上类似于 const babel = code => code; 将代码解析之后再输出同样的代码。如果想要 babel 做一些实际的工作,就需要为其添加插件

插件分为两类:

转换插件: 用于转换代码, 转换插件会启用相应语法插件,因此不必同时指定两种插件

语法插件:这些插件只允许babel 解析(parser) 特定类型的语法 而不是转换

可以用parserOption, 传递给parser解析器 任何plugins参数

1 { 2 "parserOpts": { 3 "plugins": ["jsx", "flow"] 4 } 5 } 6

预设

preset是插件的组合,甚至可以共享options配置

创建 Preset

如果需要创建自己的预设,只需要导出一份配置即可

可以是返回一个插件数组

1 module.exports = function() { 2 return { 3 plugins: [ 4 "pluginA", 5 "pluginB", 6 "pluginC", 7 ] 8 }; 9 } 10

预设可以包含其他预设,以及带参数的插件

1 module.exports = () => ({ 2 presets: [ 3 require("@babel/preset-env"), 4 ], 5 plugins: [ 6 [require("@babel/plugin-proposal-class-properties"), { loose: true }], 7 require("@babel/plugin-proposal-object-rest-spread"), 8 ], 9 }); 10

插件/预设路径

直接使用,写入包名名, babel会检测他是否在node_modules

1 { 2 "plugins": ["babel-plugin-myPlugin"] 3 } 4
1 { 2 "presets": ["babel-preset-myPreset"] 3 } 4

指定自己的插件/预设, 相对路径或者绝对路径

1 { 2 "plugins": ["./node_modules/asdf/plugin"] 3 } 4
1 { 2 "presets": ["./myProject/myPreset"] 3 } 4

插件/预设的短名称

如果插件的前缀为 babel-plugin- 则可以使用它的短名称

如果预设的前缀为 babel-preset- 则可以使用它的短名称

1 { 2 "plugins": [ 3 "myPlugin", 4 "babel-plugin-myPlugin" // 两个插件实际是同一个 5 ] 6 } 7
1 { 2 "presets": [ 3 "myPreset", 4 "babel-preset-myPreset" // equivalent 5 ] 6 } 7

对于带有作用域的插件也适用

1 { 2 "plugins": [ 3 "@org/babel-plugin-name", 4 "@org/name" // 两个插件实际是同一个 5 ] 6 } 7
1 { 2 "presets": [ 3 "@org/babel-preset-name", 4 "@org/name" // equivalent 5 ] 6 } 7

自从babel7之后 babel包的形式都改为@babe/开头了 插件的名称一般都是@babel/plugin- 开头 所以这个感觉没有什么用处

执行顺序

插件的排序很重要

如果两个转换插件都要处理某个代码片段,则将根据转换插件或preset的排列顺序,依次执行

1.插件在presets之前执行

2.插件的执行顺序是从前往后的

3.preset的执行顺序是颠倒的 从后往前的

1 { 2 "plugins": ["transform-decorators-legacy", "transform-class-properties"] 3 } 4

先执行 transform-decorators-legacy ,在执行 transform-class-properties

preset 的顺序是 颠倒的, 如下设置:

1 { 2 "presets": ["@babel/preset-env", "@babel/preset-react"] 3 } 4

先执行@babel/preset-react 再执行@babel/preset-env

参数

插件和preset都支持接收参数,由包名和参数对象组成的一个数组

如果不指定参数,下边的几种形式都是一样的

1 { 2 "plugins": ["pluginA", ["pluginA"], ["pluginA", {}]] 3 } 4
1 { 2 "presets": [ 3 "presetA", 4 ["presetA"], 5 ["presetA", {}], 6 ] 7 } 8

要指定参数,传递一个以参数名作为键(key)的对象。

1{ 2 "plugins": [ 3 [ 4 "transform-async-to-module-method", 5 { 6 "module": "bluebird", 7 "method": "coroutine" 8 } 9 ] 10 ] 11} 12
1{ 2 "presets": [ 3 [ 4 "env", 5 { 6 "loose": true, 7 "modules": false 8 } 9 ] 10 ] 11} 12