angular入门

程序设计原则:

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方式父组件可以获得子组件中的任意数据——一定程度上违反了“最少知识法则”。

@ViewChild的作用是声明对子组件元素的实例引用,意思是通过注入的方式将子组件注入@ViewChild容器中,你可以想象成依赖注入的方式注入。
只不过@ViewChild不能在构造器constructor中注入,因为@ViewChild()会在ngAfterViewInit()回调函数之前执行(可以在ngAfterViewInit生命周期钩子中获取到改子组件的实例
 

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);
  });

 

posted @ 2023-06-28 18:49  聂丽芳  阅读(77)  评论(0编辑  收藏  举报