Babel 入门指引

发布于 2023-07-03 12:42:29 字数 12865 浏览 52 评论 0

20200810223955

本文将讲述:

  • Babel 是什么以及怎么在日常开发中使用它?
  • presets and plugins 是什么及区别,在 babel 执行中,他们的执行顺序是什么?

虽然原文标题看似是一个系列,但作者似乎没有继续,但我已经想好了下一部分要写的内容;非专业翻译,夹带自己理解,有小改动。

Babel 是什么

babel 是一个免费开源的 JavaScript 编译库. 它根据你的配置将代码转化成各式各样的 JS 代码。

最常见的使用方式就是将现代语法 JavaScript es6+编写的代码 转化成 es5,从而兼容更多的浏览器(特别是 IE),下面以 Babel 转换 es6 箭头函数 为 es5 函数的为例。

// The original code
const foo = () => console.log('hello world!');

转移后

// The code after babel transpilation
var foo = function foo() {
  return console.log('hello world!');
};

你可以在这里 在线尝试

使用 Babel

在线 Repl

这是使用 Babel 的最简单方法。这也许不是一个非常实用的工具,但是是最快的测试或者实验 Babel 如何工作的工具, 在线 Repl 地址

构建库

使用 Babel 的最流行方法是使用 WebpackGulpRollup 等构建库进行打包构建。每个方式都使用 Babel 作为构建的一部分来实现自己的构建过程。

比如,我们最常用的 webpack:

{
  ...
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules|bower_components)/,
        use: {
          loader: 'babel-loader',
          options: {
            plugins: ['@babel/plugin-transform-arrow-functions']
          }
        }
      }
    ]
  },
  ...
}

库常用的构建工具:Rollup

rollup.rollup({
  ...,
  plugins: [
    ...,
    babel({
      plugins: ['@babel/plugin-transform-arrow-functions']
    }),
    ...
  ],
  ...
}).then(...)

脚手架 cli

除了依赖构建库,也可以直接用命令行依赖官方提供的@babel/cli 包来编译 NIIT 的代码:

# install the core of babel and it's cli interface
npm install @babel/core @babel/cli

# use the babel command line
babel script.js --out-file script-compiled.js

Babel Plugins

Babel 是通过 插件 配置的。开箱即用,Babel 不会更改您的代码。没有插件,它基本上是这样的:

// parse -> generate, 大白话就是英翻中,中翻英
const babel = code => code;

通过 Babel 插件运行代码,您的代码将转换为新代码,如下所示:

// parse -> do something -> generate, 大白话就是英翻中,添油加醋,中翻英
const babel = code => babelPlugin2(babelPlugin1(code));

Babel Presets

您可以单独添加 Babel 插件列表,但是通常更方便的方法是使用 Babel presets。

Babel presets 结合一系列插件的集合。传递给 presets 的选项会影响其聚合的插件, 这些选项将控制使用哪些插件以及这些插件的配置。

比如,我们前面看到的 @babelplugin-transform-arrow-functions 插件是 @babel/preset-env presets 的一部分。

@babel/preset-env 可能是最受欢迎的 presets。 它根据用户传递给预设的配置(比如 browsers:目标浏览器/环境), 将现代 JavaScript(即 ES Next)转换为较旧的 JavaScript 版本。

比如:它可以将 () => arrowFunctions,{…detructuring} ​ 和 class {} ​转换为旧版浏览器支持的 JavaScript 语法,举个例子,

目标浏览器为 IE11:

// 新版语法
class SomeClass {
  constructor(config){
    this.someFunction = params => {
      console.log('hello world!', {...config, ...params});
    }
  }
  someMethod(methodParams){
    this.someFunction(methodParams);
  }
  someOtherMethod(){
    console.log('hello some other world');
  }
}

编译后:

// explained here:  https://www.w3schools.com/js/js_strict.asp 
"use strict";
// this is a babel helper function injected by babel to mimic a {...destructuring} syntax
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }
// this is a babel helper function injected by babel for a faster-than-native property defining on an object
// very advanced info can be found here:
//  https://github.com/babel/babel/blob/3aaafae053fa75febb3aa45d45b6f00646e30ba4/packages/babel-helpers/src/helpers.js#L348 
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

// this is a babel helper function that makes sure a class is called with the "new" keyword like "new SomeClass({})" and not like "SomeClass({})"
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

// like "_defineProperty" above, but for multiple props
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

// used by babel to create a class with class functions 
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

var SomeClass =
/*#__PURE__*/ // this marks the function as pure. check  https://webpack.js.org/guides/tree-shaking/  for more info.
function () {
  // the class got converted to a function
  function SomeClass(config) {
    // make sure a class is called with the "new" keyword
    _classCallCheck(this, SomeClass);
    // someFunction is set on SomeClass 
    this.someFunction = function (params) {   
      // notice how the {...config, ...params} became _objectSpread({}, config, params) here
      console.log('hello world!', _objectSpread({}, config, params));
    };
  }
  // this function adds the class methods to the transpiled class created above
  _createClass(SomeClass, [
    {
      key: "someMethod",
      value: function someMethod(methodParams) {
        this.someFunction(methodParams);
      }
    },
    {
      key: "someOtherMethod",
      value: function someOtherMethod() {
        console.log('hello some other world');
      }
    }
  ]);
  return SomeClass;
}();

对于更复杂的构建要求,配置将使用项目根目录中的 babel.config.js 文件。由于是 JavaScript 文件,因此比.babelrc 更加灵活。例如:

module.exports = function (api) {
  
  // Only execute this file once and cache the resulted config object below for the next babel uses.
  // more info about the babel config caching can be found here:  https://babeljs.io/docs/en/config-files#apicache 
  api.cache.using(() => process.env.NODE_ENV === "development")
  return {
    presets: [
      // Use the preset-env babel plugins
      '@babel/preset-env'
    ],
    plugins: [
      // Besides the presets, use this plugin
      '@babel/plugin-proposal-class-properties'
    ]
  }
}

@babel/preset-env 不同配置,编译出的代码可能大不一样,比如当配置为: latest 10 Chrome versions 是,上面一段代码编译结果与编译前一致,因为上面的特性,chrrome 都支持;但如果将 10 调整为 30,40 时,你会发现,编译的代码将会越来越多;可以点击这里 尝试一下

配置

Babel Plugins and Presets 是非常重要的概念,Babel 的配置由 Plugins and Presets 组合而成(也可以使用其他几个高级属性);

简单的配置,可以直接使用.babelrc,babelrc 是一种 JSON5 文件(和 JSON 一样,但其允许注释),被放置在项目根目录下,比如下面这样:

// Comments are allowed as opposed to regular JSON files
{
  presets: [
    // Use the preset-env babel plugins
    '@babel/preset-env'
  ],
  plugins: [
    // Besides the presets, use this plugin
    '@babel/plugin-proposal-class-properties'
  ]
}

对于更复杂的配置,一般使用 babel.config.js 文件来代替 .babelrc 文件,因为他是 js 文件,所以比 .babelrc 配置更灵活,举个例子:

module.exports = function (api) {
  // Only execute this file once and cache the resulted config object below for the next babel uses.
  // more info about the babel config caching can be found here:  https://babeljs.io/docs/en/config-files#apicache 
  api.cache.using(() => process.env.NODE_ENV === "development")
  return {
    presets: [
      // Use the preset-env babel plugins
      '@babel/preset-env'
    ],
    plugins: [
      // Besides the presets, use this plugin
      '@babel/plugin-proposal-class-properties'
    ]
  }
  
}

一些配置文件可能非常复杂, 例如: Babel 项目本身的 babel.config.js 。莫慌!阅读本指南系列后,您将知道此复杂配置的每一行的意义(看,有头牛在天上飞)。

Babel Plugins and Presets 执行顺序

如果您在配置中混合使用了 Plugins 和 Presets,Babel 将按以下顺序应用它们:

  • 首先从上到下应用插件;
  • 然后,将预设应用在插件之后,从下到上;

举个例子

{
  presets: [
    '@babel/preset-5', //   ↑    ** End Here ** Last preset to apply it's plugins *after* all the plugins below finished to run 
    '@babel/preset-4', //   ↑
    '@babel/preset-3', //   ↑    2
    '@babel/preset-2', //   ↑
    '@babel/preset-1', //   ↑    First preset to apply it's plugins *after* all the plugins below finished to run 
  ],
  plugins: [
    '@babel/plugin-1', //   ↓    >>> Start Here <<< First plugin to transpile the code.
    '@babel/plugin-2', //   ↓  
    '@babel/plugin-3', //   ↓    1
    '@babel/plugin-4', //   ↓  
    '@babel/plugin-5', //   ↓    Last plugin to transpile the code before the preset plugins are applied
  ]
}

另外,值得一提的是,每个 Presets 中的插件也自上而下应用。

以正确的顺序配置 Plugins 和 Presets 非常重要! 正确的顺序可能会加快翻译速度,错误的顺序可能会产生不需要的结果或者导致错误。

可能上面的例子不够真实,那就来个真实的吧:

{
  "presets": [
    "@babel/preset-env",
    "@babel/preset-react",
  ],
  "plugins": [
    ["@babel/plugin-proposal-decorators", { "legacy": true }],
  ]
}

decorators 是装饰器语法,现在还处于 stage-3 阶段难产,而 JSX 则是 React 的专有语法;如果没有 @babel/plugin-proposal-decorators@babel/preset-react 先编译,直接运行 @babel/preset-env 编译,就会报 @<div> 无效的语法标识,所有正确的配置插件和插件顺序是多么重要。

Babel Plugins and Presets 选项

上面提到过给 @babel/preset-env 设置不同的 browsers 选项,会得到不同的编译结果;通过将选项包装在数组中并向其中添加选项,可以将选项配置传递给 Babel Plugins and Presets,比如位于 @babel/preset-env 后面的对象就是一个选项配置,告诉编译的目标是兼容到 chrome 58 版本和 IE11:

{
  presets: [
    // Notice how @babel/preset-env is wrapped in an array with an options object
    ['@babel/preset-env', {
      "targets": {
        "chrome": "58",
        "ie": "11"
      }
    }],
    '@babel/some-other-preset'
  ]
}

@babel/preset-env 基本是项目编译必选的 Presets,除了 targets,还有 useBuiltIns,esmodules,modules 等常见选项,还有更多关于配置可 参考官网

最后

关于指引,就这么多,将在不久后推出进阶篇, 关于 babel-runtime 与 babel-polyfill。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

0 文章
0 评论
22 人气
更多

推荐作者

13886483628

文章 0 评论 0

流年已逝

文章 0 评论 0

℡寂寞咖啡

文章 0 评论 0

笑看君怀她人

文章 0 评论 0

wkeithbarry

文章 0 评论 0

素手挽清风

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文