Babel is often introduced during project development to address code compatibility issues. There are three preset modes, respectively, babel-polyfill, babel-Runtime and babel-preset-env. What are the differences between these three modes and which is the better effect of webpack?

The preparatory work

To start the comparison, we need to initialize a WebPack project

npm init babel-test
Copy the code

We are not going to install the webPack 4.x version here, just install the 3.X version

NPM I - D [email protected]Copy the code

Create a new webpack.config.js file in the project root directory and configure it as follows

const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin'); Module.exports = {// JavaScript executables entry: {app: ['./main.js'}, output: {// merge all dependent modules into a bundle.js file filename:'bundle.js'Path: path.resolve(__dirname,'./dist'),
  },
  devServer: {
    contentBase: path.join(__dirname, "dist"),
    inline: true}, module: {rules: [{// Use the re to match the CSS file to be converted with this loadertest: /\.css$/,
        // use: ['style-loader'.'css-loader? minimize'], use: ExtractTextPlugin extract ({/ / conversion. CSS files you need to use the Loader use: ['css-loader']})}, {test: /\.js$/,
        use: ['babel-loader']}, plugins: [new ExtractTextPlugin({//.css filename: '[name].css',})]};Copy the code

Package. json is configured as follows

{
  "name": "babel-test"."version": "1.0.0"."description": ""."main": "index.js"."scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"."start": "webpack --config webpack.config.js"."dev": "webpack-dev-server --open"
  },
  "keywords": []."author": ""."license": "ISC"."devDependencies": {
    "babel-core": "^ 6.26.3"."babel-loader": "^" 7.1.4."babel-plugin-transform-runtime": "^ 6.23.0"."babel-preset-env": "^ 1.6.1." "."css-loader": "^ 0.28.11"."extract-text-webpack-plugin": "^ 3.0.2." "."style-loader": "^ 0.20.3"."webpack": "^ 3.7.0"."webpack-cli": "^ 1.5.3." "."webpack-dev-server": "^ 2.11.1." "
  },
  "dependencies": {
    "babel-polyfill": "^ 6.26.0"."babel-runtime": "^ 6.26.0"}}Copy the code

The packages involved can be installed by performing NPM install, which will not be described here. Here part of the package will be repeated in the next, explain why it is packed like this

The project structure after initialization is as follows

Let’s write some code to main.js

const elements = [1, 2, 3].map((item) => {
  return (
    console.log('9999'))}); console.log(elements); asyncfunction azumia() {
  console.log('begin');
  await new Promise((resolve) => {
    setTimeout(() => {
      resolve();
    }, 1000)
  })
  console.log('done');
}
azumia();

console.log(Object.values({ 1: 2 }));

console.log(Array.isArray([]));
Copy the code

babel-polyfill

Babel-polyfill is designed to emulate a complete ES2015+ environment and is intended for use with applications rather than libraries/tools. And when using babel-Node, the polyfill is automatically loaded. Note here that Babel-Polyfill is introduced into your project once and compiled into production with the project code. And it pollutes global variables. Things like Map, array.prototype. find exist in the global space.

So here you install it into production

npm install babel-polyfill --save
Copy the code

Webpack.config.js can be configured in this way

  entry: {
    app: ['babel-polyfill'.'./main.js']}Copy the code

Let’s execute the order and start packing

npm run start
Copy the code

The bundled bundle.js file size is 259K, while the bundle size before babel-Polyfill was 4K, which is a lot bigger. So can we reference babel-Polyfill on demand to reduce the package size? The answer is yes, thanks to babel-Runtime.

babel-runtime

Babel-runtime does not pollute global space and built-in object prototypes. Babel-runtime is actually a module that you can use as a dependency for ES2015 support.

If your environment doesn’t support promises, you can include them in your project

Require (" Babel - the runtime/core - js/promise ')Copy the code

To get the Promise.

In this way we compensate for the shortcomings of Babel-Polyfill and achieve the effect of loading on demand. However, in the actual project development process, we tend to write a lot of new ES6 API, and it is troublesome to manually introduce the corresponding package each time, and it is not convenient to maintain, and the repeated introduction of each file also leads to the bloated code.

To solve this problem, we use the babel-plugin-transform-Runtime, which analyzes our AST to see if there are any references to spacers from babel-Rumtime (via mapping), and if so, inserts the required spacers at the top of the current module.

Next we will try to install babel-Runtime and babel-plugin-transform-Runtime

npm install --save babel-runtime
npm install --save-dev babel-plugin-transform-runtime
Copy the code

Since babel-Runtime simply centralizes the library of polyfills, the required polyfills are imported into the project and packaged with the project code, so they are added to the production environment dependencies

Let’s add the following configuration to. Babelrc

{
  "plugins": ["transform-runtime"]}Copy the code

The bundle.js package is 63K, which is much smaller than importing polyfill in its entirety. But there are two sides to every story. The downside of babel-Runtime is that it doesn’t emulate instance methods, which are methods on built-in object prototypes, so you can’t use things like array.prototype. find with babel-Runtime. This can only be transcoded with Babel-polyfill, because babel-polyfill adds methods directly to the prototype chain. That’s sad. Is babel-Polyfill going to be introduced in its entirety? Another way to fix this is to use babel-preset-env

babel-preset-env

Babel-preset -env automatically determines plugins and polyfills you need based on the current running environment. According to the support status of each ES standard feature in different browsers and Node versions, the mapping relationship between a feature and plugins is maintained, and the required plugins are finally determined. For detailed configuration instructions, click here

Let’s modify the configuration of babelrc

{
  "presets": [["env", {
      "targets": {
        "chrome": 52,
        "browsers": ["last 2 versions"."safari 7"]},"modules": false."useBuiltIns": "usage"."debug": false}}]]Copy the code

UseBuiltIns is whether to enable automatic support for polyfill, which automatically adds the poly-fill required for each file. Let’s try it by mapping babel-polyfill in main.js

require('babel-polyfill')
Copy the code

You read that correctly, is to be introduced in main.js, directly in the Webpack configuration does not seem to work.

After executing the package command, bundle.js is 194K in size, which is relatively small.

If you open the page in the browser, you will find the following error

Please click here for the cause and solution of this problem. Let’s modify the introduction of babel-polyfill by changing require to import, and promoting the introduction to the front

 - require('babel-polyfill')
 + import 'babel-polyfill'
Copy the code

Pack it again

conclusion

Comparing the above three schemes, we draw the following conclusions

plan Packaged size advantages disadvantages
babel-polyfill 259K Complete simulation of ES2015+ environment Excessive volume; Contaminate global objects and built-in object prototypes
babel-runtime 63K Introduction on demand, small packing volume No instance methods are simulated
Babel-preset -env (enable useBuiltIns) 194K Import on demand, high configurability

There is no absolute advantages and disadvantages of the program, in the development process or flexible use according to the actual situation.

  • Reference documentation

Do you really know how to use Babel?

babel-polyfill VS babel-runtime