所谓PWA是 Progressive Web App 的缩写,中文意思是 “渐进式 Web 应用”,PWA有与传统的Web应用相比有什么优势?在《PWA开发实战》一书中列出了以下几点:
- 无连接状态下的可用性
- 加载速度快
- 推送通知
- 主屏幕快捷方式
- 媲美原生
从渐进学习的角度,本文将初步探索使用Angular框架来实现PWA的“无连接状态下的可用性”
准备工作
1、安装脚手架工具
$ npm install -g @angular/cli
2、创建项目,命名为 gotham-hotel 即《PWA开发实战》中的 哥谭帝国酒店
$ ng new gotham-hotel
...
$ cd gotham-hotel
3、初始化配置, 添加样式库,本项目选用 NG-ZORRO
$ ng add ng-zorro-antd
4、安装后端数据模拟工具 json-server
$ npm install --save-dev json-server
5、修改package.json文件,添加json-server的启动指令
...
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e",
"json": "json-server data.js -p 3500"
},
...
6、添加模拟数据:在package.json 同级目录添加 data.js 文件,文件中的 rooms 数组 模拟了 酒店的房型
module.exports = function () {
return {
rooms: [
{ id: '1', name: "标准房",
description: "45-58㎡ 1张双人床 有wifi 有窗", price: 5959 },
{ id: '2', name: "城景两张大床房",
description: "46-58㎡ 2张双人床 有wifi 禁烟 有窗", price: 7340 },
{ id: '3', name: "一室特大床套房",
description: "65㎡ 1张双人床 有窗", price: 8487 },
{ id: '4', name: "露台套房",
description: "94㎡ 1张双人床 有窗", price: 19755 },
],
}
}
准备Angular控件和服务
1、创建房型 数据接口
$ ng g interface services/room
接口定义如下:
export interface Room {
id: string;
name: string;
description: string;
price: number;
}
2、创建 房型 数据获取服务
$ ng g s services/rooms
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Room } from './room';
@Injectable({
providedIn: 'root'
})
export class RoomsService {
roomsUrl = `http://${location.hostname}:3500/rooms`;
constructor(private http: HttpClient) { }
getRooms(): Observable<Room[]> {
return this.http.get<Room[]>(this.roomsUrl);
}
}
3、创建房型列表组件, 用于展示房型信息
$ ng g c components/rooms
修改 rooms.component.ts 文件如下:
import { Component, OnInit } from '@angular/core';
import { Room } from '../../services/room';
import { RoomsService } from '../../services/rooms.service';
@Component({
selector: 'app-rooms',
templateUrl: './rooms.component.html',
styleUrls: ['./rooms.component.css']
})
export class RoomsComponent implements OnInit {
rooms: Room[];
error: string;
constructor(private roomsService: RoomsService) { }
ngOnInit() {
this.roomsService.getRooms()
.subscribe(
rooms => this.rooms = rooms,
error => this.error = error.message
);
}
}
修改rooms.component.html文件如下:
<nz-list [nzDataSource]="rooms" [nzRenderItem]="item" [nzItemLayout]="'horizontal'">
<ng-template #item let-item>
<nz-list-item [nzActions]="[price]">
<ng-template #price>
<nz-tag nzColor="green">{{item.price | currency:'CNY'}}</nz-tag>
</ng-template>
<nz-list-item-meta
[nzTitle]="nzTitle"
[nzAvatar]="'assets/room' + item.id + '.png'"
[nzDescription]="item.description"
>
<ng-template #nzTitle>
{{ item.name }}
</ng-template>
</nz-list-item-meta>
</nz-list-item>
</ng-template>
</nz-list>
<h3 *ngIf="error">错误信息: {{error}}</h3>
4、创建 home 组件
$ ng g c components/home --inlineTemplate=true
修改 home.component.ts 文件如下
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-home',
template: `
<a nz-button nzType="primary" nzSize="large" routerLink="rooms">
欢迎光临哥谭帝国酒店
</a>
`,
styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}
5、设置路由
在 app.module.ts 文件内加入如下路由信息
...
RouterModule.forRoot([
{ path: '', component: HomeComponent},
{ path: 'rooms', component: RoomsComponent },
])
...
修改app.component.ts 文件如下 (注意,要自己把logo图片和 room1、2、3、4图片放到assets目录下)
<nz-row nzType="flex" nzJustify="center">
<nz-col>
<img src="assets/logo.png" routerLink="">
</nz-col>
</nz-row>
<nz-row nzType="flex" nzJustify="center">
<router-outlet></router-outlet>
</nz-row>
初步测试
1、启动模拟后端数据
$ npm run json
2、启动angular服务
$ ng serve --port 0 --open
如果一切没有问题的话,在浏览器中可以看到
点击蓝色的按钮,可以进入房型列表页面
断线会如何?
到这里为止,一切都还只是一个常规的Web应用,让我们来模拟一下断线会出现什么情况
在 Chrome 中:
1、选择 Tools > Developer Tools (从右上角的 Chrome 菜单)。
2、进入 Network 页。
3、勾选Offline 复选框。
刷新页面,小恐龙就会出现了
PWA登场
做足了前戏,才轮到我们的主角上场了
1、安装pwa
$ ng add @angular/pwa
2、配置缓存信息
我们会发现项目的src目录下多出了一个 ngsw-config.json 文件,该文件用于定义pwa要缓存的信息,我们可以根据项目的需要进行修改,详细信息可参见angular官网 Service Worker 配置。
我们这个demo暂时只配置房型列表的api信息,在 ngsw-config.json 文件中添加内容如下:
...
"dataGroups": [
{
"name": "api-rooms",
"urls": ["/rooms"],
"cacheConfig" : {
"maxSize": 100,
"maxAge": "5d"
}
}],
...
表示对后端api中的rooms路径的请求进行缓存
3、重新构建项目
$ ng build --prod
构建完成后,会在dist目录下多出一个对应项目名的文件夹
4、安装独立的 HTTP 服务器
由于 ng serve 对 Service Worker 无效,所以必须用一个独立的 HTTP 服务器在本地测试我们的项目。 我们可以使用任何 HTTP 服务器,本例使用http-server
先安装http-server
$ npm install -g http-server
5、启动HTTP 服务
$ http-server -p 8080 -c-1 dist/gotham-hotel
6、访问 http://127.0.0.1:8080 如果没问题的话,首页和房型列表页都会正常展示。
7、按照上一节的步骤模拟断线,返回到首页,刷新浏览器,再进入列表页,会发现房型列表可以正常展示。
至此,PWA在无连接状态下的可用性就初步展现出来了。同时可以看到,angular在PWA方面也是非常方便的!