本文基于 babel 7 作叙述,若是以前一直使用 babel 6 的同窗能够先看本文关于 babel 6 升级 babel 7 的相关模块javascript
Babel 的编译过程能够分为三个阶段:java
使用 @babel/core 中的 parse API 来进行词法分析(分词+语法分析),转化成 AST,为 Transform 准备。
Tip:感兴趣的同窗能够阅读这篇文章深刻了解下:www.alloyteam.com/2017/04/ana… 本文重在讲述 babel 在项目中的实践呀。node
使用 @bable/core 中的 transform API 来进行转义;咱们熟知的插件就是应用在这一过程,若是这一阶段不使用任何插件,Babel 将在生成阶段输出原样的代码;react
// react + typescript
"presets": [
[
"@babel/preset-env",
{
"targets": "cover 95%, safari >= 7", # 根据项目实际状况配置
"modules": "cjs",
"useBuiltIns": "usage"
}
],
"@babel/react",
"@babel/preset-typescript", # 若是项目使用 typescript 开发须要安装这个预设
]
复制代码
用 babel-generator 经过 AST 生成 ES5 代码; TIP:若是在 babel 的配置中,不使用任何 presets 和 plugins,那么生成的代码和原代码无异;webpack
@babel-polyfill 的不足,在平常业务开发中,影响不是很大的,可是若是在通用的第三方库的开发中引入 @babel-polyfill,就会带来潜在的问题:好比咱们项目中自定义了一份新的 API 的实现,可是引入的第三方通用库使用了polyfill,而且实现了一样的方法,就会将咱们原有的方法覆盖(至少会有存在这样的风险),为了不这样的问题,babel 推出了 @babel-runtime 来实现 @babel-polyfill 的绝大部分的功能,它将开发者依赖的全局内置 对象等,单独抽成模块,经过模块导入的方式,避免了对全局做用域的污染。 因此,若是是开发库、工具,能够考虑使用 @babel-runtime;git
# babel-plugin-transform-runtime 用于构建过程的代码转换
npm install --save-dev babel-plugin-transform-runtime
npm install --save babel-runtime
复制代码
{
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"absoluteRuntime": false,
"corejs": false,
"helpers": true,
"regenerator": true,
"useESModules": false
}
]
]
复制代码
@babel/plugin-transform-runtime 插件主要作了以下三件事情:es6
# js
var sym = Symbol();
var promise = new Promise();
console.log(arr[Symbol.iterator]());
# 转化后
"use strict";
var _getIterator2 = require("@babel/runtime-corejs2/core-js/get-iterator");
var _getIterator3 = _interopRequireDefault(_getIterator2);
var _promise = require("@babel/runtime-corejs2/core-js/promise");
var _promise2 = _interopRequireDefault(_promise);
var _symbol = require("@babel/runtime-corejs2/core-js/symbol");
var _symbol2 = _interopRequireDefault(_symbol);
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : { default: obj };
}
var sym = (0, _symbol2.default)();
var promise = new _promise2.default();
console.log((0, _getIterator3.default)(arr));
复制代码
# js
class Person() {}
# 转化后
"use strict";
var _classCallCheck2 = require("@babel/runtime/helpers/classCallCheck");
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : { default: obj };
}
var Person = function Person() {
(0, _classCallCheck3.default)(this, Person);
};
复制代码
# js
function* foo() {}
# 转化后
"use strict";
var _regenerator = require("@babel/runtime/regenerator");
var _regenerator2 = _interopRequireDefault(_regenerator);
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : { default: obj };
}
var _marked = [foo].map(_regenerator2.default.mark);
function foo() {
return _regenerator2.default.wrap(
function foo$(_context) {
while (1) {
switch ((_context.prev = _context.next)) {
case 0:
case "end":
return _context.stop();
}
}
},
_marked[0],
this
);
}
复制代码
因为 @babel-runtime 和 @babel/plugin-transform-runtime 实现方式致使它不会对实例方法进行扩展(例如 Array.prototype.includes()
),因此在使用中,存在很大的隐患,简单的说,若是你使用的是 @babel-runtime 实现的polyfill,那么,就会出现你使用的部分原型方法得不到支持而报错,这是不能接受的,因此我推荐在项目开发中,仍是使用 @babel-polyfill 比较稳妥。github
TIP: 关于 @babel/preset-env 的详细内容能够查看下一小节的内容 经过在 presets 里使用 @babel/preset-env preset,设置 useBuiltInts 为 usage 或者或者 entry 来实现按需引入; 与@babel/preset-env一块儿使用时(注意,仍然须要安装@ babel-polyfill。)web
从 2018-9-17 发布至今,每周的下载量都在稳步提高,推荐小伙伴们在项目中使用基于 babel 7的 @babel/preset-env 插件预设;chrome
@babel-preset-env 将基于你的实际浏览器及运行环境,自动的肯定 babel 插件及 polyfill,编译ES2015 及此版本以上的语言,在没有配置项的状况下,@babel-preset-env 表现的同 babel-preset-latest同样(或者能够说同babel-preset-es201五、babel-preset-es201六、 babel-preset-es2017 结合到一块儿表现的一致)。
对于实验属性(babel-preset-latest 不支持的)须要手动安装配置相应的 plugins 或者 presets; 能够经过 babeljs.io/docs/en/plu… 来查询相关的实验属性对应的插件,来引入支持业务开发;
"babel": {
"presets": [
[
"env",
{
"targets": {
"browsers": ["last 2 versions", "ie >= 7"]
}
}
]
]
},
复制代码
"targets": {
"browsers": "> 5%"
}
复制代码
"targets": {
"chrome": 56
}
复制代码
@babel-preset-env 默认只支持对语法的转化,须要开启useBuiltIns配置才能转化 API 和实例方法, 来为标准库中的新功能提供了 polyfill,为内置对象,静态方法,实例方法,生成器函数提供支持。 @babel-preset-env 能够实现基于特定环境引入须要的polyfill。
若是你经过Babel编译你的Node.js代码,babel-preset-env 颇有用,设置 "targets.node" 是 "current",支持的是当前运行版本的nodejs:
const presets = [
[
"@babel/env",
{
node: 'current',
},
],];
module.exports = { presets };
复制代码
在迁移以前建议熟悉 babel 7 具体带来的改动,这样有助于咱们在项目中进行具体的优化和进行针对性的配置修改
熟悉相关重要改动,能够帮助咱们更好的去配置项目,使用最新支持的特性,提升编码效率
# right
{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}
# wrong
{
"presets": "@babel/preset-env, @babel/preset-react"
}
复制代码
render() {
return (
<>
<ChildA />
<ChildB />
</>
);
}
// output 👇
render() {
return React.createElement(
React.Fragment,
null,
React.createElement(ChildA, null),
React.createElement(ChildB, null)
);
}
复制代码
interface Person {
firstName: string;
lastName: string;
}
function greeter(person : Person) {
return "Hello, " + person.firstName + " " + person.lastName;
}
复制代码
使用后(移除类型声明)
function greeter(person) {
return "Hello, " + person.firstName + " " + person.lastName;
}
复制代码
import "@babel/polyfill";
复制代码
但它包括整个 polyfill,若是浏览器已经支持,你可能不须要导入全部内容。这与@babel/preset-env 试图用语法解决的问题相同,因此咱们在这里将它应用于polyfill。选项 useBuiltins:“entry” 经过将原始导入仅拆分为必要的导入来实现此目的。 可是咱们能够经过仅导入代码库中使用的 polyfill 来作得更好。选项“useBuiltIns:”usage“是咱们第一次尝试启用相似的东西(详细说明)。 关于 @babel/preset-env 详细的说明,能够参考这篇文档:@babel-preset-env 配置说明 ,理解其中每个配置,来利用 babel 新增的 api 来结合项目自身特色进行配置,进行优化。
# 不安装到本地而是直接运行命令,npm 的新功能
npx babel-upgrade --write
# 或者常规方式
npm i babel-upgrade -g
babel-upgrade --write
复制代码
以一个依赖 babel 6 的项目为例:经过 babel 官方提供的升级工具,轻松搞定依赖相关的问题:
["@babel/plugin-proposal-pipeline-operator", {
# 因为项目中并无使用管道符,因此我这里就直接给一个 minimal 的属性值,其余项目能够根据具体须要作调整
"proposal": "minimal"
}]
复制代码
babel 7 带来了@babel/preset-env 这个插件,它扩展了对 polyfill 的配置,让使用者能够优化垫片的体积,作到按需加载,具体细节能够参考 @babel-preset-env 配置说明
能够参考我这篇 JavaScript 项目迁移 TypeScript 实践分享 文章来作迁移(在余下时间我会从新整理发布到掘金)
# .babelrc
# yarn add @babel/preset-typescript
"presets": [
[
"@babel/env",
{
"targets": "cover 95%, safari >= 7",
"modules": "cjs",
"corejs": 2,
"useBuiltIns": "usage",
}
],
"@babel/react",
"@babel/typescript"
],
复制代码
{
"compilerOptions": {
"module": "esnext",
"target": "es6",
"allowSyntheticDefaultImports": true,
"baseUrl": ".",
"sourceMap": true,
"outDir": "../client-dist",
"allowJs": true,
"jsx": "react",
"noEmit": true,
"moduleResolution": "node",
"isolatedModules": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
// `vs code`所须要的,在开发时找到对应的路径,真实的引用是在`webpack`中配置的`alias`
"paths": {
}
},
# 这里这么设计,是由于咱们项目历史缘由,js 的业务代码比较多,在跑 ci tsc 作类型检查的时候,只走 tsx? 类型文件
"include": ["src/**/*.ts", "src/**/*.tsx"],
"exclude": [
"node_modules"
]
}
复制代码
# package.json
{
"scripts": {
"tsc": "tsc --noEmit"
}
}
复制代码
本人想经过本文和你们分享一下本身关于 Babel 在项目中使用的一些心得,但愿能够给小伙伴们带来一些帮助,Babel 不是一个黑盒子,而是一个给开发者提供了各类各样选择的工具集,术业有专攻,只须要稍微花一点时间来理解它,就能够在项目中很安全地使用它,利用 Babel 团队不断努力给咱们带来的新的语言特性的支持; 你们有什么疑问能够直接给我留言评论。
gitissue.com/issues/5c18… juejin.im/post/5d0373… juejin.im/post/5d74d4… segmentfault.com/q/101000000… www.jianshu.com/p/d078b5f30… jsweibo.github.io/2019/08/05/… juejin.im/post/5c03a4… blog.hhking.cn/2019/04/02/… github.com/sorrycc/blo… github.com/Kehao/Blog/… juejin.im/post/5b07e7… jsweibo.github.io/2019/08/09/… github.com/SunshowerC/… zhuanlan.zhihu.com/p/43249121 pdsuwwz.github.io/2018/09/29/… - babel 升级踩坑 juejin.im/post/5b174f… juejin.im/entry/5b108… github.com/lmk123/blog… segmentfault.com/a/119000001… segmentfault.com/a/119000001… www.zcfy.cc/article/bab… juejin.im/entry/5b108…