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. socketserver 入门练习

    个人理解: 个人感觉socketserver其实就是为服务端专门提供的一个用于解决多用户并发访问需求的一个模块 小试牛刀: 服务端socketserver_server.py import socke ...

  2. iOS蓝牙开发总结-4

    蓝牙开发总结 只要熟悉蓝牙的流程,和蓝牙中每一个角色的作用,其实蓝牙通讯并没有想象中的难 1.蓝牙中心CBCentralManager:一般指得是iPhone手机 2.设备(外设)CBPeripher ...

  3. #10:wannanewtry——6

    HDU3401,列完转移方程拆分一下,正着.反着跑优先队列优化代表买或卖.初始化不大会搞…… #include <bits/stdc++.h> using namespace std; c ...

  4. springMVC-接收数据-参数绑定

    接收数据-参数绑定 #Method Arguments概观 Same in Spring WebFlux The table below shows supported controller meth ...

  5. css hack 笔记

    body{background-color:#000\9;}/*ie*/ body{background-color:#0f0\9\0;}/*ie9及以上*/ body{background-colo ...

  6. 基于.net core封装的xml序列化,反序列化操作

    需求: 由于在.net core中去除了Xml序列化XmlSerializer操作类.因此,在于一此数据传输当中出,需要用到对xml格式字符串的处理问题.因此封装了一个xml序列化与反序列化操作的类库 ...

  7. POJ 3461 kmp

    Oulipo Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 40168   Accepted: 16135 Descript ...

  8. 能挣钱的微信JSSDK+H5混合开发

    H5喊了那么久,有些人都说不实用,有些人却利用在微信中开发H5应用赚得盆满钵满.微信JSSDK + HTML 5,让移动Web开发与微信结合轻而易举!跨平台.零成本,让大众创业变得更方便. 我觉得现在 ...

  9. option标签selected="selected"属性失效的问题

    要在select标签上面加上autocomplete="off"关闭自动完成,不然浏览器每次刷新后将自动选择上一次关闭时的option,这样默认属性selected="s ...

  10. cvLoadImage,cvCloneImage的内存泄露问题

    本文转自: http://hi.baidu.com/%C3%A8%D1%DB%D3%E3/blog/item/9d947e1b2b05555742a9adfd.html/cmtid/9872c2260 ...