面对前端六年历史代码,如何接入并应用ES6解放开发效率

很荣幸有机会和大家分享自己在前端工作中的一些经验。更高兴能邀请我的同事颜海镜同我一起做这件事情。其实经验说不上,只是希望能更多的和大家一起交流、学习。

为什么要讲“面对前端六年历史代码,如何接入并应用ES6解放开发效率”这个主题呢?其实相信很多人认为ES6已经不再新鲜。在前端迭代迅速的今天,会不会有些“老生常谈”?我理解并不是这样的,因为很多人其实对ES6的理解主要集中在新特性、语言用法等层面上。这些内容是大部分学习者都能通过共享得到的。但是,对于ES6往往缺少了在实际大型工程中的接入和应用。尤其维护开发PV亿级以上的产品,并不是每个人都有机会的。

所以,相比于ES6语言本身,我更希望能介绍一些工程上、设计上的想法,以及接入ES6过程中不为人知,但又至关重要的的“细枝末节”上。同时,希望想了解BAT公司技术项目流程从立项到落地的读者、参与者能有所收获。

全篇文章分为五个部分:

  • 对前端发展的态度和看法。

  • 大型互联网公司对新技术的前期调研和评估。

  • 我们面临的历史背景。

  • 当我们说调研时到底在说什么。

  • 为什么非要折腾ES6。

  • 正确解锁ES6开发姿势。

  • 使用Babel进行ES6编译。

  • 传说中的“最佳实践”。

  • 一个设计实例。

  • ES6带来的困扰和展望。

篇幅并不短,其中还有一些ES6“黑魔法”和Babel编译分析,以及兼容性处理等内容。

第一部分:对前端发展的态度和看法

借用查尔斯·狄更斯在《双城记》中的不朽开篇来形容如今的前端开发,我觉得再合适不过了:

这是最好的时代,这是最坏的时代,这是智慧的时代,这是愚蠢的时代;这是信仰的时期,这是怀疑的时期;这是光明的季节,这是黑暗的季节;这是希望之春,这是失望之冬;人们面前有着各样事物,人们面前一无所有;人们正在直登天堂;人们正在直下地狱。

没错,我们脱离了之前“刀耕火种”的“脚本玩具”时期。伴随着nodeJS的强势崛起,社区交流的如火如荼,模块化开发的如虎添翼,HTML5的攻城掠地,彻底迎来了 前端“工业革命”的爆发。

同时,这也意味着大量的技术更迭。即便没有“南朝四百八十寺,多少楼台烟雨中”那般夸张,也足以让各阶段前端开发者疲于奔命,应接不暇。举个例子,想想我们也许刚熟悉了CommonJS,又要去了解AMD、CMD,稍不留神,就在2017年5月这个初夏:ES6 module要开始在浏览器端实现了!

好吧,也正好以此“ES6发展的标志性事件”来为这次分享拉开序幕,我们今天就要谈谈:ES6在大型项目中的接入和发展的方方面面。所谓“沉舟侧畔千帆过,病树前头万木春”,古诗中以“沉舟”、“病树”比喻纷扰和困惑,但却并不尤怨,反而表现的是一种对世事变迁和潮起潮落的豁达开朗。同样我们认为ES6代表了未来,对未来理应拥抱。

全文看下来,也许你会理解“所有的发展都是站在历史的基础上”,停止不前的“沉舟”也有指引千帆航向的意义。“合抱之木,生于毫末;九层之台,起于垒土”。技术更迭中,深厚的基础是多么重要。

这次分享,我们不会去把时间浪费在ES6新特性的讲解和语法细节层面上,这些内容毕竟都可以轻松且“免费”地找到。比如阮一峰老师的书中讲解就很透彻了。
我们会把重点放在ES6工程接入和开发维护上,背靠大流量的产品,这些不是所有人都能接触到的。

第二部分:新技术的前期调研和评估

我们面临的历史背景

先从背景说起,我们负责的项目是百度知识搜索部某明星产品,该产品代码历史在6年以上。在很多大型互联网公司里,这种“历史负担”其实屡见不鲜。也就不奇怪为什么知乎上会有人质疑:“QQ空间的前端技术水平如何?”,“为什么很牛的互联网公司代码却不能看?”等等。

在我们这边,历史问题主要集中在以下几点:

  • 使用古老Tangram类库,开发体验不友好。

  • 构建工具以FIS为主,但是版本不统一。

  • 模块设计不合理,内外耦合严重。

  • JS,需要兼容IE6+。

这些问题都会对ES6接入,造成一些潜在障碍。这就需要对新技术进行更加合理的评估和调研。

当我们说调研时到底在说什么?

也许会有一些读者认为“这有什么好调研评估的,不就是新的特性学习吗?”,其实在大型工程中这样的想法是片面的。

首先,对于新特性的熟悉,当然是最基本的。

此外,对于保证PV过亿的大型线上产品,就要求对ES6的方方面面面要足够了解。会一些let,const,箭头函数,模块化等语言层面知识还是不够的。

这就说明,在新技术前期调研工作当中,新特性、新语法的学习仅仅是很小的一方面。同样重要的是执行环境保障、生产配置、线下开发流程、线上bug跟踪等各环节内容。

比如,这个项目的前期调研就包括但不限于:

  • 如何兼容旧版本浏览器(IE6+)?

  • 编译器/转换器是否真能摆平一切,应用是否完全可靠?

  • 编译器/转换器面临版本更新怎么办?

  • 编译器/转换器的接入对于现有的代码是否有影响?

  • 编译器/转换器的编译结果对于现有的代码是否有影响,能否完全兼容?

  • 引入ES6后开发效率是否真的可以提升?

  • 就算开发效率确实提升了,上线的代码量是不是更大了?对于产品性能是否有影响?

  • 所有可能产生的负面影响如何回滚?谁来担责?

  • ES6现在处于什么阶段,是否会被废除,就像第四版本一样?

  • 对于ECMAScript语言标准的提案分为哪几个阶段?

等等一切可能影响产品稳定或存在潜在Bug的问题……

为什么非要折腾ES6?

这个问题其实就是“如何评估ES6?”,“ES6的接入能带来哪些收益”。或者更直白一些:“你靠什么来说服技术经理,分配给你时间、人力去搞ES6?”毕竟大公司里的资源申(争)请(夺),都要拿收益来说话。

这就需要以自己所负责的业务为背景,在充分调研的基础上做出合理评估。

最终我们认为从以下几个角度来看,ES6的推广势在必行:

  • 解放开发效率。

    • 新特性的合理使用,优雅而简洁。

    • 减少第三方库的依赖。

    • 可维护性提升,代码量减少。

  • 面向未来。

    • 向标准靠拢。

    • 官方支持。

    • “迟早要学”。

  • 其他方面。

    • 提升技术先进性。

    • 促进技术交流,提高技术氛围。

    • “编程激情”。

    • 整合部分历史代码的好机会。

    • 面试中的加分项。

以上是出于我们自身产品开发的角度。同时,整个前端在ES6发展环境和普及率上,我们参考了ponyfoo.com在2015年底做的一个知名调查:JavaScript Developer Survey Results,该调查以5千多个前端开发者为背景,得出以下结论:

  • ES6普及率?

面对前端六年历史代码,如何接入并应用ES6解放开发效率

  • ES6是否是一个很重大的版本进步?

面对前端六年历史代码,如何接入并应用ES6解放开发效率

  • 你都使用哪些ES6新特性?

面对前端六年历史代码,如何接入并应用ES6解放开发效率

所以,不管是因为大势所趋还是从自身收益出发,我们决定了ES6接入作为该年度最大的技术项目之一。

第三部分:正确解锁ES6开发姿势

使用Babel进行ES6编译

目前各大浏览器和开发环境对ES6的支持情况参差不齐,我们的产品对浏览器兼容性要求又比较高。所以,当然不能荒谬地“裸写”ES6代码,发布上线。因此,在实际项目开发中,需要降级为ES5语法以兼容各平台。

幸好有几款工具可以将ES6语法转换成ES5,让我们在使用ES6新特性编写代码的同时,不需要考虑具体的兼容性情况。比较知名的两款编译器为:

  • Babel

  • Traceur

我们选择了Babel 5.x版本,主要是因为以下几个原因:

  • Babel对ES6的支持程度比其它同类更高或相当。

  • Babel拥有完善的文档和较好体验的在线编译环境。

  • Babel使用广泛,用户基础好。

关于第一点原因的主要数据支持可以在Bebel官网,我们可以看到不同版本Babel对ES6跟进和支持的情况;

另外,关于在线编译平台,可以访问官网:进行体验,这对于研究Babel编译结果十分方便。

关于Babel的接入和使用方法,社区上的资料很多,这里不再进行科普浪费时间了。以下,从几个关键性的工程问题进行延伸。

配合构建工具

首先,因为我们使用的是百度自己的FIS来做前端构建工具,所以只需要在FIS的配置文件中加入依赖,并安装插件就可直接使用。这一切,就像社区上使用更多的webpack一样。

babel-polyfill

同样需要说明的是,Babel默认只转换新的JavaScript语法(syntax),而不转换新的API。

比如:Babel可以编译let, const等特性,但是诸如Iterator、Generator、Reflect、Promise等全局对象,或者数组实例的find这些新的方法并不会得到编译。如果想让这个方法运行,必须使用babel-polyfill,同时要保证这个polyfill在你的所有其他脚本之前就要加载执行。同时,因为编译产出为ES5代码,所以又要处在ES5垫片ES5-shim,ES5-sham之后。

实际情况中,我们放弃了使用babel-polyfill,这是出于减少JS引用的考虑。我们页面已经加载很多JS了,并且babel-polyfill由于其特殊性(抢先执行),难以和其他业务脚本打包。再者,我们认为ES6新增的这些方法的必要性并不绝对。就像上图统计的那样,ES6新特性被广泛使用的大多是let, const, 解构,箭头函数等,这些使用默认Babel编译就已经可以达到要求了。

当然,Promise这个使用广泛的特性我们专门引入了单独的polyfill来处理。这样的定制化完全可以满足需求。

babel-runtime

babel-runtime是为了减少重复代码而生的。Babel编译生成的代码,可能会用到一些_extend(),classCallCheck()之类的工具函数(后文在分析编译结果部分会有介绍)。默认情况下,这些工具函数的代码会被引入在编译后的文件中。如果存在多个文件,那每个文件都有可能含有一份重复工具函数的代码。

这种冗余一定是我们不能忍的。

babel-runtime插件能够将这些工具函数的代码转换成require语句,指向为对babel-runtime的引用,如 :


require('babel-runtime/helpers/classCallCheck');

这样,classCallCheck这个工具函数的代码就不需要在每个文件中都存在了。当然,最终你需要利用webpack之类的打包工具,将runtime代码打包到目标文件中。

但是要注意,这是Babel 6版本才引入的,对我们来说,这就面临一个关于“Babel版本升级部署”的问题。

关于这个插件的更多介绍,同样可以在官方网站中找到。

Babel的部署和升级

在真正部署Babel的前前后后,我和我的同事针对每一个ES6特性的编译稳定性都进行了严密的测试。测试包括了验证黑盒输出情况和不同浏览器的支持情况,以确保上线后的万无一失。

另一方面,我们在使用的Babel版本就如上所说,为5.x,当然Babel在社区的蓬勃发展和自身定位的调整,使得自身版本更新换代也非常频繁。同时,随着越来越多的库升级至babel6,将我们的项目升级至babel6似乎也有必要。这样的升级工作想想也确实头疼,尤其是要保证线上代码的稳定运行。

截止目前为止,我们还未对Babel进行升级,因为这个需求还并不迫在眉睫。但是,着眼于未来还是很有必要的。我们及时关注了Babel 6.x版本带来的新变化。这方面对于大家的建议其实只有一个,就是紧盯官网+快速调研。点击这里会把大家链接到Babel官方博客,里面同步了每一次更新的细枝末节,内容非常详尽。

同时,你可能会问,那我们就保持初始版本不去升级,岂不是一劳永逸了吗?

当然不是这样,我认为,每一个版本的迭代和演进自然有其原因。如果一直固守成规,不管是在代码组织上和工程化上都会吃亏。除了刚才提到的babel-runtime插件,新版本的Babel(5.x-6.x)收益还体现在:

  • 性能提升:据说compile速度提升20%。

  • 可配置的插件:更强的灵活性,以及更简单的插件API.

  • 更简洁的配置。

选择编译和其他

在进行ES6编译的同时,对于大量的历史代码文件,我们不会进行ES6的翻新重写。这些历史代码因此就不需要使用Babel进行编译。为此,我们使用了文件后缀名来进行区分,并在构建工具的配置文件中进行正则匹配,达到选择性编译的效果。最终的规范是,ES6代码统一以.es为后缀名。

最后,Babel社区的蓬勃发展,导致“你以为的Babel”其实已经不再是那个Babel了;同时,Babel知识的广泛性远远超乎了很多人的想象,比如Babel编译的loose模式、normal模式;比如Babel依赖的引擎babylon;比如babylon fork的acorn;比如Babel将源码转换AST的理解等等。很多东西其实我研究的还只是皮毛,但是不到浏览器广泛支持ES6的一天,不到摆脱兼容性需求的一天,恐怕我们是脱离不开Babel了。

传说中的“最佳实践”

在ES6大量的新特性中,我们推荐并有广泛应用的包括但不限于:

  • 默认参数

  • 模版表达式

  • 多行字符串

  • 解构赋值

  • 改进的对象表达式

  • 箭头函数 =>

  • Promise

  • 块级作用域的let和const

  • 模块化

当然还有很多优秀的新特性,但是在应用中频率相对较少,不再一一列出。

我认为,一切所谓的最佳实践都要依赖基础。在抛出几个“奇技异巧“之前,我想从一个简单的例子说起。

const例子:

举一个简单的例子(出自阮一峰ES6一书),可能大家都了解const声明一个只读的常量。一旦声明,常量的值就不能改变。

const a = 4;
a; // 4
a = 3;
// TypeError: Assignment to constant variable.

为此,我们可以延伸出:const声明的变量不得改变值,这意味着,const一旦声明变量,就必须立即初始化,不能留到以后赋值。

const b;
// SyntaxError: Missing initializer in const declaration

同时,我们还要注意:const的作用域与let命令相同:只在声明所在的块级作用域内有效。

因而,它也不存在常量提升的概念。

但是,还需要了解的是:

const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。

对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。

但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指针,const只能保证这个指针是固定的,至于它指向的数据结构是不是可变的,就完全不能控制了。

const foo = {};

// 为 foo 添加一个属性,可以成功
foo.prop = 123;
foo.prop // 123

// 将 foo 指向另一个对象,就会报错
foo = {}; // TypeError: "foo" is read-only

上面代码中,常量foo储存的是一个地址,这个地址指向一个对象。不可变的只是这个地址,即不能把foo指向另一个地址,但对象本身是可变的,所以依然可以为其添加新属性。

所以,仅仅就是一个声明常量的const,里边牵扯出的基础内容却很多。这就需要在掌握ES6基本用法的同时,需要有更强大的基础概念才能进一步提升理解。

这里,给大家留一个思考题目: 如何真的讲一个对象冻结?

ES6黑魔法:

其实我想大家对ES6特性越来越熟悉,以及社区的大力宣传,一些ES6黑魔法已经“非常平常”了。

比如,扩展运算符结合解构赋值,除了“你想象的那种用法”外,它还可以优雅完成:

  • 合并数组

arr1.push(...arr2); // 把arr2合并到arr1之后
arr1.unshift(...arr2); // 把arr2合并到arr1之前
let arr2 = [1, 2, ...arr1, 4]; // 数组内合并数组

复制数组

let arr2 = [...arr1]; // 相当于arr1.slice()

把伪数组转为数组

[...document.querySelectorAll('div')]

交换两个变量值

[a, b] = [b, a]; // 不再需要中间变量

等等。。。

具体可见这里。或者英文好的可以戳这里: Six nifty ES6 tricks

Babel到底编译成了什么?

这是一个很关键的问题。也是正确使用ES6的高难度姿势。

因为我们所有的ES6代码都依赖Babel编译,所以如果你不去了解它的编译产出,那么最后上线的代码都是“心里没底”的。

举例来说,我刚才提到的const,在经过Babel编译后其实一律换成var;

可能你紧接着会问:“那如何保证不变性呢?”,原因就在于如果你在源码中第二次修改const常量的值,babel编译会直接报错。

这是一个比较轻量甚至取巧的例子。接下来, 我们再来看看class+extends的编译情况。

Javascript实现OOP其实一直以来都是热门话题,这些争议性的内容我们不去讨论。先来看看Babel的实现过程。

class Person {
    constructor (){
        this.type = 'person'
    }
}

会被编译为:

var Person = function Person() {
    _classCallCheck(this, Person);
    thistype = 'person';
};

我们看到,还是用了构造函数来完成。同时,上文提到过的_classCallCheck也出现了,他作为工具函数,保障class调用的正确性:

function _classCallCheck(instance, Constructor) { 
    if (!(instance instanceof Constructor)) { 
        throw new TypeError("Cannot call a class as a function"); 
    }
}

关于继承:

class Student extends Person {
    constructor(){
        super()
    }
}

编译结果:

// 实现定义Student构造函数,它是一个自执行函数,接受父类构造函数为参数
var Student = (function(_Person) {
    // 实现对父类原型链属性的继承
    _inherits(Student, _Person);

    // 将会返回这个函数作为完整的Student构造函数
    function Student() {
        // 使用检测
        _classCallCheck(this, Student);  
        // _get的返回值可以先理解为父类构造函数
        _get(Object.getPrototypeOf(Student.prototype), 'constructor', this).call(this);
    }

    return Student;
})(Person);

上面_inherits方法的本质其实就是让Student子类继承Person父类原型链上的方法。它实现原理可以归结为一句话:

Student.prototype = Object.create(Person.prototype);
Object.setPrototypeOf(Student, Person)

所以说到底,还是“构造函数+原型原型链”内容,并且辅助Object.create等ES5功能实现。

我建议大家对于编译源码尝试去进行了解,对于自己的基础也是一种提高。

了解了这些,对于ES6的接入是很有帮助的。试想一下,我们在ES6环境下声明的类,如何在历史代码中(ES5环境下)实现继承呢?

通过对Babel编译结果的研究,我也实现了一个工具函数,用来完成这两种开发环境下类的衔接和过渡。具体代码实现难度不大,可以简要参考:

function inherits(childClass, superClass) {
    childClass.prototype = Object.create(superClass.prototype, {
        constructor: {
            value: subClass,
            enumerable: false,
            writable: true,
            configurable: true
        }
    });
    // 这里注意兼容性,IE11以上才完全支持
    if (Object.setPrototypeOf) {
        Object.setPrototypeOf(childClass, superClass)
    }
    else {
        childClass.__proto__ = superClass;
    }
}

如果您有兴趣,可以看我的系列文章:揭秘babel的魔法之class魔法处理。

打通两种开发环境的任督二脉

这里还是聊聊上面展示inherits工具方法,其实这属于“打通ES5和ES6环境”。同样还是在ES6环境下定义的Person Class,ES6环境代码:

class Person {
    constructor(){
        this.type = 'person'
    }
}

在ES5环境中就可以直接进行引用并继承Person类,ES5环境代码:

funtion Student () {
    ...
}

inherits(Student, Person);

这当然是极其有必要的。想象一下6年代码历史,ES5环境的代码量是多么的庞大,这样我们在维护过程中,便可直接获利于ES6的特性。

同样,对于模块化上,我们也存在两种环境共生的问题:之前的代码我们遵循了commonjs规范,并通过打包工具(FIS部分功能),来保证浏览器端的支持。接入ES6之后,自然也就有了ES6模块化的写法。

那么JS文件内如何兼容这两种模块化写法的表达方式呢?

也很简单,同样依赖于Babel的实现。我们在Babel官网上可以找到关于模块化插件的内容:

面对前端六年历史代码,如何接入并应用ES6解放开发效率

其中有一个es2015_modules_commonjs,就是将ES6 Modules编译转换成commonjs形式的。我们当然选用这种编译方式。

对ES next支持

截止目前,ES7也已经取得了重大进展。很多最新一代的ES特性已经被广大开发者熟知并应用。那么在我们的环境中,对于ES next的支持也自然要跟进。这又回到了Babel的话题,我们当然还是离不开这个神器。

同时,你首先要知道,ES7不同阶段语法提案包括:

  • Stage 0:Strawman: just an idea, possible Babel plugin.

  • Stage 1:Proposal: this is worth working on.

  • Stage 2:Draft: initial spec.

  • Stage 3:Candidate: complete spec and initial browser implementations.

  • Stage 4:Finished: will be added to the next yearly release.

对应的,官方提供以下的规则集来对不同阶段的特性进行编译,你可以根据需要安装:

  • $ npm install —save-dev babel-preset-stage-0

  • $ npm install —save-dev babel-preset-stage-1

  • $ npm install —save-dev babel-preset-stage-2

  • $ npm install —save-dev babel-preset-stage-3

需要注意的是,这些工作应当在初期调研设计时,就要有规划和方案。而不是,今天头脑一热,想应用async/await ES7新特性,再去花费时间进行了解。因为,在公司内成熟的开发体系中,严谨的排期需求与个人私下的学习了解完全是两码事。

这些年踩过的兼容性的坑

我们代码能够兼容到IE6+,接入ES6之后,对于兼容性的保证是个挑战。在实际情况中,我们也踩过这方面的坑。

Babel对于ES6的编译是在ES5之上的,那么想要兼容IE6,自然编译产出的ES5代码是无法满足要求的。为此,解决方式只有提供ES5的polyfill,并保证在所有其他脚步加载之前执行。

我们采用了最出名的ES5-shim+ES5-sham来进行ES5代码的“降级”。期间各种IE版本兼容性的测试,那可谓是“一把鼻涕一把泪”。

同时,这里所指的兼容性也不仅仅是浏览器兼容性。也要考虑到引用社区上第三方组件库、类库的问题,如果这些源代码是基于ES6的,那就要慎重考虑是否符合我们使用的Babel版本,我们是否保证并兼容了Babel插件进行编译等相关性问题。这当然也是不小的工作量。

第四部分:一个设计实例

这个实例充分反映了ES6 class带来的便捷之处。

我们产品当中,一个页面可能存在多处“收藏”组件:点击按钮,对页面进行收藏,成功收藏之后,按钮的状态会变为“已收藏”,再点击不会有响应。

面对前端六年历史代码,如何接入并应用ES6解放开发效率

面对前端六年历史代码,如何接入并应用ES6解放开发效率

这样就出现页面中多处“收藏”组件之间通信问题,点击页面顶部收藏按钮成功收藏之后,页面底部的收藏按钮状态也需要变化,进行同步。

其实实现这个功能很简单,但是历史代码实现方式较为落后,耦合严重。良好的设计和肆意而为的实现差别是巨大的。

在利用ES6设计之后,我们的所有组件(包括收藏组件)都会继承UIBase:

class Widget extends UIBase {
    constructor() {
        super();
        ...
    }
}

而UIBase本身会产生一个全局唯一的UUID,这样使得所有组件都有一个唯一的ID标识。同时,UIBase又继承“EventEmitter”这个pub/sub模式组件:

class UIBase extends EventEmitter{
    constructor() {
        super();
        this.guid = guid();
    }
}

因此,所有的组件也同样拥有了pub/sub模式,既事件发布订阅功能。这就相对完美的解决了组件之间的通信问题。达到了“高内聚、低耦合”的效果。

具体来说,我们的任何组件,当然包括收藏按钮在发起收藏行为时:

widget.fire('favorAction');

同时,其他的收藏组件:

widget.on('favorAction', function () {
    ... // toggle status
})

具体的实现结构如图:

面对前端六年历史代码,如何接入并应用ES6解放开发效率

这样的组件行为在一些先进的MVVM、MVC等框架中可以良好的实现。比如优秀的react框架中,我们可以对组件的state设计并切换。但是,我们的技术栈还停留在传统的操作DOM中,jquery类似类库可以满足我们的业务需求。我认为,所有抛离业务场景和需求的的谈新框架,也是一种“耍流氓”。

第五部分:那些年ES6带来的困扰和展望

不可否认,ES6的接入并不是百利而无一害的,我们要正确客观地看待它。伴随着开发效率提升的同时,它还带来了以下困扰:

  • 额外的编译流程。

  • 编译代码排错追错成本。

  • Babel版本升级是个负担。

  • api转换还需shim.

  • 潜在的bug.

  • 很多特性面向node.js,浏览器端并不实用。

  • 学习成本。

然而,未来已来,接下来我们又该做什么呢?

  • 更大范围的重构。

  • 紧盯ES6实现和ES next发展。

同时,需要指出的是ES6的先进性,还体现在和React框架的配合上。去年,我们团队也接入了React开发栈,ES6+React让我们更加面向未来。

最后,呼应一下本文开篇,谈一下想法和总结。每一名前端开发者可能都会感觉到处在前端发展的历史时刻。面对未来,我们也许正在感受着葡萄牙诗人安德拉德的诗句:

我同样不知道什么是海,

赤脚站在沙滩上,

急切地等待着黎明的到来。

ES6注定载入开发史册,最后也许也难逃被替代的命运,完成承前启后的使命。同样是葡萄牙人的诗句:

大陆,在这里是尽头;大海,在这里才开头(陆止于此,海始于斯)。

在技术的海洋里,这一站,既是一种结束,更是一个开始。

原文链接:http://mp.weixin.qq.com/s/zt-AOOiKjq96QaKFaTTUWg

上一篇:如何正确的阅读源代码?


下一篇:Windows server 2012 新功能试用---- powershell 3.0 进程和服务的操作