1. babel基本配置和自定义插件编写

1.1. babel基本配置

const myPlugin = require("./plugin")
module.exports = {
  presets: [
    [
      "@babel/preset-env", // es 语法编译
      {
        useBuiltIns: "usage", // 按需添加 polyfills
        corejs: "3",
        targets: {
          browsers: ["ie >= 12"] // 浏览器版本
        }
      }
    ]
  ],
  plugins: [
    myPlugin,
    [
      "@babel/plugin-transform-runtime",
      {
        "corejs": 2
      }
    ]
  ]
}

1.1.1. 目前babel官网维护的只有四个 presets:

  • @babel/preset-env 自动管理浏览器版本 和 es 语法转换。
  • @babel/preset-flow:编译js代码静态类型检查器flow
  • @babel/preset-react:编译react语法
  • @babel/preset-typescript:编译ts语法

1.1.2. core-js 版本:

  • core-js 2:这是较早期的 core-js 版本,用于填充 ECMAScript 6 和 ECMAScript 7 中引入的特性,以及一些附加的功能。它广泛用于许多项目,但随着时间的推移,更现代的版本逐渐取代了它。

  • core-js 3:这是 core-js 的较新版本,旨在提供更好的性能和模块化。它支持 ECMAScript 6+ 的特性,并且按需引入 polyfills,以减小生成的代码大小。core-js 3 是目前在许多项目中常用的版本。

  • core-js 4:core-js 4 是 core-js 的下一个重大版本,它将进一步改进性能和模块化,以适应 JavaScript 生态系统的变化。截止到我知道的时间,core-js 4 还处于开发中,尚未正式发布。

1.1.3. @babel/plugin-transform-runtime 的作用

代码中的辅助函数不再以全局变量的形式存在。相反,它们被导入自@babel/runtime模块,并且以模块范围内的方式使用,避免了全局污染问题。

举例说明

  • 源码
// example.js
class MyClass {
  constructor(name) {
    this.name = name;
  }

  async greet() {
    return `Hello, ${this.name}!`;
  }
}

const obj = new MyClass('Alice');
obj.greet().then((message) => {
  console.log(message);
});
  • before
"use strict";

function _classCallCheck(instance, Constructor) {
  if (!(instance instanceof Constructor)) {
    throw new TypeError("Cannot call a class as a function");
  }
}

var MyClass = /*#__PURE__*/function () {
  function MyClass(name) {
    _classCallCheck(this, MyClass);

    this.name = name;
  }

  var _proto = MyClass.prototype;

  _proto.greet = /*#__PURE__*/function () {
    var _greet = /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() {
      return regeneratorRuntime.wrap(function _callee$(_context) {
        while (1) {
          switch (_context.prev = _context.next) {
            case 0:
              return _context.abrupt("return", "Hello, " + this.name + "!");

            case 1:
            case "end":
              return _context.stop();
          }
        }
      }, _callee, this);
    }));

    function greet() {
      return _greet.apply(this, arguments);
    }

    return greet;
  }();

  return MyClass;
}();

var obj = new MyClass('Alice');
obj.greet().then(function (message) {
  console.log(message);
});
  • after
"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

var _asyncToGenerator2 = require("@babel/runtime/helpers/asyncToGenerator");

var _classCallCheck2 = require("@babel/runtime/helpers/classCallCheck");

var _createClass2 = require("@babel/runtime/helpers/createClass");

function _createForOfIteratorHelperLoose(o, allowArrayLike) {
  var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"];
  if (!it) return;
  if (typeof it === "function" && it.prototype) it = it.prototype;
  if (typeof it === "object" && it !== Array.prototype) return { __iterator: it };
  if (allowArrayLike && it === Array.prototype.entries) return { __iterator: it };
  return it;
}

var MyClass = /*#__PURE__*/function () {
  function MyClass(name) {
    (0, _classCallCheck2.default)(this, MyClass);
    this.name = name;
  }

  (0, _createClass2.default)(MyClass, [{
    key: "greet",
    value: function () {
      var _greet = (0, _asyncToGenerator2.default)( /*#__PURE__*/regeneratorRuntime.mark(function _callee() {
        return regeneratorRuntime.wrap(function _callee$(_context) {
          while (1) {
            switch (_context.prev = _context.next) {
              case 0:
                return _context.abrupt("return", "Hello, " + this.name + "!");

              case 1:
              case "end":
                return _context.stop();
            }
          }
        }, _callee, this);
      }));

      function greet() {
        return _greet.apply(this, arguments);
      }

      return greet;
    }()
  }]);
  return MyClass;
}();

var obj = new MyClass('Alice');
obj.greet().then(function (message) {
  console.log(message);
});

1.2. babel自定义插件编写

1.3. 文档地址

https://github.com/jamiebuilds/babel-handbook/blob/master/translations/zh-Hans/README.md

1.4. babel 编译原理

经典流程图

image

babel 主要模块及作用

const babel = require("@babel/core") // babel.parseSync 将文件读取的代码转成 AST
const template = require("@babel/template").default // 将手写的模板代码转成 AST
const traverse = require("@babel/traverse").default // traverse 遍历AST树并进行替换、添加、删除操作
const generate = require("@babel/generator").default // 将 AST 还原成代码

demo 代码:在某句代码前插入一段新的代码

const path = require('path')
const fs = require('fs')
const babel = require("@babel/core")
const template = require("@babel/template").default
const generate = require("@babel/generator").default
const traverse = require("@babel/traverse").default

const Path = entryPath.join(__dirname, 'dist/app.js')
const content = fs.readFileSync(entryPath, 'utf-8')

const appJsAst = babel.parseSync(content, {sourceType: 'script'})

const buildRequire = template(`
 const taroApp = require("./taro/app.js");
`)

const ast = buildRequire()

traverse(appJsAst, {
  ExpressionStatement(path) {
    const calleePath = path.get('expression.callee')
    if (calleePath.isIdentifier({ name: 'App' })) {
      const prevPath = path.getPrevSibling()
      const isVariableDeclaration = prevPath.isVariableDeclaration()
      const firstDeclarationPath = isVariableDeclaration && prevPath.get('declarations.0')
      const isTaroApp = firstDeclarationPath && firstDeclarationPath.node.id.name === 'taroApp'
      console.log(firstDeclarationPath.node.id.name)
      if (!isTaroApp) {
        path.insertBefore(ast)
      }
    }
  }
})

const { code: appJsCode } = generate(appJsAst, { sourceMaps: false })

const destPath = path.join(__dirname, 'dist/new.js')

fs.writeFileSync(destPath, appJsCode)

@babel/traverse 方法详解

ast-explorer 网址:https://lihautan.com/babel-ast-explorer

const traverse = require("@babel/traverse").default

traverse(appJsAst, {
  ExpressionStatement(path) {
    const calleePath = path.get('expression.callee')
    if (calleePath.isIdentifier({ name: 'App' })) {
      const prevPath = path.getPrevSibling()
      const isVariableDeclaration = prevPath.isVariableDeclaration()
      const firstDeclarationPath = isVariableDeclaration && prevPath.get('declarations.0')
      const isTaroApp = firstDeclarationPath && firstDeclarationPath.node.id.name === 'taroApp'
      console.log(firstDeclarationPath.node.id.name)
      if (!isTaroApp) {
        path.insertBefore(ast)
      }
    }
  }
})

// ExpressionStatement
// prevPath.get('declarations.0') 虽然是数组,但还是只能使用点操作,prevPath.get('declarations[0]') 无效
// path.getPrevSibling() 获取兄弟节点
// prevPath.isVariableDeclaration() 节点判断
// firstDeclarationPath.node.id.name === 'taroApp' node 不能少
// path.insertBefore(ast) 节点插入

1.5. babel 和 polyfill 的区别

  • babel 默认只转换新的 JavaScript 语法(syntax),而不转换新的 API。比如:箭头函数转化成普通函数。

  • polyfill 将新的 API 用 es5 的方法实现。比如:Promise、Array.of

module.exports = {
  presets: [
    [
      '@babel/preset-env',
      {
        useBuiltIns: 'usage', // 使用的才引入
        corejs: 3,
      },
    ],
  ],
};

1.6. SWC 相对于 Babel 之所以更快,有以下几个主要原因:

  • Rust 编写:SWC 是用 Rust 编写的,Rust 是一种系统级编程语言,以其性能和内存管理的安全性而闻名。Rust 具有出色的性能特性,因此 SWC 在编译速度上具有优势。

  • 并行编译:SWC 具有并行编译的能力,可以充分利用多核处理器。这意味着它可以同时处理多个文件,从而加速整个编译过程,特别是在大型项目中表现出色。

  • 更少的内存开销:SWC 针对内存开销进行了优化,相对于 Babel 使用更少的内存。这有助于避免在大型项目中出现内存问题,并提高了稳定性。

  • 高度优化的代码生成:SWC 生成的 JavaScript 代码非常紧凑和高效,它对代码进行了深度优化,包括减小变量名、减少不必要的操作等。这可以减小生成的代码的体积,提高代码执行性能。

  • 针对现代特性的优化:SWC 针对现代 JavaScript 特性进行了优化,特别是针对 ECMAScript 6+ 和 TypeScript。它可以更有效地识别和转换这些特性,而不需要像 Babel 那样复杂的插件和配置。

Babel 是一个非常灵活的工具,它可以通过各种插件和配置来满足各种需求,但这种灵活性可能会导致一些性能开销。相比之下,SWC 更专注于性能和速度,因此在某些场景下可能会更快。

Copyright © tomgou 2022 all right reserved,powered by Gitbook该文章修订时间: 2023-09-07 23:52:13

results matching ""

    No results matching ""