网站PWA升级

前面的话

  渐进式网络应用 ( Progressive Web Apps ),即咱们所熟知的 PWA,是 Google 提出的用前沿的 Web 技术为网页提供 App 般使用体验的一系列方案。PWA 本质上是 Web App,借助一些新技术也具有了 Native App 的一些特性。本文将详细介绍针对现有网站的PWA升级html

 

效果演示

  之前端小站xiaohuochai.cc的PWA效果作演示,github移步至此前端

【添加到桌面】react

pwa

【离线缓存】webpack

   因为手机录屏选择没法进行离线录制,改由模拟器模拟离线效果git

pwa

 

概述

  PWA 的主要特色包括下面三点:github

  一、可靠 - 即便在不稳定的网络环境下,也能瞬间加载并展示web

  二、体验 - 快速响应,而且有平滑的动画响应用户的操做chrome

  三、粘性 - 像设备上的原生应用,具备沉浸式的用户体验,用户能够添加到桌面express

  主要功能包括站点可添加至主屏幕、全屏方式运行、支持离线缓存、消息推送等json

【PRPL模式】

  “PRPL”(读做 “purple”)是 Google 的工程师提出的一种 web 应用架构模式,它旨在利用现代 web 平台的新技术以大幅优化移动 web 的性能与体验,对如何组织与设计高性能的 PWA 系统提供了一种高层次的抽象

  “PRPL”其实是 Push/Preload、Render、Precache、Lazy-Load 的缩写

  一、PUSH/PRELOAD,推送/预加载初始 URL 路由所需的关键资源

  二、RENDER,渲染初始路由,尽快让应用可被交互

  三、PRE-CACHE,用 Service Worker 预缓存剩下的路由

  四、LAZY-LOAD 按需懒加载、懒实例化剩下的路由

【Service workers】

  Service Workers 是谷歌 chrome 团队提出并大力推广的一项 web 技术。在 2015 年,它加入到 W3C 标准,进入草案阶段

  PWA 的关键在于 Service Workers 。就其核心来讲,Service Workers 只是后台运行的 worker 脚本。它们是用 JavaScript 编写的,只需短短几行代码,它们即可使开发者可以拦截网络请求,处理推送消息并执行许多其余任务

  Service Worker 中用到的一些全局变量:

self: 表示 Service Worker 做用域, 也是全局变量 caches: 表示缓存 skipWaiting: 表示强制当前处在 waiting 状态的脚本进入 activate 状态 clients: 表示 Service Worker 接管的页面

  Service Worker 的工做机制大体以下:用户访问一个具备 Service Worker 的页面,浏览器就会下载这个 Service Worker 并尝试安装、激活。一旦激活,Service Worker 就到后台开始工做。接下来用户访问这个页面或者每隔一个时段浏览器都会下载这个 Service Worker,若是监测到 Service Worker 有更新,就会从新安装并激活新的 Service Worker,同时 revoke 掉旧的 Service Worker,这就是 SW 的生命周期

  由于 Service Worker 有着最近的权限接触数据,所以 Service Worker 只能被安装在 HTTPS 加密的页面中,虽然无形当中提升了 PWA 的门槛,不过也是为了安全作考虑

 

离线缓存

  下面来经过service worker实现离线缓存

  通常地,经过sw-precache-webpack-plugin插件来实现动态生成service worker文件的效果

  不过,首先要在index.html中引用service worker

<script> (function() { if('serviceWorker' in navigator) { navigator.serviceWorker.register('/service-worker.js'); } })() </script>

【SPA】

  经过create-react-app生成的react SPA应用默认就进行了sw-precache-webpack-plugin的设置。可是,其只对静态资源进行了设置

  若是是接口资源,则通常的处理是优先经过网络访问,若是网络不通,再经过service worker的缓存进行访问

  webpack.config.prod.js文件的配置以下

const SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin'); new SWPrecacheWebpackPlugin({ // By default, a cache-busting query parameter is appended to requests // used to populate the caches, to ensure the responses are fresh. // If a URL is already hashed by Webpack, then there is no concern // about it being stale, and the cache-busting can be skipped.
      dontCacheBustUrlsMatching: /\.\w{8}\./, filename: 'service-worker.js', logger(message) { if (message.indexOf('Total precache size is') === 0) { // This message occurs for every build and is a bit too noisy.
          return; } if (message.indexOf('Skipping static resource') === 0) { // This message obscures real errors so we ignore it. // https://github.com/facebookincubator/create-react-app/issues/2612
          return; } console.log(message); }, minify: true, // For unknown URLs, fallback to the index page
      navigateFallback: publicUrl + '/index.html', // Ignores URLs starting from /__ (useful for Firebase): // https://github.com/facebookincubator/create-react-app/issues/2237#issuecomment-302693219
      navigateFallbackWhitelist: [/^(?!\/__).*/], // Don't precache sourcemaps (they're large) and build asset manifest:
      staticFileGlobsIgnorePatterns: [/\.map$/, /asset-manifest\.json$/], runtimeCaching: [{ urlPattern: '/', handler: 'networkFirst' }, { urlPattern: /\/api/, handler: 'networkFirst' } ] })

【SSR】

  若是是服务器端渲染的应用,则配置基本相似。但因为没法使用代理,则须要设置网站实际路径,且因为静态资源已经存到CDN,则缓存再也不经过service worker处理

  配置以下

new SWPrecacheWebpackPlugin({ dontCacheBustUrlsMatching: /\.\w{8}\./, filename: 'service-worker.js', logger(message) { if (message.indexOf('Total precache size is') === 0) { return; } if (message.indexOf('Skipping static resource') === 0) { return; } console.log(message); }, navigateFallback: 'https://www.xiaohuochai.cc', minify: true, navigateFallbackWhitelist: [/^(?!\/__).*/], dontCacheBustUrlsMatching: /./, staticFileGlobsIgnorePatterns: [/\.map$/, /\.json$/], runtimeCaching: [{ urlPattern: '/', handler: 'networkFirst' }, { urlPattern: /\/(posts|categories|users|likes|comments)/, handler: 'networkFirst' }, ] }) ]

 

添加到屏幕

  没人愿意画蛇添足地在移动设备键盘上输入长长的网址。经过添加到屏幕的功能,用户能够像从应用商店安装本机应用那样,选择为其设备添加一个快捷连接,而且过程要顺畅得多

【配置项说明】

  使用manifest.json文件来实现添加到屏幕的功能,下面是该文件内的配置项

short_name: 应用展现的名字 icons: 定义不一样尺寸的应用图标 start_url: 定义桌面启动的 URL description: 应用描述 display: 定义应用的显示方式,有 4 种显示方式,分别为:   fullscreen: 全屏   standalone: 应用   minimal-ui: 相似于应用模式,但比应用模式多一些系统导航控制元素,但又不一样于浏览器模式   browser: 浏览器模式,默认值 name: 应用名称 orientation: 定义默认应用显示方向,竖屏、横屏 prefer_related_applications: 是否设置对应移动应用,默认为 false related_applications: 获取移动应用的方式 background_color: 应用加载以前的背景色,用于应用启动时的过渡 theme_color: 定义应用默认的主题色 dir: 文字方向,3 个值可选 ltr(left-to-right), rtl(right-to-left) 和 auto(浏览器判断),默认为 auto lang: 语言 scope: 定义应用模式下的路径范围,超出范围会以浏览器方式显示

  下面是一份常规的manifest.json文件的配置

{ "name": "小火柴的前端小站", "short_name": "前端小站", "start_url": "/", "display": "standalone", "description": "", "theme_color": "#fff", "background_color": "#d8d8d8", "icons": [{ "src": "./logo_32.png", "sizes": "32x32", "type": "image/png" }, { "src": "./logo_48.png", "sizes": "48x48", "type": "image/png" }, { "src": "./logo_96.png", "sizes": "96x96", "type": "image/png" }, { "src": "./logo_144.png", "sizes": "144x144", "type": "image/png" }, { "src": "./logo_192.png", "sizes": "192x192", "type": "image/png" }, { "src": "./logo_256.png", "sizes": "256x256", "type": "image/png" } ] }

【注意事项】

  一、在 Chrome 上首选使用 short_name,若是存在,则优先于 name 字段使用

  二、图标的类型最好是png,,且存在144px的尺寸,不然会获得以下提示

Site cannot be installed: a 144px square PNG icon is required, but no supplied icon meets this requirement

  三、start_url表示项目启动路径

  若是是'/',则启动路径为

localhost:3000/

  若是是'/index.html',则启动路径为

localhost:3000/index.html

  因此,最好填写'/'

【HTML引用】

   在HTML文档中经过link标签来引用manifest.json文件

<link rel="manifest" href="/manifest.json">

  要特别注意manifest文件路径问题,要将该文件放到静态资源目录下,不然,会找不到该文件,控制台显示以下提示

Manifest is not valid JSON. Line: 1, column: 1, Unexpected token

  若是index.html也位于静态资源目录,则设置以下

<link rel="manifest" href="/manifest.json">

  若是index.html位于根目录,而静态资源目录为static,则设置以下

<link rel="manifest" href="/static/manifest.json" />

【meta标签】

  为了更好地SEO,须要经过meta标签设置theme-color

<meta name="theme-color" content="#fff"/>

【SSR】

  若是是服务器端配置,须要在server.js文件中配置manifest.json、logo、icon等文件的静态路径

app.use(express.static(path.join(__dirname, 'dist'))) app.use('/manifest.json', express.static(path.join(__dirname, 'manifest.json'))) app.use('/logo', express.static(path.join(__dirname, 'logo'))) app.use('/service-worker.js', express.static(path.join(__dirname, 'dist/service-worker.js')))
相关文章
相关标签/搜索