程序设计原则:
1、YAGNI:You Are't Gonna Need It(不写不需要的代码)
2、DRY:Don't Repeat Yourself(不要cv自己的代码)
3、OCP:Open Close Principle(开闭原则:对扩展开放、对修改封闭)
4、Low Coupling, High Cohesion(高内聚低耦合)
5、Dimeter Law(迪米特法则,也称为"最少知识法则")一个对象/组件只负责必须的少量功能
angular框架
MVVM框架的组成:数据绑定、指令系统、组件式编程、路由和导航、状态保持、第三方组件库。
angular是由Google开发的2009年开发的MVVM框架,可用于开发web、移动、桌面应用的中大型框架。
速度与功能:Canvas 、 svg 、 视频播放 、 Web Worker 、WebSocket、 服务端渲染。
Angular开发方式:脚手架的方式,不能使用script的引入方式。
1、项目启动过程分析
1)、angular.json:NG项目的配置:
index:'src/index.html' --> 渲染dom树的入口 <app-root />
main:'src/main.ts' --> 编译打包的入口文件
2)、main.js --> bootstrapModule(AppModule)引导启动一个模块,ng中一个模块就是一个容器
model:模型,即数据,MVVM中的第一个M
module:模块,主要出现在 node.js、ES6和angular中
modal:弹框(模态对话框)
3)、app.module.ts --> bootatrap:[AppComponent]
4)、app.component --> selector:'app-root' templateUrl::'app.component.html'
5)、app.component.html --> HTML片段
2、angular核心概念之1:模块
Module:不同于node.js或ES6中的模块,NG中的模块就是一个抽象的容器,用于对组件进行分组。
整个应用初始时有且只有一个主组件:AppModule
3、angular核心概念之2:组件
组件:是一段可以反复使用的页面片段,如页头。。。
组件 = 模板template + 脚本script + 样式style。
NG中任何一个组件都必须声明在具体的模块中。
创建组件流程:
1)、创建组件class
import { Component } from '@angular/core'; // 装饰器 Decorator,用于指定class的用途 @Component({ selector: 'myc01', template: '<h2>我的组件c02</h2><hr>', styleUrls: [], }) export class MyC01Component {}
2)、在某一个模块中注册组件class
3)、使用已经注册过的组件
Angular提供的创建组件化的简化工具:
ng generate component 组件名 or npx ng generate(g) component 组件名
上述可简化为 ng g c 组件名
扩展:Nodejs官方安装的工具:
npm:第三方模块的维护工具
npx:第三方可执行文件的 执行工具,Node Package Executor,npx可用于执行当前项目中node_modules/.bin目录下的可执行文件。
4、angular核心概念3:数据绑定
一、数据绑定
1)、HTML绑定 {{ NG表达式 }}
NG表达式中可以执行的代码:算数运算,比较运算,逻辑运算,三目运算,调用函数。
创建对象和JSON序列化不可以在NG表达式中使用。
2)、属性绑定
形式1、直接在属性上用 {{ }} <p title="{{title}"/>
形式2、使用 [ ] 做属性绑定 <p [title]="title" />
3)、指令绑定
①、循环绑定: *ngFor
<ul> <li *ngFor="let item of empList; index as i">{{i+1}}-{{item}}</li> </ul>
②、选择绑定: *ngIf
<div *ngIf="isPaidUser; else elseBlock" class="vip">会员专享</div> <ng-template #elseBlock><span>非会员</span></ng-template>
③、样式绑定 [ngStyle] 和 [ngClass]
<div [ngStyle]="myStyleObj">样式绑定</div>
<button [ngClass]="myClassObj" (click)="loadMore()">加载更多</button> // ts myClassObj = { btn: true, 'btn-success': true, };
④、特殊的选择绑定 [ngSwitch]
<div [ngSwitch]="userLevel"> <p *ngSwitchCase="'normal'">欢迎您,普通客户</p> <div *ngSwitchCase="'vip'">欢迎贵宾归来</div> <h3 *ngSwitchCase="'blackgold'">黑金您好,可以为您服务吗?</h3> <span *ngSwitchDefault>您尚未登录</span> </div>
⑤、自定义指令:
创建指令的命令:ng g directive 指令名
自定义指令都是作为元素属性来使用的,selector应该是:[指令名]
Angular中的指令分为3类:
1)、组件指令:NG中Component继承自Directive。
2)、结构型指令:会影响DOM树的结构,必须以 * 开头。
3)、属性型指令:不会影响DOM树的结构,只会影响元素的外观或行为,必须以 [ ] 括起来。
④、事件绑定
<button (click)="printUname()">输出用户名</button>
⑤、双向数据绑定 [(ngModel)]
方向1、Model => View:模型变则视图变,用 [ ] 表示
方向2、View => Model:视图(表单元素)变则模型变,用 ( ) 绑定
// 首先在app.module.ts中引入模块 import { FormsModule } from '@angular/forms'; // BrowserModule:浏览器模块,其中导出了CommonModule模块,所以 ngIf 等可以直接使用,不包含ngModel。FormsModule包含了ngModel imports: [BrowserModule, AppRoutingModule, FormsModule],
<input [(ngModel)]="uname" (ngModelChange)="doUnameChange()" placeholder="请输入用户名" >ngModelChange:监听模型数据的改变
5、angular核心概念4:过滤器/管道
1)、Filter:用于在View中呈现数据时显示为另外一种格式,过滤器的本质是一个函数,接收原始数据转换为新的格式输出:function(oldVal){return newVal}
使用过滤器:{{ e.sex| 过滤器名 }}
angular2.x中,过滤器更名为 "管道(Pipe)"
自定义管道的流程:
①、创建管道class,实现转换功能
import { Pipe } from '@angular/core'; @Pipe({ name: 'sex', }) export class SexPipe { // 管道中执行过滤任务的是一个固定的函数 transform(val: number,lang='zh') { if (val === 1) { return '男'; } else if (val === 0) { return '女'; } else { return '未知'; } } }
②、在模块中注册管道
// app.module.ts中注册声明 import { SexPipe } from './sex.pipe'; declarations:[SexPipe ]
③、使用管道(可以使用 : 进行传参)
<p>性别过滤器:{{ 0 | sex:'en' }}</p>
创建管道的简便方式:ng g pipe 管道名
2)、预定义管道
angular中提供了几个预定义管道:lowercase、uppercase、titlecase、slice(切割字符串,其他显示为...)、json(object序列化为json字符串)、number(number:'4.2-3' 表示保留4位整数,保留至少2位小数,最多保留3位小数)、currency:'¥'(表示金额前加¥符号)、date(date:'yyyy-MM-dd HH:mm:ss')
3)、创建对象
①、手工创建式 ------- 自己创建 new
②、依赖注入式 ------- 无需手工new,只需要声明依赖
6、Angular核心概念5:服务和依赖注入
1、Service:angular认为组件是与用户交互的一种对象,其中内容都应该与用户操作有关的;而与用户操作无关的内容都应该剥离出去,放在“服务对象”中,为组件服务。例如:日志记录、计时统计、数据服务器的访问等。
提供者就会创建被依赖的对象,注入给服务需要者。
第一步:构建服务对象并指定服务提供者。
import { Injectable } from '@angular/core'; // 所有的服务对象都是"可被注入的" @Injectable({ providedIn: 'root', //指定当前服务对象在"根模块"中提供--AppModule }) export class LogService { doLog(action: string) { let uname = 'admin'; let time = new Date().getTime(); console.log(`管理员:${uname} 时间:${time} 动作:${action}`); } }
第二步:在组件中声明依赖,服务提供者就会自动注入进来,组件直接使用服务对象即可。
import { Component } from '@angular/core'; import { LogService } from '../log.service'; @Component({ selector: 'app-myc07', templateUrl: './myc07.component.html', styleUrls: ['./myc07.component.css'], }) export class Myc07Component { // 依赖注入:组件--服务使用者,必须声明依赖 log; constructor(log: LogService) { this.log = log; } doAdd() { let action = '添加新的商品:xxx'; this.log.doLog(action); } }
2、使用Angular官方提供的服务对象——HttpClient Service
HttpClient:服务对象用于向指定URL发送异步请求,使用步骤:
①、在主模块app.module.ts中导入HttpClient服务所在的模块
import { HttpClientModule } from '@angular/common/http'; imports:[HttpClientModule]
②、在需要使用异步请求的组件中声明依赖于HttpClient服务对象,就可以使用该对象发起异步请求了。
constructor(private http: HttpClient) {} //依赖注入 // 观察者模式 this.http.get(url).subscribe((res) => { console.log('得到了订阅的异步响应消息', res); });
面试题:前端有哪些异步请求工具?
①、原生XHR
const xhr = new XMLHttpRequest() 浏览器支持的原生技术,基于回调方式处理响应,有回调地狱的问题。
②、jQuery.ajax()
本质还是XHR,进一步封装而已,使用上比原生要简单,基于回调方式处理响应。
③、Axios
本质还是XHR,比原生要简单,基于“Promise”处理响应,请求可以排队也可以并发。
④、Angular HttpClient
底层也是XHR,比原生简单,基于“观察者模式”处理响应,请求可以排队、并发、撤销。
⑤、Fetch
本质不再是XHR,是W3C提出的新技术,有望取代XHR,目前部分浏览器不支持。天然基于Promise。
3、高阶话题:服务对象的作用范围
声明服务提供者的方式:
①、在根模块中提供服务对象——在整个应用中服务是单例。
@Injectable({ providedIn: 'root', })
②、在组件中提供服务对象——在每一个组件实例中,服务都有一个实例。
@Component({
...
providers:[HttpClient] })
注意:项目中只要服务对象中有属性,只能用方式②,否则推荐使用方式①。
扩展TypeScript
1、TS对属性和方法的访问定义了3种访问修饰符:
private:私有的,私有成员只能在本类内部使用。
protected:受保护的,只能在本类内部和子类的内部使用,其他地方不允许。
public:公共的,公共成员可以在本类内部和外部使用。
提示:一般情况下,class内属性不应该让外界随意访问,通常设置为private;方法一般运行调用,通常设置为public。
下面2种写法作用相同:
2、类和接口,class和interface
interface:特殊的类,要求某个class必须具备XX方法,例如管道类必须提供transform方法。
/** * 要求小汽车必须提供start和stop方法 */ interface Runnable{ start:Function, stop:Function } export class Car implements Runnable{ start(){} stop(){} }
7、生命周期
生命周期钩子函数调用的顺序:
1)、constructor:组件对象被创建时
2)、ngOnChanges():组件绑定的属性值发生改变
3)、ngOnInit():初始化指令/组件完毕——类似于Vue中的mounted
4)、ngDoCheck():组件检测到了系统对自己的影响
5)、ngAfterContentInit():组件内容初始化完成
6)、ngAfterContentChecked():组件的内容发生变化需要检查
7)、ngAfterViewInit():组件的视图初始化完成
8)、ngAfterViewChecked():组件的视图发生变化需要检查
9)、ngOnDestroy():组件即将被销毁(从DOM树上卸载),适合执行一些资源释放性语句。
8、组件间的数据通讯
1、父子间的组件传递:vue和angular中的父子间消息传递原理一样,都可以使用口诀:'Props Down Events Up'
2、子传父:子通过触发特定的事件,把数据传递给父组件,父组件提供事件处理的方法
父子组件传值的简便方式:父亲直接使用子组件的引用
提示:ViewChild装饰器用于将子组件识别符与某个属性关联起来,第一个参数必须是已经存在的子组件识别符(带#),第二个参数是static指定组件是否为“静态组件”——不会时有时无的组件。
注意:通过ViewChild方式父组件可以获得子组件中的任意数据——一定程度上违反了“最少知识法则”。
9、路由和导航
多页面应用:一个项目中有多个完整的HTML文件,使用超链接跳转——销毁一颗DOM树,同步请求另一颗,得到之后再重建新的DOM树;不足:DOM树会重复创建,间隔中客户端一片空白。
单页面应用:整个项目中有且只有一个“完整的”HTML文件,其他页面都只是DIV片段。整个项目中客户端只需要下载一个HTML页面,创建一个完整的DOM树,页面跳转都只是一个div替换另一个div而已——能够实现过场动画。不利于SEO访问优化。
1)、angular定义路由步骤:
注意:
①、路由词典中的路由地址不能以 / 开头或结尾,中间可以。
②、路由中可以指定一个默认首页地址: { path:' ' }。
③、路由词典中每个路由要嘛指定component,要嘛指定redirectTo,同时需要指定路由匹配方式pathMatch。
④、路由 ** 匹配所有地址。该地址只能用于整个路由词典的最后一个。
2)、路由跳转/导航
路由跳转的方式:
方式1、使用模板方法
<!-- 传统的链接可以跳转,但是属于DOM树完全重建 --> <!-- <a href="ucenter"></a> --> <!-- 可以用于任何标签上,跳转地址应该以 / 开头 --> <a routerLink="/ucenter">进入用户中心</a> <button routerLink="/ucenter">进入用户中心</button>
方式2、使用脚本方法
Router类是RouterModule提供的一个服务类,声明依赖即可使用。
// 服务注入 import { Router } from '@angular/router' constructor(private router: Router) {}
this.router.navigateByUrl('/url');
3)、路由参数的获取
constructor(private route: ActivatedRoute) {} ngOnInit() { this.route.params.subscribe((data) => { console.log('得到了订阅的路由参数:', data); }); }
4)、路由嵌套
5)、路由守卫
某些路由地址只能在特定条件下才能访问。
ng g guard xxx
步骤:
①、创建路由守卫class
@Injectable({ providedIn: 'root', }) export class LoginGuard{ canActivate(){ return true //允许激活 } }
②、在路由词典中使用守卫
path: 'ucenter', component: UserCenterComponent, canActivate: [LoginGuard], //导航守卫
angular移动开发
创建移动APP可用的技术:
关于三大框架的组件/资源库:
github上搜索 awesome vue、awesome angular、awesome react
UI组件库——ionic
Rxjs解决异步
1、回调函数解决异步
function getPromiseData(cb:Function){ setTimeout(()=>{ let userName = '张三' cb(userName) },1000) } // 使用 getPromiseData((data:string)=>{console.log(data); })
2、promise
function getPromiseData(){ return new Promise((resplve,reject)=>{ setTimeout(()=>{ let userName = '张三' resplve(userName) },1000) }) } getPromiseData().then(res=>console.log(res))
3、rxjs获取异步方法:比Promise要强大,可以中途撤销,可以发射多个值,提供了多种函数工具等。
import {Observable} from "rxjs" function getRxjsData(){ return new Observable((observe)=>{ setTimeout(()=>{ let userName = '张三' observe.next(userName) },3000) }) } getRxjsData().subscribe(data=>{ console.log(data); })
1)、unsubscribe
const stream = getRxjsData() const d = stream.subscribe(data=>{ console.log(data); }) // 取消订阅 setTimeout(()=>{ d.unsubscribe() },1000)
2)、多次执行
以下代码promise其实只会执行1次
function getPromiseIntervalData(){ return new Promise((resolve)=>{ setInterval(()=>{ let userName = '张三' resolve(userName) },1000) }) }
使用rxjs:会源源不断的执行
function getRxjsIntervalData(){ return new Observable((observe)=>{ setInterval(()=>{ let userName = '张三' observe.next(userName) },1000) }) }
3)、rxjs提供的工具函数map、filter
import { filter, map } from 'rxjs/operators'; function getRxjsData() { let count = 0; return new Observable((observe) => { setInterval(() => { count++; observe.next(count); }, 1000); }); } const stream = getRxjsData(); stream .pipe( filter((val) => { if (val % 2 === 0) { return true; } return false; }) ) .subscribe((data) => { console.log(data); });
标签:log,绑定,angular,组件,class,路由,入门 From: https://www.cnblogs.com/nielifang/p/17485966.html