{"action":"created","issue":{"url":"https://api.github.com/repos/xufei/blog/issues/21","labels_url":"https://api.github.com/repos/xufei/blog/issues/21/labels{/name}","comments_url":"https://api.github.com/repos/xufei/blog/issues/21/comments","events_url":"https://api.github.com/repos/xufei/blog/issues/21/events","html_url":"https://github.com/xufei/blog/issues/21","id":82690191,"number":21,"title":"后Angular时代二三事","user":{"login":"xufei","id":2725159,"avatar_url":"https://avatars.githubusercontent.com/u/2725159?v=3","gravatar_id":"","url":"https://api.github.com/users/xufei","html_url":"https://github.com/xufei","followers_url":"https://api.github.com/users/xufei/followers","following_url":"https://api.github.com/users/xufei/following{/other_user}","gists_url":"https://api.github.com/users/xufei/gists{/gist_id}","starred_url":"https://api.github.com/users/xufei/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/xufei/subscriptions","organizations_url":"https://api.github.com/users/xufei/orgs","repos_url":"https://api.github.com/users/xufei/repos","events_url":"https://api.github.com/users/xufei/events{/privacy}","received_events_url":"https://api.github.com/users/xufei/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":26,"created_at":"2015-05-30T04:03:40Z","updated_at":"2015-06-02T05:07:04Z","closed_at":null,"body":"后Angular时代二三事\r\n====\r\n\r\nJavaScript框架/库一直就是百花齐放，最近几年更是层出不穷。回顾这几年，有两个最引人注目的东西，一个是Angular，一个是React。其中，Angular最火的时间是2013年中到2014年末，React从2014年中开始升温，然后又由于ReactNative等周边项目，导致关注度很高。\r\n\r\n2014年末，Angular官方宣布了一个大新闻，要完全重写Angular 2.0。这个事情让很多想要使用Angular的人止步不前，也给很多人带来了困惑。\r\n\r\n随后，Angular 2.0的开发者之一创建了新的框架Aurelia，整体思路上与Angular相似，有一些细节的差异。那么，我们应当如何看待这些框架呢？\r\n\r\n## 为什么Angular 2重写\r\n\r\n如果不是有重大原因，没有哪个开发者会做出彻底重写，产生很多不兼容变更的决定。对于Angular来说，它面临这么一些原因：\r\n\r\n- Web标准的升级，主要是Web Components相关标准和ECMAScript的后续版本\r\n- 自身存在的一些问题：性能，模块，过于复杂的指令等等\r\n\r\n## 使用转译语言\r\n\r\n也正是Angular 2.0那篇大新闻，使大家知道了AtScript这样的语言，它在TypeScript的基础上添加了注解等功能。\r\n\r\n有很多语言可以转译成JavaScript，比如CoffeeScript，Dart，TypeScript等，从最近的一些事件来看，TypeScript可以算是JavaScript转译领域的最大赢家。\r\n\r\n很多人可能会有这样的疑问：为什么我们要用这些东西，而不是直接编写原生的JavaScript？开发语言的选择，很大程度上反映了我们对JavaScript组件化方案抽象度的需求。\r\n\r\n比如说，Angular中，可以使用TypeScript来写业务代码，React中，通过JSX来使用组件，这都是具有较高抽象度的方案，能够让业务代码变得更直观。\r\n\r\n### ES6\r\n\r\n先不看这些转译语言，来看看ES6，它给我们带来了很多编程的便利，每一次这种语言细节的升级，都引入了一些好用的东西，所以我们当然是期望尽早使用它。但问题是，浏览器的支持程度总是落后的，如果用它写了，在很多浏览器上不支持，比如箭头函数：\r\n\r\n```JavaScript\r\nthis.removeTodo = function(todo) {\r\n    this.todos = this.todos.filter(item => todo!=item);\r\n};\r\n```\r\n\r\n所幸，我们有Babel这样的转换器，可以把这样的代码翻译成ES5代码，它的生成结果就是\r\n\r\n```JavaScript\r\nthis.removeTodo = function(todo) {\r\n    this.todos = this.todos.filter(function(item) {\r\n        return todo != item;\r\n    });\r\n};\r\n```\r\n\r\n这个例子并不明显，如果你使用class之类的东西，就能体会到更大的改变。虽然说class这些只是语法糖，但用起来还是很爽的，可以复用一些传统的设计模式之类。\r\n\r\n对于那些只需支持ES5+的项目而言，现在开始选用ES6语法编写代码是非常合适的，因为我们有Babel这样的东西，我们可以享受ES6新语法带来的愉悦编程体验，而无需承担兼容风险。\r\n\r\nES6新语法有很多，想要在生产过程中更好地使用，可以参见百度ecomfe的这篇[使用ES6进行开发的思考](http://otakustay.com/es6-develop-overview/)\r\n\r\n### TypeScript\r\n\r\nAngular和Aurelia都支持TypeScript，可以直接使用TypeScript编写业务代码。如果选用这样的框架，个人建议直接使用TypeScript。\r\n\r\n为什么在类似Angular这样的体系里，我要建议使用TypeScript呢，因为这么几个原因：\r\n\r\n#### 长期的兼容性更好\r\n\r\n很可能在现在这个阶段，你的项目还需要面对一些不支持ES6的浏览器，所以不能直接写ES6代码，但有可能有一天，浏览器支持了，但你的代码还是老的，它基本上还在使用ES5编写，想要迁移到ES6比较麻烦，以后每次迁移都是痛苦的过程。TypeScript就是以生成JavaScript为目标的，所以如果你用它写，只需选择生成参数，比如生成es5，es6就可以了，就算以后es继续升级，也只要改个参数就完事。\r\n\r\n#### 编写体验更好\r\n\r\nTypeScript为代码提示作了很多特殊优化，比如：\r\n\r\n```JavaScript\r\nele.on(\"click\", function(e) {\r\n    // 这里我们是不知道e上面有什么，在编写的时候得不到提示\r\n});\r\n```\r\n\r\n但是如果使用TypeScript编写，因为这个e的类型确定，所以就能有提示。\r\n\r\n使用这样的语言也能够更快让非前端方向的人参与项目。\r\n\r\n#### 工作流程与管控\r\n\r\nAngular的整体方案，由于分层很清晰，在JavaScript代码中基本就是纯逻辑，这样的代码如果使用TypeScript编写，会更加精炼，更加清晰。\r\n\r\n这几年，大家逐渐接受了一个现实，那就是：前端也是需要构建的，所以我们有grunt，gulp这样的构建工具。之前我们不愿意写转译语言，是因为其他环节不需要构建，为了一些语法糖而引入整个构建环节代价太大。现在，既然发布之前的构建环节不可缺少，使用转译语言也不过就是加一段配置而已，这个使用代价已经小很多了。\r\n\r\nAngular这样的解决方案，所面向的多数都是重量级产品，这些产品本身就会有构建环节，也基本上会使用IDE，所以，使用TypeScript的代价不大。\r\n\r\n当项目变大的时候，我们会面临很大的管理成本，比如对代码的分析，结构调整，模块依赖关系梳理等，在TypeScript上面做，会比在JavaScript上面做更有优势。\r\n\r\n最近几年前端领域“工程化”这个词被说得太多，但其实绝大部分说的都只是“工具化”。早在Visual Studio 2005中，就存在很多Factory插件，举例来说，一个普通项目的工作流程可能是这样：\r\n\r\n- 使用ER图设计模型结构\r\n- 一键生成数据库表结构和存取过程\r\n- 一键生成数据库访问层和实体定义代码\r\n- 一键生成Web Service接口\r\n- 根据WSDL，一键生成客户端的调用接口\r\n- 剩下的就是做界面，调用这些接口了\r\n\r\n比如说我们做到一半，需要变更模型，也只是需要在ER图那边修改，然后依次一键变更过来。很多时候我们也会有代码的目录调整，批量更名，如果使用约束较强的语言，这部分可靠性会更高。\r\n\r\n## 组件化与路由\r\n\r\n如果用过angular 1.x，会对它的路由机制印象深刻。有复杂业务需求的人一般都不会使用内置的ng-route，而是会使用第三方的ui-router，这两者的核心差别是子路由的定义。\r\n\r\n比如：\r\n\r\nA界面有两个选项卡，分别B，C，如果我们想要：\r\n\r\n```\r\napp.html#a/b\r\napp.html#a/c\r\n```\r\n\r\n这样的多级路由，在ng-route中想要定义，就比较麻烦，而在ui-router中，允许使用嵌套的ui-view指令，可以比较方便地支持这一功能。\r\n\r\n在这两种方式下，路由都是全局配置的，但我们考虑在全组件化的场景下，组件的嵌套会受到这种路由配置的制约。比如，本来我们只是期望把某个组件嵌入到另外一个组件中，就能完成功能，但为了路由，不得不额外在全局路由配置的地方，加一个配置，而且每当组件层级发生变更的时候，这个配置都需要改，这就大幅拖累了我们组件体系的灵活度。\r\n\r\n为此，我们可能会期望把路由配置放在每个组件中，比如说，组件A定义自己的路由为a，组件B的路由为b，组件C的路由为c，无需额外的配置，当B和C放在A中作为选项卡的时候，上面那两条路由会自动生效。\r\n\r\n在Angular的新路由机制中，就是这样处理的，这也是Angular 2.0和Aurelia的共同路由机制。在这种机制下，如果有一天我们在另外一个更高层的组件D中，引入了组件A，那路由就会自己变成类似：\r\n\r\n```\r\napp.html#d/a/b\r\napp.html#d/a/c\r\n```\r\n\r\n这个是非常灵活的，这对于我们构建一个全组件化的系统很有利，另外，这实际上实现了路由的动态配置。\r\n\r\n当然，对这个问题，也是有争议的，因为路由不再集中配置，很难有一个地方能查看所有的路由状况了。\r\n\r\n此外，由于在Angular 2和Aurelia中都凸显了组件的概念，组件的生命周期被引入了，比如说，组件的四个状态：\r\n\r\n- 创建前\r\n- 创建\r\n- 销毁前\r\n- 销毁\r\n\r\n这些跟路由进行配合，可以把我们的加载过程，前置、后置条件过程都整理得很清楚。\r\n\r\n## 指令与Web Components\r\n\r\n最近，越来越多的人开始关注Web相关标准的推进，在HTML这个方面，最重要的标准就是Web Components，它主要是提供扩展HTML元素的能力（Custom Elements）。\r\n\r\n> HTML is great for declaring static documents, but it falters when we try to use it for declaring dynamic views in web-applications. AngularJS lets you extend HTML vocabulary for your application. The resulting environment is extraordinarily expressive, readable, and quick to develop.\r\n\r\n这一段来自Angular的官方介绍。扩展HTML的词汇，是Angular的一种愿景，在这个里面，除了包含对元素的扩展，还有属性（Attribute）。\r\n\r\n很多时候，仅仅有元素的扩展，是不足以满足需求的。举例说，让某个按钮闪烁，我们有两种方式实现：\r\n\r\n- 创建一种可以闪烁的按钮\r\n- 创建一种可以闪烁的行为\r\n\r\n其中，前者是特定的解决方案，创建一个自定义元素&lt;blink-button&gt;&lt;/blink-button&gt;可以达到目的，但闪烁这个动作可以是一种通用行为，我们可能需要让图片闪烁，让链接闪烁，让各种元素都能闪烁，把这种行为扩展到不同的元素上。\r\n\r\n如果用jQuery，我们可能会写：\r\n\r\n```JavaScript\r\n$.fn.blink = function(options) {\r\n    // 这里对DOM进行处理，添加闪烁功能\r\n};\r\n```\r\n\r\n然后在使用的时候：\r\n\r\n```JavaScript\r\n$('.some-element').blink();\r\n```\r\n\r\n如果说有自定义属性，可能我们就只要写：\r\n\r\n```HTML\r\n<span blink>aaa</span>\r\n<a blink>aaa</a>\r\n<button blink>aaa</button>\r\n```\r\n\r\n借助数据绑定，还可以把blink绑定到一个变量上，由这个变量动态控制是否闪烁。\r\n\r\n```HTML\r\n<div blink=\"hasNewMessage\">aaa</div>\r\n```\r\n\r\n在Angular 1.x中，使用指令（directive）来实现自定义元素和自定义属性，这个东西设计得很复杂，所以不太容易上手，在2.0中，这一块改了。\r\n\r\n在Angular 2和Aurelia中，使用很简单的标记来表明某个东西是自定义元素还是属性。\r\n\r\n```JavaScript\r\n@customAttribute('blink')\r\n@inject(Element)\r\nexport class Blink {\r\n  element:any;\r\n  \r\n  constructor(element) {\r\n    this.element = element;\r\n  }\r\n}\r\n```\r\n\r\n```JavaScript\r\n@customElement('my-calendar')\r\nexport class Calendar {\r\n}\r\n```\r\n\r\n自定义属性的理念，在早期IE中实现的HTML Components中有很好的体现，它允许使用JavaScript编写DOM元素相关的代码，然后在css中作为行为附加到选择器上。\r\n\r\n## 组件化与MVVM\r\n\r\n对于大型Web应用来说，组件化是必须的，但是如何实现组件化，每个人都有自己的看法，所以组件化这个词就像民主，法制一样，容易谈，难做。\r\n\r\n我们所期望的组件化往往是这样：\r\n\r\n![我们期望的组件化](https://raw.githubusercontent.com/xufei/blog/master/assets/components/ic.jpg)\r\n\r\n但实际上，很可能是这样：\r\n\r\n![现实中的组件化](https://raw.githubusercontent.com/xufei/blog/master/assets/components/reality.jpg)\r\n\r\n实际在用组件，尤其UI组件的时候，会出现很多尴尬的地方，比如说同一个组件在不同场景下形态不一致，所以我们需要多个层次的组件复用级别。\r\n\r\n在Angular 1.x中，组件化并不是一个很明确的概念，它的整体思路还是：逻辑层+模板层这样的概念，此外，有一些指令（directive），用于表达对HTML标签、属性的增强。\r\n\r\n在2.0版本中，组件成为了一个很清晰的东西。一个常见的组件，包含界面模板片段和逻辑类两个部分。\r\n\r\n如果我们经历过Angular 1.2之前的版本，可能会感受到controller的一些变化。比如说，之前我们写一个controller，可能是：\r\n\r\n```JavaScript\r\nfunction TestCtrl($scope) {\r\n    $scope.counter = 0;\r\n    \r\n    $scope.inc = function() {\r\n        $scope.counter++;\r\n    };\r\n}\r\n```\r\n\r\n然后这样用：\r\n\r\n```HTML\r\n<div ng-controller=\"TestCtrl\">\r\n    {{counter}}\r\n    <button ng-click=\"inc()\">+1</button>\r\n</div>\r\n```\r\n\r\n在1.2之后，我们会这样写：\r\n\r\n```JavaScript\r\nfunction TestCtrl() {\r\n    this.counter = 0;\r\n    \r\n    this.inc = function() {\r\n        this.counter++;\r\n    };\r\n}\r\n```\r\n\r\n然后这样用：\r\n\r\n```HTML\r\n<div ng-controller=\"TestCtrl as test\">\r\n    {{test.counter}}\r\n    <button ng-click=\"test.inc()\">+1</button>\r\n</div>\r\n```\r\n\r\n注意TestCtrl的实现，里面没有$scope了，这意味着什么呢？意味着这个“controller”已经不再是controller了，而是view model，这个部分的代码变得更加纯净，每有一个对应的界面片，就实例化一个出来与之对应绑定。\r\n\r\n在Angular 2和Aurelia里面，HTML模板与视图模型被视为一体，当做一个组件，而Aurelia的灵活度更高，因为它尽可能地把额外的配置放在HTML模板中，所以视图模型变得更单纯，也存在复用价值了。\r\n\r\nAurelia跟Angular 2有不少细节差异，写法上大致的对比可以从这里看出：[Porting an Angular 2.0 App to Aurelia](http://blog.durandal.io/2015/05/20/porting-an-angular-2-0-app-to-aurelia/)\r\n\r\nAngular支持使用pojo作为数据模型，这可以算是它的优点之一，这样，它对模型层的定义就比BackBone和Knockout简洁很多。\r\n\r\n但是在2.0时代，我个人是倾向于预定义模型类型的，因为在MVVM这三层中，不宜过于淡化VM和M的分界，分清哪些东西是从属于模型的，哪些东西是从属于视图模型，在很多情况下都会很重要。这会影响我们另外一些工程策略，比如测试环节的处理方式。\r\n\r\n在大型应用中，model应当与store视为一体，在比如数据的共享，缓存，防冲突，防脏等方面综合考虑，而view model可以不要考虑得这么复杂。\r\n\r\n基于MVVM，我们可以在不同层级复用组件，可以把模板和视图模型当做一个整体复用，也可以只复用视图模型，使用不同的模板。在这一点上，Angular 2显然比Aurelia欠考虑。\r\n\r\n## 代码的迁移\r\n\r\nAngular的这次升级，最令人不满的是它的不兼容变更。这些变更很多方面来说，是无奈之举，因为前后的差距确实有那么大，想要短期平滑，就得在未来背负更重的历史负担。\r\n\r\n但事实上，我们在很多场景下，比如企业应用领域，并没有比它更好的解决方案，所以这时候需要来看看如果想要作一个版本迁移，需要做哪些事情。\r\n\r\n如果我们要做从Angular 1.x到2.0的代码迁移，相对最容易，也最值得做迁移的部分是数据模型，但这个问题说难也难，说简单也简单。\r\n\r\n很多对分层理解不深的人，很可能把这个代码迁移想得过于复杂。但其实，一个规划良好的Angular 1.x工程，它的代码结构应该是非常有序的，什么东西放在模板里，什么东西放在controller，service，都是非常清楚的，而且，绝大多数controller和service中，是不应有DOM相关的代码的。\r\n\r\n比如，service中是什么？主要是数据模型的存取，与服务端的交互，本地缓存，公共方法等，这些东西要迁移到2.0中，是很容易的，只是写法会稍有差别。\r\n\r\n接下来往上看看，看这个所谓的controller。在2.0中，不再有controller，service这些东西的区分，一切都是普通的ES类，但是理念还是有的。比如一个含有视图的组件，它的逻辑部分就会是一个ES类，这个也就是视图模型，基本上也就对等于1.x中的controller。\r\n\r\n比如最简单的todo:\r\n\r\n```JavaScript\r\nfunction TodosCtrl() {\r\n    this.todos = [];\r\n    this.newTodo = {};\r\n    \r\n    this.addTodo = function() {\r\n        this.todos.push(this.newTodo);\r\n        this.newTodo = {};\r\n    };\r\n    \r\n    this.removeTodo = function(todo) {\r\n        this.todos = this.todos.filter(function(item) {\r\n            return item != todo;\r\n        });\r\n    };\r\n    \r\n    this.remainingCount = function() {\r\n        return this.todos.filter(function(item) {\r\n            return item.finished;\r\n        }).length;\r\n    };\r\n}\r\n```\r\n\r\n这代码很简单，就是给一个列表添加移除东西，假设我们要把这个代码移植到2.0，可以说基本没有代价，因为在2.0里你要实现这样的功能，也得这么写。\r\n\r\n（注意，下面这段是Aurelia代码，并且不是使用ES6，而是使用TypeScript编写）\r\n\r\n```JavaScript\r\nexport class Todos {\r\n    public todos: Array<Object> = [];\r\n    public newTodo: Object = {};\r\n\r\n    addTodo(): void {\r\n        this.todos.push(this.newTodo);\r\n        this.newTodo = { content: \"\" };\r\n    }\r\n\r\n    removeTodo(todo): void {\r\n        this.todos = this.todos.filter(item => todo != item);\r\n    }\r\n\r\n    get remainingCount() {\r\n        return this.todos.filter(item => item[\"finished\"]).length;\r\n    }\r\n}\r\n```\r\n\r\n这么一看，好像也很容易迁移过去，多数情况下是这样，但这里面有坑。坑在什么地方呢？主要是手动添加变更检测的部分。变更检测是个复杂的话题，在本文中先不讲，后面专门写一篇来讲。\r\n\r\n现在我们把逻辑层摆平了，来看界面层，这里主要有三个东西，一个是原先的指令，一个是普通的模板，还有一个是过滤器。\r\n\r\n指令的问题好办，我们刚才提到的自定义元素，自定义属性，其实对使用者是没什么差别的，也就是实现的人要把代码迁移一下。\r\n\r\n我个人并不赞同在一个业务型的项目中封装太多自定义元素，仅仅那种被称为“控件”的东西才有这个必要，其他东西可以直接采用模板加视图模型的方式，具体理由在前一篇的组件化之路中提到过。如果是按照这种理念去实现的业务项目，指令这块迁移成本也不算高。\r\n\r\n过滤器也很好办，2.0 有同样类似的机制实现。\r\n\r\n普通模板这边，绝大部分都是固定的工作量，比如ng-repeat，ng-click换个写法而已，里面有一些影响，但基本上是可以用批量转换去搞定的。\r\n\r\n所以我们发现，迁移的成本并没有想象的那么大，为了更好地拥抱Web标准和更好的性能，这样的事情是比较值得去做的。\r\n\r\n## Angular与React\r\n\r\n这两种东西代表着现代Web前端的两种方法论，前者是以分层和绑定为核心的大一统框架，后者提供了渲染模型多样化，带生命周期的多层组件机制。由于实现理念的不同，用它们分别开发同样的Web应用也会有很大差异。好比我们造一个仿生机器人，用Angular是先造完骨架，把基本运动功能调试完，然后加装肌肉等部件，最后贴皮肤，眉毛，头发，指甲；用React是先造出各种器官，肢体，然后再拼装。\r\n\r\n方法论的事情那个很难说对错，只有看场景。比如亚洲农民跟美洲农民种地，理念肯定是不同的，因为他们面临的场景不同，比如亚洲种地普遍很精细化，美国种地很粗放。这也有些像React和Angular的差别。\r\n\r\n我个人不赞同在框架的问题上有太多争论，因为天下武功，到底什么厉害，完全是看人的，一阳指在段正淳手里，只能算二流，到了南帝段智兴手里，可与降龙十八掌齐名。聚贤庄一战，乔帮主用最普通的太祖长拳，打得天下英雄落花流水。如果深刻理解了一个技术的优点和缺点所在，扬长避短，则无往而不利。\r\n\r\n近年来，各框架是在互相学习的过程，但是每个东西到底有什么不同，最好还是列出需求，分别用代码体现。现在已经有todomvc这么一个库，用各种框架实现todo，但在我看来，这个需求还太小，不足以表达各自的优势。\r\n\r\n我倡议，每个框架的熟练使用者能够选出一些典型场景，然后写一些demo，供更多的人学习对比之用。\r\n\r\n## Angular与未来\r\n\r\n到目前为止，我们在浏览器中看到系统从规模来说都是中小型的，与传统桌面的大型软件们相比，还很幼小。比如Office的开发团队，千人以上的规模，无论是代码的架构，还是人员的分工协作，都可以算是伟大的工程。\r\n\r\n在大型系统中，组件化可以说是立足的基础，但怎样去实践组件化的思想，是一个见仁见智的话题。\r\n\r\n还是以Office为例，它除了提供图形化的操作界面，还提供了一套API，可以被VBA这样的嵌入语言调用。\r\n\r\n比如说，我们可以在界面上选中一个工作表，然后在某行某列填入数据，也可以在VBA中使用这样的语句去达到同样的目的\r\n\r\n这就意味着，对于同一种操作，存在多样化的外围接口。继续分析下去，我们会发现，存在一种叫做Office Object Model的东西，这也就是一个核心数据模型，我们所有的操作其实都是体现在这个模型上的，GUI和VBA分别是这个模型的两个外围表现。\r\n\r\n所以可以想象，如果Office的测试团队想要测试功能是否正确，他是有两条路要走：\r\n\r\n- 通过VBA这么一个相对简单直接的方式，去调用OOM上的方法和属性，然后再次通过VBA去验证结果\r\n- 通过GUI上类似录屏的操作，去模拟人的一些操作，然后，通过VBA或者是界面选取的方式验证结果\r\n\r\n从这里可以大致感受到，当系统越复杂的时候，独立的模型层越重要，因为必须保持这一层的绝对清晰，才能确保整个系统是正确而稳定的。层层叠加，单向依赖，这使得软件正确性的验证过程变得更加可控。\r\n\r\n在业务系统中，又存在另外一些问题。以我曾经从事过的电信行业软件系统为例，整个运营与业务支撑系统由若干个子系统构成，比如：\r\n\r\n- 资源管理，管理卡、号、线等资源\r\n- 营业系统，负责对外营业\r\n- 计费与结算\r\n- 运维与调度，负责人员权限考核调度等\r\n- 相关的内部管理系统\r\n\r\n这些系统基本都已经Web化，如果我们要探讨它们的组件化方式，必须作相当深远的考虑，因为，还可能出现终极杀手——比如呼叫中心系统。\r\n\r\n大家打客服电话的时候，有没有注意到，客服人员可以操作的东西，是超过了前台营业员的，这也就说明他实际上能够操作以上某几个系统。可是我们也没有发现他在切换多种功能的时候，花太多时间，说明其实他有一个高度集成的界面入口。\r\n\r\n这就来了问题了，如果这里的多数功能是集成其他系统的组件所致，那都该是一些什么样的组件啊？\r\n\r\n## 小结\r\n\r\n篇幅所限，不在本文中讨论这些问题。抛出这样的问题来，是为了让大家察觉，在很多不为人知的地方，存在很值得思考的东西。一些新的Web标准是为了解决Web系统的大型化，应用化，但仅仅以这些标准本身而言，还是存在一定的不足，需要更深刻的改变。\r\n\r\n我们期望Angular2和Aurelia为代表的新型框架能够给这些领域带来一些灵感，互相碰撞，解放更多人的生产力。\r\n\r\n总而言之：\r\n\r\n> “I think we agree, the past is over.” – George W. Bush\r\n"},"comment":{"url":"https://api.github.com/repos/xufei/blog/issues/comments/107810215","html_url":"https://github.com/xufei/blog/issues/21#issuecomment-107810215","issue_url":"https://api.github.com/repos/xufei/blog/issues/21","id":107810215,"user":{"login":"xufei","id":2725159,"avatar_url":"https://avatars.githubusercontent.com/u/2725159?v=3","gravatar_id":"","url":"https://api.github.com/users/xufei","html_url":"https://github.com/xufei","followers_url":"https://api.github.com/users/xufei/followers","following_url":"https://api.github.com/users/xufei/following{/other_user}","gists_url":"https://api.github.com/users/xufei/gists{/gist_id}","starred_url":"https://api.github.com/users/xufei/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/xufei/subscriptions","organizations_url":"https://api.github.com/users/xufei/orgs","repos_url":"https://api.github.com/users/xufei/repos","events_url":"https://api.github.com/users/xufei/events{/privacy}","received_events_url":"https://api.github.com/users/xufei/received_events","type":"User","site_admin":false},"created_at":"2015-06-02T05:07:04Z","updated_at":"2015-06-02T05:07:04Z","body":"@yyx990803 不熟悉的东西我实在不敢说。。。"}}