zoukankan      html  css  js  c++  java
  • PWA天气应用

    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等。

  • 相关阅读:
    The nineteenth day
    The eighteen day
    弱读下
    弱读上
    失爆 爆破音
    连读
    The seventeenth day
    The sixteenth day
    React 官方脚手架 create-react-app快速生成新项目
    pc端引入微信公众号文章
  • 原文地址:https://www.cnblogs.com/hellohello/p/8331304.html
Copyright © 2011-2022 走看看