谈谈Angular--从Angular1到Angular4
更新日期:
本文跟随着Angular的变迁聊聊这个框架,分享一些个人的理解。
本骚年刚好是在之前的项目中使用到Angular1,然后现在项目中负责将Angular-beta升级到Angular-release版本。与Angular结识较深,或许也是缘分吧。
与Angular共事
初始
算了算,或许本骚年大概是两年前开始认识Angular,那会backbone.js、underscore.js也还是刚开始流行,而热门的当然还是jQuery、zepto那些吧。
不得不说,当你去仔细理解jQuery的整个设计和架构的时候,才会知道里面的精华和美妙之处。而对jQuery的熟悉也使得我养成了从dom出发的编程思维。
而Angular的出现,则颠覆了大多数jQueryer的思维方式。是的,我们需要从数据和状态开始思考组件,而不是随意地调动dom做想做的事情了。
什么依赖注入(DI)
、控制器(Controller)
、指令(Directive)
、服务(Service)
,Scope
又是什么,为什么还有digest()
和apply()
。
很多完全没接触过听说过的概念一拥而入,我没写过JAVA,工程化又是什么?
当时的我,并不知道这些MVC/MVVM框架将对前端产生怎样的一个影响,或者说是带来哥怎样的未来。
然而,就像我开始接触javascript一样,非科班的我没学过其他的语言,所以也不懂得为什么很多人诟病它。
Angular的出现也是一样,从我和它们的初始开始它们就是以这样的形态存在,带来方便的同时也免不了有自己的小缺点,没有对错,没有好坏,只有优势或是不适合的一些场景。
并肩作战
在一遍又一遍去理解Angular中的各种新概念和思维,以及一些业余的尝试之后,我开始把它选为队友,一起战斗。
那时候其实React已经热门起来了,而Vue还没稳定。基于函数式编程的React受到很多人的追捧,而我因为还没去了解熟悉,光从社区和论坛的讨论中无法分辨好坏。
直到产品经理问我,为什么Angular不行呢?
是啊,为什么不行呢?于是乎我把项目从jQuery迁移到Angular上了,准确来说,重构了。
坑是不少,但是越到后面才越发现这次重构的正确性。项目的维护和拓展性能大幅度提升了,于是进入了业务模块快速开拓的阶段。
其实我一直认为,作为一个业务产品,除了性能上的瓶颈,基本上没有框架好坏之分,只有设计架构的人能否正确理解业务和进行抽象架构。
框架纷争
Angular的工程化,一直到现在都会比React/Vue等框架更适合大规模业务的管理吧。尤其是像PC端的管理系统,围绕着简单的增删查改,需要的只是快速拓展业务,而很少复杂的需求和设计。
听过一句话,好的语言设计,能让刚接触的人写出的代码,和牛逼的人写出的代码几乎一样。
函数式编程也好,面向对象也好,其实方式并不是那么重要,我们的最终目标一致的话,其实剩下的更多是相互之间磨合、配合然后高效前进。
React很棒,它的虚拟DOM提升了性能,函数式编程让我们不需要再针对对象设计,我们可以畅快地抽象成一块块功能,然后一层层地封装或者调用。状态这种本不应该维护在函数式编程里面的东西,就让Redux/Flux等抽离出来进行管理吧。
Vue也很不错,它相比React对前端人员友好和易上手,性能也紧跟React,相比Angular要轻量、性能要好。尤其随着移动端的应用场景越来越大,单薄的H5页面很是适合吧。
看看Angular,概念太多上手难,框架太笨重不够灵活,甚至每次的升级都不做向下兼容(Angular1、Angular2-beta、Angular2-release)。
然后我们对Angular的认识停留在这些一般般的风评中,忘记了它的工程化场景更适合大量业务模块的管理,也忽视了它的自我颠覆同时带来了一些很棒的特性(Rxjs
和模块化),也不曾注意到现在的它更轻量、更灵活了。
又或者是原Angular1的使用者,看着Angular2的断崖式升级以及beta时期也在不断跳级,望而止步,也没机会深入探视,然后发现灵魂还在,光芒耀眼。
其实框架很多很杂,无法分辨不敢恭维又怎样。每个曾处于浪尖的新事物,必然有它的过人之处,多去了解学习,然后把精髓和优秀的思维带走,把不足记下以防踩坑。
若不去认识认识,怎么会知道这些框架的相似(路由、HTTP、生命周期、模板引擎等)。同样是服务的设计,依赖注入新的服务实例,而使用状态的抽离管理或许需要自行重置状态。
打怪升级
ES6和Webpack的出现,使得很多在Angular1中的设计变得冗余或者稍微落后了。新式特性和语法糖,模块化在Webpack配合Babel的强助攻下破浪而出。
然后Angular推翻重来,出现了Angular2-beta版本。当初宣言不进行Angular1版本的兼容使得它吃了不少亏,毕竟后面还是出了迁移方案的。
我们的项目从Angular1.2升级到1.5+,然后随着浪潮甩掉Grunt和Gulp,调整上了Webpack,同时Angular1.5+的版本已经部分引入了Angular2的一些新的思维(模块化),以及可以尽情使用ES6/ES7的新特性了。
不得不说,像Promise、Module、Class这些API的出现,真的是改变了很多的写码方式和效率。
然后因为项目业务的不断拓展,以及维护人员的增加,团队到后面一致赞同Typescript的引入。事实证明约束的同时也省去了一些不必要的交流,代码的维护性变强了。
这个时候我们的项目状态是,Angular1.5
+ Webpack
+ Typescript
,同时因为联调的排期问题我们使用了Angular的mock,真的很实用。
你好Angular2
现在的项目也是相似的,PC端的管理系统。当然它已经换了新马甲,虽然还是beta版本,不过很开心地说:你好呀,Angular2。
控制器(Controller)变成了组件(Component),然后定义都变成了装饰器(@decoretor from es7/typescript)。工厂(factory)没了,服务也要声明可注入(@injectavle)。
然后依赖注入的方式变了,beta版的每次使用分类注入(分directives/providers/pipe注入),然后release版本的模块注入(NgModule的decleration/imports/exports/providers)。
事件绑定((click)
)和属性绑定([ngClass]
)的方式变了,定义某类组件都使用了Class,我们看到了this,作用域Scope被藏了起来。
Angular2的beta到release版本也出现了一些大的变动,导致当初的版本已经找不到对应的文档和API了,遇到问题只能翻源码的效率太低了,于是我们升级到release的4.0版本。
Angular-beta到release也有不少的调整吧,具体大家可以参考《从Angular2-beta到Angular4-release框架升级总结》。
项目里面也有些调整,上了Angular-cli,它的内核也是webpack。虽然不是很灵活,但在没有很多时间自己实践的情况下还是个不错的选择。然后有了lazyload,有了公用组件模块SharedModule。
团队的小伙伴也说过这么一句话,Angular的项目,基本上拿过来复制粘贴就可以快速相应需求了。
我们的工作很多时候最终目标是一个很棒的产品,无论是体验还是对用户友好。当然免不了地,对自己有些要求的我们也想要一个看起来漂亮的代码和项目架构。
而我个人的习惯一般是,在产品刚启动的时候快速开发,然后规模大了,进行抽象、规范、架构调整,以及多人配合时如何提高开发效率,减少开发和维护成本。
很多人说,你要考虑以后的拓展性,要设计得完美。只是很多时候我们的产品总不能按照想象的顺利发展,我能做的,是和目前项目状况最适合的设计和架构,不多也不少。
谈谈Angular2
依赖注入
Angular的依赖注入可谓是灵魂了,之前有篇详细讲这个的文章《谈谈Angular2中的依赖注入》,这里大致摘录一些。
依赖注入在项目中,体现为项目提供了这样一个注入机制,有人负责提供服务,有人负责消耗服务,而这样的机制提供了中间的接口,并替使用者进行了创建并初始化这样的处理。
我们只需要知道,拿到的是完整可用的服务就好了,至于这个服务内部的实现,甚至是它又依赖了怎样的其他服务,都不需要关注。
其实像我们设计一个项目,自行封装的一些组件和服务,然后再对它们的新建和初始化等等,也经常需要用到依赖注入这种设计方式的。
我们的服务也可以分为有记忆的和无记忆的,关键在于抽象完的组件是否内部记录自身状态,以及怎样维护这个状态等等,甚至设计不合理的话,这样的状态管理会经常使我们感到困扰,所以Redux、Flux和Mobx这样的状态管理框架也就出现了。
而Angular在某种程度上替我们做了这样的工作,并提供我们使用。
在Angular里面我们常常通过服务来共享一些状态的,而这些管理状态和数据的服务,便是通过依赖注入的方式进行处理的。
依赖注入还有有个很棒的地方,就是单元测试很方便,测试的时候也注入需要的服务就好了。
模块化组织
Angular2模块把组件、指令和管道打包成内聚的功能块,每个模块聚焦于一个特性区域、业务领域、工作流或通用工具。
我们不再只是将组件和功能进行模块抽象,也不只是再将一些组件和功能进行再封装,我们还要把这样的功能模块一步步放射到整个应用程序。
这样的模块化思想层层包裹,我们的结构组织也层层地抽象封装,树结构的设计思想从模块组织到依赖注入延伸。
同时,依赖注入使得每个模块,不管层次薄的厚的,都能方便简单地进行状态管理。
Angular2也提供了封装好的一些功能模块,像表单模块FormModule
、路由模块RouterModule
、Http模块等等。
数据绑定
我们都知道,在Angular中是使用双向绑定的,当然Angular2中调整了分为事件绑定和属性绑定。
这在很多时候都解决了不少的问题,增删查过或者父子通讯最爱用了,换成React/Vue还得手动setState()
之类的更新或者通过状态管理工具触发更新。
说脏检查性能太糟,在Angular1中,当数据状态多了一次变更可能导致多次的联动脏检查计算,想想也是挺累的。
脏检查无非是,保存旧的数据,然后和新的数据比较计算,接着更新后再计算,直到没有新的变化为止。
虽然有了很多像React的虚拟DOM,以及Vue的数据跟踪getter和setter,但在Angular2中依然使用脏检查。
不一样的是,添加了zone.js对异步任务进行跟踪,把这个计算放进worker,完了更新回主线程,是个类似多线程的设计,也提升了性能。
在Angular2中应用的组织类似DOM,也是树结构的,脏检查会从根组件开始,自上而下对树上的所有子组件进行检查。
相比Angular1中的带有环的结构,这样的单向数据流效率更高,而且容易预测。
路由和lazyload
路由在Angular-release版本中设计成单独的功能模块,不再跟组件Component有密切的关系。
我没有仔细研究过其他框架的路由模块,不过像lazyload这样的设计也肯定已经考虑进去了吧。
既然这样的话,那这里我拿Angular来说明一下lazyload的闪光之处。
像我们打包页面,很多时候最终生成了一个bundle.js
文件。这样,每次当我们请求页面的时候,都请求整个bundle.js
并加载,有了Webpack或许我们只需要加载其中的某些模块,但还是需要请求到所有的代码。
很多时候我们或许不需要进入所有的模块,这个时候浪费了很多的资源,同时首屏体验也受到了影响。
通过路由的lazyload以及上面提到的模块化,我们可以把每个lazyload的模块单独打包成一个chunk
文件,当进入模块时才请求和加载,当我们的业务规模很大的时候,首屏速度得到大幅度提升。
使用Angular-cli可以很方便地进行路由的lazyload,而若自行使用webpack构建,也只需要加上一个angular-router-loader
就好了。
编程方式
Angular2相对Angular1来说,编程方式改变太多。但是如果你熟悉ES6/ES7,就发现Angular2中的很多编程方式都很熟悉。
其实Angular2看起来和Angular1完全是两个框架,很多时候是编程思维习惯导致的。就像当初使用jQuery的我们去学习Angular的感觉,但其实像ES6/ES7的出现这是一个必然的趋势吧。
Typescript的优势也体现在项目的维护性、代码的可读性以及多人配合的沟通效率。它也只是一种方案选择,当然如果有约定好的规范也是很不错的,最终还是看团队的意愿了。
Promise的设计也让代码变得清晰连贯,数据状态的响应处理,同时我们可以方便地进行流式处理和异常处理。
Rxjs的引入可以方便实现事件订阅,有时候可以配合Websocket来进行应用的部分推送更新等功能。
Class的定义方式,配合装饰器Decoretor,可以方便地统一的组件/服务/模块等的创建。
用AOT进行编译
JIT编译导致运行期间的性能损耗。由于需要在浏览器中执行这个编译过程,视图需要花更长时间才能渲染出来。
由于应用包含了Angular编译器以及大量实际上并不需要的库代码,所以文件体积也会更大。更大的应用需要更长的时间进行传输,加载也更慢。
预编译(AOT)会在构建时编译,这样可以在早期截获模板错误,提高应用性能。
事实上只有一个Angular编译器,AOT和JIT之间的差别仅仅在于编译的时机和所用的工具。
使用AOT,编译器仅仅使用一组库在构建期间运行一次;使用JIT,编译器在每个用户的每次运行期间都要用不同的库运行一次。
AOT使得页面渲染更快,无需等待应用首次编译,以及减少体积,提早检测模板错误等等。
拥抱变化,迎接未来
身为框架,Angular和React、Vue各有各的优劣,哪个更适合则跟产品设计、应用场景以及团队等各种因素密切相关,没有谁是最好的,只有当前最适合的一个。
Angular的经常性不兼容,以及1到2的断崖式升级,或许对开发者不大友好。但其实,我很喜欢它的这些改变,干脆利落,自身的缺点由自己去克服。
我们也何尝不是,为何要死守某个框架、某种语言,或是争好坏、分高下。
何尝不抱着开放的心态,拥抱变化,然后迎接未来呢?
参考
结束语
以上只是本人的个人理解,或许存在偏差。世上本就没有十全十美的事物,大家都在努力地相互宽容和理解。
那些我们想要分享的东西,肯定是存在很棒的亮点。而我们要做的,是尽力把自己看到的那完美的一面呈现给大家。
码生艰难,写文不易,给我家猪囤点猫粮了喵~
查看Github有更多内容噢:https://github.com/godbasin
更欢迎来被删的前端游乐场边撸猫边学前端噢
如果你想要关注日常生活中的我,欢迎关注“牧羊的猪”公众号噢