Babel入门教程

1,528 阅读3分钟
原文链接: developerdoc.com

大家都知道js十分依赖浏览器(或Node环境),不同浏览器对js的支持不尽相同。现如今ECMAScrip标准的更新已经到了一年一次的节奏,
Babel 就是为了解决这个问题 ,它可以将使用新标准的JavaScript代码转换为浏览器支持可以运行的JavsScript(ES5)代码。

//例如babel将这段代码
[1, 2, 3].map(n => n ** 2);
//转换为:
"use strict";

[1, 2, 3].map(function (n) {
  return n + 1;
});

在webpack中配置Babel

假设在react项目配置webpack:

一、安装npm包:

npm install --save-dev babel-loader babel-core babel-plugin-transform-runtime babel-preset-es2015 babel-preset-react webpack-preset-babel-stage-0

二、在webpack配置文件中添加规则:

module: {
  rules: [
    { test: /\.js$/, exclude: /node_modules/, loader: "babel-loader" }
  ]
}

三、 在项目根目录创建名为.babelrc的文件,这是Babel的配置文件

在配置文件中安装插件(plugins)或预设(presets,也就是一组插件)来指示 Babel 去做什么事情。在下面配置中:

babel-preset-es2015 打包了 es6 的特性;
babel-preset-stage-0 打包处于 strawman 阶段的语法;
babel-preset-react 打包react全家桶语法;

Babel 几乎可以编译所有时新的 JavaScript 语法,但对于 APIs 来说却并非如此。例如: Promise、Set、Map 等新增对象,Object.assign、Object.entries等静态方法,babel-plugin-transform-runtime为了达成使用这些新API。

//.babelrc内容
{
	"presets": ["es2015", "stage-0", "react"],
	"plugins": ["transform-runtime"]
}

Babel模块

Babel6.0后将功能划分成了不同的模块,在 Babel packages 仓库看到babel现在有哪些模块:

babel-core

参考 babel-core doc

babel-core是作为babel的核心存在,babel的核心api都在这个模块里面。babel-core把 js 代码分析成 ast ,方便各个插件分析语法进行相应的处理。有些新语法在低版本 js 中是不存在的,如箭头函数,rest 参数,函数默认值等,这种语言层面的不兼容只能通过将代码转为 ast,分析其语法后再转为低版本 js。

// npm install babel-core
// var babel = require("babel-core");

//用法:

//babel.transform(code: string, options?: Object, callback: Function)
//字符串形式的 JavaScript 代码可以直接使用 babel.transform 来编译
babel.transform("code();", options, function(err, result) {
  result.code;
  result.map;
  result.ast;
});

//注意 :可以在options中指定 preset 和 plugin
//      写法为: babel.transform(
//                  "code();", 
//                  {presets: ["react"],}, 
//                  function(err, result){}

//babel.transformFile(filename: string, options?: Object, callback: Function)
//编译文件
babel.transformFile("filename.js", options, function(err, result) {
  result; // => { code, map, ast }
});

//babel.transformFromAst(ast: Object, code?: string, options?: Object, callback: Function): FileNode | null
//将ast进行转译
const { code, map, ast } = babel.transformFromAst(ast, code, options);

其中 options 可以配置多项内容,包括Plugin和Preset配置,SourceMap配置等

babel-cli

参考 babel-cli doc

Babel 的 CLI 是一种在命令行下使用 Babel 编译文件的简单方法。

//基本使用 (输出到控制台)
npx babel script.js
//指定输出文件
npx babel script.js --out-file script-compiled.js

假设script.js长如下这样,直接在命令行执行 babel script.js,发现输出的代码好像没有转译……

//script.js
[1, 2, 3].map(n => n ** 2);

var [a,,b] = [1,2,3];

var name = "Guy Fieri";
var place = "Flavortown";
`Hello ${name}, ready for ${place}?`;

let yourTurn = "Type some code in here!";

var obj = {
    shorthand,
    method() {
      return "😀";
    }
  };

function addAll() {
  return Array.from(arguments).reduce((a, b) => a + b);
}

这和前面一样需要 配置 presets 和 plugins 信息。

这时候有两种方式一种是和上文一样使用 .babelrc 配置文件

//1. 转换 ES2015+ 的 env preset
npm install babel-preset-env --save-dev

//2. 创建一个 .babelrc 文件并启用一些插件
//.babelrc 文件信息:
{
  "presets": ["env"]
}

这时候可以使用了

npx babel script.js

//输出 可以看到箭头函数等一些新语法已经被转义了。
[1, 2, 3].map(function (n) {
  return Math.pow(n, 2);
});

var _ref = [1, 2, 3],
    a = _ref[0],
    b = _ref[2];


var name = "Guy Fieri";
var place = "Flavortown";
"Hello " + name + ", ready for " + place + "?";

var yourTurn = "Type some code in here!";

var obj = {
  shorthand: shorthand,
  method: function method() {
    return "😀";
  }
};

function addAll() {
  return Array.from(arguments).reduce(function (a, b) {
    return a + b;
  });
}

另一种方式是直接在命令中指定presets

//1. 转换 ES2015+ 的 env preset
npm install babel-preset-env --save-dev

//2.指定present 
//输出也和上文一样
npx babel script.js --presets=env

babel-runtime / babel-plugin-transform-runtime

Babel 几乎可以编译所有时新的 JavaScript 语法,但对于 APIs 来说却并非如此。例如: Promise、Set、Map 等新增对象,Object.assign、Object.entries等静态方法。

//下列含有箭头函数的需要编译的代码:
function addAll() {
  return Array.from(arguments).reduce((a, b) => a + b);
}

//在上文中转义为:
//然而这段代码在浏览器中可能抛出 Uncaught TypeError: Array.from is not a function
//它依然无法随处可用因为不是所有的 JavaScript 环境都支持 Array.from
function addAll() {
  return Array.from(arguments).reduce(function (a, b) {
    return a + b;
  });
}

在实现新的API的基础上有两种方式: babel-polyfillbabel-runtime + babel-plugin-transform-runtime

这两者虽然都是模拟 es6 环境新增API,但实现方法完全不同。

  • babel-polyfill 的做法是将全局对象通通污染一遍,比如想在 node 0.10 上用 Promise,调用 babel-polyfill 就会往 global 对象挂上 Promise 对象。

  • babel-runtime 的做法是自己手动引入 helper 函数, const Promise = require(‘babel-runtime/core-js/promise’) 就可以引入 Promise。为了解决手动引入Helper函数的麻烦,babel 又开发了 babel-plugin-transform-runtime,这个模块会将我们的代码重写,如将 Promise 重写成 _Promise(只是打比方),然后引入_Promise helper 函数。这样就避免了重复打包代码和手动引入模块的痛苦。

@babel/standalone 在浏览器环境/web工程中运行babel

由于 Babel 本身的设计是基于 node.js 环境下运行使用的, 上面的例子都是在 Node.js环境中跑的,但是你头铁 一定要在浏览器环境中跑babel怎么办?

答案就是使用 @babel/standalone

参考 github.com/babel/babel… babeljs.io/docs/en/nex…

//npm i @babel/standalone @babel/core @babel/preset-react @babel/preset-env @babel/plugin-transform-runtime
//为什么写成这种require样式呢……@babel/standalone集成了很多插件 但是好像兼容性不好……
const babelCore = require('@babel/standalone');
let {code} = babelCore.transform(
    '({item}) => <Text>{item.key}</Text>',
    {
        'presets': [require('@babel/preset-react'),require('@babel/preset-env')],
        'plugins': [require('@babel/plugin-transform-runtime')]
    });
console.log('code:',code);

或者

//npm i @babel/standalone 
const babelCore = require('@babel/standalone');
let {code} = babelCore.transform(
    '({item}) => <Text>{item.key}</Text>',
    {
        'presets': ['react'],
    });
console.log('code:',code);