Angular 1 与 Angular 2——高级比较

一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / Java 学习路线 / 一对一提问 / 学习打卡/ 赠书活动

目前,正在 星球 内带小伙伴们做第一个项目:全栈前后端分离博客项目,采用技术栈 Spring Boot + Mybatis Plus + Vue 3.x + Vite 4手把手,前端 + 后端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,陪伴式直到项目上线,目前已更新了 204 小节,累计 32w+ 字,讲解图:1416 张,还在持续爆肝中,后续还会上新更多项目,目标是将 Java 领域典型的项目都整上,如秒杀系统、在线商城、IM 即时通讯、权限管理等等,已有 870+ 小伙伴加入,欢迎点击围观

Angular 2 目前仍处于 Alpha/Developer Preview 阶段,但主要功能和核心文档都已经 可用 。让我们在这里收集目前已知的关于 Angular 2 设计目标的信息,以及它们计划如何实现:

  • Angular 2 的主要目标
  • 推理更简单
  • Angular 1 与 Angular 2 变化检测
  • 使用区域更透明的内部结构
  • 改进的堆栈跟踪
  • 大大提高了性能(以及为什么)
  • 改进的模块化
  • 改进的依赖注入
  • Web 组件友好(如何以及为什么)
  • 支持 Shadow DOM
  • 支持 Android 和 iO 中的 原生 移动渲染
  • 支持服务端渲染
  • 改进的可测试性
  • 迁移到 Angular 2 的路径
  • 结论

Angular 2 的主要目标

Angular 2 的主要目标是创建一个超级易学且有效的 Web 框架。让我们看看这是如何实现的:

目标:更容易推理

在当前版本的 Angular 中,我们有时不得不对某些用例的框架内部进行推理,例如必须对应用程序事件初始化和摘要周期进行推理:

  • 在 Angular 1 中,没有 摘要循环完成 事件(请参阅新 Forms 模块的 原因 ),因为此类事件可能会触发进一步的更改,从而使摘要循环继续进行。
  • 我们不得不考虑何时调用 $scope.apply $cope.digest ,这并不总是那么简单
  • 有时我们不得不调用 $timeout 来让 Angular 完成它的摘要循环并只在 DOM 稳定时才做一些操作

为了让 Angular 2 更容易理解,目标之一是创建更透明的开箱即用的内部结构。

首先,让我们看一下 Angular 1 绑定机制是如何实现的,以及如何使它更加透明。

Angular 1 如何实现绑定

在 Angular 1 中,能够编辑表单并立即将这些更改反映在 Javascript POJO 上的 ng-model 功能是该框架变得如此流行的主要原因之一。

根据此 播客 (见 3:50),它在 Angular 1 中的工作方式如下:

  • 在 Javascript 运行时,一切都可以通过设计进行修补——如果需要,我们可以更改 Number
  • Angular 在启动时会修补所有异步交互点:
    • 超时
    • Ajax 请求
    • 浏览器事件
    • 网络套接字等
  • 在这些交互点,Angular 将对范围对象运行脏检查以查看是否发生了变化,如果发生变化则触发相应的观察者
  • 重新跑dirty checking看是否有更多变化发生,重新跑watchers等。

Angular 1 中绑定工作原理的后果

结果是 DOM 与 POJO 永久保持同步并且它正常工作,但所有这些有时很难推理:

  • 目前尚不清楚哪些观察者将被解雇,以何种顺序或多少次
  • 模型更新的顺序很难推理和预测
  • 摘要循环可以运行多次,这很耗时

Angular 团队在 Angular 2 的方向上采取的第一步是从 Angular 代码库中提取修补所有异步交互点的机制,并使其可重用。

介绍区域

这种重构的结果是 Zone.js ,它可以与 Java 中的线程本地上下文进行比较。

它可以用于许多用例,例如允许框架生成跨越多个 Javascript VM 轮次的长堆栈跟踪。

Angular 2 如何因区域而变得更加透明

Angular 2 使用区域机制使很多关于摘要循环的推理不再必要。如果由区域内的组件触发,这段普通的非 Angular 特定 Javascript 代码将透明地触发 Angular 2 摘要:


 element.addEventListener('keyup', function () {  
  console.log('Key pressed.');
});
});

不再需要 $scope.apply $scope.digest ,一切都透明地工作。在大多数用例中,我们可能不必对区域进行一般推理,并且仍然可以通过使用 VmTurnZone 在 Angular 区域外运行代码。

目标:提高性能

上面对摘要周期的描述清楚地表明,所有这些都可能很耗时,尽管在 Angular 1.3 和 Angular 1.4 中都进行了大量的性能改进。

但目前尚不清楚这些性能改进可以走得更远,原因之一是可能存在变化检测循环。

为了更好地理解性能提升是如何实现的(最后比 Angular 1 快 5-10 倍),最好参考这个 播客 博客文章 。我将尝试在这里总结 Angular 2 更快的两个主要原因:

更快地检查单个绑定

检查单个绑定的机制经过优化,允许 Javascript VM 通过即时编译将该代码优化为本机代码。不是递归地扫描对象树,而是在 Angular 启动时创建一个函数来查看绑定是否已更改。

这个绑定检查函数看起来像是我们手动编写的函数来测试变化,它可以很容易地被 VM 优化掉。

避免扫描组件树的各个部分

Angular 2 将允许开发人员为变更检测机制提供一些保证,以避免扫描组件树的某些部分。开发者基本上有两种选择:

  • 使模型成为可观察对象:Angular 将检测到这一点并注册自己以观察模型。这样,如果模型发生变化/保持不变,Angular 将通过可观察机制获知,因此它不需要对该对象运行变化检测。
  • 使模型不可变,例如使用 Facebook 的 immutable.js 。同样的想法是 Angular 将检测到这一点并避免在不可变对象上运行变化检测。

目标:改进模块化

在 Angular 1 中,Angular 模块主要是对相关功能进行分组的依赖注入容器。

例如,在第一次需要依赖项时,这些模块不会根据它们的依赖项列表进行异步加载,例如 AMD 模块。

Angular 1 和模块延迟加载

Angular 1 延迟加载仍然可以使用 ocLazyLoad 之类的解决方案,但理想情况下,它应该是框架原生的东西并且更透明,并且根据这个 播客 ,似乎在 Angular 2 中也是如此(见 13:06) .

作为前端包管理器对 npm 的改进

此外,一般而言,Angular 团队正在研究 改进 npm 以使其对前端更加友好,不仅对于 Angular,而且对于任何前端库。

Angular 2 并不强制这样做,但如果我们选择这样做,我们可以利用新的 ES6 模块加载器,这是一个标准的异步模块加载器,可以通过 es6-module-loader pollyfill 使用。

目标:改进依赖注入

Angular 1 依赖注入是构建更多模块化应用程序的飞跃,但它有几个极端情况,如果不进行重大破坏性更改就无法再修复。

Angular 1 有一个全局对象池

Angular 1 中的一种 DI 极端情况是每个应用程序只有一个全局对象池。这意味着如果例如主路由加载 backendService 并且我们导航到路由 B,我们可以在那里延迟加载特定于该路由的其他服务。

问题是,假设我们用完全不同的实现延迟加载第二个 backendService :它会覆盖第一个!目前没有办法让两个服务名称相同但实现不同,这会阻止延迟加载以一种安全的方式在 Angular 1 中实现。

如果模块具有相同的名称,Angular 1 会悄悄地覆盖它们

这是一个允许例如在测试时用模拟替换服务层服务的功能,但如果我们不小心加载同一模块两次可能会导致问题。

Angular 1 中有多种 DI 机制

在 Angular 1 中,我们可以用不同的方式在多个地方注入依赖:

  • 在链接功能中按位置
  • 在指令定义中按名称
  • 在控制器函数中按名称等

在 Angular 2 中,目标是将这些机制统一为一个机制,以减少学习曲线并提高可读性。

Angular 2 DI 将如何改善这种情况

在 Angular 2 中,只有一种 DI 机制:按类型注入构造函数。


 element.addEventListener('keyup', function () {  
  console.log('Key pressed.');
});
});

只有一种机制这一事实使其更容易学习。此外,依赖注入器是分层的,这意味着在组件树的不同点可能有相同类型的不同实现。

如果组件没有定义依赖项,它将把查找委托给它的父注入器等等。这为在 Angular 2 中提供原生延迟加载支持奠定了基础。

目标:Web 组件友好

Web 的未来是 Web Components,而 Angular 2 从一开始就希望与未来的 Web 组件库兼容。为此,Angular 2 模板语法的目标之一是保持属性干净,不要在它们上面放置任何 Angular 表达式——一切都仅通过属性绑定。

要理解为什么这很重要,请看这个例子:


 element.addEventListener('keyup', function () {  
  console.log('Key pressed.');
});
});

这里我们有一个 Angular 1 组件,它与未来的 Web 组件库交互。

这里有什么问题?

好吧,Web 组件的行为就像浏览器组件一样,例如具有 img 标签。

因此,在页面启动时和 Angular 有机会启动之前,Angular 表达式将传递给直接作用于它的组件,就像图像元素立即使用提供的 url 加载图像一样。

这实际上就是为什么需要像 ng-src 这样的所有属性来解决这个问题的原因。

Angular 2 如何更好地与 Web 组件交互?

在 Angular 2 中,模板语法将避免绑定到普通属性,除非读取常量:


 element.addEventListener('keyup', function () {  
  console.log('Key pressed.');
});
});

[setting] 是一个属性绑定,它将表达式的值写入组件属性。任何地方都不会在普通属性中有 Angular 表达式,以防止与 Web 组件的互操作性问题。

支持 Shadow DOM

Web 组件的关键特性之一是 Shadow DOM 。这是一种本机浏览器机制,允许构建本机外观的组件,比如 select 的新实现。

Web 组件仍然可以在纯 HTML/CSS 中实现,但与主页隔离,在某些方面类似于在具有单独文档根的 iframe 中。

由于 Shadow DOM 目前仅在 Chrome 中实现,Angular 2 将通过 3 种不同的机制支持它:

  • 默认模式:默认情况下,Shadow DOM 未打开,组件内部显示在与主页相同的文档树中
  • 模拟 Shadow DOM:可以使用 Polymer 对 Shadow DOM CSS 隔离机制进行 pollyfiled,方法是在组件内动态添加 CSS 前缀,使 CSS 更加具体
  • 真正的 Shadow DOM:如前所述,这仅适用于 Chrome

目标:支持 Native Mobile – iOS 和 Android

Angular 2 将有两层,应用层和渲染层。例如,一个组件可以使用不同的 @View 注释进行注释,这些注释可以在运行时根据环境启用。

React Native 类似,Angular 2 将允许支持以下概念:

一次学习,随处写作

这意味着可以重用 Angular 的知识来构建本机应用程序和 Web 应用程序,尽管总会有一些差异。

目标:支持服务器端渲染

支持服务器端渲染对于 SEO 和用户感知很重要:在大型 Angular 1 应用程序中,我们可以在启动过程中清楚地看到页面的引导过程,即使使用模板缓存预填充也是如此。

这是 Angular 2 中目前不太清楚的部分之一,但这个想法可能会像这样实现:

  • 启动发生并且所有组件都被绑定
  • 但渲染不会发生
  • 页面在服务器中呈现并通过网络发送
  • Angular 将解析它并将页面的一部分注入 DOM,避免闪烁效果

目标:提高可测试性

在 Angular 2 中,编写真正的单元测试相对困难,因为例如 ng-model 确实需要测试 DOM,这导致使用 PhantomJs 等解决方案。

这种方法的问题是那些测试不再是单元测试,它们是具有以下问题的集成测试:

  • 他们执行起来很慢
  • 它们很脆弱,更难维护

这就导致了 测试金字塔 的倒置,也就是我们大部分的测试要么是UI测试,要么是集成测试,几乎没有真正的单元测试。这意味着构建不断地因真正的错误以外的原因而中断,并且测试工作所发挥的作用比我们想要的要小。

引入单独的渲染层将使单元测试变得超快且依赖性最小,从而使它们更易于编写和维护,并允许更频繁地运行它们。

目标:迁移到 Angular 2 的路径

Angular 2 的目标之一是为 Angular 1 提供一条清晰的迁移路径。只有当 Angular 2 接近其初始版本时,这一点才会变得清晰,但目前路由器将成为该迁移的主要推动者之一。

新的 Angular 2 路由器正在向后移植到 Angular 1,并将允许同一个应用程序同时拥有 Angular 1 和 Angular 2 路由。

结论

我对 Angular 2 感到非常兴奋,在尝试了几个组件之后,我可以看到它如何更容易学习并且对开发人员来说更透明。同样,本文中描述的许多内容(例如区域)都可以正常工作。

与 3rd 方库的集成得到了很大改进,如果 npm 可以改进以对前端代码更有帮助,那将是巨大的。

想尝试一下吗?

尝试一下肯定不会太早,如果你想试一试,这是一个 种子项目 ,Visual Studio Code 编辑器 Webstorm 已经提供了很好的 Typescript 1.5 支持。