为什么需要编译
Angular应用中包含的组件、HTML模板(比如:@Directive、@Component、@NgModule、@Pipe)很多都是JS VM无法解析的,所以在浏览器渲染应用之前,组件和模板必须要被Angular编译器转换为可以执行的JavaScript。
angular2编译模式
在 Angular 2 中有两种编译模式:
- JIT - Just-In-Time
- AOT - Ahead-Of-Time
JIT事件流:
开发流程:
- 使用TypeScript开发Angular应用(此处以ts举例)
- tsc编译
- 构建
- 压缩
- 部署
用户打开浏览器,他将经历以下步骤 (没有严格的CSP):
- 下载所有的JavaScript资源
- 启动Angular
- 通过JIT 编译处理生产js代码
-
渲染应用
AOT事件流:
- 使用Typescript开发Angular 应用
- 使用 ngc来编译应用(目前的ngc编译报错还不太好用,angular团队正在用tsc的方式进行优化,据说很快就会发包)
- 模板会被ngc编译成TypeScript文件(通常是)
- TypeScript编译为JavaScript代码
- 构建
- 压缩
- 部署
用户打开浏览器,他将经历以下步骤:
- 下载所有的资源
- 启动Angular
-
渲染应用
aot将compile的过程放在应用部署前,所以浏览器端承载的工作量就会大幅度减少,相应的页面加载时间也会大幅度减少,这也就意味着更快更好的用户体验。
JIT vs AOT:
编译方式 | 编译时机 | 构建速度 | 打包大小 | 性能/渲染速度 | 模板错误检查时间 | 安全性 |
---|---|---|---|---|---|---|
JIT | app运行时 | 快 | - | - | app运行时 | 低 |
AOT | app构建阶段 | 慢 | - | 更快 | app构建阶段 | 高 |
AOT优势:
渲染得更快
使用AOT,浏览器下载预编译版本的应用程序。 浏览器直接加载运行代码,所以它可以立即渲染该应用,而不用等应用完成首次编译,我们两个项目AOT加载速度相比JIT有3-5倍的提高需要的异步请求更少
编译器把外部HTML模板和CSS样式表内联到了该应用的JavaScript中。 消除了用来下载那些源文件的Ajax请求。需要下载的Angular框架体积更小
如果应用已经编译过了,自然不需要再下载Angular编译器了。 该编译器差不多占了Angular自身体积的一半儿,所以,省略它可以显著减小应用的体积。但是angular采用 Code Generation 的方式,生成的 ts/js 代码肯定是会比原来的 html 的文件大小要大的,所以在应用足够大(模版足够多)的情况下 AOT 的大小是可以反超 JIT 的大小的,很不幸,我们项目就是如此提早检测模板错误
AOT编译器在构建过程中检测和报告模板绑定错误,避免用户遇到这些错误。更安全
AOT编译远在HTML模版和组件被服务到客户端之前,将它们编译到JavaScript文件。没有模版可以阅读,没有高风险客户端HTML或JavaScript可利用,所以注入攻击的机会较少
JIT优势:
编译时间短,除非确实有动态组件的需求,否则jit唯一的优势就是能用来做在线 Demo和开发调试,参考知乎答案
我们两个项目AOT和JIT对比效果:
项目A:
编译方式 | 打包大小 | 渲染速度 |
---|---|---|
JIT | 5.86M | |
AOT | 10.8M |
项目B:
编译方式 | 打包大小 | 渲染速度 |
---|---|---|
JIT | 4.12M | |
AOT | 6.21M |
官方在View 和 DI 上了 View Engine 后aot编译后没有太多静态文件了,目前aot的编译大小已经低于jit(2017/10/19更新)
懒加载
懒加载也叫延迟加载,即在需要的时候进行加载,随用随载
在单页面应用中,如果没有应用懒加载,进入首页时会导致需要加载的内容过多,延时过长,不利于用户体验
运用懒加载将页面进行划分,按需加载页面,可以分担首页所承担的加载压力,减少加载用时
如果对首屏启动有更严格的要求,最好采用服务端渲染
angular2懒加载可以参考官方文档
摇树优化:
作用:消除unused code
原理:通过跟踪import和export语句进行静态分析,排除那些被导出过但又从未被导入的代码(ES6 modules 的静态特性)
目前大部分工具只能对ES2015模块摇树,因为那里有import和export语句,所以需要将ts编译成es2015(通过tsconfig配置实现)
- 摇树优化详细介绍可以参考angular官方文档
- gulp+rollup摇树优化可以参考angular-seed
- webpack2自带了tree-shaking,配置可以参考工程angular-starter
- angular-cli搭建工程:推荐使用,已经帮你集成,不需要再去繁琐配置各种打包摇树优化等
webpack2的摇树和rollup摇树区别可以参考知乎上这个回答
webpack2注意事项
项目目前使用的是webpack2,总结了下开发过程中遇到的坑:
- ngc-webpack不要设置resourceOverride,否则打包后的图片url等会有问题
- "JavaScript heap out of memory"可以通过设置node参数 node --max_old_space_size=4096(如不管用,参数可以设置更大试试)
- typescript使用2.0以上版本(摇树需要)
- 使用awesome-typescript-loader包的2.x及以上版本(摇树需要)
- 保证我们的应用和Angular2库代码在同一个位置(摇树需要)
- UglifyJsPlugin压缩代码, Webpack2可以删除Bundle中未使用的引用代码,但不会从Bundle中删除未使用的代码,在这种情况下就需要使用UglifyJsPlugin,它能够智能的移除未使用的代码
- 使用AOT在build时,浏览器渲染快,但是在模板足够多的情况下大于jit打包体积,配合gzip使用最佳,项目使用的是nginx,gzip在nginx详细配置可参考这篇文章
AOT注意事项
由于AoT的特性,部分在JIT模式下可用的方法在AoT下是不可行或者官方不建议的,开发代码的童鞋在aot模式下需要注意额外注意这些情况,github上8000+star的angular-starter工程总结如下(英文捉急,就不在这献丑翻译了):
- Don’t use require statements for your templates or styles, use styleUrls and templateUrls, the angular2-template-loader plugin will change it to require at build time.
- Don’t use default exports.
- Don’t use form.controls.controlName, use form.get(‘controlName’)
- Don’t use control.errors?.someError, use control.hasError(‘someError’)
- Don’t use functions in your providers, routes or declarations, export a function and then reference that function name
- @Inputs, @Outputs, View or Content Child(ren), Hostbindings, and any field you use from the template or annotate for Angular should be public
补充一些我们项目开发过程中遇到的问题,O(∩_∩)O 都是开发时代码不规范埋下的坑:
- 定义的函数传参必须匹配
- 等和非等判断类型必须一致
- 未在ts定义的变量不要在html模板中使用
总结
推荐大家在dev时使用jit可以提高开发调试效率,在prod时使用aot
参考:
http://blog.mgechev.com/2016/08/14/ahead-of-time-compilation-angular-offline-precompilation/
http://blog.rangle.io/optimize-your-angular2-application-with-tree-shaking/