欢迎来到 黑吧安全网 聚焦网络安全前沿资讯,精华内容,交流技术心得!

无处不在的Babel

来源:本站整理 作者:佚名 时间:2017-04-18 TAG: 我要投稿

海子在《面朝大海,春暖花开》中写到:“从明天起,做一个幸福的人”,Babel在介绍自己时说到:“从今天起,做一个幸福的人,使用下一代javascript”。
JavaScript学名叫ECMAScript,主要版本有2000年发布的ES3,2010年发布的ES5,以及2015年发布的ES2015。其中,ES2015相对于ES5简直是翻天覆地的变化,大量新特性极好地解决了开发人员面临的问题。然而市场份额占比极高的Internet Explorer 9发布于2011年,对ECMAScript的支持只到第5版。
难道为了兼容低版本浏览器用户而就此放弃?不!在新达达,我们拥抱社区,跟随潮流,始终走在技术前沿。有了Babel,使用下一代JavaScript成为可能。如今,从手机应用到桌面网站,再到后端服务,Babel的身影无处不在。但通往幸福的路上,我们遇到不少困难。随着项目复杂度增加,文件打包体积变大,程序运行性能变差。这个问题在高性能设备上还好,在低端安卓手机上表现尤为明显。
困难
以IE9为例,它对ECMAScript的支持只到第5版,为了能使用ES2015,我们需要将ES2015转译成ES5,这样才能被IE9正确执行。在这个过程中,有些特性能用ES5直接实现,比如箭头函数:
// ES2015const square = n => n * n// ES5"use strict";var square = functionsquare(n){return n * n;};
但有些特性则不行,比如创建类:
// ES2015classPerson{}// ES5"use strict";function_classCallCheck(instance, Constructor){ if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }var Person = functionPerson(){_classCallCheck(this, Person);};
类似 _classCallCheck 的函数叫helper,每当遇到创建类这种无法直接实现的特性时,Babel都会在该文件内嵌入helper,这导致整个项目包含大量重复代码。对于这个问题,大多数人推荐的做法是引入 babel-polyfill ,对已有执行环境进行扩展,但这样做一会导致文件打包体积变大,二会导致特性检测失败。另一种做法是使用 transform-runtime 插件,改变Babel默认行为,将文件内嵌入helper改为引入外部模块,这样整个项目只存在一份helper的代码,文件打包体积变大的问题迎刃而解。由于是引入外部模块而非扩展执行环境,特性检测失败的问题也就不复存在。
一切看起来都很美好,但新的问题出现了。在使用Babel转译代码时,通常只对项目源码进行处理,第三方模块保持不变。当第三方模块依赖一个ES2015特性时,由于不被处理,自然无法执行。这时需要引入额外的模块,比如GitHub出品的 whatwg-fetch :
import 'es6-promise';import 'whatwg-fetch';
babel-presets-es2015 本身包含Promise,现在为了保证 whatwg-fetch 正常执行而重复引入,有没有什么办法能够避免?
除此之外,还有一个问题比较隐蔽。在使用ES2015语法引入模块时,通常会这样写:
import { find } from 'lodash'
看似没有问题,实际上问题很大。上面的代码经Babel转译后:
// ES2015import { find } from 'lodash'// ES5'use strict';var _lodash = require('lodash');
原本只是想引入1kb的 find 函数,最终却变成引入540kb的 lodash ,完全无法接受。类似的问题还存在其它地方,怎样解决?
基础
所有这些问题的解决都离不开对Babel的深入理解,就从Babel的安装、配置开始,逐步深入到 plugins 和 presets 的区别,源码转译顺序, ECMAScript Modules 和 CommonJS 的区别等问题上。
安装
Babel的安装主要针对浏览器和Node.js两种环境,构建方式主要是命令行和构建工具两种,这里只关注浏览器环境下基于Webpack的构建方式。新建项目,并执行以下命令:
$ npm i -D webpack babel-loader babel-core
其中 babel-loader 的作用是让Webpack能够通过Babel转译JavaScript,而 babel-core 才是真正负责JavaScript转译的工具。
安装完后,对Webpack进行如下配置,就能自动转译 main.js 及其引入的其它模块:
module.exports = {entry: './main.js',output: {filename: 'bundle.js'},module: {loaders: [{test: /\.js$/,loader: 'babel-loader',exclude: /node_modules/}]}}配置
有了上述配置,Babel就能自动转译,但如何转译特定的语言特性仍需配置。通常会将Babel的配置保存为单独的 .babelrc 文件,该文件的内容为JSON格式,主要包含 presets 和 plugins 两部分。
{"presets": [],"plugins": []}Plugins vs. Presets
简单来说, plugins 是包含规则的函数,在转译时用来处理输入内容。通常以 babel-plugin- 作为前缀,因此在书写时可以省略:
$ npm i -D babel-plugin-transform-es2015-arrow-functions{"presets": [],"plugins": ["transform-es2015-arrow-functions"]}
而 presets 则是 plugins 的合集,省却一个个安装的繁琐过程,比如 babel-preset-es2015 包含所有ES2015的语言特性:
$ npm i -D babel-preset-es2015{"presets": ["es2015"],"plugins": ["transform-es2015-arrow-functions"]}转译顺序
当 presets 和 plugins 同时存在时,按照什么顺序进行转译?Babel官方文档提及如下规则:
plugins 先于 presets 执行plugins 的执行顺序是从头到尾presets 的执行顺序和 plugins 相反
假定配置如下:
{"presets": ["es2015"],"plugins": ["transform-async-to-generator"]}
源码如下:
const beautify = data => JSON.stringify(data, null, 2)async functionfetchUserDetail(userID){return await fetch(`/user/${userID}`)}
第一步:
let fetchUserDetail = (() => {var _ref = _asyncToGenerator(function* (userID){return yield fetch(`/user/${ userID }`);});return functionfetchUserDetail(_x){return _ref.apply(this, arguments);};})();function_asyncToGenerator(fn){...}const beautify = data => JSON.stringify(data, null, 2);

[1] [2]  下一页

【声明】:黑吧安全网(http://www.myhack58.com)登载此文出于传递更多信息之目的,并不代表本站赞同其观点和对其真实性负责,仅适于网络安全技术爱好者学习研究使用,学习中请遵循国家相关法律法规。如有问题请联系我们,联系邮箱admin@myhack58.com,我们会在最短的时间内进行处理。
  • 最新更新
    • 相关阅读
      • 本类热门
        • 最近下载