上一篇:未来Web应用的前端技术选型畅想

下一篇:十年来感受的前端技术变化

Angular的变革

作为Web前端,你幸福吗?

每隔18个月,前端都要难一倍。

每一年,前端都会冒出更多的概念,更多的框架/库,更多的实践。

2015年,前端有哪些东西转入流行?

  • ES2015,Babel
  • 以React和Vue为代表的前端组件化框架

但是,在这关键的一年里,Angular社区相对来说,有些沉寂了,主要原因是Angular自身处于一个剧烈变革期,1.x已经趋向成熟,难有本质提升,而2.0尚未有正式发布的消息。

2015年被黑得最多的主流前端组件化框架是什么?Angular。

黑得最漂亮的一句:

其实是 java 程序员把他们习惯的那一套『仪式感』带入了前端框架导致的。
——尤雨溪

Angular最近在搞的2.0版本,变更可谓剧烈,几乎完全是个新框架。但如果我们关注过1.x版本的演进,就会发现,它也曾经历过不少变革。我们所能看到的变化,其实都是有伏笔的,而Angular官方也在努力做一些事情,让这两代之间能尽量衔接起来。

每个框架在发展过程中,都经历过一次一次的自我革新,那么,Angular经历了什么?

  • controllerAs (1.2)
  • One-time bindings (1.3)
  • new router (1.4)
  • decorator (1.4)
  • components (1.5)
  • …… (2.0)

这些变更,分别都有怎样的含义呢?

业务模型的纯化

在1.2版本之前,我们这样写一个控制器:

然后这样使用它:

在1.2版本之后,我们有了controllerAs,可以不必注入$scope了:

然后这样使用它:

更进一步,还可以把上面的逻辑代码改造成这样:

经过这样的转变,我们可以发现,原先的“controller”很清晰了,变成了很纯净的视图模型。这给我们带来的好处是,这一层的东西更容易测试和迁移。

使用controllerAs语法还有一个好处,可以做到逻辑代码的跨版本通用性,甚至是跨框架通用性。

性能优化

在Angular 1.x的整个发展过程中,一直有人在质疑它的性能,为此,开发组也进行了大量的优化。

因为1.x采用的是脏检查的方式来判断数据变更,所以,如何提升变动项的查找会是一件比较重要的事。从1.0到1.4,几乎每个版本都在这个方面作了一些提升,尽可能压榨出更高的性能来。

Angular也添加了诸如单次绑定之类的特性,以减少对初次加载,但不再变更的变量的追踪。

业界有不少类似的框架使用的是存取器做数据变更观测,Angular2使用zone.js来观测数据变更。

脏检查的原理是:我们所有的对数据的赋值,都是在某些特性场景下触发的,比如:

  • UI事件
  • 网络事件
  • 定时器

如果在每次操作之后,对数据保留一份复制,然后下一次再有事件发生的时候,把新老数据进行比对,就可以判定哪些数据产生了变更,从而可以更新关联的界面。

而zone.js更像是一种“多线程”的技术。它把数据的变更过程利用worker切换出去,等执行完了再更新回来,这样就不会阻塞主线程,这是一个非常有创意的做法,因此,Angular2的渲染性能是比较好的。

这种理念在Web开发中前所未有,但是其实在其他一些客户端领域早有实践。

组件化的开发理念

在Angular 1.4之前版本中,并未刻意强调组件化的理念,业务开发人员拥有较高的自由度,比如说,可以选择使用directive,用自定义元素、自定义属性的方式来实现一定程度的组件化,也可以直接使用ng-include和路由,以比较松散的方式完成业务功能。

但是在1.5版本中,新的组件注册语法诞生了,这就是components。

这样使用:

在Angular 2中,组件化更是变成了一种强制的理念。一个组件包含以下部分:

  • 模板
  • 控制器
  • 可选的路由

注意到在这里,我们不再有controller,service,directive这些概念,因为都已经转化为纯粹的ES模块。其中,组件可大致对等于以前的directive,只是配置方式更加友好了。

尽管粗略看上去,这段代码会比较奇特,但你可以这么想:主体逻辑都是放在普通的class里,剩下的组件相关的配置放在注解中。这样一想,就没有那么别扭了。

更灵活的路由

Angular 1.x早期自带的路由ngRoute比较简单,可以满足最基本的业务开发需求。

但是,在很多较复杂业务中,子路由成为了比较迫切的需求,我们可能会需要路由的嵌套,或者平级存在多个路由,因此,很多开发者选用了第三方的路由库uiRouter。

这两种路由配置方式都是典型的集中式配置,集中式路由在跟踪、定位等方面有优势,但绝大部分情形下,不灵活。

路由的实质是什么?是组件关系的一种映射,既然是这样,集中化的配置会导致,每当组件包含关系有变化,就可能需要修改全局配置,这是不太好的。

如果构建一个全组件化的系统,我们每个组件实际上只关注自身和所包含的子组件的url映射,基于这个理念,就有了Angular2的路由系统。

Angular2的路由是组件式路由,分散定义在每个组件上,并且,管理了所在组件的一些生命周期。

比如上一节的例子中,我们可以看到:

这段代码是个路由配置,指明了本组件下属两个组件,分别有不同的url,它们会被加在到模板中的router-outlet部分中:

在1.4版本中,Angular引入了ngNewRouter,实际上这个就是Angular2的兼容版本,理念完全一致。

基于这套路由机制,我们可以通过canDeactivate,deactivate,canActivate,activate等方法来更好地控制组件的生命周期。

开发语言的升级

在使用Angular 1.x的时候,我们可以使用ES3进行开发,也可以使用ES5,尤其是后者,目前绝大部分主流浏览器都支持,所以可以直接使用,使用ES5编写纯逻辑代码会是一件比较舒服的事情。

但我们也可以用ES6,CoffeeScript,TypeScript之类的语言去编写Angular 1.x应用,只是需要进行一些转化,因为它们不是Angular 1.x的默认开发语言。

到2.0的时代,官方推荐用来开发Angular应用的语言就变成了TypeScript和ES6了,严格的语法检查和各种增强特性,使得开发过程变得更加准确高效。

到现在这个时间点,因为Babel之类转译工具的极大发展,前端又普遍对构建过程逐渐习惯,使用ES6和TypeScript的好处已经远远大于坏处了,所以可以从现在开始就立刻切换到这些语言,无论是在用Angular 1.x还是将来要使用2.0。

编程模型的改变

如果用过Angular2的HTTP模块,会发现跟1.x版本的已经很大不同了。

假如我们要实现一个mapData,从远程请求到一个数值数组,把里面每个元素乘以2之后,再传递给下一个方法。

在1.x里,我们是这样写的:

但是在2里面,是这样写:

可以看到,这两者有不少差别。1.x的$http.get方法,返回结果是个Promise,所以,如果要持续传递下去,我们也要新建一个Promise并且返回。但是在2里面,Http.get的返回类型是RX Observable,它对很多东西的处理方式会不太一样,所以业务代码的写法也会有所不同,从代码上看,会有很明显的简化。

粗略一看,可能觉得这个RX Observable的例子没什么奇特的,即使你返回一个普通的数组,它也可以map啊,可以reduce,可以filter之类,仍然能传递下去,但RX的这个还可以subscribe之类,像这样:

这类特性能很大程度上减少我们实现业务功能所需的代码量。

参阅:RxJS

小结

综合以上,我们发现Angular从1.x到2.0的发展过程中,出现了这样一些变革:

  • 视图模型的纯化
  • 性能的优化
  • 组件化的开发理念
  • 更灵活的路由
  • 开发语言的升级
  • 编程模型的改变

这些变革体现了Angular在往一个强大而灵活,复杂而高效的前端组件化框架方向努力。

对自我的彻底革新,并不代表过去“错了”,而是代表过去曾经辉煌过的一些东西,随着时代的发展,渐渐走向过时。如果一个东西不随着时代的发展而修正自己,很快就会被历史的车轮无情碾过。(上面一句请勿联想,不主动不拒绝不承认不负责)

上一篇:未来Web应用的前端技术选型畅想

下一篇:十年来感受的前端技术变化