angularjs与angular
ts方面:
强类型语言约束,来减少运行时错误,在编写的时候就能发现
框架方面:
- 编译器支持 AOT 模式,大大减少了包的大小和提高了应用的性能,之前是JIT模式,运行时编译
- 重新设计的DI系统
- angular1 应用程序中所有的依赖项都是单例,我们不能控制是否使用新的实例。状态不好维护
- 命名空间冲突
- 不能和模块加载器一起使用
- DI 耦合度太高:angular1 中 DI 功能已经被框架集成了,我们不能单独使用它的 DI 特性
- 新的组件和模块化方案,支持组件继承
- 去除了scope,变化检测不再使用\$scope.\$watch()、\$scope.\$apply()、\$timeout() 等
- 路由支持懒加载和预加载
- 跨平台能力:为了能够支持跨平台,Angular 通过抽象层封装了不同平台的差异,统一了 API 接口
- ELementRef:
- 等等
- 并且angularjs 不再维护了,业务代码也不容易维护了
DI与IoC
IoC 是什么
Ioc - Inversion of Control , 即”控制反转”。在开发中, IoC 意味着你设计好的对象交给容器控制,而不是使用传统的方式,在对象内部直接控制。
如何理解好 IoC 呢?理解好 IoC的关键是要明确”谁控制谁,控制什么,为何是反转(有反转就应该有正转),哪些方面反转了”,我们来深入分析一下。
- 谁控制谁,控制什么: 在传统的程序设计中,我们直接在对象内部通过 new 的方式创建对象,是程序主动创建依赖对象;而 IoC 是有专门一个容器来创建这些对象,即由 IoC 容器控制对象的创建;谁控制谁?当然是 IoC 容器控制了对象;控制什么?主要是控制外部资源获取。
- 为何是反转了,哪些方面反转了: 有反转就有正转,传统应用程序是由我们自己在对象中主动控制去获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转了;哪些方面反转了?依赖对象的获取被反转了。
DI是什么
DI - Dependency Injection,即”依赖注入”:组件之间的依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。
IoC 和 DI 有什么关系?
其实它们是同一个概念的不同角度描述
angularjs中的应用
1 | var INSTANTIATING = {}, // 是否实例化中 |
getService 方法,如果实例存在,直接从缓存中拿出来。如果不存在就创建并缓存
angularjs实现DI的方式和缺点
- 单例:由它的创建方式可以知道
- 命名空间冲突:在系统中我们使用字符串来标识 service 的名称,假设我们在项目中已有一个 CarService,然而第三方库中也引入了同样的服务,这样的话就容易出现混淆
- 未能和模块加载器结合: 在浏览器环境中,很多场景都是异步的过程,我们需要的依赖模块并不是一开始就加载好的,或许我们在创建的时候才会去加载依赖模块,再进行依赖创建,而 angualr 的 IoC 容器没法做到这点。
angular中的DI
虽然也是单例的,但是Angular 中注入器是有层级结构的,即创建完注入器,我们可以基于它创建它的子注入器。
- resolveAndCreate() - 根据设置的 provider 数组创建注入器
- resolveAndCreateChild() - 调用已有注入器对象上的该方法,创建子注入器
脏值检查
Zones
Zone 是下一个 ECMAScript 规范的建议之一。Angular 团队实现了 JavaScript 版本的 zone.js ,它是用于拦截和跟踪异步工作的机制。
Zone 是一个全局的对象,用来配置有关如何拦截和跟踪异步回调的规则。Zone 有以下能力:
- 拦截异步任务调度
- 提供了将数据附加到 zones 的方法
- 为异常处理函数提供正确的上下文
- 拦截阻塞的方法,如 alert、confirm 方法
示例:
1 | Zone.current.fork({}).run(function () { |
在angularjs中
1 |
|
在angularjs中必须使用\$timeout或者$scope.\$digest()的方式来触发更新
在angular中
1 | // 浏览器环境 |
其实在 Angular 2 应用程序启动之前,Zone 采用猴子补丁 (Monkey-patched) 的方式,将 JavaScript 中的异步任务都进行了包装,这使得这些异步任务都能运行在 Zone 的执行上下文中,每个异步任务在 Zone 中都是一个任务,除了提供了一些供开发者使用的钩子外,默认情况下 Zone 重写了以下方法:
- setInterval、clearInterval、setTimeout、clearTimeout
- alert、prompt、confirm
- requestAnimationFrame、cancelAnimationFrame
- addEventListener、removeEventListener
NgZone
NgZone 是基于 Zone 实现的,它是Zone派生出来的一个子Zone,在 Angular 环境内注册的异步事件都运行在这个子 Zone 内 (因为NgZone拥有整个运行环境的执行上下文),它扩展了自有的一些 API 并添加了一些功能性的方法到它的执行上下文中。
在 Angular 源码中,有一个 ApplicationRef_
类,其作用是用来监听 NgZone 中的 onMicrotaskEmpty
事件,无论何时只要触发这个事件,那么将会执行一个 tick
方法用来告诉 Angular 去执行变化检测,简化版的代码如下:
1 | class ApplicationRef { |
现在我们先来总结一下前面所讲的内容:
- 异步操作被安排为任务
- Zones 跟踪任务的执行
- Angular 处理由执行异步操作引起的事件
- Angular 对所有组件执行变化检测,若发生变化则更新视图
JIT与AOT
在 Angular 应用程序中,包含了我们通过 Angular 提供的 API 实现的自定义指令。这些自定义指令对浏览器来说,都是无法识别的,因此每个 Angular 应用程序在运行前,都需要经历一个编译的阶段。
Just-in-Time 编译模式开发流程
- 使用 TypeScript 开发 Angular 应用
- 运行 tsc 编译 TypeScript 代码
- 使用 Webpack 或 Gulp 等其他工具构建项目,如代码压缩、合并等
- 部署应用
应用部署后,当用户通过浏览器访问我们应用的时候,她将经历以下步骤(非严格 CSP):
- 下载应用相关的资源,如 JavaScript 文件、图片、样式资源
- Angular 启动
- Angular 进入 JiT 编译模式,开始编译我们应用中的指令或组件,生成相应的 JavaScript 代码
- 应用完成渲染
Ahead-Of-Time 编译模式开发流程
- 使用 TypeScript 开发 Angular 应用
- 运行 ngc 编译应用程序
- 使用 Angular Compiler 编译模板,一般输出 TypeScript 代码
- 运行 tsc 编译 TypeScript 代码
- 使用 Webpack 或 Gulp 等其他工具构建项目,如代码压缩、合并等
- 部署应用
应用部署后,相比于 JIT 编译模式,在 AOT 模式下用户访问我们的应用,只需经历以下步骤:
- 下载应用相关的资源,如 JavaScript 文件、图片、样式资源
- Angular 启动
- 应用完成渲染
特性 | JIT | AOT |
---|---|---|
编译平台 | (Browser) 浏览器 | (Server) 服务器 |
编译时机 | Runtime (运行时) | Build (构建阶段) |
包大小 | 较大 | 较小 |
执行性能 | - | 更好 |
启动时间 | - | 更短 |
除此之外 AOT 还有以下优点:
- 在客户端我们不需要导入体积庞大的 angular 编译器,这样可以减少我们 JS 脚本库的大小
- 使用 AOT 编译后的应用,不再包含任何 HTML 片段,取而代之的是编译生成的 TypeScript 代码,这样的话 TypeScript 编译器就能提前发现错误。总而言之,采用 AOT 编译模式,我们的模板是类型安全的。
ELementRef
在应用层直接操作 DOM,就会造成应用层与渲染层之间强耦合,导致我们的应用无法运行在不同环境。