Rax PWA - 快速升级 Web 体验
作者:洋风 发布于:2019-12-02 14:36:51

Rax PWA,基于 Rax 1.x 的 PWA 解决方案。轻松通过简单的配置便可构建功能齐全的 PWA,为用户提供媲美原生 APP 体验的 Web 应用。


随着移动用户的增加,在手机上优化用户体验变得至关重要。但目前大多 Web 应用体验依然不佳,脆弱的网络连接与注重实时的交互方式,制约了 Web 应用的发展。我们希望通过被称为下一代 Web 应用的 Progressive Web Apps(以下简称 PWA)来改善这一现状。PWA 不是一项具体的技术,它是应用了一系列技术进行使用体验优化后的 Web 应用,目标为具有与原生应用媲美的用户体验。


我们为 Rax 1.x Web 工程提供了 Rax PWA 解决方案。与其他的 PWA 方案不同,除谷歌 PWA checklist 功能外,我们认为任何一个能够被整合成完整且轻配置的优化体验方案都可被看做是 Rax PWA 的内容。希望通过 Rax PWA 的努力,能够为开发者提供,提升用户体验同时提升开发效率的 PWA 方案,使开发者能够通过极简的配置开启 PWA 优化,增强 Web 应用的体验。


PS:可通过 npm init rax 快速创建一个 Rax 1.x 应用。


优化跳转


在由多个页面组成的 Web 应用中,切换页面时会重新加载并刷新所有资源:



通过单页应用(Single Page Web Application,以下简称 SPA),使 Web 应用更具响应性和即时性,拥有更流畅的类 APP 体验:



Rax 1.x 工程内置统一的 SPA 方案,无需安装和维护 Router 组件,配置 app.json 即可生成路由和组件对应关系。


{
  "routes": [
    {
      "path": "/",
      "source": "pages/Home/index"
    },
    {
      "path": "/page1",
      "source": "pages/Page1/index"
    }
  ]
}


对于移动端 SPA 加载性能是我们必须要考虑的问题,一起打包所有页面的资源必然会产生很大的 JS Bundle,在网络环境较差的情况下,容易阻塞资源加载加长等待时间,影响页面的可用性。


我们在 Rax 1.x 工程中内置了基于页面组件的代码拆分及按需加载方案:打包时将页面代码分离到不同的 JS Bundle 中,可得到更小的 JS Bundle,极大影响加载时间。 当用户访问一个路由时,动态加载对应页面所需要的文件,动态提高 APP 的性能,控制资源大小同时保证使用体验。


通过 SPA 方案赋予页面更好的交互体验,但在应用首次访问加载过程中,由于 SPA 渲染强依赖 JS 控制,JS 文件未完成加载时还可能存在白屏等待的情况,所以我们还需关注应用首次加载时的用户体验。


消除白屏


可通过 Rax PWA 提供的 App Shell 方案消除白屏等待。App Shell 是指支持页面展示的最小 HTML、CSS 和 JS 的资源集合,内容可为页面结构,页面片段,骨骼图等。它往往是纯 HTML 片段,一般只包括内联 CSS 和 base64 图片,不强依赖于 JS 框架。可以在加载、解析、执行 JS 之前就渲染出来,几乎消除了白屏时间,大大提高用户体验,确保提供即时、可靠的加载交互体验。


在 Rax 1.x 项目 app.json 文件中增加 shell 配置,通过 source 配置的文件即可快速生成 App Shell。


{
  "shell": {
    "source": "shell/index"
  },
  "hydrate": true
}


相比于谷歌提出的 App Shell 概念,Rax PWA 中的 App Shell 可使用 JSX 进行开发,它会被提前构建并将渲染结果插入 HTML 中,使其可快速加载到用户屏幕。在 JS 运行时,App Shell 会复用 HTML 中的节点绑定事件并激活组件。



如上图所示,应用正文部分在加载和 JS 控制渲染之前,展示的便是 App Shell 渲染出的 header 和 footer 部分。其中 {props.children} 即是页面的主体内容(App Shell 为应用最顶层组件)。


App Shell 和其他组件一样,拥有生命周期、可使用 hooks API 、可引用外部依赖等等,可通过 App Shell 实现很多功能,比如:


TabBar


App Shell 的内容可为页面最外层通用可交互结构。比如 TabBar,将其注入 HTML 中,可方便用户快速切换页面进行页面预览:



骨骼图


如上图所示,中间的空白空分对于用户来说体验非常不好,所以在渲染出数据之前,还可以通过骨骼图在白屏位置占位。骨骼图需要在最短的时间内渲染给用户,期望浏览器在加载完 HTML 之后就能先显示骨架屏,所以在 App Shell 中还可以通过骨骼图提升用户体验。


// /src/shell/index.jsx
import { createElement, Fragment, useEffect, useState } from 'rax';

function Shell(props) {
  const [showSkeleton, setShowSkeleton] = useState(true);
  useEffect(() => {
    setShowSkeleton(false);
  });
  return (
    <>
      <img style={{display:showSkeleton?'block':'none'}} src="data:image/png;base64,xxx" />
      {props.children}
    </>
  );
}

export default Shell;


效果如下:



最外层 App Shell 方案能缓解用户等待的焦虑,消除白屏影响,并进行简单的交互。但当用户网速较差,页面仍迟迟不能完成加载时,我们还可以帮助用户更快渲染出页面内容。


首屏瞬开


如下图所示,加载或刷新页面,页面内容瞬间渲染,我们将这种体验称为首屏瞬开



我们为 Rax 1.x 工程提供了 rax-plugin-pwa 插件,通过此插件可选择开启对应的 PWA 功能点,通过 快照缓存控制 功能可快速展示页面。


PS:可通过这篇文档,了解 Rax 1.x 工程插件。


快照


用户所处环境网速较差导致 JS 文件加载慢时,白屏时间增长会造成不好的体验。如果在内容变化不大的 Web 应用中可将页面内容填充为用户上一次访问时对应的内容,极速展示页面内容,提升首屏加载速度。


页面会记录当前页面内容称之为快照,下次访问相同页面时,快速将快照填充渲染至当前空白页面,待 JS 加载完成后再进行状态和事件绑定,完成页面渲染。效果如下:



在 build.json 中配置 rax-plugin-pwa 即可开启快照功能:


{
  "plugins": [
    ["rax-plugin-pwa", { "snapshot": true }]
  ]
}


缓存控制


在 Web 应用中我们可以通过 Service Worker (以下简称 SW) 控制页面缓存,它是 PWA 中最重要的概念之一。它独立于浏览器的主线程运行,不仅可以拦截用户的网络请求,还可以操作缓存,支持 Push 和后台同步等功能。相比使用重型封装框架 Workbox ,我们希望提供给用户更简单轻量透明并且易学习的解决方案。


在 build.json 中配置 rax-plugin-pwa 设置开启 SW 控制缓存:


{
  "plugins": [
    [ "rax-plugin-pwa", {
        "serviceWorker": {
          "preCacheUrlList": ["https://img.alicdn.com/tfs/rax.png"],   // 预缓存 url 列表
          "savedCachePatternList": ["/my-app\/.+/i"],   // 加入缓存的 URL 正则字符串列表
        }
      }
    ]
  ]
}


快速配置预缓存和缓存的 URL 正则字符串列表可快速开启 SW ,同时支持离线访问应用。


我们使用了缓存优先的控制方案,优先从缓存中读取结果,若缓存中不存在结果,则进行网络请求,网络请求成功后,再将结果写入缓存。保证请求可用同时,加速资源获取。



优化效果


对比标准 Rax 1.x 工程和开启快照和 SW 后的页面,加载时间和 Chrome 开发者工具中的 First Contentful Paint 时间都有了大幅度提升:

方案网络环境FCP时间Load时间
标准工程Chrome - Slow 3G15558.7 ms15474.7 ms
开启快照Chrome - Slow 3G2032.5 ms(87%)16317.9 ms
开启 SWChrome - Slow 3G3096.2 ms(80%)5147.8 ms(66%)


影响页面交互体验还有非常重要的一个环节-数据获取,数据的加载体验会直接影响页面的使用体验,在完成了首屏加速后,当然我们也准备了数据加速获取方案。


加速数据请求


可通过服务端渲染(Server-Side Rendering,以下简称 SSR),加速首屏渲染和数据请求。


SSR 是指将页面渲染逻辑前置到服务器端执行,由服务器端直接返回渲染好的页面,然后再由浏览器端进行状态和事件绑定,来达到页面的可交互状态。服务端控制数据请求可彻底摆脱弱网环境,将数据请求网络延迟减少到了最小程度。


SSR 拥有包含数据层面更快的首屏加载速度。使用客户端渲染时,完成首屏渲染一般包括以下步骤:解析 HTML -> 加载 JS -> 获取数据 -> 渲染。设备的运行状态或网络情况,将直接影响到每一个环节的执行时间,最终成倍地影响首屏的呈现时间。而服务端请求数据脱离了弱网环境渲染,将这种数据加载网络延迟减少到了最小程度。


SSR 还可以解决页面 SEO 问题,由于其页面内容是相对完整的,所以可以更好地被搜索引擎所理解。对于一些有 SEO 需求的业务,SSR 显得尤为重要。


我们还为 Rax 1.x 工程提供了 rax-plugin-ssr 插件,可以快速开始一个 SSR 应用。在 build.json 配置即可开启 SSR 功能。


{
  "plugins": [
    [ "rax-plugin-ssr" ]
  ]
}


作为 PWA 工程,我们还可以使用户更快速启动应用。


快速启动应用


无需打开浏览器并输入 URL 地址访问 Web 应用,通过点击保存至桌面的 PWA,用户可快速定位并启动应用。不仅支持全屏预览应用,还能定制 PWA 的启动画面的图标和颜色等,增强 Web 应用与操作系统的集成能力,提供更好的类 Native 体验。


通过配置 manifest 可快速生成 Web App Manifest(支持应用在手机主屏上创建图标的技术方案):


{
  "plugins": [
    [ "rax-plugin-pwa", {
        "manifest": {
          "name": "My Progressive Web App",
          "short_name": "MyPWA",
          "description": "My awesome Progressive Web App!",
          "background_color": "#ffffff",
          "icon": "src/public/icon.png",
          "start_url": "//market.m.taobao.com/app/mtb/rax-pwa-demo/web/index.html#/",
          "display": "standalone"
        }
      }
    ]
  ]
}


应用构建时会将应用图标文件 - icon 自动切割为96x96、128x128、180x180、256x256、512x512 五种尺寸的正方形 Icon 图以满足不同屏幕尺寸的需求,达到高保真的效果,同时兼容不同平台按照浏览器标准生成 Web App Manifest 。


用户在访问开启 manifest 的 Web 应用后,便可选择将其添加到桌面。


持续关注移动 Web 体验


Rax PWA 解决方案充斥着 PRPL (Push/Preload、Render、Precache、Lazy-Load)开发模式。当前 Rax PWA 仍处在进行时,我们将继续探索 PWA 的更多可能性,不断在技术、业务上提升产品体验。


当前我们也在积极筹备优化开发者的开发体验、提供能有更好交互体验和高性能的组件体系、不同容器中的 Web 应用体验并打通更多链路(比如在手淘中的 Web 页面加速)等等,任何可以切实优化或改进移动 Web 体验的方案我们都将付诸行动,为用户带来更好的移动体验。


当然我们也将在今年双十一中使用 Rax PWA 方案优化端外页面性能体验。让我们一起努力,再一次带来让用户惊叹的 Web 应用体验吧~


详细使用请见:https://rax.js.org/pwa