In recent years, if you are a front-end developer, if you have not used or even heard of babel, you might be regarded as a traverser, right?
Speaking of babel, a series of nouns will pop up:
- babel-cli
- babel-core
- babel-runtime
- babel-node
- babel-polyfill
- ...
Are these all babel? What do they do? Is there a difference?
What exactly did babel do? How to do it?
Simply put, the new syntax of es2015/2016/2017/2046 in JavaScript is converted to es5, so that low-end operating environments (such as browsers and node) can recognize and execute. This article uses babel 6.x as a benchmark for discussion. Recently, babel released 7.x, let’s talk about it at the end.
Strictly speaking, babel can also be transformed into a lower specification. But in the current situation, the es5 specification is sufficient to cover most browsers, so it is a safe and popular practice to switch to es5 in general.
If you don't understand es5/es2015 and so on, then you may really need to make up the lesson first.
Instructions
There are three ways in total:
- Use standalone script
- Command line (cli)
- Build tool plugins (babel-loader for webpack, rollup-plugin-babel for rollup).
The latter two are more common. The second is more common in scripts
paragraph in package.json; the third is directly integrated into the build tool.
These three methods only have different entrances. The processing methods of the called babel kernel are the same, so let's not entangle the problem of entrances.
Operation mode and plug-in
Babel is divided into three stages in total: parsing, conversion, and generation.
Babel itself does not have any transformation function, it decomposes all transformation functions into plugins. Therefore, when we do not configure any plug-ins, the code and input through babel are the same.
There are two types of plug-ins:
- When we add the grammar plug-in , the parsing step enables babel to parse more grammars. (By the way, the parsing library used inside babel is called babylon, which is not developed by babel itself)
For a simple example, when we define or call a method, it is not allowed to add a comma after the last parameter. For example, callFoo(param1, param2,)
is illegal. If the source code is written in this way, a syntax error will be prompted after passing through babel.
But the recent JS proposal has allowed this new way of writing (to make the code diff more clear). babel-plugin-syntax-trailing-function-commas
errors, you need to add the grammar plug-in 0611e24a46e1b7
- After we add the translation plug-in , in the conversion step, the source code is converted and output. This is also the most essential requirement for us to use babel.
Compared with grammar plugins, translation plugins are actually better understood. For example, the arrow function (a) => a
will be converted to function (a) {return a}
. The plug-in that completes this work is called babel-plugin-transform-es2015-arrow-functions
.
The same type of grammar may have both a grammar plug-in version and a translation plug-in version. If we use the translation plug-in, there is no need to use the grammar plug-in.
Configuration file
Since the plug-in is the root of babel, how to use it? There are 2 steps in total:
- Add the name of the plug-in to the configuration file (create .babelrc or package.json in
babel
in the root directory, the format is the same) - Use
npm install babel-plugin-xxx
to install
The specific writing format is not detailed.
preset
For example, es2015 is a set of specifications, including about ten or twenty translation plug-ins. If developers are required to add and install them one by one each time, the configuration file is very long, not to mention, npm install
will be very long, not to mention that we may have to use other specifications at the same time.
To solve this problem, babel also provides a collection of plug-ins. Because it is commonly used, there is no need to repeat the definition & installation. (The difference between a la carte and a package, the package saves a lot of time and configuration effort)
The presets are divided into the following categories:
- Official content, currently includes env, react, flow, minify, etc. The most important thing here is env, which will be described in detail later.
- Stage-x, which contains the latest draft specifications of the year, updated every year.
This is also subdivided into - Stage 0-Scarecrow: It's just an idea, it can be put forward by TC39 members.
- Stage 1-Proposal: Initial attempt.
- Stage 2-First Draft: Complete the preliminary specifications.
- Stage 3-Candidate: Complete the specification and preliminary implementation of the browser.
- Stage 4-Complete: Will be added to the next year's release.
For example, syntax-dynamic-import
is the content of stage-2, and transform-object-rest-spread
is the content of stage-3.
In addition, the low-level stage will contain all the content of the high-level stage, for example, stage-1 will contain all the content of stage-2, stage-3.
The stage-4 will be placed directly in the env in the next year's update, so there is no separate stage-4 available for use.
- es201x, latest
These are grammars that have been incorporated into the standard specification. For example, es2015 containsarrow-functions
and es2017 containssyntax-trailing-function-commas
. But because of the emergence of env, both es2016 and es2017 have been abandoned. So we can often see es2015 listed separately, but rarely see the other two.
Latest is the prototype of env. It is a preset that is updated every year and aims to include all es201x. But also because of the emergence of more flexible env, it has been abandoned.
Execution order
A few very simple principles:
- Plugin will run before Preset.
- Plugin will execute sequentially from front to back.
- The order of Preset is , which is exactly the opposite. (from back to front).
The reverse order of preset is mainly to ensure backward compatibility, because most users write order is ['es2015', 'stage-0']
. In this way, stage-0
must be executed first to ensure that babel does not report an error. , we must also pay attention to the order. In fact, 1611e24a46e769 only needs to be listed in the time sequence of the specification.
Plug-in and preset configuration items
In short, plug-ins and presets only need to list their names in string format. But if a preset or plug-in needs some configuration items (or parameters), you need to turn yourself into an array first. The first element is still a string, representing your name; the second element is an object, that is, the configuration object.
The most need to configure is env, as follows:
"presets": [
// 带了配置项,自己变成数组
[
// 第一个元素依然是名字
"env",
// 第二个元素是对象,列出配置项
{
"module": false
}
],
// 不带配置项,直接列出名字
"stage-2"
]
env (emphasis)
Because env is the most commonly used and most important, we need to focus on it.
The core purpose of env is to learn the characteristics of the target environment through configuration, and then only do the necessary conversions. For example, the target browser supports es2015, then the es2015 preset is actually unnecessary, so the code can be smaller (generally the converted code is always longer), and the build time can be shortened.
If you don't write any configuration items, env is equivalent to latest and also equivalent to three additions of es2015 + es2016 + es2017 (excluding the plugins in stage-x). The list of plugins included in env is maintained at here
Several common configuration methods are listed below:
{
"presets": [
["env", {
"targets": {
"browsers": ["last 2 versions", "safari >= 7"]
}
}]
]
}
The above configuration will consider the features of the latest 2 versions of all browsers (safari greater than or equal to 7.0), and convert the necessary codes. The existing functions of these versions will not be converted. The syntax here can refer to browserslist
{
"presets": [
["env", {
"targets": {
"node": "6.10"
}
}]
]
}
The above configuration sets the target to nodejs, and supports version 6.10 and above. You can also use node: 'current'
to support the latest stable version. For example, the arrow function will not be converted in nodejs 6 and above, but it will be converted if it is nodejs 0.12.
Another useful configuration item is modules
. Its value can be amd
, umd
, systemjs
, commonjs
and false
. This allows Babel to output code in a specific modular format. If you select false
no modularization will be performed.
Other supporting tools
The core processing mechanism and configuration method of babel are discussed above, and this is the same regardless of any entry to call babel. babel-*
mentioned at the beginning of the article is still confusing. In fact, babel-*
are different entrances (methods) to use babel, here is a brief introduction.
babel-cli
As the name suggests, cli is a command line tool. After installing babel-cli
you can use the babel
command to compile files on the command line.
The following patterns are often used when developing npm package:
- The
babel-cli
installeddevDependencies
scripts
(such asprepublish
) to package.json, and use thebabel
command to compile the filenpm publish
In this way, the source code can be written using the newer standard JS syntax, and at the same time it can support the old environment. Because the project may not be too big to use a build tool (webpack or rollup), so use babel-cli
for processing before release.
babel-node
babel-node
is babel-cli
, it does not need to be installed separately.
Its function is to run the code of es2015 directly in the node environment without additional transcoding. For example, we have a js file written in es2015 syntax (such as using arrow functions). We can directly use babel-node es2015.js
for execution without transcoding.
It can be said: babel-node
= babel-polyfill
+ babel-register
. So who are these two?
babel-register
The babel-register module rewrites the require
command and adds a hook to it. After that, whenever require
used to load .js
with the suffixes 0611e24a46ebe3, .jsx
, .es
and .es6
, they will be transcoded with babel first.
When using, you must load require('babel-register')
first.
It should be noted that babel-register will only require
the file loaded by will not transcode the current file .
In addition, because it is real-time transcoding, only suitable for using in the development environment.
babel-polyfill
Babel only converts js syntax by default, and does not convert new APIs, such as Iterator, Generator, Set, Maps, Proxy, Reflect, Symbol, Promise and other global objects, as well as some methods defined on global objects (such as Object.assign
). Transcoding.
For example, es2015 added the Array.from
method to the Array object. Babel will not transcode this method. If you want this method to work, you must use babel-polyfill
. (Integrated internally core-js
and regenerator
)
require('babel-polyfill')
before all the code runs. Or a more conventional operation is to use babel-polyfill
as the first entry webpack.config.js
babel-polyfill
must be regarded as dependencies
instead of devDependencies
babel-polyfill
has two main disadvantages:
- Using
babel-polyfill
will result in a very large package, becausebabel-polyfill
is a whole, and all methods are added to the prototype chain. For example, we only useArray.from
, but itObject.defineProperty
, which is a waste. This problem can be used alonecore-js
to resolve a library,core-js
are separate. babel-polyfill
will pollute global variables and modify the prototype chain of many classes. If we develop a class library for other developers to use, this situation will become very uncontrollable.
Therefore, in actual use, if we cannot tolerate these two shortcomings (especially the second one), we usually tend to use babel-plugin-transform-runtime
.
But if the code contains instance methods of types in higher version js (for example, [1,2,3].includes(1)
), you still need to use polyfill.
babel-runtime and babel-plugin-transform-runtime (emphasis)
.Babelrc we often see used in the project babel-plugin-transform-runtime
, and package.json
in dependencies
(note not devDependencies
) but also contains babel-runtime
, that these two sets is not using it? What role do they play?
Let me talk about babel-plugin-transform-runtime
first.
Babel will convert the js syntax, which has been mentioned before. Take async/await
for example, if you don't use this plugin (that is, the default), the converted code is probably:
// babel 添加一个方法,把 async 转化为 generator
function _asyncToGenerator(fn) { return function () {....}} // 很长很长一段
// 具体使用处
var _ref = _asyncToGenerator(function* (arg1, arg2) {
yield (0, something)(arg1, arg2);
});
Don't worry too much about the specific syntax, just see that this _asyncToGenerator
is defined in the current file and then used to replace the await
source code. But each converted file will insert a section of _asyncToGenerator
which leads to duplication and waste.
After using babel-plugin-transform-runtime
, the converted code will become
// 从直接定义改为引用,这样就不会重复定义了。
var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator');
var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2);
// 具体使用处是一样的
var _ref = _asyncToGenerator3(function* (arg1, arg2) {
yield (0, something)(arg1, arg2);
});
Change from the definition method to the reference, then the repeated definition becomes the repeated reference, and there is no problem of code duplication.
But here, we also found that babel-runtime
out, which is the collection of these methods, and therefore, must regard babel-runtime
as a dependency when using babel-plugin-transform-runtime
Besides babel-runtime
, it is integrated internally
core-js
: Convert some built-in classes (Promise
,Symbols
etc.) and static methods (Array.from
etc.). Most of the conversion is done here. Automatic introduction.regenerator
: Ascore-js
, it is mainly supported by the two groups ofgenerator/yield
andasync/await
It is automatically introduced whengenerators/async
is used in the code.asyncToGenerator
, such as 0611e24a46f0d1 above is one of them, and others such asjsx
,classCallCheck
etc., you can check babel-helpers . When there are built-in helpers in the code (such as the first piece of code above), remove the definition and insert the reference (then it becomes the second piece of code).
babel-plugin-transform-runtime
does not support the instance method (e.g. [1,2,3].includes(1)
)
In addition, there is a plugin that can also do the work of separating and unifying the helpers to avoid duplication of code, called babel-plugin-external-helpers
. But because the transform-runtime
we use already contains this function, it is not necessary to reuse it. And the authors of babel have also begun to discuss that these two plugins are too similar. They are discussing external-helpers
in babel 7. The discussion is in issue#5699 .
babel-loader
Three ways to use babel have been mentioned earlier, and babel-cli
has already been introduced. But some large projects will have build tools (such as webpack or rollup) for code building and compression (uglify). In theory, we can also babel the compressed code, but that will be very slow. So if you add babel processing before uglify, wouldn't it be perfect?
So there is a need for babel to be inserted into the build tool. Take (I am fairly familiar with) webpack as an example. Webpack has the concept of loader, so babel-loader
appeared.
Like babel-cli
, babel-loader
will also read the babel
section in .babelrc or package.json as its configuration, and the subsequent kernel processing is also the same. The only thing more babel-cli
is that it needs to interact with webpack, so it needs to be configured on the webpack side. The more common ones are as follows:
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel-loader'
}
]
}
If you want to pass in babel configuration items here, you can also change it to:
// loader: 'babel-loader' 改成如下:
use: {
loader: 'babel-loader',
options: {
// 配置项在这里
}
}
The configuration items here have the highest priority. But I think it is more clear and reasonable to put it in a separate configuration file, and it is more readable.
Summary
Babel 7.x
Recently babel released 7.0. Because the above part is written for 6.x, we will pay attention to the changes brought by 7.0 (there is no change in the core mechanism, plug-ins, presets, parsing and translation generation are not changed)
I only select some that have a greater relationship with the developer and list them here, and most of the omitted are changes to a certain plugin. For a complete list, please refer to official website .
Preset changes: Eliminate es201x, delete stage-x, and force env (emphasis)
The purpose of eliminating es201x is to hand over the work of selecting the environment to env to do it automatically, without the developer's effort. Any developer who uses es201x should use env instead of . But the deprecated (original deprecated) here is not deleted, but it is not recommended. It is hard to say that babel 8 is really deleted.
In contrast, stage-x is not so lucky, they are deleted directly. This is because the babel team believes that it is wasteful to spend energy on updating presets for these "unstable drafts". Although stage-x has been deleted, the plug-ins it contains have not been deleted (just renamed, see the next section), we can still explicitly declare these plug-ins to obtain equivalent effects. full list
In order to reduce the mechanical work of developers to replace configuration files, babel has developed a babel-upgrade
tool , which will detect the stage-x in the babel configuration and replace it with the corresponding plugins. In addition, it has other functions, we will look at it in detail later. (In short, the purpose is to make your migration to babel 7 smoother)
Changes in the name of the npm package (emphasis)
This is a major change in babel 7. All babel-*
renamed to @babel/*
, for example:
babel-cli
becomes@babel/cli
.babel-preset-env
becomes@babel/preset-env
.preset
can be omitted and abbreviated as@babel/env
.babel-plugin-transform-arrow-functions
becomes@babel/plugin-transform-arrow-functions
. Likepreset
,plugin
can also be omitted, so it is abbreviated as@babel/transform-arrow-functions
.
This change is not only applied to the dependencies of package.json, plugins
presets
) to be consistent. E.g
{
"presets": [
- "env"
+ "@babel/preset-env"
]
}
By the way, the babylon
parsing grammar kernel 0611e24a46f585 mentioned above is now renamed to @babel/parser
, which seems to be included.
The stage-x mentioned above has been deleted. Although the plug-ins it contains are retained, they have also been renamed. babel team hopes to more clearly distinguish already in the specification of plug-ins (such as es2015 of babel-plugin-transform-arrow-functions
) and is located only draft a plug-in (such as stage-0 of @babel/plugin-proposal-function-bind
). The way is to add proposal
name. All translation plugins included in stage-x use this prefix, and syntax plugins are not included.
Finally, if the plug-in name contains the canonical name ( -es2015-
, -es3-
, etc.), delete it. For example, babel-plugin-transform-es2015-classes
becomes @babel/plugin-transform-classes
. (I haven't used this plug-in alone, I am ashamed)
No longer supports low version node
Babel 7.0 no longer supports the four versions of nodejs 0.10, 0.12, 4, 5, which is equivalent to requiring nodejs >= 6 (the current nodejs LTS is 8, so the requirement is not too much).
The no longer support here means that Babel cannot be used to translate code in these low-version node environments, but the code translated by Babel can still run in these environments. Don't confuse this point.
only and ignore changes in matching rules
In babel 6, if the ignore
*.foo.js
, the actual meaning (converted to glob) is ./**/*.foo.js
, that is, the current directory includes foo.js
ending with in the subdirectory 1611e24a46f6a6. This may be contrary to the developer's conventional understanding.
So in babel 7, the same expression *.foo.js
only applies to the current directory, not to the subdirectories. If you still want to act on the subdirectory, you must write it as ./**/*.foo.js
in accordance with the complete specification of glob. only
is the same.
This rule change only applies to wildcards, not to paths. So node_modules
still contains all its subdirectories, not just one level. (Otherwise developers all over the world will explode)
@babel/node is independent from @babel/cli
Unlike babel 6, if you want to use @babel/node
, you must install it separately and add it to the dependencies.
babel-upgrade
tool when I mentioned deleting stage-x. Its purpose is to help users upgrade from babel 6 to 7 automatically.
The functions of this upgrade tool include: (The complete list is not listed here, only the more important and commonly used content)
- package.json
- Replace
babel-*
in dependencies (and development dependencies)@babel/*
- Update these
@babel/*
dependent versions to the latest version (for example,^7.0.0
) - If
scripts
is used inbabel-node
@babel/node
is automatically added as a development dependency - If there is a
babel
, check theplugins
andpresets
among them, and replace the short name (env
) with the full name (@babel/preset-env
) - .babelrc
- Check the
plugins
andpresets
and replace the short name (env
) with the full name (@babel/preset-env
) - Check whether it contains
preset-stage-x
, if any, replace it with the corresponding plug-in and add it toplugins
The usage is as follows:
# 不安装到本地而是直接运行命令,npm 的新功能
npx babel-upgrade --write
# 或者常规方式
npm i babel-upgrade -g
babel-upgrade --write
babel-upgrade
tool itself is also under development, and it lists many TODOs that have not been completed, so the subsequent functions may be more abundant, such as the wildcard conversion ignore
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。