渐进式 Web 应用中的脱机和网络连接支持Offline and network connectivity support in Progressive Web Apps

多年来,组织不愿意通过本机软件大量投资基于 Web 的软件,因为 Web 应用程序依赖于稳定的网络连接。For many years organizations were reluctant to invest heavily in web-based software over native software because web applications depended on stable network connections. 如今,Web 平台现在提供了强大的选项,使用户能够继续工作,即使网络连接不稳定或完全脱机也是如此。Today, the web platform now offers robust options that enable users to continue working, even if the network connection becomes unstable or goes completely offline.

使用缓存来提高 PBA 的性能Use the caching to improve performance of PWAs

随着服务 工作者的引入,Web 平台添加了 Cache API 以提供对托管缓存资源的访问权限。With the introduction of Service Workers, the web platform added the Cache API to provide access to managed cached resources. 此基于承诺的 API 允许开发人员存储和检索许多 Web 资源:HTML、CSS、JavaScript、图像、JSON 等。This Promise-based API allows developers to store and retrieve many web resources—HTML, CSS, JavaScript, images, JSON, and so on. 通常,缓存 API 在服务工作线程的上下文中使用,但它在对象的主线程中 window 也可用。Usually, the Cache API is used within the context of a Service Worker, but it is also available in the main thread on the window object.

API 的一个常见用途是在安装服务工作器时预缓存关键资源, Cache 如以下代码片段所示。One common use for the Cache API is to pre-cache critical resources when a Service Worker is installed, as shown in the following code snippet.

self.addEventListener( "install", function( event ){
    event.waitUntil(
        caches.open( "static-cache" )
              .then(function( cache ){
            return cache.addAll([
                "/css/main.css",
                "/js/main.js",
                "/img/favicon.png",
                "/offline/"
            ]);
        })
    );
});

此代码在服务工作线程生命周期事件期间运行,它将打开一个名为 的缓存,然后在它具有指向缓存的指针时,使用 方法向它添加 install static-cacheaddAll() 个资源。This code, which runs during the Service Worker install life cycle event, opens a cache named static-cache and then, when it has a pointer to the cache, adds four resources to it using the addAll() method. 通常,此方法与事件期间缓存检索 fetch 结合Often the approach is coupled with cache retrieval during a fetch event

self.addEventListener( "fetch", event => {
    const request = event.request,
                    url = request.url;
    
    // If we are requesting an HTML page.
    if ( request.headers.get("Accept").includes("text/html") ) {
        event.respondWith(
            // Check the cache first to see if the asset exists, and if it does, return the cached asset.
            caches.match( request )
                  .then( cached_result => {
                if ( cached_result ) {
                    return cached_result;
                }
                // If the asset is not in the cache, fallback to a network request for the asset, and proceed to cache the result.
                return fetch( request )
                       .then( response => {
                    const copy = response.clone();
                    // Wait until the response we received is added to the cache.
                    event.waitUntil(
                        caches.open( "pages" )
                              .then( cache => {
                            return cache.put( request, response );
                        });
                    );
                    return response;
                })
                // If the network is unavailable to make a request, pull the offline page out of the cache.
                .catch(() => caches.match( "/offline/" ));
            })
        ); // end respondWith
    } // end if HTML
});

只要浏览器对此网站提出请求,代码段就会在服务 fetch 工作器中运行。The code snippet runs within the Service Worker whenever the browser makes a fetch request for this site. 在该事件中,有一个条件语句在请求针对 HTML 文件时运行。Within that event, there is a conditional statement that runs if the request is for an HTML file. 服务工作线程使用 方法\ (检查该文件是否位于任何缓存 match() ) 。The Service Worker checks to see if the file already exists in any cache (using the match() method). 如果缓存中存在该请求,则返回缓存的结果。If the request exists in the cache, that cached result is returned. 如果没有,则运行该资源的新增内容,缓存响应副本供以后 fetch 使用,并返回响应。If not, a new fetch for that resource is run, a copy of the response is cached for later, and the response is returned. 如果 fetch 由于网络不可用而失败,则从缓存中返回脱机页面。If the fetch fails because the network is unavailable, the offline page is returned from the cache.

此简单介绍演示如何在 PWA 应用程序中使用渐进 (缓存) 。This simple introduction shows how to use caching in your progressive web app (PWA). 每个 PWA 是不同的,并且可能使用不同的缓存策略。Each PWA is different and may use different caching strategies. 代码看起来可能有所不同,并且你可能对同一应用程序中的不同路由使用不同的缓存策略。Your code may look different, and you may use different caching strategies for different routes within the same application.

在 PWA 中使用 IndexedDB 存储结构化数据Use IndexedDB in your PWA to store structured data

IndexedDB 是存储结构化数据的 API。is an API for storing structured data. 与 API 类似,它也是异步的,这意味着您可以在主线程中或 Web 工作线程(如服务工作线程) Cache 中使用它。Similar to the Cache API, it is also asynchronous, which means you may use it in the main thread or with Web Workers such as Service Workers. 使用 API 在客户端上存储大量结构化数据或二进制数据,如 IndexedDB 加密媒体对象。Use the IndexedDB API for storing a significant amount of structured data on the client, or binary data, such as encrypted media objects. 有关详细信息,请导航到使用 IndexedDB 上的 MDN 开始。For more information, navigate to MDN primer on using IndexedDB.

了解 PBA 的存储选项Understand storage options for PWAs

有时,你可能需要存储少量数据,以便为用户提供更好的脱机体验。Sometimes you may need to store small amounts of data in order to provide a better offline experience for your users. 如果是这样,你可能会发现 Web 存储的键值对系统的简单性满足您的需求。If that is the case, you may find the simplicity of the key-value pair system of web storage meets your needs.

重要

Web 存储是一个同步进程,在工作线程(如服务工作线程)中不可用。Web Storage is a synchronous process and is not available for use within worker threads such as Service Workers. 大量使用可能会给应用程序造成性能问题。Heavy usage may create performance issues for your application.

有两种类型的 Web 存储: localStoragesessionStorageThere are 2 types of Web Storage: localStorage and sessionStorage. 每个数据都作为单独的数据存储进行维护,与创建它的域隔离。Each is maintained as a separate data store isolated to the domain that created it. sessionStorage 仅在浏览会话期间保留 (例如,浏览器打开时(包括刷新和还原) )。persists only for the duration of the browsing session (for example, while the browser is open, which includes refresh and restores). localStorage 将一直保留,直到代码、用户或浏览器删除数据 (例如,当可用存储空间有限时) 。persists until the data is removed by the code, the user, or the browser (for example, when there is limited storage available). 下面的代码段演示如何使用 localStorage ,这类似于如何使用 sessionStorageThe following code snippet shows how to use localStorage, which is similar to how sessionStorage is used.

var data = {
    title: document.querySelector("[property='og:title']").getAttribute("content"),
    description: document.querySelector( "meta[name='description']" ).getAttribute("content")
};
localStorage.setItem( window.location, JSON.stringify(data) );

此代码段获取有关当前页面的元数据,并存储在 JavaScript 对象中。This code snippet grabs metadata about the current page and stores it in a JavaScript object. 然后,它使用 方法将该值存储为 JSON,并 localStorage setItem() 分配一个等于当前 URL 的 window.location 键。Then it stores that value as JSON in localStorage using the setItem() method, and assigns a key equal to the current window.location URL. 您可以使用 方法检索 localStorage getItem() 信息。You may retrieve the information from localStorage using the getItem() method.

以下代码段演示如何使用缓存来枚举缓存页面和提取元数据以执行任务,如生成链接 localstorage 列表。The following code snippet shows how to use caching with localstorage to enumerate cached pages and extract metadata to perform a task, such as building a list of links.

caches.open( "pages" )
      .then( cache => {
    cache.keys()
         .then( keys => {
        if ( keys.length )
        {
            keys.forEach( insertOfflineLink );
        }
    })
});

function insertOfflineLink( request ) {
    var data = JSON.parse( localStorage.getItem( request.url ) );
    // If data exists and this page is not an offline page, assuming that offline pages have the word offline in the URL.
    if ( data && request.url.indexOf('offline') < 0  )
    {
        // Build a link and insert it into the page.
    }
}

insertOfflineLink()方法将请求的 URL 传递给 localStorage.getItem() 方法以检索任何存储的元数据。The insertOfflineLink() method passes the URL of the request into the localStorage.getItem() method to retrieve any stored metadata. 检查检索到的数据,以查看数据是否存在,如果数据存在,可以针对数据采取操作,例如生成和插入标记以显示数据。The retrieved data is checked to see if it exists, and if it does, an action can be taken on the data, such as building and inserting the markup to display it.

在 PWA 中测试网络连接Test for network connections in your PWA

除了存储信息以便脱机使用之外,了解网络连接何时可用,以便同步数据或通知用户网络状态已更改也很有用。In addition to storing information for offline use, it is helpful to know when a network connection is available in order to synchronize data or inform users that the network status has changed. 使用以下选项测试网络连接。Use the following options to test for network connectivity.

navigator.onLine属性是一个布尔值,可让你了解网络的当前状态。The navigator.onLine property is a boolean that lets you know the current status of the network. 如果值为 true ,则用户处于联机状态,否则用户处于脱机状态。If the value is true, the user is online, otherwise the user is offline.

联机和脱机事件Online and Offline Events

了解网络是否可用非常有用,但你可能想要在网络连接发生更改时采取措施。Knowing whether the network is available is helpful, but you may want to take action when your network connectivity changes. 在这种情况下,你可能想要侦听网络事件并采取措施以响应网络事件。In this situation, you may want to listen and take action in response to network events. 事件在 、 和 window document 元素上可用 document.body ,如以下代码片段所示。The events are available on the window, document, and document.body elements as displayed in the following code snippet.

window.addEventListener("online",  function(){
    console.log("You are online!");
});
window.addEventListener("offline", function(){
    console.log("Oh no, you lost your network connection.");
});

另请参阅See also

若要了解有关管理脱机方案的信息,请导航到以下页面。To learn more about managing offline scenarios, navigate to the following pages.