一.创建web服务器
1.使用nodejs创建服务器
-
创建一个server文件夹
-
初始化server文件夹
npm init -y
,创建package.json -
引入node的类型定义文件
npm i @types/node --save
,类型定义文件的作用:让开发者在typescript中使用现在已有的javascript的库(安装类型定义文件是保证对应内容可以使用typescript进行开发) -
搭建简单的web服务器
-
创建server文件夹,并创建hello_server.ts,内容填写
import * as http from 'http'; const server = http.createServer((request,response)=>{ response.end('hello node'); }); server.listen(8000);
-
通过webstrom的自动编译将ts转换成js,如下
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var http = require("http"); var server = http.createServer(function (request, response) { response.end('hello node'); }); server.listen(8000);
-
使用
node hello_server.js
启动服务器,访问8000端口就可以看到页面显示hello node
-
2.使用express创建restful的http服务
-
安装express框架,
npm install express --save
-
安装express的类型定义文件
npm install @types/express --save
-
创建服务器配置文件
aution_server.ts
- 内容如下
import * as express from 'express'; const app = express();//app用来声明服务器端可以使用的服务 app.get('/',(req,res)=>{ res.send('hello express'); });//声明用来处理get请求的服务 app.get('/products',(req,res)=>{ res.send('接收到商品查询请求'); }); const server = app.listen(8000,"localhost",()=>{ console.log('服务器已启动,地址是:http://localhost:8000');//服务器启动时执行的操作 });
-
将对象或是数组以json的格式返回使用
res.json(对象名);
-
通过请求查询参数获得对应的数据信息
app.get('/product/:id',(req,res)=>{ res.json(products.find((product)=>product.id == req.params.id)); });
3.监控服务器文件的变化
- 安装nodemonitor工具来监控源代码,当代码改变时自动重启node服务器并加载最新的代码。
npm install -g nodemon
- 使用
nodemon aution_server.js
启动服务 - 当修改服务器文件时,服务器就会自动重启
- 使用
二.Http通讯
- angular使用响应式编程的方式处理http请求
1.了解angular的Http服务
- Http的class就是标准的typescript类,对应不同的方法就是发送不同请求的
- 方法的参数
- url:发送的路径
- RequestOptionsArgs(可选的)定义了一组与请求相关的参数
- 每个方法都返回一个可观测的流
Observable<Response>
,通过订阅流来得到服务端响应数据和错误信息
2.发送Http请求
-
生成组件
ng g component product
-
代码内容
//控制器内容 import 'rxjs/Rx';//引入内容就可以进行响应式编程 export class ProductComponent implements OnInit{ dataSource:Observable<any>;//用来接受http服务中返回的流 products:Array<any>=[];//用于和模板进行数据绑定 constructor(private http:Http){//需在module引入http模块 this.dataSource = this.http.get('/api/products').map((res)=>res.json());//get方法只是定义了http请求 } ngOnInit(){ this.dataSource.subscribe((data)=>this.products=data);//当使用subscripe订阅方法时,请求才真正发出去,将结果存到products中 } }
-
创建
proxy.conf.json
文件- 文件内容
{ "/api":{//api表示请求的前缀 "target":"http://localhost:8000"//表示如果访问/api开头的路径,则将请求内容转发到localhost的8000端口上.如访问/api/products则会访问http://localhost:8000/api/products } }
-
修改一下脚本内容package.json
"scripts":{ "start":"ng serve --proxy-config proxy.conf.json"//添加相关参数配置 }
-
在模板上使用async异步管道获得对应的流数据
- 控制器代码
import 'rxjs/Rx';//引入内容就可以进行响应式编程 export class ProductComponent implements OnInit{ products:Observable<any>; constructor(private http:Http){//需在module引入http模块 this.products = this.http.get('/api/products').map((res)=>res.json());//直接获得对应流传给模板,由异步管道来处理 } ngOnInit(){} }
- 模板代码
<ul> <li *ngFor="let product of products | async"><!-- 使用async管道 --> {{product.title}} </li> </ul>
-
发送Http请求时带上请求头
- 控制器代码
import 'rxjs/Rx';//引入内容就可以进行响应式编程 import {Headers} from '@angular/http';//通过angular包获得Headers类 export class ProductComponent implements OnInit{ products:Observable<any>; constructor(private http:Http){//需在module引入http模块 let myHeaders:Headers = new Headers(); myHeaders.append("Authorization","Basic 123456"); this.products = this.http.get('/api/products',{headers:myHeaders}).map((res)=>res.json());//将请求头Header放到方法的第二个参数中 } ngOnInit(){} }
三.Websocket通讯
1.了解websocket通讯
- websocket是低负载的二进制协议,目前主流的浏览器已经内置了对websocket的支持
- 使用http协议进行通讯时,客户端与服务器的连接在同一时间数据传递的方向只有一个,要么在发送请求数据,要么在接受响应数据,是不能同时发送和接受的
- 在websocket协议中,是允许同一时间双方向数据传递的,如在发送请求的时候还能接受数据,另外websocket是一个长连接协议,不需要在每次发送和接受数据时建立连接,所以websocket的通讯延迟比http要低,且由于是长连接的存在,不需要在每次请求时都携带连接相关信息(如请求头中连接相关信息)
- 在nodejs的生态圈中,有很多项目实现了websocket协议,我们使用ws。安装ws依赖库
npm install ws --save
,安装类型定义文件npm install @types/ws --save-dev
2.使用ws创建websocket服务器
-
服务器端代码
import {Server} from 'ws'; const wsServer = new Server({port:8085});//配置在8085端口上创建websocket服务器 wsServer.on('connection',websocket => { websocket.send('这个消息是服务器主动推送的');//当有任意一个客户端连接到服务器的时候,给客户端推送一个消息 websocket.on('message',message=> console.log('接收到消息:'+message));//当接受到消息的时候,将消息打印到控制台上 }) setInterval(()=>{ if(wsServer.clients){//判断当前websocket上是否有客户端连接 wsServer.clients.forEach(client => {//利用forEach方法向所有客户端广播消息 client.send('这是定时推送'); }) } },2000);//每个两秒钟向客户端推送消息
-
浏览器端代码
- 生成新的service
ng g service shared/webSocket
- 在WebSocketService中编写代码
@Injectable() export class WebSocketService{ ws:WebSocket; constructor(){} //返回一个流,流中包含服务器推送的消息 createObservableSocket(url:string):Observable<any>{ this.ws = new WebSocket(url);//通过url连接一个对应的websocket服务器 return new Observable( this.ws.onmessage = (event) => observer.next(event.data);//在websocket接收到消息的时候发送事件 this.ws.onerror = (event) => observer.error(event);//在websocket发生错误的时候发送异常 this.ws.onclose = (event) => observer.complete();//在websocket关闭的时候流发出一个关闭的信号 ); } //向服务器发送一个消息 sendMessage(message:string){ this.ws.send(message);//利用websocket发送消息 } }
- 使用组件接受服务器消息,点击按钮向服务器发消息
ng g component webSocket
- 在组件中编写代码
export class WebSocketComponent implements OnInit { constructor(private wsService:WebSocketService) {}//注意需要声明提供器provider ngOnInit(){ this.wsService.createObservableSocket("ws://localhost:8085")//使用的是websocket协议,所以不是http .subscribe( data => console.log(data), err => console.log(err), () => console.log('流已经结束') );//参数一是正常回调,参数二是发生异常,参数三是流结束的时候的回调 } sendMessageToServer(){//在模板上设置按钮设置点击事件 this.wsService.sendMessage('Hello from client');//向服务器发送消息 } }
- 生成新的service
四.实战
-
当拿到的是一个Observable流时(如
Observable<Product[]>
),获得流中数据的方式- 在模板使用异步管道的方式获取数据
let product of products | async
,此时products是Observable<Product[]>
- 在控制器使用订阅的方式获得对应数据的内容
this.productService.getProduct().subscribe(products=>this.products=products)
,此时products是Product[]
- 在模板使用异步管道的方式获取数据
-
由于是异步属性,当我们在ngOnInit中调用异步请求方法获得内容时,另一个进程在渲染页面,页面上有异步请求需要的对象方法或属性时由于可能还没有拿到对应数据而报错,我们可以使用**
{{对象?.属性}}
**的方式调用,即加一个?号,此时当对象没有数据时则不会显示 -
利用service传递数据
//1.在service中设置事件信息,如下(订阅的传递的是ProductSearchParams) searchEvent:EventEmitter<ProductSearchParams> = new EventEmitter(); //2.订阅事件,如下(当事件被发射后,此处会在参数接受到事件发射的内容,并执行对应更新数据的操作,注意需要注入ProductService) this.productService.searchEvent.subscribe( param => this.products = this.productService.search(param); ); //3.发射事件,如下(其中this.formModel.value的值为ProductSearchParams类型,注意需要注入ProductService) this.productService.searchEvent.emit(this.formModel.value);