kekobin/blog

babel知识点

Opened this issue · 0 comments

简介

Babel 模块都是作为独立的 npm 包发布的,并且(从版本 7 开始)都是以 @babel 作为冠名的。这种模块化的设计能够让每种工具都针对特定使用情况进行设计。 其中,最重要的两个是 @babel/core 和 @babel/cli。

npm install --save-dev @babel/core @babel/cli

CLI 命令行工具

使用示例

npx babel src --out-dir dist

npx babel === ./node_modules/.bin/babel 命令

主要三个options: --out-dir、--plugins、--presets

插件和预设(preset)

代码转换功能以插件的形式出现,插件是小型的 JavaScript 程序,用于指导 Babel 如何对代码进行转换。(即ast三大步骤中的transform,一般的插件实现就是编写ast transform的vistor)

如:将es2015+语法的箭头函数转换成es5语法,通常使用插件:

npm install --save-dev @babel/plugin-transform-arrow-functions

使用方式:

npx babel src --out-dir dist --plugins=@babel/plugin-transform-arrow-functions

具体实现,就是获取ast语法,然后创建一个functionExpression去替换arrowFunctionExpression。参考抽象语法树AST

那么,是不是所有需要转换的语法,都需要配置一个个插件来进行转换呢?
答案是不需要的。

可以使用一个 "preset" (即一组预先设定的插件)。
@babel/preset-env,这个preset 所包含的插件将支持所有最新的 JavaScript (ES2015、ES2016 等)特性,即这个插件会转换大部分默认的语法,而不用一个个的配置插件。

配置文件

babel除了使用cli的方式来进行转换语法外,更多的场景是使用配置文件的形式。

babel配置文件支持两种方式:babel.config.js、.babelrc

区别(根据使用场景)

babel.config.js (官方推荐)

  • 希望以编程的方式创建配置文件
  • 希望编译 node_modules 目录下的模块
module.exports = function (api) {
  api.cache(true);

  const presets = [
  [
    "@babel/env",
    {
      targets: {
        "targets":{
        "chrome":"58",
        "ie":"10"
      },
     useBuiltIns: "usage",
    },
  ],
];
  const plugins = [ ... ];

  return {
    presets,
    plugins
  };
}

现在,名为 env 的 preset 只会为目标浏览器中没有的功能加载转换插件。

.babelrc

  • 只是需要一个简单的并且只用于单个软件包的配置
{
  "presets": [...],
  "plugins": [...]
}

另外,还可以 .babelrc.js: 与 .babelrc 的配置相同,但你可以使用 JavaScript 编写。

const presets = [ ... ];
const plugins = [ ... ];

module.exports = { presets, plugins };

Polyfill

@babel/polyfill 模块包括 core-js 和一个自定义的 regenerator runtime 模块用于模拟完整的 ES2015+ 环境

这意味着你可以使用诸如 Promise 和 WeakMap 之类的新的内置组件、 Array.from 或 Object.assign 之类的静态方法、 Array.prototype.includes 之类的实例方法以及生成器函数(generator functions)(前提是你使用了 regenerator 插件)。为了添加这些功能,polyfill 将添加到全局范围(global scope)和类似 String 这样的内置原型(native prototypes)中。

对于软件库/工具的作者来说,这可能太多了。如果你不需要类似 Array.prototype.includes 的实例方法,可以使用 transform runtime 插件而不是对全局范围(global scope)造成污染的 @babel/polyfill。这能够确保你的库或是组件不包含不必要的 polyfills。通常来说,打包 polyfills 应当是最终使用你的库的应用的责任

更进一步,如哦你确切地指导你所需要的 polyfills 功能,你可以直接从 core-js 获取它们。

@babel/preset-env插件提供了配置属性useBuiltIns,此参数设置为 "usage" 时,会同时包含你所需要的 polyfill(即设置这个时,可以不用引入@babel/polyfill)。
Babel 将检查你的所有代码,以便查找目标环境中缺失的功能,然后只把必须的 polyfill 包含进来。示例代码如下:

Promise.resolve().finally();

将被转换为(由于 Edge 17 没有 Promise.prototype.finally):

require("core-js/modules/es.promise.finally");
Promise.resolve().finally();

vue项目推荐使用 @vue/babel-preset-app,它已经预包含所需要的 polyfill。

presets: ['@vue/app']

-proposal

推荐的插件(指的是没有纳入官方release版本的那些属性插件):

  • @babel/plugin-proposal-function-bind
  • @babel/plugin-proposal-class-properties

插件顺序

  • 插件在 Presets 前运行。
  • 插件顺序从前往后排列。
  • Preset 顺序是颠倒的(从后往前)。

最新的@babel/polyfill只是core-js的一个软链,core-js是一个javascript模块标准库,是最新的所有javascript语法的一个polyfill.
babylon引入方式变成了@babel/parser.
以前所有babel-的变成了@babel/.
babel默认是进来啥code,出去就啥code,即要使其转换代码,必须配置相关插件.
@babel/preset-env takes any target environments you've specified and checks them against its mappings to compile a list of plugins and passes it to Babel.(默认读取.browserslistrc 文件来获取支持的浏览器版本target)

babel-plugin-transform-runtime 和 babel-runtime

1.在转换 ES2015 语法为 ECMAScript 5 的语法时,babel 会需要一些辅助函数,例如 _extend。
babel 默认会将这些辅助函数内联到每一个 js 文件里,这样文件多的时候,项目就会很大。
所以 babel 提供了 transform-runtime 来将这些辅助函数“搬”到一个单独的模块 babel-runtime 中,这样做能减小项目文件的大小。
2.开发应用建议使用 babel-polyfill 会在全局新增对应方法
开发框架建议babel-plugin-transform-runtime局部变量 不会污染全局 局部使用es6的函数或方法

优点:
(1)提高代码重用性,缩小编译后的代码体积。
(2)防止污染全局作用域。(启用corejs配置)

几个包关系

babel-polyfill仅仅是引用core-js、regenerator-runtime这两个npm包。

@babel/runtime包含两个文件夹:helpers(定义了一些处理新的语法关键字的帮助函数)、regenerator(仅仅是引用regenerator-runtime这个npm包)。

@babel/runtime-corejs2包含三个文件夹:core-js(引用core-js这个npm包)、helpers(定义了一些处理新的语法关键字的帮助函数)、regenerator(仅仅是引用regenerator-runtime这个npm包)。

可以看出,@babel/runtime-corejs2≈@babel/runtime + babel-polyfill:

@babel/runtime只能处理语法关键字,而@babel/runtime-corejs2还能处理新的全局变量(例如,Promise)、新的原生方法(例如,String.padStart );

使用了@babel/runtime-corejs2,就无需再使用@babel/runtime了。

@babel/plugin-proposal-optional-chaining

使用该插件可以用来做数据防御处理。

以前的处理:

if(res && res.data && res.data.length > 0 {
...
}

现在可以使用:

if(res?.data?.length>0){
...
}

是不是方便、简洁很多,告别了繁琐各种 & 和 各种层层检查?

跟多使用:

  • 结合数组: a?.b?.[0]
  • 结合函数: a?.b?[0]
  • 结合执行: a?.()
  • 结合delete: delete a?.b

browserslist

你会发现有 package.json 文件里的 browserslist 字段 (或一个单独的 .browserslistrc 文件),指定了项目的目标浏览器的范围。这个值会被 @babel/preset-env 和 Autoprefixer 用来确定需要转译的 JavaScript 特性和需要添加的 CSS 浏览器前缀。

参考

es6features
core-js
babel
Babel插件:@babel/plugin-transform-runtime