babel学习笔记
babel: 把符合最新标准的js代码向下编译成现在随处可用的版本,还支持语法扩展,如支持React用的JSX语法,还支持用于静态类型检查的流式语法(Flow Syntax)。
babel的一切都是简单的插件,你也可以利用babel创建自己的插件。
babel自身被分解成了数个核心模块,任何人都可以利用它们来创建下一代的js工具(围绕着babel已涌现出了大规模和多样化的生态系统)。
Babel 几乎可以编译所有时新的 JavaScript 语法,但对于 APIs 来说却并非如此。如它能编译箭头函数语法,却无法编译Array.from()这个新的api。为了解决这个问题,我们使用一种叫做 Polyfill(代码填充,也可译作兼容性补丁) 的技术。 简单地说,polyfill 即是在当前运行环境中用来复制尚不存在的原生 api 的代码。 能让你提前使用还不可用的 APIs,Array.from 就是一个例子。
js发展史:
1996年,网景浏览器,网景把javascript提交给 ECMA International(欧洲计算机制造商协会) 进行标准化,并最终确定出新的语言标准,它就是 ECMAScript。自此,ECMAScript 成为所有 JavaScript 实现的基础,不过,现实中我们只用 ECMAScript 称呼标准,平时都还是使用 JavaScript 来称呼这个语言。
标准(Standard): 用于定义与其他事物区别的一套规则。
实现(Implementation): 某个标准的具体实施/真实实践。
1996--2011年,相继发布了ECMAScript3,(ECMAScipt4由于太过激进而被抛弃),ECMAScript5,但由于现实中的很多实现和标准大相径庭,大部分开发者依然写着ECMAScript3风格的代码。
2012年,由于开始停止对旧版浏览器的支持,用ECMAScript5(ES5)编写代码变得可行。并且新的ES6标准开始启动。
2015 年,负责制定 ECMAScript 规范草案的委员会 TC39 决定将定义新标准的制度改为一年一次(所以开始以年份来命名ECMAScript标准了),这意味着每个新特性一旦被批准就可以添加,而不像以往一样,规范只有在整个草案完成,所有特性都没问题后才能被定稿。因此,ES6在2015年6月份公布之前又被重命名为了 ECMAScript 2015(ES2015)
2016年,提议中的新的js语法ES2016,或称为ES7。
2017年,提议中的新的js语法ES2017,或称为ES8,如async, await。
js提案要被正式采纳必须经过5(0-4)个阶段,相继通过各个阶段,最终在阶段4被标准正式采纳。
.babelrc:
babel的配置文件,用来让babel做你要它做的事情的配置文件,存放在项目的根目录下,用来设置转码规则和插件。在 babel 6 里,执行 babel test.js,只会输出原样的文本,因为 babel 不再包含任何 transform 功能,babel 6 里把它们作为插件(plugin)分割出去,需要我们自己配置插件,.babelrc文件内容为:
{ "presets": [], // 转码规则 "plugins": [] // 插件 }
比如要使用es6的箭头函数,必须配置"plugins": ["transform-es2015-arrow-functions"]。基本上 ES6/ES7 的各种功能,babel 都提供了相应的插件用于转换,但如果我们要一个一个配置 – 那就太恼人了。所以 babel 还提供了一个方法:presets。我们不妨把 presets 理解为套餐,不同套餐有不同的插件组合,比如 ES2015 preset 里打包了所有用于转换 ES2015 代码的插件,React preset 则打包了转换 react.js jsx 语法的插件。
转码规则可以根据需要按照:
# ES2015转码规则 $ npm install --save-dev babel-preset-es2015 //包含所有es2015的特性 # react转码规则 $ npm install --save-dev babel-preset-react # ES7不同阶段语法提案的转码规则(共有4个阶段),选装一个 $ npm install --save-dev babel-preset-stage-0 //包含了async,await等 $ npm install --save-dev babel-preset-stage-1 $ npm install --save-dev babel-preset-stage-2 $ npm install --save-dev babel-preset-stage-3
然后,将这些规则加入.babelrc:
{ "presets": [ "es2015", "react", "stage-2" ], "plugins": [] }
以下所有Babel工具和模块的使用,都必须先写好.babelrc
。也可以把内容写进package.json文件里:
"babel": { "presets": [ "react", "node5", "stage-0" ], "plugins": [] },
babel-cli,babel-node:
全局按照babel-cli,就可以在命令行下使用Babel编译文件了,如babel src -d lib (把src目录整个编译成一个新的目录)或babel example.js -o compiled.js (把example.js编译后的结果写入到compiled.js)。但是不推荐全局安装,导致项目产生了对环境的依赖,另一方面,这样做也无法支持不同项目使用不同版本的Babel。一般在项目中安装babel: npm install babel-cli --save-dev ,把命令写在npm scripts里,如"scripts": {"build": "babel src -d lib"},然后就可以在终端里运行npm run build。
babel-node不用单独安装,而是随babel-cli
一起安装。babel-node命令,它近似于 node,只不过它在运行代码前会预先编译 ES2015 的代码。提供一个支持ES6的REPL环境。它支持Node的REPL环境的所有功能,而且可以直接运行ES6代码或脚本文件。
把npm scripts改写成"scripts": {"build": "babel-node scripts.js"},这样script.js
本身就不用做任何转码处理。
babel-register:
运行babel的另一个方法,只需要引入文件就可以运行 Babel,或许能更好地融入你的项目设置。babel-register模块改写require命令,为它加上一个钩子。此后,每当使用require加载.js、.jsx、.es和.es6后缀名的文件,就会先用Babel进行转码。如创建一个register.js文件:
require("babel-register")
require("./index.js")
这样做就可以把 Babel注册到 Node 的模块系统中并开始编译其中 require
的所有文件。这样执行命令node register.js就可以运行的就是编译后的index.js文件了。需要注意的是,babel-register只会对require命令加载的文件转码,而不会对当前文件转码。另外,由于它是实时转码,所以只适合在开发环境使用。
babel-core:
以编程的方式来使用 Babel,如果某些代码需要调用Babel的API进行转码,就要使用babel-core
模块。
var babel = require('babel-core'); // 字符串转码 babel.transform('code();', options); // => { code, map, ast } // 文件转码(异步) babel.transformFile('filename.js', options, function(err, result) { result; // => { code, map, ast } }); // 文件转码(同步) babel.transformFileSync('filename.js', options); // => { code, map, ast }
babel-polyfill:
Babel默认只转换新的JavaScript句法(syntax),而不转换新的API,比如Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全局对象,以及一些定义在全局对象上的方法(比如Object.assign
)都不会转码。如果想让这些方法运行,必须使用babel-polyfill,为当前环境提供一个垫片。
要使用 Babel polyfill,首先用 npm 安装它:npm install --save babel-polyfill,然后只需要在文件顶部导入 polyfill 就可以了:import 'babel-polyfill'或是在webpack.config.js
中加入babel-polyfill
到你的入口数组:entry:["babel-polyfill","./app/js"]
babel-runtime:
与 babel-polyfill 一样,babel-runtime 的作用也是模拟 ES2015 环境。只不过,babel-polyfill 是针对全局环境的引入它的,babel-runtime 更像是分散的 polyfill 模块,我们可以在自己的模块里单独引入,比如 require(‘babel-runtime/core-js/promise’) ,它们不会在全局环境添加未实现的方法,只是,这样手动引用每个 polyfill 会非常低效。我们借助 Runtime transform 插件来自动化处理这一切。安装:npm install --save-dev babel-plugin-transform-runtime npm install --save babel-runtime; 配置.babelrc: "plugins":["transform-runtime"],这样Babel会把这样的代码:
class Foo {
method() {}
}
编译成:
import _classCallCheck from "babel-runtime/helpers/classCallCheck"; import _createClass from "babel-runtime/helpers/createClass"; let Foo = function () { function Foo() { _classCallCheck(this, Foo); } _createClass(Foo, [{ key: "method", value: function method() {} }]); return Foo; }();
至于要用 babel-polyfill 还是 babel-runtime,则需要根据具体需求。举个例子,如果一个库里引用了 babel-polyfill,别人的库也引用了 babel-polyfill,我们很可能会跑两个 babel-polyfill 实例,这里,使用 babel-runtime 会更合适。
手动指定插件:
你可以安装一些别的插件,配置.babelrc的plugins,这样能让你对正在使用的转换器进行更细致的控制。如babel-plugin-transform-es2015-classes插件。插件列表可参考babel官方网址。
插件选项:很多插件也有选项用于配置他们自身的行为。 例如,很多转换器都有“宽松”模式,通过放弃一些标准中的行为来生成更简化且性能更好的代码。如"plugins":[ ["transform-es2015-classed", {"loose":true}] ]。
基于环境自定义babel:
Babel 插件解决许多不同的问题。 其中大多数是开发工具,可以帮助你调试代码或是与工具集成。 也有大量的插件用于在生产环境中优化你的代码。因此,想要基于环境来配置 Babel 是很常见的。你可以轻松的使用 .babelrc
文件来达成目的。
{ "presets": [...], "plugins": [...], "env": { "development": { "plugins": [...] }, "production": { "plugins": [...] } } }
Babel 将根据当前环境来开启 env
下的配置。当前环境可以使用 process.env.BABEL_ENV
来获得。 如果 BABEL_ENV
不可用,将会替换成 NODE_ENV
,并且如果后者也没有设置,那么缺省值是"development"
。
手动指定插件?插件选项?环境特定设置?所有这些配置都会在你的项目里产生大量的重复工作。为此可以创建自己的预设并发布,使用时直接安装就可以了,比如 babel-preset-airbnb,
Babel和其他工具:
以eslint为例,eslint是最流行的语法检查工具之一。首先安装 eslint
和 babel-eslint,然后创建或使用项目现有的
.eslintrc
文件并设置 parser
为 babel-eslint,现在添加一个
lint
任务到 npm 的 package.json
脚本中就可以了,"scripts": {"lint": "eslint my-files.js"}。
webpack中定义babel-loader:
webpack.config.js中的loaders配置参考:
loaders: [ { test: /\.jsx?$/, loader: 'babel-loader', include: [ path.resolve(__dirname, '../src'), ], query: { babelrc: false, presets: [ 'react', 'es2015', 'stage-0', ], plugins: [ 'transform-runtime' ] } } ]