Babel overview
In the project generated using vuE-CLI tool, if you select In dedicated Config files, after the project is completed, there will be a babel.config.js file and the Babel In the project will be configured In this file.
A quick introduction to Babel, which is a tool for converting ECMAScript 2015+ code into backwardly compatible JavaScript syntax so it can run in current and older browsers or other environments. So Babel will divide the source code into syntax and API parts for translation:
- Syntax: similar to expand object, optional chain, let, const, etc.
- API: similar to [1,2,3]. Includes, map, etc.
@babel/preset-env
For vue CLI projects using Webpack, babel-Loader is automatically introduced after the project scaffolding is generated to handle the new syntax used in the code, while polyfill is introduced for apI-related transformations. In the babel.config.js file, There is an @babel/preset-env configuration that tells Babel how to handle the API.
The code is as follows:
{
"presets": [["@babel/preset-env",
{
"useBuiltIns": "usage"."corejs": 3}}]]Copy the code
The key configuration option, useBuiltIns, has three values:
- False: the API is not processed, the API for each file is not automatically converted, and polyfills are not introduced.
- Entry: API conversion is performed for all files, and manual import is required in the entry file
import from @babel/polyfill
. - Usage: The API conversion is loaded on demand, that is, the corresponding conversion code is automatically introduced to the method, instead of the full introduction of polyfill, there is no need to manually import at the page entrance
import from @babel/polyfill
.
Where false is the default value and is rarely used. @babel/polyfill is divided into two libraries core-js and Regenerator-Runtime after Babel 7.4, so it is necessary to introduce these two libraries to replace it. Generally, usage is the most appropriate. The new API used in the code is automatically scanned during the build and the corresponding transformation code is introduced instead of being introduced in full, thus reducing the size of the resource bundle, but it is important to note that:
-
Configuring Usage can introduce conversion code on demand, but @babel/ Polyfill still needs to be installed. However, the import mode needs to be changed to core-js and Regenerator-Runtime.
-
The usage configuration can introduce conversion code as needed, but the code in node_modules folder is not converted by default (babel-loader does not convert this code by default for projects created using vue CLI), so similar to ant-Design, Libraries like element-UI that use the new API are not converted in node_modules.
Node_modules: node_modules: node_modules: node_modules: node_modules: node_modules: node_modules
module.exports = {
...
transpileDependencies: ['ant-design-vue'],}Copy the code
In addition, Corejs in the @babel/ PRESET -env configuration item must be the same as the installed Corejs version.
@babel/plugin-transform-runtime
If you look at the code converted by Babel, you can see that the includes API is directly required, rather than the more intuitive way. The code looks like this:
var includes = require('xxx/includes')
Copy the code
So Babel’s polyfill mechanism is that for static methods such as array. from, it adds directly to global.array; For instance methods such as includes, add them directly to global.array. prototype. This direct modification of the global variable prototype can lead to unexpected problems. This issue is especially important when developing third-party libraries that modify global variables and may conflict with another third-party library that also modified global variables, or with users of our third-party library. Generally accepted programming paradigms also discourage direct modification of global variables and global variable prototypes.
In addition, when Babel interprets syntax, it sometimes uses some auxiliary functions to help it, such as:
/ / before the conversion
class A {}typeof a
/ / after the transformation
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol= = ="function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol= = ="function" && obj.constructor === Symbol&& obj ! = =Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
function _instanceof(left, right) { if(right ! =null && typeof Symbol! = ="undefined" && right[Symbol.hasInstance]) { return!!!!! right[Symbol.hasInstance](left); } else { return left instanceofright; }}function _classCallCheck(instance, Constructor) { if(! _instanceof(instance, Constructor)) {throw new TypeError("Cannot call a class as a function"); }}var A = function A() {
_classCallCheck(this, A);
};
typeof a === "undefined" ? "undefined" : _typeof(a);
Copy the code
In class syntax, Babel has a custom function called _classCallCheck to assist; Typeof is a direct rewrite of the _typeof function to assist. These functions are called helpers. As you can see in the figure above, the helper is defined directly in the translated file. If a project has 100 files and each file has a class, then the final package of the project will have 100 _classCallCheck functions that look and function exactly the same, which makes no sense.
@babel/plugin-transform-runtime the plugin-transform-runtime plugin is designed to solve the two problems mentioned above. Run the following commands to install the two libraries:
npm i @babel/plugin-transform-runtime -D
npm i @babel/runtime-corejs3
Copy the code
The @babel/plugin-transform-runtime function is to translate the code. The translated code may introduce modules from @babel/ Runtime-corejs3. So the former runs at compile time and the latter at run time. Similar to Polyfill, the latter needs to be packaged into the final product to run in the browser.
Then modify the configuration, the code is as follows:
{ "presets": [ [ "@babel/preset-env", { "useBuiltIns": "usage", "corejs": 3 } ] ], "plugins": [["@babel/plugin-transform-runtime", {"corejs": 3 // specify runtime corejs version}]]} ["@babel/plugin-transform-runtime", {"corejs": 3 // specify runtime corejs version}]Copy the code
After introduction, the original conversion code was programmed as follows:
/ / before the conversion
class A {}typeof a
/ / after the transformation
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var A = function A() {(0, _classCallCheck2.default)(this, A);
};
typeof a === "undefined" ? "undefined" : (0, _typeof2.default)(a);
Copy the code
As you can see, after introducing the transform-Runtime plugin:
- Instead of directly modifying the prototype, the API was introduced from a unified module, avoiding contamination of global variables and their prototypes.
- Helpers have been changed from being defined in place to being included in a unified module, so that only one helper exists for each packaged result.
A mixture of the two
Most of the use of @babel/ plugin-transform-Runtime is in writing third-party libraries to avoid global contamination of the user’s JavaScript environment.
In its own independent projects, it is recommended to use @babel/preset-env with useBuiltIns instead of @babel/ plugin-transform-Runtime in most cases, refer to issues