https://codelabs.developers.google.com/codelabs/your-first-pwapp/#0

1.介绍

  这里将使用PWA技术来构建一个天气web应用,这个app将会:

  1. 使用以及验证PWA的特性
  2. 使用API获取最新的天气数据
  3. 添加城市时,可以提供类似原生应用的交互

我们将会学到

  1. 怎么使用app shell来设计一个应用
  2. 怎么使app离线工作
  3. 怎么储存数据用于离线工作

我们需要什么

  1. 最新版本的chrome。其实用其他浏览器也可以,只不过我们想用chrome的devTools来体验一些新版本浏览器的特性
  2. 自己做一个web服务器,或者用web server for chrome(ps:这是一个方便快捷的静态文件服务器,访问chrome://apps/或者书签最左边进入应用,进入web server,选择一个目录,启动服务器即可)
  3. 下载示例代码
  4. 一个文本编辑器
  5. 基础的web知识

2.开始

  下载解压以上的实例代码,然后打开静态文件服务器,以实例代码中的work为根目录,然后通过服务器访问里面的index.html(把chrome改为手机模式)。

  访问可以看到有个圆形进度条在转。work目录中仅仅是一个骨架,后续会添加剩余的功能

3.app shell

  app shell是html、css和js的最小集合,用于为用户提供WAP接口以及保证了良好的性能。它的第一次加载是非常快的而且马上进行缓存。任意时刻用户打开app,app都会从本地缓存中加载shell,这使app打开的速度非常快。

  shell的结构将数据从核心的公共结构和UI中分离开来,所有的公共结构和UI都使用service worker进行本地缓存,PWA仅仅请求必须的数据。可以理解为shell就是app的架子(包括UI以及公共的结构),而数据则显示在这个架子上,数据经常会发生变化,所以必要的数据需要都次都去请求获取。用另一种说法解释就是shell就是应用商店中的原生应用,运行的时候再请求数据来显示。

  service worker是一个浏览器运行在后台的脚本,用于提供各种特性

为什么要使用app shell结构

  这可以使我们专注于速度,提供原生应用的用户体验:瞬间完成加载、实时更新,而且不需要应用商店

设计app shell

首先是把核心组件从设计中拆分出来,需要明白:

  • 界面上什么需要马上显示?
  • 其他关键的UI组件是什么?
  • 什么资源是app shell所需的?如图片、脚本和样式等。

在这个天气app中,关键的组件如下:

  • 头部组件:标题、添加和刷新按钮
  • 天气预报版块的容器组件
  • 天气预报版块的模板
  • 一个用于添加城市的对话框
  • 用于显示loading的指示器

4.实现app shell

  有很多种方式可以初始化一个项目,我们推荐使用web starker kit,因为在这个例子我们希望尽可能的简单,所以已经提供好了必备的资源。

为shell创建html

  index.html已经在work目录中了,而且样式也已经写好了

检查关键的JS代码

  以上界面已经准备好了。在scripts/app.js中可以发现:

  • app对象包含了一些应用关键的信息
  • 四个监听器:头部组件的添加和刷新、添加城市的对话框的添加和取消
  • app.updateForcecastCard用于添加或更新天气预报
  • app.getForecast用于获取最新的天气预报信息
  • app.updateForecasts用于更新所有的天气预报信息
  • initialWeatherForecast用于mock数据,能快速测试界面

测试

  以上JS和界面都准备好了,解除以下两端代码的注释(分别在html和js文件底部位置):

<!--<script src="scripts/app.js" async></script>-->
// app.updateForecastCard(initialWeatherForecast);

  重新运行,即可看到天气预报效果

5.快速初始化

  PWA应该是快速启动而且马上可以使用,以上app可以快速打开,但是还不可用,因为没有数据,需要通过ajax来获取数据,但这额外的请求会导致初始的加载变慢,所以应该在app第一次加载的时候,服务端进行一次数据直出,来提高速度。

注入数据(数据直出)

  服务端直接把天气数据注入到JS中,但是在生产环境,注入的天气数据要基于用于的IP地址。这里假设initialWeatherForecast就是服务器已经注入的数据,我们直接拿来用

区分是否是第一次运行

  什么时候才需要展示缓存中可能已经过时的天气数据呢?

  对于用户所添加的城市,应该本地保存到一个存储系统中,为了尽可能简单,这里使用localStorage,这对于生产环境不是非常好,因为它是阻塞的,对于某些设备可能非常慢。

  首先,需要保存用户的选项,添加代码如下:

  // TODO add saveSelectedCities function here
// Save list of cities to localStorage.
app.saveSelectedCities = function() {
var selectedCities = JSON.stringify(app.selectedCities);
localStorage.selectedCities = selectedCities;
};

  接着,添加初始化的代码,用来检查用户是否本地保存了一些城市(如果是则渲染出来),否则使用注入的数据:

// TODO add startup code here
app.selectedCities = localStorage.selectedCities;
if (app.selectedCities) {
app.selectedCities = JSON.parse(app.selectedCities);
app.selectedCities.forEach(function(city) {
app.getForecast(city.key, city.label);
});
} else {
/* The user is using the app for the first time, or the user has not
* saved any cities, so show the user some fake data. A real app in this
* scenario could guess the user's location via IP lookup and then inject
* that data into the page.
*/
app.updateForecastCard(initialWeatherForecast);
app.selectedCities = [
{key: initialWeatherForecast.key, label: initialWeatherForecast.label}
];
app.saveSelectedCities();
}

保存城市信息

  最后,需要修改添加城市butAddCity的监听器,来保存被选择的城市到localStorage中:

document.getElementById('butAddCity').addEventListener('click', function() {
// Add the newly selected city
var select = document.getElementById('selectCityToAdd');
var selected = select.options[select.selectedIndex];
var key = selected.value;
var label = selected.textContent;
if (!app.selectedCities) {
app.selectedCities = [];
}
app.getForecast(key, label);
app.selectedCities.push({key: key, label: label});
app.saveSelectedCities();
app.toggleAddDialog(false);
});

6.使用service worker来预缓存app shell

  PWA应该支持离线工作,而且对于断续的,缓慢的网络环境,也可以正常工作。实现这一点需要通过service worker来缓存app shell和data

注册sw

  先进行判断,支持的话再进行sw的注册

  if ('serviceWorker' in navigator) {
navigator.serviceWorker
.register('./service-worker.js')
.then(function() { console.log('Service Worker Registered'); });
}

缓存站点的资源

  当sw注册完成后的第一次访问页面,install事件就会被触发,在这个事件中对资源进行缓存,在sw.js内部执行如下代码:

var cacheName = 'weatherPWA-step-6-1';
var filesToCache = []; self.addEventListener('install', function(e) {
console.log('[ServiceWorker] Install');
e.waitUntil(
caches.open(cacheName).then(function(cache) {
console.log('[ServiceWorker] Caching app shell');
return cache.addAll(filesToCache);
})
);
});

  以上根据一个名字,打开一个cache,每个cache相当于一个缓存的集合,两两之间不会互相影响。addAll将一系列资源添加到cache中,这是一个原子操作。

  添加完以上代码后,刷新页面,在调试工具中可以看到当前域中有一个sw处于running状态(页面刷新前,这里是一片空白的):

  接着添加一个activate事件监听:

self.addEventListener('activate', function(e) {
console.log('[ServiceWorker] Activate');
});

  再次刷新页面,以上的sw状态,变成

  这是因为旧的sw仍然控制着当前页面,新的sw无法生效,就处于wating状态了(添加的activate回调也没执行)。这里旧的sw是指最开始页面刷新后,处于running状态的sw,里面只监听了一个install事件。后来我们修改了sw的代码,添加了一个activate监听,这就属于一个新的sw了。

  为了使新的sw能够生效,即能够更新的sw。需要手动关闭页面,然后重新打开页面,或者点击上面的skipWaiting。但是对于调试环境下,为了更加方便,可以启用 update on reload 选项,这样每次刷新页面,sw都会被强制更新生效。启用这个选项后强制更新,控制台会报一个错误(这是可以忽略的):

  更新完成的第一件事情就是。将旧的sw的缓存,或者更新后用不到的缓存移除掉,需要被移除的cache的名字保存在cacheName中

self.addEventListener('activate', function(e) {
console.log('[ServiceWorker] Activate');
e.waitUntil(
caches.keys().then(function(keyList) {
return Promise.all(keyList.map(function(key) {
if (key !== cacheName) {
console.log('[ServiceWorker] Removing old cache', key);
return caches.delete(key);
}
}));
})
);
return self.clients.claim();
});

   claim函数处理一个边缘情况:(未完...)

其他

  只要sw缓存了数据,下次离线访问的时候,请求会被sw拦截,sw可以返回对应的数据,包括当前的页面html等。

PWA天气应用的更多相关文章

  1. PWA学习心得

    PWA学习心得 一.什么是PWA Progressive  Web  App , (渐进式增强 WEB 应用) 简称 PWA ,是提升WebApp的体验的一种新方法,能给用户原生应用的体验. PWA ...

  2. PWA之消息推送——Notification

    原文 简书原文:https://www.jianshu.com/p/69042b92cae1 大纲 1.推送通知的概念 2.消息推送的知识点 3.实例 1.推送通知的概念 大部分现代 Web 应用都需 ...

  3. 【开源】分享2011-2015年全国城市历史天气数据库【Sqlite+C#访问程序】

    由于个人研究需要,需要采集天气历史数据,前一篇文章:C#+HtmlAgilityPack+XPath带你采集数据(以采集天气数据为例子),介绍了基本的采集思路和核心代码,经过1个星期的采集,历史数据库 ...

  4. C#+HtmlAgilityPack+XPath带你采集数据(以采集天气数据为例子)

    第一次接触HtmlAgilityPack是在5年前,一些意外,让我从技术部门临时调到销售部门,负责建立一些流程和寻找潜在客户,最后在阿里巴巴找到了很多客户信息,非常全面,刚开始是手动复制到Excel, ...

  5. 根据ip判断返回城市名称查询当地天气

    <?phpheader("content-type:text/html;charset=utf-8");date_default_timezone_set("Asi ...

  6. Android开发学习之路-自定义控件(天气趋势折线图)

    之前写了个天气APP,带4天预报和5天历史信息.所以想着要不要加一个折线图来显示一下天气变化趋势,难得有空,就写了一下,这里做些记录,脑袋不好使容易忘事. 先放一下效果: 控件内容比较简单,就是一个普 ...

  7. (福利)分享一个用android编写的简单的APP——爱吖天气

    这是本人随便编写的一个天气的APP,超级简单. 项目已同步至:https://github.com/nanchen2251/AiYaWeatherDemo 基本实现了天气查看,闪屏引导,天气基本信息, ...

  8. H5天气查询demo(二)

    最近刚好有空,学长帮忙让做个毕设,于是我提到了那个基于H5地理位置实现天气查询的方法,学长听了也觉得不错,于是就这个主题,扩展了一下,做了一个航班管理查询系统,为上次博客中提到的利用H5 api中的经 ...

  9. 阶段一:为View设置阴影和弹出动画(天气应用)

    “阶段一”是指我第一次系统地学习Android开发.这主要是对我的学习过程作个记录. 上一篇阶段一:通过网络请求,获得并解析JSON数据(天气应用)完成了应用的核心功能,接下来就要对它进行优化.今天我 ...

随机推荐

  1. @PathVariable注解的使用

    带占位符的 URL 是 Spring3.0 新增的功能,该功能在SpringMVC 向 REST 目标挺进发展过程中具有里程碑的意义. 通过 @PathVariable 可以将 URL 中占位符参数绑 ...

  2. Jquery | 基础 | .hover()

    https://api.jquery.com/hover/#hover-handlerIn-handlerOut http://jquery.cuishifeng.cn/hover.html

  3. TCP长链接调试利器nc

    最近做了不少TCP长链接的开发,包括服务端和客户端.本人感觉服务器端与客户端通信时最好采用字符串形式,这样可以做要平台无关,跨语言.如果采用对象序列化机制通用性会较差.另外采用字符串形式用nc调试很方 ...

  4. morphia(2)-添加

    1.简单 @Test public void add() throws Exception { final Employee em = new Employee("遥远2",500 ...

  5. Storm编程入门API系列之Storm的Topology默认Workers、默认executors和默认tasks数目

    关于,storm的启动我这里不多说了. 见博客 storm的3节点集群详细启动步骤(非HA和HA)(图文详解) 建立stormDemo项目 Group Id :  zhouls.bigdata Art ...

  6. Spring AOP初步总结(二)

    该篇为Spring AOP的一个应用案例:系统日志 需求:将任何删除,更改或新增数据库的操作汇总到数据库中 步骤1:编写切面 @Aspect @Component public class SysLo ...

  7. BootStrap的基本使用

    bootstrap 现成的css样式,直接调用类作用是快速写出页面又称UI框架Bootstrap中文网LESS是预处理器CSS预处理器定义了一种新的语言,基本的思想是用一种专门的编程语言,开发者只需要 ...

  8. 利用nodejs读取数据库数据生成树结构的json数据

    在做后台管理界面的时候,几乎少不了的一个结构就是树形结构,用来做菜单导航: 那么,最希望的就是树结构的所有数据都是读取的数据库,而不是直接代码当中写死,那我们就一步一步来看: 一,建表 字段通常包括: ...

  9. mui开发中获取单选按钮、复选框的值

    js获取单选按钮的值 function getVals(){ var res = getRadioRes('rds'); if(res == null){mui.toast('请选择'); retur ...

  10. 解析C++普通局部变量与指针类型的对象变量的应用区别

    首先我们先来了解一下MFC中的CPtrArray类,他可以说是CObject类型指针对象的集合.通过intAdd( CObject* newElement );注意参数是一个指针类型)可以向集合中添加 ...