🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] Babel是一个广泛使用的ES6转码器,可以将ES6代码转为ES5代码,从而在现有环境执行。 ECMAScript 6是JavaScript语言的下一代标准。因为当前版本的ES6是在2015年发布的,所以又称ECMAScript 2015。但是目前浏览器对es6不完全兼容,需要借住babel编译。 ### 配置文件 通过 .babelrc 来配置Babel 转码规则,放在项目根目录下 /.babelrc ~~~ { "presets": ["es2015", "react", "stage-2"], "plugins": [] } ~~~ 上面配置了 Babel 转码规则为,将ES6的语法转为ES2015的转码,支持React的JSX语法转码,使用 stage-2 模式转码(相关模式下面会详细介绍) Babel 通过三个步骤 语法解析-转码-生成文件来编译Javascript。 Babel 是通过一系列插件来完成对JS的编译。 stage-x 是对非标准或未定案的标准的API的转码实现 ### Presets presets 是Babel内置的一些预设,是一系列插件的组合 ### 官方预设 Official Presets 1. env 根据配置的环境自动加载相关的插件 2. es2015 将ES6的语法转码为ES5的语法 3. es2016 幂运算语法糖插件 2**3 => 2*2*2 4. es2017 Async / Await / Generator 5. react 支持React编译 6. flow 支持静态类型检测编译 ### 插件 #### 支持的模块加载Modules 1. es2015-modules-amd 浏览器端的AMD模块加载管理 2. es2015-modules-commonjs 服务端的同步模块加载管理 3. es2015-modules-systemjs ES6的模块管理方式 4. es2015-modules-umd 兼容AMD CMD的模块管理方式 #### 实验性插件Experimental 1. async-generator-functions 支持将ES7的 async await 函数转换为ES6的 generator 函数 2. async-to-module-method 支持将ES7的 async await 函数转换为 bluebird 语法以兼容低版本[需要配置] 3. class-properties 类的静态属性与方法 4. decorators 类的装饰器 @connect 5. do-expressions 支持模板中使用 if/else 6. export-extensions 模块导出的扩展 7. function-bind 支持通过 :: 来绑定上下文 8. object-rest-spread 支持使用 … 将对象展开 9. 优化编译插件 Minification #### 优化编译插件 Minification 1. inline-environment-variables 简化环境变量 2. inline-consecutive-adds 简化对象与数据定义 3. merge-sibling-variables 将多次变量申明,合并为一次 4. minify-booleans 将true 和 false 转换为 !0 和 !1 5. minify-constant-folding 优化数字与字符串的计算: a = 2 * 4 => a = 8; 6. minify-dead-code-elimination 简化代码,去掉冗余的代码片段 7. minify-flip-comparisons 优化 Gzip 的压缩算法 8. minify-guarded-expressions 优化 && 表达式 9. minify-infinity Infinity => 1/0 10. minify-mangle-names 简化局部变量的名称 11. minify-numeric-literals 将大数字转成科学计算法表示 1000 => 1e3 12. minify-replace 自定义在编译过程需要缩短变量的变量 13. minify-simplify 优化if else 语句、undefined => void 0, Number(foo)=> +foo, foo[‘bar’]=>foo.bar 14. minify-type-constructors 简化使用基本类型构造的变量 15. node-env-inline node中将环境变量转换成行内变量 16. property-literals 对象字符属性转成唯一属性,关键字除外 17. regexp-constructors 如果Regext()参数是字符串,则转成字面量 18. remove-console 去掉console语句 19. remove-debugger 去掉 debugger 语句 20. simplify-comparison-operators 如果两边的类型一致,则将 === 转换为 == 21. undefined-to-void undefined => void 0 #### React 1. react-constant-elements 静态模板转常量 2. react-display-name 为组件添加 displayName 属性 3. react-inline-elements 使用性能更好的 babelHelpers.jsx() 替换 React.createElement() 转换模板 4. react-jsx JSX模板转为JS语句 5. react-jsx-compat JSX模板转成 0.12之前语法 6. react-jsx-self 添加__self属性标识本身信息 7. react-jsx-source 添加源码信息到JSX模板 #### Other 1. eval 转码eval()中的ES6语法 2. flow-comments 转码后添加类型检查注释 3. flow-strip-types 类型检测转成ES5语法 4. jscript 函数表达式转换为申明式函数 5. object-assign 转码 Object.assign 为ES5的 _extends()语法 6. object-set-prototype-of-to-assign 转码 setPrototypeOf 为 _defautls() 方法 7. proto-to-assign 使用_defaults() 替换 __proto__ 为对象扩展属性 8. regenerator 转码generator语法 9. runtime 提供一些工具方法如_extends 来帮助转码 10. strict-mode 添加 ‘use strict’; #### 语法解析插件Syntax 语法解析插件,只提供编译过程中的语法解析,并不转码,转码需要想关的转码插件支持,在使用相关转码插件时,会自动安装使用相关语法解析插件,不需要手动安装配置。 1. async-functions 2. async-generators 3. class-constructor-call 4. class-properties 5. decorators 6. do-expressions 7. exponentiation-operator 8. export-extensions 9. flow 10. function-bind 11. jsx 12. object-rest-spread 13. trailing-function-commas #### ES2015 默认情况下,Babel 自带了一组 ES2015 语法转化器。这些转化器能让你现在就使用最新的 JavaScript 语法,而不用等待浏览器提供支持。(每个一支持的特性都有独立的插件可以使用) ~~~ check-es2015-constants 检查const的使用规则 transform-es2015-arrow-functions 箭头函数 ()=>{} transform-es2015-block-scoped-functions 确实块级作用域下函数重复申明 transform-es2015-block-scoping 块级作用域 let & const transform-es2015-classes 类 class Header {} transform-es2015-computed-properties 动态属性名 obj[getKey('enabled')] = true; transform-es2015-destructuring 解构 let [a,b] = [0,0] transform-es2015-duplicate-keys 防止对象属性重复 transform-es2015-for-of 迭代器遍历 transform-es2015-function-name 支持通过 func.name 获取函数名称 transform-es2015-literals 将ES2015的整数和unicode文字编译为ES5 transform-es2015-modules-commonjs 支持 commonjs 模块导入导出方式 transform-es2015-object-super ES6对象编译为ES5对象 transform-es2015-parameters 支持函数参数设置默认值 function foo (a=1,b){} transform-es2015-shorthand-properties 属性简写 var d = {a, b} transform-es2015-spread 展开符 ... transform-es2015-sticky-regex 支持正则的粘性特性 transform-es2015-template-literals 支持ES6的字符串模板 `/a/${c}` transform-es2015-typeof-symbol 提升typeof的兼容性 transform-es2015-unicode-regex 支持正则匹配 unicode 字符 transform-regenerator 编译 genenerator 语法,并不转码(需要 babel-polyfill 或 regenerator runtime 的支持) ~~~ Strick regex 是否可以从指定的 lastIndex 开始搜索 Flow - JS静态类型检查工具 ~~~ function foo(one: any, two: number, three?): string {} ~~~ ### 常用ES6语法与转码对比 Arrow Function、Class、Funciton bind、Let、Const、Template、Modules、Destructuring、Spreade #### 函数 ~~~ //in var nums = evens.map((v, i) => v + i); //out var nums = evens.map(function (v, i) { return v + i; }); //保持方法的上下文 //in var bob = { _name: "Bob", _friends: [], printFriends() { this._friends.forEach(f => console.log(this._name + " knows " + f)); } }; //out var bob = { _name: "Bob", _friends: [], printFriends: function printFriends() { var _this = this; this._friends.forEach(function (f) { return console.log(_this._name + " knows " + f); }); } }; //in function foo(one: any, two: number, three?): string {} //out function foo(one, two, three) {} ~~~ #### 类 ~~~ //in class Bork { //Property initializer syntax instanceProperty = "bork"; boundFunction = () => { return this.instanceProperty; } //Static class properties static staticProperty = "babelIsCool"; static staticFunction = function() { return Bork.staticProperty; } } // out "use strict"; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var Bork = function Bork() { var _this = this; _classCallCheck(this, Bork); this.instanceProperty = "bork"; this.boundFunction = function () { return _this.instanceProperty; }; } //Property initializer syntax //Static class properties ; Bork.staticProperty = "babelIsCool"; Bork.staticFunction = function () { return Bork.staticProperty; }; ~~~ #### Let + Const ~~~ //in function f() { { // 块级作用域下的变量 let x; { // 块级作用域下的const常量 const x = "sneaky"; x = 'foo'; // 报错,常量不允许修改 } x = "bar"; let x= 'foo'; // 报错,变量重复定义 } } //out "use strict"; function f() { { // 块级作用域下的变量 var x = void 0; { // 块级作用域下的const常量 var _x = "sneaky"; } x = "bar"; } } ~~~ #### 属性与方法的语法糖转换 ~~~ var o = { a, b, c }; // var o = { a: a, b: b, c: c }; //in var cat = { getName() { return name; } }; //out var cat = { getName: function () { return name; } }; ~~~ #### 模板字符串 ~~~ //in var name = "Bob", time = "today"; `Hello ${name}, how are you ${time}?` //out var name = "Bob", time = "today"; "Hello " + name + ", how are you " + time + "?"; ~~~ #### 解构赋值 ~~~ // 从数组中解构 //in var [a, b,c] = [1,2,3]; //out var a = 1, b = 2, c = 3; // 从函数返回值中解构 //in var {op, lhs, rhs} = getASTNode() //out var _getASTNode2 = getASTNode(), op = _getASTNode2.op, lhs = _getASTNode2.lhs, rhs = _getASTNode2.rhs; //从实参中解构与默认值配置 //in function r({x, y, w = 10, h = 10}) { return x + y + w + h; } r({x:1, y:2}) === 23 //out function r(_ref4) { var x = _ref4.x, y = _ref4.y, _ref4$w = _ref4.w, w = _ref4$w === undefined ? 10 : _ref4$w, _ref4$h = _ref4.h, h = _ref4$h === undefined ? 10 : _ref4$h; return x + y + w + h; } r({ x: 1, y: 2 }) === 23; ~~~ #### 模块内容导出与导入 ~~~ //in export function sum(x, y) { return x + y; } export var pi = 3.141593; //out "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.sum = sum; function sum(x, y) { return x + y; } var pi = exports.pi = 3.141593; //in import * as math from "lib/math"; console.log("2π = " + math.sum(math.pi, math.pi)); //out var _math = require("lib/math"); var math = _interopRequireWildcard(_math); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } console.log("2π = " + math.sum(math.pi, math.pi)); // app.js ~~~ ### Babel-polyfill 针对默认不转换的API,需要另外添加一个Polyfill,或针对某个API添加插件。 Babel 默认只转换新的JavaScript句法(syntax),而不转换新的API,比如Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全局对象,以及一些定义在全局对象上的方法(比如Object.assign)都不会转码。 举例来说,ES6在 Array 对象上新增了 Array.from 方法。Babel就不会转码这个方法。如果想让这个方法运行,必须使用 babel-polyfill,为当前环境提供一个垫片。 ~~~ npm install --save-dev babel-polyfill ~~~ babel-polyfill 提提供了对新的标准API的支持,兼容老的版本。 下列特性需要使用 babel-polyfill 才能转码,或者单独引用相关插件 ~~~ // 数组操作 ArrayBuffer Array.from Array.of Array#copyWithin Array#fill Array#find Array#findIndex Function#name // 数学方法 Math.acosh Math.hypot Math.imul // 数字类型的扩展方法 Number.isNaN Number.isInteger // 对象的扩展方法 Object.assign Object.getOwnPropertyDescriptors Object.is Object.entries Object.values Object.setPrototypeOf Promise Reflect RegExp#flags // 字符串的扩展方法 String#codePointAt String#endsWith String.fromCodePoint String#includes String.raw String#repeat String#startsWith String#padStart String#padEnd // 新扩展的数据类型 Map Set Symbol WeakMap WeakSet ~~~ 使用 ~~~ import 'babel-polyfill'; // 或者 require('babel-polyfill'); ~~~ ### Stage说明 Babel的转换级别依据 ES7不同阶段的语法提案设置了4个阶段:stage-0 、stage-1、stage-2、stage-3,使用时选装一个。(0的级别最高,包含的转码插件最多,往后越来越少) #### stage-0 提供ES7的支持 包含 stage-1、stage-2、stage-3 的内容 特有的插件 * transform-do-expressions * transform-function-bind transform-do-expressions 支持 模板中使用if else ~~~ <div className="parents"> { do { if(color == 'blue') { <BlueComponent/>; }else if(color == 'red') { <RedComponent/>; }else { <GreenComponent/>; } } } } </div> ~~~ transform-function-bind 绑定上下文 ~~~ // 语法 obj::func // func.bind(obj) obj::func(val) // func.call(obj, val) ::obj.func(val) // func.call(obj, val) // 基本用法 const box = { weight: 2, getWeight() { return this.weight; }, }; const { getWeight } = box; console.log(box.getWeight()); // prints '2' const bigBox = { weight: 10 }; console.log(bigBox::getWeight()); // prints '10' // Can be chained: function add(val) { return this + val; } console.log(bigBox::getWeight()::add(5)); // prints '15' // 处理Nodelist(Array Like 类型) const { map, filter } = Array.prototype; let sslUrls = document.querySelectorAll('a') ::map(node => node.href) ::filter(href => href.substring(0, 5) === 'https'); console.log(sslUrls); // is equivalent to const { map, filter } = Array.prototype; let sslUrls = document.querySelectorAll('a'); sslUrls = map.call(sslUrls, node => node.href); sslUrls = filter.call(sslUrls, href => href.substring(0, 5) === 'https'); console.log(sslUrls); //绑定自身 $('.some-link').on('click', ::view.reset); // is equivalent to: $('.some-link').on('click', view.reset.bind(view)); ~~~ #### stage-1 包含 stage-2 、stage-3 提供对 类的静态、实例属性和方法的支持 以及 对模块导入方式的扩展 特有插件 * transform-class-properties * transform-export-extensions transform-class-properties 支持 ~~~ class Bork { //Property initializer syntax instanceProperty = "bork"; boundFunction = () => { return this.instanceProperty; } //Static class properties static staticProperty = "babelIsCool"; static staticFunction = function() { return Bork.staticProperty; } } ~~~ transform-export-extensions 扩展export导出方式 ~~~ export * as ns from 'mod'; export v from 'mod'; ~~~ #### stage-2 包含 stage-3 提供尾逗号函数功能 及 支持 Object 使用延展符展开 特有插件 * syntax-trailing-function-commas * transform-object-reset-spread syntax-trailing-function-commas 添加行尾逗号,减少文件的改动 ~~~ function clownPuppiesEverywhere( param1, param2, ) { /* ... */ } clownPuppiesEverywhere( 'foo', 'bar', ); ~~~ transform-object-reset-spread 支持使用展开符展开对象 ~~~ let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 }; console.log(x); // 1 console.log(y); // 2 console.log(z); // { a: 3, b: 4 } // 属性展开 let n = { x, y, ...z }; console.log(n); // { x: 1, y: 2, a: 3, b: 4 } ~~~ #### stage-3 提供对ES7的async和await的支持 及提供幂操作的语法糖 特有插件 * transform-async-to-generator * transform-exponentiation-operator transform-async-to-generator 转码ES7的 async与await ~~~ //in async function* agf() { await 1; yield 2; } //out var _asyncGenerator = ... let agf = (() => { var _ref = _asyncGenerator.wrap(function* () { yield _asyncGenerator.await(1); yield 2; }); return function agf() { return _ref.apply(this, arguments); }; })(); ~~~ transform-exponentiation-operator 支持幂运算语法糖 ~~~ // x ** y let squared = 2 ** 2; // 相当于: 2 * 2 let cubed = 2 ** 3; // 相当于: 2 * 2 * 2 ~~~ 为了防止某些实验中的标准在未来不能定案,一般使用 stage-1 或 stage-2 来进行转码。