何在前端JavaScript开发中使用ES新特性?babel是什么?.babelrc文件又是干什么用的?文件中的presets和plugins又是什么鬼?本文将解答上述疑问。
babel是一个JavaScript编译器
ES规范今年来更新较为频繁,近几个版本推出很多新的特性,而用户的浏览器版本众多,很多用户用的是老版本浏览器,老版本浏览器不支持ES新特(nodejs也存在同样问题,参见),babel就是用来让你可以使用ES新特性,又可以使代码运行在老版本浏览器上。
Babel 通过语法转换器支持最新版本的 JavaScript 。 语法转换插件允许你立刻使用新语法,无需等待浏览器支持。
解析-->转换-->生成代码
1.在构建工具中(以webpack为例)使用:
// 在webpack.config.js中增加babel-loader,如下所示 module: { rules: [ { test: /\.js$/, exclude: /node_modules/, loader: "babel-loader" } ] }
2.在babel-cli中使用
npm install -g babel-cli babel src -d lib // 将src中的代码转译并将生成的文件放到lib目录下 复制代码
babel的配置是要告诉babel工具使用哪些插件转译代码,主要作用于babel工作的第二阶段(转译)
babel工作前会从.babelrc文件读取配置,根据配置对JavaScript进行转译。
下面以.babelrc文件的配置方式为例介绍,配置项主要有两个,presets,plugins;
{ "presets": ["env"], "plugins": ["babel-plugin-transform-object-assign"] }
plugins配置具体插件,而对于ES新特性需要配置的插件多达几十个,如果一一配置就太麻烦来,于是babel给我们提供一些插件集,插件集配置在presets配置项中,目前官方提供的插件集有:
其中 env(babel-preset-env)相当于 es2015 ,es2016 ,es2017 及最新版本。
4. stage-X
Stage-x preset 中的任何转换都是对未被批准为 JavaScript 版本一部分的语言的变化(如 es6 / es2015 ),其分为以下5各阶段:
Stage 0 - 稻草人: 只是一个想法,可能是 babel 插件。 Stage 1 - 提案: 初步尝试。 Stage 2 - 初稿: 完成初步规范。 Stage 3 - 候选: 完成规范和浏览器初步实现。 Stage 4 - 完成: 将被添加到下一年度发布。
上述配置的插件和插件集也是需要通过npm安装的
npm install babel-preset-env --save-dev npm install babel-plugin-transform-object-assign --save-dev
babel presets(转换插件集)配置
"presets": ["env"] // 默认将运行所有transfrom和集成所有的polyfill
也可以仅仅配置项目所支持浏览器所需的polyfill和transform。只编译所需的代码会使你的代码包更小。如下所示,该转换只支持每个浏览器最后两个版本和safari大于等于7的版本所需的polyfill和代码转换。
、前言
虽然在 JavaScript 中对象无处不在,但这门语言并不使用经典的基于类的继承方式,而是依赖原型,至少在 ES6 之前是这样的。当时,假设我们要定义一个可以设置 id 与坐标的类,我们会这样写:
// Shape 类 function Shape(id, x, y) { this.id = id; this.setLocation(x, y); } // 设置坐标的原型方法 Shape.prototype.setLocation = function(x, y) { this.x = x; this.y = y; };
上面是类定义,下面是用于设置坐标的原型方法。从 ECMAScript 2015 开始,语法糖 class被引入,开发者可以通过 class 关键字来定义类。我们可以直接定义类、在类中写静态方法或继承类等。上例便可改写为:
class Shape { constructor(id, x, y) { // 构造函数语法糖 this.id = id; this.setLocation(x, y); } setLocation(x, y) { // 原型方法 this.x = x; this.y = y; } }
一个更符合“传统语言”的写法。语法糖写法的优势在于当类中充满各类静态方法与继承关系时,class 这种对象模版写法的简洁性会更加突出,且不易出错。但不可否认时至今日,我们还需要为某些用户兼容我们的 ES6+ 代码,class 就是 TodoList 上的一项:
作为当下最流行的 JavaScript 编译器,Babel 替我们转译 ECMAScript 语法,而我们不用再担心如何进行向后兼容。
本地安装 Babel 或者利用 Babel CLI 工具,看看我们的 Shape 类会有哪些变化。可惜的是,你会发现代码体积由现在的219字节激增到2.1KB,即便算上代码压缩(未混淆)代码也有1.1KB。转译后输出的代码长这样:
"use strict";var _createClass=function(){function a(a,b){for(var c,d=0;d<b.length;d++)c=b[d],c.enumerable=c.enumerable||!1,c.configurable=!0,"value"in c&&(c.writable=!0),Object.defineProperty(a,c.key,c)}return function(b,c,d){return c&&a(b.prototype,c),d&&a(b,d),b}}();function _classCallCheck(a,b){if(!(a instanceof b))throw new TypeError("Cannot call a class as a function")}var Shape=function(){function a(b,c,d){_classCallCheck(this,a),this.id=b,this.setLocation(c,d)}return _createClass(a,[{key:"setLocation",value:function c(a,b){this.x=a,this.y=b}}]),a}();
Babel 仅仅是把我们定义的 Shape 还原成一个 ES5 函数与对应的原型方法么?
一、揭秘
好像没那么简单,为了摸清实际转译流程,我们先将上述类定义代码简化为一个只有14字节的空类:
class Shape {}
首先,当访问器走到类声明阶段,需要补充严格模式:
"use strict"; class Shape {}
而进入变量声明与标识符阶段时则需补充 let 关键字并转为 var:
"use strict"; var Shape = class Shape {};
到这个时候代码的变化都不太大。接下来是进入函数表达式阶段,多出来几行函数:
"use strict"; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var Shape = function Shape() { _classCallCheck(this, Shape); };
该阶段不仅替换了 class,还在类中调用了叫做 _classCallCheck 的方法。这是什么呢?
这个函数的作用在于确保构造方法永远不会作为函数被调用,它会评估函数的上下文是否为 Shape 对象的实例,以此确定是否需要抛出异常。接下来,则轮到 babel-plugin-minify-simplify上场,这个插件做的事情在于通过简化语句为表达式、并使表达式尽可能统一来精简代码。运行后的输出是这样的:
"use strict"; function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) throw new TypeError("Cannot call a class as a function"); } var Shape = function Shape() { _classCallCheck(this, Shape); };
可以看到 if 语句中由于只有一行代码,于是花括号被去掉。接下来上场的便是内置的 Block Hoist ,该插件通过遍历参数排序然后替换,Babel 输出结果为:
"use strict"; function _classCallCheck(a, b) { if (!(a instanceof b)) throw new TypeError("Cannot call a class as a function"); } var Shape = function a() { _classCallCheck(this, a); };
最后一步,minify 一下,代码体积由最初的14字节增为338字节:
"use strict";function _classCallCheck(a,b){if(!(a instanceof b))throw new TypeError("Cannot call a class as a function")}var Shape=function a(){_classCallCheck(this,a)};
二、再说一些
这是一个什么都没干的类声明,但现实中任何类都会有自己的方法,而此时 Babel 必定会引入更多的插件来帮助它完成代码的转译工作。直接在刚刚的空类中定义一个方法吧。
class Shape { render() { console.log("Hi"); } }
我们用 Babel 转译一下,会发现代码中包含如下这段:
var _createClass = function () { function a(a, b) { for (var c, d = 0; d < b.length; d++) c = b[d], c.enumerable = c.enumerable || !1, c.configurable = !0, "value" in c && (c.writable = !0), Object.defineProperty(a, c.key, c); } return function (b, c, d) { return c && a(b.prototype, c), d && a(b, d), b; }; }();
类似前面我们遇到的 _classCallCheck ,这里又多出一个 _createClass ,这是做什么的呢?我们稍微把代码状态往前挪一挪,来到 babel-plugin-minify-builtins 处理阶段(该插件的作用在于缩减内置对象代码体积,但我们主要关注点在于这个阶段的 _createClass 函数是基本可读的),此时 _classCallCheck 长成这样:
var _createClass = function() { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function(Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; } ();
可以看出 _createClass 用于处理创建对象属性,函数支持传入构造函数与需定义的键值对属性数组。函数判断传入的参数(普通方法/静态方法)是否为空对应到不同的处理流程上。而 defineProperties 方法做的事情便是遍历传入的属性数组,然后分别调用 Object.defineProperty 以更新构造函数。而在 Shape 中,由于我们定义的不是静态方法,我们便这样调用:
_createClass(Shape, [{ key: "render", value: function render() { console.log("Hi"); } }]);
T.J. Crowder 在 How does Babel.js create compile a class declaration into ES2015? 中谈到 Babel 是如何将 class 转化为 ES5 兼容代码时谈到了几点,大意为:
这些概述大致总结了类定义在两个 ES 版本中的一些差异,其他很多方面比如 extends ——继承关键字,它的使用则会使 Babel 在转译结果加上 _inherits 与 _possibleConstructorReturn两个函数。篇幅所限,此处不再展开详述。
三、最后
语法糖 class 给我们带来了很多写法上的便利,但可能会使我们在代码体积上的优化努力“付诸东流”。
另一方面,如果你是一名 React 应用开发者,你是否已经在想将代码中的所有 class 写法换为 function 呢?那样做的话,代码体积无疑会减少很多,但你一定也知道 PureComponent 相比 Component 的好处。所以虽然 function 给你的代码体积减负了,但他在哪里又给你无形增加负担了呢?
因此,真的不推荐开发者用 class 这种写法么,你觉得呢?
融界2024年3月30日消息,据国家知识产权局公告,上海联影医疗科技股份有限公司申请一项名为“网页转译方法、装置、应用商店系统、设备和介质“,公开号CN117786256A,申请日期为2023年12月。
专利摘要显示,本申请涉及一种网页转译方法、装置、应用商店系统、设备和介质,包括:获取html页面数据;其中,html页面数据包含文本节点的样式标签、图片存储路径、文本和图片的分组信息;根据文本和图片的分组信息,图形在用户界面的窗体中生成容器控件;根据文本节点的样式标签,生成文本样式列表,并将文本样式列表和相应文本加入对应容器控件;根据图片存储路径获取图片,并将图片加入对应容器控件;执行容器控件,并在图形用户界面的窗体中以WPF格式呈现容器控件的执行结果。通过在图形用户界面的窗体生成容器控件,使WPF端中文本和图像的位置准确且格式一致,解决了网页端内容转换为WPF格式后,WPF端呈现内容还原度低的问题。
本文源自金融界
*请认真填写需求信息,我们会在24小时内与您取得联系。