Angular初探PWA

0.663字数 1027阅读 255

所谓PWA是 Progressive Web App 的缩写,中文意思是 “渐进式 Web 应用”,PWA有与传统的Web应用相比有什么优势?在《PWA开发实战》一书中列出了以下几点:

  • 无连接状态下的可用性
  • 加载速度快
  • 推送通知
  • 主屏幕快捷方式
  • 媲美原生
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 复选框。


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方面也是非常方便的!

推荐阅读更多精彩内容