App Shell 模型

作者:AddyOsmani

App Shell架构是构建 Progressive Web App 的一种方式,这种应用能可靠且即时地加载到您的用户屏幕上,与本机应用相似。

App“shell”是支持用户界面所需的最小的 HTML、CSS 和 JavaScript,如果离线缓存,可确保在用户重复访问时提供即时、可靠的良好性能。这意味着并不是每次用户访问时都要从网络加载 App Shell。 只需要从网络中加载必要的内容。

对于使用包含大量 JavaScript 的架构的单页应用来说,App Shell 是一种常用方法。这种方法依赖渐进式缓存 Shell(使用服务工作线程)让应用运行。接下来,为使用 JavaScript 的每个页面加载动态内容。App Shell 非常适合用于在没有网络的情况下将一些初始 HTML 快速加载到屏幕上。

换个说法,App Shell 就类似于您在开发本机应用时需要向应用商店发布的一组代码。 它是 UI 的主干以及让您的应用成功起步所需的核心组件,但可能并不包含数据。

注:请尝试第一个 Progressive Web App代码实验室,了解如何为天气应用构建和实现第一个 App Shell。使用 App Shell 模型即时加载视频也演练了这种模式。

何时使用 App Shell 模型

构建 PWA 并不意味着从头开始。如果您构建的是现代单页应用,那么您很可能使用的就是类似于 App Shell 的模型,不管您是否这么称呼它。根据您使用的内容库或框架的不同,详细内容可能略有不同,但该概念本身与框架无关。

App Shell 架构具有相对不变的导航以及一直变化的内容,对应于和网站意义重大。 大量现代 JavaScript 框架和内容库已经鼓励拆分应用逻辑及其内容,从而使这种架构更能直接应用。对于只有静态内容的某一类网站,您也可以使用相同的模型,但网站 100% 是 App Shell。

如需了解 Google 构建 App Shell 架构的方式,请查看构建 Google I/O 2016 Progressive Web App。这个真实的应用以 SPA 开始创建 PWA,使用服务工作线程预先缓存内容、动态加载新页面、在视图之间完美过渡,并且在第一次加载后重用内容。

优势

使用服务工作线程的 App Shell 架构的优势包括:

  • 始终快速的可靠性能。重复访问速度极快。 第一次访问时即可缓存静态资产和 UI(例如 HTML、JavaScript、图像和 CSS),以便在重复访问时即时加载。内容可能会在第一次访问时缓存到系统中,但一般会在需要时才进行加载。

  • 如同本机一样的交互。通过采用 App Shell 模型,您可以构建如同本机应用一样的即时导航和交互,包括离线支持。

  • 数据的经济使用。其设计旨在实现最少的数据使用量,并且可以正确判断缓存的内容,因为列出不需要的文件(例如,并不是每个页面都显示的大型图像)会导致浏览器下载的数据超出所必需的量。尽管在西方国家和地区中,数据相对较廉价,但新兴市场并非如此,这些市场中连接和数据费用都非常昂贵。

要求

App Shell 应能完美地执行以下操作:

  • 快速加载
  • 尽可能使用较少的数据
  • 使用本机缓存中的静态资产
  • 将内容与导航分离开来
  • 检索和显示特定页面的内容(HTML、JSON 等)
  • 可选:缓存动态内容

App Shell 可保证 UI 的本地化以及从 API 动态加载内容,但同时不影响网络的可链接性和可检测性。 用户下次访问您的应用时,应用会自动显示最新版本。无需在使用前下载新版本。

注:Lighthouse审核扩展可用于验证使用 App Shell 的 PWA 是否获得了高性能。To the Lighthouse介绍了使用这个工具优化 PWA 的过程。

构建您自己的 App Shell

构建您自己的应用,明确区分页面 Shell 和动态内容。 一般而言,您的应用应加载尽可能最简单的 Shell,但初始下载时应包含足够的有意义的页面内容。 确定每个数据来源的速度与数据新鲜度之间的正确平衡点。

Jake Archibald 的离线维基百科应用就是使用 App Shell 模型的 PWA 好例子。它会在重复访问时即时加载,但同时使用 JS 动态抓取内容。系统随后会离线缓存此内容,以备以后访问。

App Shell 的 HTML 示例

此示例将核心应用基础架构和 UI 从数据中分离出来。请务必使初始加载尽可能简单,在打开网络应用后仅显示页面的布局。有些数据来自于应用的索引文件(内联 DOM、样式),其他数据加载自外部脚本和样式表。

所有 UI 和基础架构都使用服务工作线程本地缓存,因此,随后的加载将仅检索新数据或发生更改的数据,而不是必须加载所有数据。

您工作目录中的index.html文件内容应类似于以下代码。 这是实际内容的子集,不是完整的索引文件。 让我们看看它包含的内容。

  • 用户界面“主干”的 HTML 和 CSS,包含导航和内容占位符。
  • 用于处理导航和 UI 逻辑的外部 JavaScript 文件 (app.js),以及用于显示从服务器中检索的帖子并使用 IndexedDB 等存储机制将其存储在本地的代码。
  • 网络应用清单和用于启用离线功能的服务工作线程加载程序。
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>App Shell</title>
  <link rel="manifest" href="/manifest.json">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>App Shell</title>
  <link rel="stylesheet" type="text/css" href="styles/inline.css">
</head>

<body>
  <header class="header">
    <h1 class="header__title">App Shell</h1>
  </header>

  <nav class="nav">
  ...
  </nav>

  <main class="main">
  ...
  </main>

  <div class="dialog-container">
  ...
  </div>

  <div class="loader">
    <!-- Show a spinner or placeholders for content -->
  </div>

  <script src="app.js" async></script>
  <script>
  if ('serviceWorker' in navigator) {
    navigator.serviceWorker.register('/sw.js').then(function(registration) {
      // Registration was successful
      console.log('ServiceWorker registration successful with scope: ', registration.scope);
    }).catch(function(err) {
      // registration failed :(
      console.log('ServiceWorker registration failed: ', err);
    });
  }
  </script>
</body>
</html>

注:请参阅https://app-shell.appspot.com/,查看一个非常简单的、使用 App Shell 和内容服务器端渲染的 PWA 的真实演示。App Shell 可通过使用任意内容库或框架实现(如我们的所有框架上的 Progressive Web App讲座中所述)。您可以使用 Polymer (Shop) 和 React (ReactHNiFixit)查看示例。

缓存 App Shell

您可以使用手动编写的服务工作线程或通过sw-precache等静态资产预缓存工具生成的服务工作线程缓存 App Shell。

注:这些示例仅为呈现一般信息以及进行说明而提供。 您的应用使用的实际资源很可能不同。

手动缓存 App Shell

以下是使用服务工作线程的install事件将 App Shell 中的静态资源缓存到Cache API中的服务工作线程代码示例:

var cacheName = 'shell-content';
var filesToCache = [
  '/css/styles.css',
  '/js/scripts.js',
  '/images/logo.svg',

  '/offline.html’,

  '/’,
];

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);
    })
  );
});

使用 sw-precache 缓存 App Shell

sw-precache 生成的服务工作线程会缓存并提供您在构建过程中配置的资源。 您可以让此线程预先缓存构成 App Shell 的每个 HTML、JavaScript 和 CSS 文件。 所有资源都可以离线工作,并且可在随后的访问中快速加载相关内容,无需其他操作。

以下是在gulp构建过程中使用 sw-precache 的基本示例:

gulp.task('generate-service-worker', function(callback) {
  var path = require('path');
  var swPrecache = require('sw-precache');
  var rootDir = 'app';

  swPrecache.write(path.join(rootDir, 'service-worker.js'), {
    staticFileGlobs: [rootDir + '/**/*.{js,html,css,png,jpg,gif}'],
    stripPrefix: rootDir
  }, callback);
});

如需了解有关静态资产缓存的详细信息,请参阅使用 sw-precache 添加服务工作线程代码实验室。

注:sw-precache 对于离线缓存您的静态资源非常有用。对于运行时/动态资源,我们建议使用我们的免费内容库sw-toolbox

结论

使用服务工作线程的 App Shell 是实现离线缓存的强大模式,但同时还可以针对 PWA 的重复访问实现即时加载这一重要性能。您可以缓存自己的 App Shell,以便它可以离线使用并使用 JavaScript 填充其内容。

如果重复访问,这样还可让您在没有网络的情况下(即使您的内容最终源自网络)在屏幕上获得有意义的像素。

results matching ""

    No results matching ""