Angular 4.0从入门到实战
AngularJS
优点
- 模板功能强大丰富,并且是声明式的,自带了丰富的Angular指令;
- 是一个比较完善的前端MVC框架,包含模板,数据双向绑定,路由,模块化,服务,过滤器,依赖注入等所有功能;
- 自定义Directive,比jQuery插件还灵活,但是需要深入了解Directive的一些特性,简单的封装容易,复杂一点官方没有提供详细的介绍文档,我们可以通过阅读源代码来找到某些我们需要的东西,如:在directive使用 $parse;
- ng模块化比较大胆的引入了Java的一些东西(依赖注入),能够很容易的写出可复用的代码,对于敏捷开发的团队来说非常有帮助
- 补充:Angular支持单元测试和e2e-testing。
缺点
- 双向数据绑过多时,会影响性能(原因:https://github.com/atian25/blog/issues/5)
- ngView路由只能有一个,不能嵌套多个视图,虽然有 angular-ui/ui-router · GitHub 解决,但是貌似ui-router 对于URL的控制不是很灵活,必须是嵌套式的,第三方模块;
- 作用域把Ag的执行环境和浏览器环境隔离开,原生事件影响不了Ag的作用域
- ng提倡在控制器里面不要有操作DOM的代码,对于一些jQuery 插件的使用,如果想不破坏代码的整洁性,需要写一些directive去封装插件,但是现在有很多插件的版本已经支持Angular了,如:jQuery File Upload Demo
- Angular 太笨重了,没有让用户选择一个轻量级的版本,当然1.2.X后,Angular也在做一些更改,比如把route,animate等模块独立出去,让用户自己去选择。
架构
Angular4.0
核心:组件
问题改进
- 默认绑定改为单向数据绑定
- TypeScript 2.1 和 2.2 的兼容Angular已更新为更新版本的TypeScript,提高了ngc的速度,并且有更好的类型检查机制。
- $scope不复存在
- 表单验证
- TypeScript 2.1 和 2.2 的兼容Angular已更新为更新版本的TypeScript,提高了ngc的速度,并且有更好的类型检查机制。
新增特性
- 命令行工具 AngularCLI
- 服务端渲染(Angular Universal)
- 加载速度
- SEO(单页应用对搜索引擎有很大的限制)
- 移动和桌面兼容
架构
其他前端框架
React->UI组件(V)
- React,虚拟DOM,更新DOM次数少,内容也少。速度快
- FlUX架构(UI组件化和数据单向更新)
- 服务器渲染(预渲染应用发送客户端,爬虫依赖服务器的响应)
Vue->雨溪大牛个人主导,只关注WEB,服务器渲染(缺点)
- 简单
- 灵活
- 性能
Ag4开发环境搭建
Amber Cli(模块的构建) -> Angular CLI <- Webpack(打包)
- Angular CLI (2以上的构建工具)
- npm install -g @angular/cli (安装)
- ng version
- ng new 项目名字 –skip-install(网络不好1)
- cnpm install(网络不好2)
- 编译,开发同步 np serve
- 测试打包 test build
安装报错
constructor(_configPath, schema, configJson, fallbacks = []) {
重装
npm uninstall -g angular-cli
npm cache clean
- 1
- 2
- 3
- 4
- 5
C:\Users\null\AppData\Roaming\npm\node_modules\@angular\cli\models\config\config
.js:17
constructor(_configPath, schema, configJson, fallbacks = []) {
^
SyntaxError: Unexpected token =
at exports.runInThisContext (vm.js:53:16)
at Module._compile (module.js:373:25)
at Object.Module._extensions..js (module.js:416:10)
at Module.load (module.js:343:32)
at Function.Module._load (module.js:300:12)
at Module.require (module.js:353:17)
at require (internal/module.js:12:17)
at Object.<anonymous> (C:\Users\null\AppData\Roaming\npm\node_modules\@angul
ar\cli\models\config.js:3:18)
at Module._compile (module.js:409:26)
at Object.Module._extensions..js (module.js:416:10)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
升级Node version 6.x or higher
Linux
npm install -g n
n stable
- 1
- 2
window直接替换就行了
运行报错
The "@angular/compiler-cli" package was not properly installed.
- 1
- 2
- Global package:
rm -rf node_modules dist
npm uninstall -g @angular/cli
npm cache clean- Reinstall and play (global)
npm install -g @angular/cli@latest
npm install
ng serve
https://stackoverflow.com/questions/42925690/angular2-cli-error-angular-compiler-cli-package-was-not-properly-installed/43021209
- .editorconfig webstrom配置文件
- karma.conf.js 自动化单元测试配置文件
- protractor.conf.js 自动化单元测试
- tslint 代码规范监测
- src
- app 应用的组件和模块
- 模块
- 组件
- assets 静态资源
- environments 环境配置 (多环境,开发,测试,生产)
- index 根文件
- main.ts 脚本执行的入口点(启动项目)
- polyfills 导入一些必要的库,支持老版本的ag
- css 全局样式
- test 自动化测试
- tsconfig TypeScript编译器
- app 应用的组件和模块
@Component**组件元数据装饰器**可以让开发者通过Angular 的Component创建一个类似Java的class,并同时提供额外的元数据用于定义在运行环境中,根据元数据的值来渲染组件,这个component将如何运行/实例化以及被使用。
Template 定义组件的外观
Controller包含组件的所有属性和方法,页面逻辑,处理模板上发生的事件
@NgModule
- declarations:只能声明组件,指令,管道
- imports 依赖模块
- Browser浏览器模块
- Form表单模块
- Http模块
- provides 模块提供了什么服务
- bootstrap 模块主组件
angular启动过程
angular-cli.json
- 启动加载页面 index.html
- 启动加载脚本 main.ts
- 脚本的工作 导入依赖模块,主模块app.module.ts,主模块的依赖模块…….在index.html寻找启动模块的主组件对应的css选择器
Angular Route
路由 - 分配特定的Url - 单页应用(Simple Page App)
利用url分配视图状态
- 子路由
- 保护路由
- 辅助路由
ctrl + 1 导入component ng new project routing
生成一个路由模块
app-routing.modul.ts路由配置,不用/开头
const routes : Routes = [
{path:'',component:IndexComponent },
......
]
第一种:
路由使用,使用数组是要解决增加传参的问题,字符串不方便
<a [routerLink] = "['/']" > 主页 </a>
第二种:
<input type="button" value="主页" (click)= "toIndex()" 事件绑定
控制器中定义
export class AppComponent {
constructor(private router: Router){}
toIndex(){
this.router.navigate(['/']);
}
}
404组件,通配符定义 **解决输入未定义路径报错的情况**
{path:'**',component:404Component },
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
404组件页要放在最后,因为Ag路由使用先配置,先匹配的原则。
路由传递数据
查询参数传递参数
/product?id=1&name=2 => ActiveRoute.queryParams[id]
路由路径传递参数
{path:/product/:id} => /product/1 => ActivatedRoute.params[id]
路由配置传递参数
{path:/product,component: ProductComponent,data:[{isProd:yrue}]}
=> ActivatedRoute.data[0][isProd]
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
查询传递参数
<a [routerLink] = "['/product']" [queryParams]="{id : 1}" > 主页 </a>
子组件:接收参数
自组件html:
{{ productId }}
路由传递参数
定义:
{path:product/:id”,component:ProductComponent },
html:
<a [routerLink] = "['/product',1]" > 主页 </a>
- 1
子组件:prams 替换queryParams
问题:当我们进行相同组件(参数不同)的路由切换时,无法正常切换(不能切换)?
问题来源:组件只创建一次,创建时constructor,ngOnInit被调用
问题解决:参数快照(snapshot) -> 参数预定
重定向路由
子路由
生成子组件=>定义模板内容=>接收路由参数=>添加子组件=>添加子组件连接
辅助路由(所有页面都显示的组件)
主路由(组件只在主路由显示):
outlets:{primary:'home',aux:"chat"}
- 1
- 2
路由守卫
通过返回的布尔值判断执行动作。
注意CanDeactivate多了一个泛型,也就是要声明一下要保护的组件
Resolve守卫要解决的问题就是当我们发送一个HTTP请求,要求路由切换时,请求是有延迟的,模板上的{{数据}}不能及时显现出来。可以在进入路由之前读取数据,立刻显现数据(略)
依赖注入(Dependency Injection)
在调用一个类的时候,先要实例化这个类,生成一个对象。
问题:写一个类,过程中要调用到很多其它类,甚至这里的其它类,也要“依赖”于更多其它的类。手工实例化过于频繁,臃肿。
依赖注入,全称是“依赖注入到容器”, 容器(IOC容器)是一个设计模式,它也是个对象,你把某个类(不管有多少依赖关系)放入这个容器中,可以“解析”出这个类的实例。
所以依赖注入就是把有依赖关系的类放入容器(IOC容器)中,然后解析出这个类的实例。
控制反转(Inversion of Control)
代码控制权从代码内部转到代码外部
在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。
传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。
Angular 注入点只有一个,构造函数
好处:
- 解耦和
- 提高可测试性(创建简单硬编码测试组件)
当我们需要重用ProductComponent,而且还需要另一个productService类时
实例:
ng g component product1
ng g service shared/product
- 1
- 2
@Injetable()装饰器:把其他服务注入该服务中。将该服务是否能注入其他模块是根据其作用域判断的
提供器声明在模块中
绑定到模板
拥有相同的Token,但是拥有不同的类
提供器作用域:声明在组件中
ng g component product2
ng g service shared/anotherProduct
- 1
- 2
引入控制器:
绑定到模板
工厂方法和值声明提供器
声明提供器,简单的new一下。
遇到问题:服务对象需要根据条件来决定具体实例化哪个对象,或者实例化对象,也就是调用构造函数时需要传递参数
工厂方法创建的对象是单例对象
问题:方法内部实例化方法,该方法和服务对象紧密的耦合在了一起
问题:用具体值,变量来实现其依赖注入
手工使用注入器(避免使用,错误风格)
数据绑定
允许将组件控制器的属性,方法与组件的模板连接起来
Ag4默认改为单向数据绑定。
Ag1使用双向(性能问题),在页面维护一个存有所有表达式的列表,事件发生,反复遍历列表,确认数据是否完全同步,耗性能
组件控制器的属性和方法与组件的模板连接
事件绑定
事件可以是函数调用,属性赋值,DOM事件,自定义事件
属性绑定
差值表达式相当于属性绑定
控制器:
private imgUrl:String="http://placehold.it/320x150";
模板:
<img [src]="imgUrl">
<img src="{{imgUrl}}"> 插值
- 1
- 2
- 3
- 4
- 5
event.target.value DOM属性
event.target.getAttribute(‘value’) HTML
DOM属性随着输入的改变而改变(指定当前值),HTML属性不变(指定初始值,初始化DOM值,任务完成)
还有一个,input的disabled的值的改变无法改变disabled的设置(true,false),但是DOM的设置是有效的
<input value="unchanging" (input)="doOnInput($event)"
- 1
HTML属性绑定
当没有DOM属性绑定时,就使用html属性绑定,比如td
双向绑定
主要使用表单from在, 记忆方法:盒子[ ]里放香蕉( )
[(ngModel)] = ""
- 1
响应式编程(集成Rx系列)
管道:格式化模板输出的一种可重用对象 |
{{birthday | date:'yyyy-MM-dd HH:mm:ss' | }}
{{name | number:'2.1-4' | }}
整数几位+小数最少几位+小数最多几位
{{name | async}} 流
- 1
- 2
- 3
- 4
自定义管道
ng g pipe pipe/multiple
{{size | multiple}}
- 1
- 2
组件间通信
组件的输入输出属性
- 方法一:@Input() 输入属性通过属性传递数据
- 只能在有父子关系的组件中,由父组件传递给子组件
- 方法二:路由参数输入属性,构造函数
- @Output() 子组件向父组件传递数据
- EventEmitter<泛型>输出属性
- 捕捉EventEmitter和捕捉原生DOM的事件是一样的,事件绑定
- 双向数据绑定 输出属性的名字 = 输入属性的名字 + Change
中间人模式传递数据
组件生命周期以及angular的变化发现机制
红色方法调用一次,绿色方法调用多次.共九个方法
接口对JS或者TS开发者可选,最好添加,优点:IDE的支持和强类型的检查
- ngOnChanges是当一个父组件修改或者初始化一个子组件的输入属性的时候被调用的。如果没有输入属性,ngOnChanges永远不会被调用
- ngOnChanges首次调用一定发生在ngOnInit之前
小知识:
- 父子组件之间应该避免直接访问彼此的内部,而应该通过输入输出属性来通讯
- 组件可以通过输出属性发射自定义事件,这些事件可以携带任何你想携带的数据
- 在没有父子关系的组件之间,尽量使用中间人模式通信
- 父组件可以在运行时投影一个或者多个模板片段到子组件中
- 每个ag组件都提供了一组生命周期钩子,供你在某些特定的事件中发生时执行相应的逻辑
- ag的变更检测机制会监控组件属性的变化并自动更新视图,这个检测非常频繁,而且默认是针对整个组件树的,所以实现相关钩子时要谨慎
- 可以标记你的组件树中的一个分支,使其被排除在变更检测机制之外
表单处理
纯html表单:显示表单项,校验用户输入(required,pattern 验证消息过于模糊,消失时间过快,样式不能修改),提交表单数据(较好的应该是提交数据之前,调用数据处理方法,校验数据合法性,而且还能指定数据提交方法(异步,wobsocket,http)),不能满足特定需求,不够灵活。
模板式表单FormsModule(快速)
- 模板式表单FormsModule
- 只能在模板中操作数据模型,不能在代码中操作
- 不能访问数据模型相关的类,不能拿到类的引用,只能拿到数据,通过模板中给定的变量操作数据
- NgForm 自动添加该指令,接管Form指令
- 隐式创建一个FormGroup类的实例,FormGroup类用来存储数据模型和表单数据
- 自动发现NgModel的所有子元素,并自动将其添加到表单数据模型中
- 也可以绑定到其他标签上
- 如果想使用原生Form,可以在标签添加ngNoForm属性
- NgForm可以被模板本地变量引用,以便在模板中访问NgForm对象的实例
- 使用ngSubmit代替submit按钮,阻止跳转
- NgModel 双向数据绑定,标记一个html元素成为表单的一部分
- 隐式创建一个FormControl类的实例,利用FormControl来存储数据的值
- NgModelGroup
- 代表表单的一部分,允许将表单字段组织在一起,形成更清晰的层次关系
- 隐式创建FromGroup的实例
- NgForm 自动添加该指令,接管Form指令
响应式表单 ReactiveFormsModule(可适配多个渲染器)
- 响应式表单 ReactiveFormsModule
- 可以直接访问数据模型相关的类,但是由于不可引用, 只能在代码中操作数据模型,不能在模板中操作
- 第一步:创建数据模型,用来保存表单数据的数据结构
- FormControl
- FromGroup
- 一般用来代表整个表单,或者表单字段固定的子集
- 如果其中一个FromControl是无效的,那么整个Fo’r’mGroup就是i无效的
- FrormArray
- 一般用来代表可以增长的字段集合
- 类似FromGroup,但是有一个长度属性
- 没有key,只能通过序号访问相关的元素
- 第二部:使用指令将html连接到数据模型
- 表单验证
与服务器通信(http,socket)
npm init -y 引入带有默认配置的package。j'son
npm i @types/node -save
- 1
- 2
- Http 服务,它来自HttpModule,只有在调用subscribe时才发送请求
- Websocket协议,更简洁高效,而且时双向的
- 定义一个流需要定义三件事
- 什么时候发射下一个元素
- 什么时候抛异常
- 什么时候发射流结束的信号
构建(编译和合并) 部署
构建
ng build -> dist
部署
添加新的部署策略 如图 Ha'shLocationStrategy 解决构建后无法跳转路由的问题 url中出现了一个#号
- 1
- 2
- 3
- 4
- 5
多坏境
ng serve ng build + 环境参数
启动参数添加 --env=prod 生产环境 环境文件里面可以生成任意的文件属性,可以去引用