Skip to content

目录:

一、介绍

二、property-mutators插件介绍与使用

三、源码解析

1、第一件事情

2、第二件事情

四、学到的知识

字数:大约900字

一、介绍

Babel 插件是用于扩展 Babel 编译器的功能的组件,它们可以添加新的语法、转换器、插件等,以便更好地适应不同的开发需求。在前端开发中,Babel 插件通常与打包工具(如 webpack)一起使用,以便将 ES6+ 代码转换为向后兼容的 JavaScript 代码,并打包成浏览器可以识别的静态资源。

二、property-mutators插件介绍与使用

上一节讲了ES3中两个插件,这一节看ES5中的插件。

js
@babel/plugin-transform-property-mutators

@babel/plugin-transform-property-mutators 是一个 Babel 插件,它提供了一组转换器,用于转换对象属性的定义方式。具体来说,它可以将对象字面量中的 get 和 set 属性转换成 Object.defineProperty 的形式,以及将对象字面量中的 method 属性转换成对象字面量的方法简写形式。

举个例子,将以下代码:

js
// babel03/src/index.js
const obj = {
  get name() {
    return this._name;
  },
  set name(value) {
    this._name = value;
  },
  method() {
    console.log("hello");
  },
};

使用 @babel/plugin-transform-property-mutators 插件转换后,会得到以下代码:

js
const obj = Object.defineProperties({
  method() {
    console.log("hello");
  }
}, {
  name: {
    get: function () {
      return this._name;
    },
    set: function (value) {
      this._name = value;
    },
    configurable: true,
    enumerable: true
  }
});

实际操作一下:

js
yarn add --dev @babel/plugin-transform-property-mutators
js
// babel03/babel.config.js
module.exports = { 
  plugins:[
    [
      '@babel/plugin-transform-property-mutators'
    ]
  ]
 };

运行

js
npx babel src/index.js -d dist

结果

js
// babel03/dist/index.js
const obj = Object.defineProperties({
  method() {
    console.log("hello");
  }
}, {
  name: {
    get: function () {
      return this._name;
    },
    set: function (value) {
      this._name = value;
    },
    configurable: true,
    enumerable: true
  }
});

可以看到,get 和 set 属性被转换成了 Object.defineProperty,而 method 属性则被转换成了对象字面量的方法简写形式。

这个插件主要用于支持一些旧版本的 JavaScript 运行环境,例如 IE8。在现代的 JavaScript 运行环境中,可以直接使用对象字面量的 get 和 set 属性,以及方法简写形式,而不需要通过 Object.defineProperty 进行定义。

三、源码解析

js
export default declare(api => {
  api.assertVersion(7);

  return {
    name: "transform-property-mutators",

    visitor: {
      ObjectExpression(path) {
        const { node } = path;
        let mutatorMap: MutatorMap | undefined;
        const newProperties = node.properties.filter(function (prop) {
          if (
            t.isObjectMethod(prop) &&
            !prop.computed &&
            (prop.kind === "get" || prop.kind === "set")
          ) {
            pushAccessor(
              (mutatorMap ??= {}),
              prop as t.ObjectMethod & { kind: "get" | "set"; computed: false },
            );
            return false;
          }
          return true;
        });

        if (mutatorMap === undefined) {
          return;
        }

        node.properties = newProperties;

        path.replaceWith(
          t.callExpression(
            t.memberExpression(
              t.identifier("Object"),
              t.identifier("defineProperties"),
            ),
            [node, toDefineObject(mutatorMap)],
          ),
        );
      },
    },
  };
});

通过分析,我们会发现,其实他就主要做了两件事情。

第一件事情

过滤对象的属性,只需要get、set、isObjectMethod,得到新的对象。

js
node.properties = newProperties;
js
// pushAccessor
export function pushAccessor(
  mutatorMap: MutatorMap,
  node: t.ObjectMethod & { kind: "get" | "set"; computed: false },
) {
  const alias = t.toKeyAlias(node);
  const map = (mutatorMap[alias] ??= {
    _inherits: [],
    _key: node.key,
  } as DefineMap);

  map._inherits.push(node);

  const value = t.functionExpression(
    null,
    node.params,
    node.body,
    node.generator,
    node.async,
  );
  value.returnType = node.returnType;
  t.inheritsComments(value, node);
  map[node.kind] = value;

  return map;
}

第二件事情

根据新对象转成Object.defineProperty 的形式

js
toDefineObject(mutatorMap)
js
export function toDefineObject(mutatorMap: any) {
  const objExpr = t.objectExpression([]);

  Object.keys(mutatorMap).forEach(function (mutatorMapKey) {
    const map = mutatorMap[mutatorMapKey];
    map.configurable = t.booleanLiteral(true);
    map.enumerable = t.booleanLiteral(true);

    const mapNode = t.objectExpression([]);

    const propNode = t.objectProperty(map._key, mapNode, map._computed);

    Object.keys(map).forEach(function (key) {
      const node = map[key];
      if (key[0] === "_") return;

      const prop = t.objectProperty(t.identifier(key), node);
      t.inheritsComments(prop, node);
      t.removeComments(node);

      mapNode.properties.push(prop);
    });

    objExpr.properties.push(propNode);
  });

  return objExpr;
}

尽管这份代码已经是6年前写的了。但是3个月前还有人在维护,并且将用到的types和方法抽离到了当前目录下。所以学这些永远都不晚,当下即最新。

四、学到的知识

一定要不停的学习。ES6、7、8、9 + 这些每个新增知识都要去记住,并使用。例如?. ?? ??= !! 这些基础的内容

代码里面不要使用乱七八糟的命令方式

不要再写 ==; 都用===

尽可能的简洁