03.一个react应用的babel配置

453 阅读3分钟

2022年,一个react应用可能需要哪些配置?

这里要搞清楚三个概念,三方面根据babel的配置和 browserlist的配置结合一起生成最终的打包代码

  1. ECMAScript规范里的JS语法和api
    1. Stage 提案阶段的api
    2. 已完成 阶段的api
    3. 活跃的提案
    4. 不活跃的提案
  1. 浏览器实现的JS语法和api(主要实现ECMAScript已完成阶段的语法)
  2. babel转译的语法和api(提案阶段和以完成的都有插件可以降级为es2022-es3)
    1. 通过browserlist知道哪个版本支持特定语法,从而实现语法降级

@babel/preset-env

解决各种浏览器兼容性问题,自动的确定babel插件及polyfills,转译ES2015及此版本以上的语言

包含了ES2022/ES2021/ES2020/ES2019/ES2018/ES2017/ES2016/ES5/ES3的一些语法转译插件

具体看文档 @babel/preset-env

@babel/preset-react

可以编译react

@babel/preset-typescript

可以编译typescript

@babel/plugin-proposal-decorators

支持class装饰器和class内函数装饰器

@annotation
class MyClass {}

function annotation(target) {
  target.annotated = true;
}

class C {
  @enumerable(false)
  method() {}
}

function enumerable(value) {
  return function(target, key, descriptor) {
    descriptor.enumerable = value;
    return descriptor;
  };
}

@babel/plugin-proposal-do-expressions

可以使用do表达式替换if else

// 不用do
const getColoredComponent = color => {
  if (color === "blue") {
    return <BlueComponent />;
  }
  if (color === "red") {
    return <RedComponent />;
  }
  if (color === "green") {
    return <GreenComponent />;
  }
};

const Component = props => (
  <div className="myComponent">{getColoredComponent()}</div>
);

// 使用do,不需要再写在函数内
const Component = props => (
  <div className="myComponent">
    {do {
      if (color === "blue") {
        <BlueComponent />;
      } else if (color === "red") {
        <RedComponent />;
      } else if (color === "green") {
        <GreenComponent />;
      }
    }}
  </div>
);

@babel/plugin-proposal-export-default-from

提供 export v from "mod"; 语法

@babel/plugin-proposal-export-namespace-from

提供 export * as ns from 'mod' 语法

@babel/plugin-proposal-function-bind

提供bind的操作符,@babel/preset-env包含了这个插件, in ES2020

obj::func;
// is equivalent to:
func.bind(obj)

::obj.func;
// is equivalent to:
obj.func.bind(obj);

obj::func(val);
// is equivalent to:
func
  .call(obj, val)

::obj.func(val);
// is equivalent to:
obj.func.call(obj, val);

@babel/plugin-proposal-private-methods

class中 私有属性操作符,@babel/preset-env包含了这个插件

class Counter extends HTMLElement {
  #xValue = 0;

  get #x() {
    return this.#xValue;
  }
  set #x(value) {
    this.#xValue = value;
    window.requestAnimationFrame(this.#render.bind(this));
  }

  #clicked() {
    this.#x++;
  }
}

@babel/plugin-proposal-private-property-in-object

object中的私有属性标识符,@babel/preset-env包含了这个插件

class Foo {
  #bar = "bar";

  test(obj) {
    return #bar in obj;
  }
}

@babel/plugin-proposal-partial-application

不完整的函数表达式,可以通过?暂存,然后继续操作

function add(x, y) { return x + y; }

const addOne = add(1, ?); // apply from the left
addOne(2); // 3

const addTen = add(?, 10); // apply from the right
addTen(2); // 12

f(x, ?)           // partial application from left
f(?, x)           // partial application from right
f(?, x, ?)        // partial application for any arg
o.f(x, ?)         // partial application from left
o.f(?, x)         // partial application from right
o.f(?, x, ?)      // partial application for any arg
super.f(?)        // partial application allowed for call on |SuperProperty|

@babel/plugin-proposal-record-and-tuple

元组的数字式替代写法

let a = #[1, 2, 3];

// ⬇ ⬇ ⬇ ⬇ ⬇ ⬇ ⬇ ⬇ ⬇ ⬇

let a = Tuple(1, 2, 3);

@babel/plugin-transform-runtime

babel-polyfill解决了Babel不转换新API的问题,但是直接在代码中插入帮助函数,会导致污染了全局环境,并且不同的代码文件中包含重复的代码,导致编译后的代码体积变大。

Babel为了解决这个问题,提供了单独的包babel-runtime用以提供编译模块的工具函数, 启用插件babel-plugin-transform-runtime后,Babel就会使用babel-runtime下的工具函数

使用transform-runtime代替babel-polyfill目的是按需引入你需要babel转换的特性,而不是将整个polyfill引入,并且不会像polyfill一样重写原本的API。缺点是某些不支持的API,例如Object.assign Array.includes, 需要引入单独插件