什么是 Babel Macros

Babel Macros 可以理解为一种 Babel 的宏,它是 babel-plugin-macros 中出现的一种概念。是在编译时将代码转换为运行时代码的一个标准接口。为Babel 提供了宏能力。

宏是一种在编译时执行代码转换的技术,而不是在运行时,这意味着使用宏的代码在编译后不会增加任何运行时开销。

babel-plugin-macros 插件

babel-plugin-macros 插件提供了一种方式来创建可重用的代码转换,这些转换可以被包含在你的项目中,就像其他任何 Babel 插件一样,与传统 Babel 插件不同的是,宏是在项目代码中明确调用的,而不是在 Babel 配置中配置的

使用babel-plugin-macros的优势在于允许我们在不配置 Babel 插件的情况下,使用特定的代码转换,这对于库的作者来说非常有用,因为他们可以提供宏,用户可以直接在他们的代码中使用这些宏,而不需要下载和配置单独的插件,无需修改 Babel 配置

Plugin 与 Macros 区别

使用插件

以一个将所有字符串转换为大写字符串的插件为例

// uppercase-plugin.js
module.exports = function ({ types: t }) {
  return {
    visitor: {
      StringLiteral(path) {
        path.node.value = path.node.value.toUpperCase();
      }
    }
  };
};
 

使用

{ 
	"plugins": ["./path/to/uppercase-plugin.js"]
}

示例代码

const lowercaseString = 'hello world';
console.log(lowercaseString); // 'HELLO WORLD'

这个插件会在编译时将所有的字符串字面量转换为大写

使用宏

创建一个宏(注意:宏文件最好以.macro.js) 结尾,这样 babel-plugin-macros才能识别出这个文件是一个宏文件

// uppercase.macro.js
const { createMacro } = require('babel-plugin-macros');
 
function uppercaseMacro({ references, babel }) {
  const { types: t } = babel;
  references.default.forEach(referencePath => {
    if (referencePath.parentPath.type === 'CallExpression') {
      const stringNode = referencePath.parentPath.node.arguments[0];
      if (t.isStringLiteral(stringNode)) {
        stringNode.value = stringNode.value.toUpperCase();
      }
    }
  });
}
 
module.exports = createMacro(uppercaseMacro);

在代码中使用宏

import uppercase from './uppercase.macro';
 
const uppercaseString = uppercase('hello world');
console.log(uppercaseString); // 'HELLO WORLD'
 

宏允许在特定的地方使用,而不是全局的应用,意味着我们有更多的控制权,可以决定哪部分的代码应该被转换。

官方文档关于插件和宏的区别说明:GitHub - kentcdodds/babel-plugin-macros: 🎣 Allows you to build simple compile-time libraries

示例库 (twin.macro)

GitHub - ben-rogerson/twin.macro: 🦹‍♂️ Twin blends the magic of Tailwind with the flexibility of css-in-js (emotion, styled-components, solid-styled-components, stitches and goober) at build time.

Twin在构建时将 TailwindCss的魔力与 css-in-js 的灵活性(emotion, styled-components, solid-styled-components)融合在一起。Twin 就是利用 Babel 的 宏特性来实现的。基本上所有的 css in js 方案都提供了对应的 marco,减少了依赖的安装,让代码更直观

它有两种配置方式

babel-plugin-macros.config.js

module.exports = {
  twin: {
    preset: 'emotion',
    config: "./tailwind.config.ts"
  },
}

package.json

"babelMacros": {
    "twin": {
      "preset": "emotion",
      "config": "./tailwind.config.ts"
    }
},

使用

import tw from 'twin.macro'
 
const Input = tw.input`border hover:border-black`

参考