Teach you how to build an offline application?

Author: Miles

What is an offline application?

Offline application refers to using offline caching technology to cache resources locally after being loaded for the first time, and return the local file directly when accessing it next time, even if there is no network connection.

advantage:

  • You can also open web pages without a network.
  • Since some of the cached resources are directly loaded from the local area, it can speed up the loading speed of web pages for users, and reduce server pressure and transmission traffic costs for website operators.

The core of offline applications is offline caching technology, which is currently relatively mature Service Workers. It can control the logic of caching through JavaScript code. In other words, this process can be handed over to the front end to handle.

Why do you need it?

Offline applications are actually a kind of caching technology. Some people may think that the HTTP caching strategy has developed quite maturely, so why do you need Service Workers? Front-end developers will think: "Why do you depend on the back-end for browser matters? The back-end just needs to provide data, and I want to control things like caching."

What are Service Workers

Service Workers are scripts that run in the background of the browser, and their lifecycle is completely independent of web pages. It has no direct access to the DOM, but can communicate with the UI process by sending messages through the postMessage interface. Intercepting network requests is an important function of Service Workers, through which it can complete functions such as offline caching, editing responses, and filtering responses.

Service Worker is not specially designed for caching, it can also solve problems such as Web application push, background long calculation and so on.

The easiest way to tell if a browser supports Service Workers is through the following code:

// 如果 navigator 对象上存在 serviceWorker 对象,就表示支持
if (navigator.serviceWorker) {
  // 通过 navigator.serviceWorker 使用
}

Register Service Workers

To access Service Workers to a web page, you need to register a script that describes the logic of Service Workers after the web page is loaded. Usually index.htmlit is registered in , the code is as follows:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
   
    </head>
    <body>
        <script>
            if ('serivceWorker' in navigator) {
                navigator.serviceWorker.register('/sw.js')
                    .then(reg => console.log('Service worker registered successfully!'))
                    .catch(err => console.log('Service worker failed to register!'));
            }
        </script>
    </body>
</html>

The logic of Service Workers will not take effect when the webpage is opened for the first time because the script has not been loaded and registered, but the logic in the script will take effect when the webpage is opened again later.

In Chrome, you chrome://inspect/#service-workerscan .

Implement offline caching

After successful registration, Service Workers will dispatch some events in its life cycle, and do some things at specific time nodes by listening to the corresponding events.

In the Service Workers script, a new keyword has been introduced to selfrepresent the current Service Workers instance.

After the Service Workers is successfully installed, an installevent , and the logic of caching resources needs to be executed in this event. The implementation code is as follows:

sw.js

// 当前缓存版本的唯一标识符,用当前时间代替
var cacheKey = new Date().toISOString();

// 需要被缓存的文件的 URL 列表
var cacheFileList = [
  '/index.html',
  '/app.js',
  '/app.css'
];

// 监听 install 事件
self.addEventListener('install', function (event) {
  // 等待所有资源缓存完成时,才可以进行下一步
  event.waitUntil(
    caches.open(cacheKey).then(function (cache) {
      // 要缓存的文件 URL 列表
      return cache.addAll(cacheFileList);
    })
  );
});

Next, you need to listen to the network request event to intercept the request and reuse the cache. The code is as follows:

self.addEventListener('fetch', function(event) {
  event.respondWith(
    // 去缓存中查询对应的请求
    caches.match(event.request).then(function(response) {
        // 如果命中本地缓存,就直接返回本地的资源
        if (response) {
          return response;
        }
        // 否则就去用 fetch 下载资源
        return fetch(event.request);
      }
    )
  );
});

The above code has implemented the caching function, but if the code changes, we also need to download new resources and update the cached resources.

The browser has the following mechanisms for Service Workers:

  1. Every time you open a webpage that has access to Service Workers, the browser will re-download the Service Workers script file (so be careful that the script file should not be too large). If there is a byte difference with the currently registered file, it will be It is considered a "new service worker".
  2. A new Service Workers thread will be started and its install event will be fired.
  3. When the currently open page on the website is closed, the old Service Workers thread will be terminated and the new Service Workers thread will take control.
  4. Once the new Service Workers thread takes control, its activate event is fired.

We can delete the original cache in the activate event.

// 当前缓存白名单,在新脚本的 install 事件里将使用白名单里的 key 
var cacheWhitelist = [cacheKey];

self.addEventListener('activate', function(event) {
  event.waitUntil(
    caches.keys().then(function(cacheNames) {
      return Promise.all(
        cacheNames.map(function(cacheName) {
          // 不在白名单的缓存全部清理掉
          if (cacheWhitelist.indexOf(cacheName) === -1) {
            // 删除缓存
            return caches.delete(cacheName);
          }
        })
      );
    })
  );
});

This completes the update.

Implement offline caching in Vue

Most of our current projects are integrated based on scaffolding, such as receiving vue. Before publishing, we are not sure how many files will be packaged. If the file directory structure of the build output is:

├── app_4c3e186f.js
├── app_7cc98ad0.css
└── index.html

sw.jsThen cacheFileListthe value in file should be:

var cacheFileList = [
  '/index.html',
  'app_4c3e186f.js',
  'app_7cc98ad0.css'
];

However, the names of these files are often not fixed. For example, some are named by hash. In this case, how to determine the list of file paths that need to be cached?

This needs to be implemented in webpack with the help of a plugin:

const path = require('path');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const { WebPlugin } = require('web-webpack-plugin');
const ServiceWorkerWebpackPlugin = require('serviceworker-webpack-plugin');

module.exports = {
  entry: {
    app: './main.js'// Chunk app 的 JS 执行入口文件
  },
  output: {
    filename: '[name].js',
    publicPath: '',
  },
  module: {
    rules: [
      {
        test: /\.css$/,// 增加对 CSS 文件的支持
        // 提取出 Chunk 中的 CSS 代码到单独的文件中
        use: ExtractTextPlugin.extract({
          use: ['css-loader'] // 压缩 CSS 代码
        }),
      },
    ]
  },
  plugins: [
    // 一个 WebPlugin 对应一个 HTML 文件
    new WebPlugin({
      template: './template.html', // HTML 模版文件所在的文件路径
      filename: 'index.html' // 输出的 HTML 的文件名称
    }),
    new ExtractTextPlugin({
      filename: `[name].css`,// 给输出的 CSS 文件名称加上 Hash 值
    }),
    new ServiceWorkerWebpackPlugin({
      // 自定义的 sw.js 文件所在路径
      // ServiceWorkerWebpackPlugin 会把文件列表注入到生成的 sw.js 中
      entry: path.join(__dirname, 'sw.js'),
    }),
  ],
  devServer: {
    // Service Workers 依赖 HTTPS,使用 DevServer 提供的 HTTPS 功能。
    https: true,
  }
};

You need to modify the above sw.jsfile with static values ​​written cacheFileListas follows:

// 需要被缓存的文件的 URL 列表
var cacheFileList = global.serviceWorkerOption.assets;

Note that the https protocol needs to be used.

Validation results

In order to verify that the Service Workers and cache are working, you need to check through Chrome's developer tools.

By opening the Application-Service Workers column of the developer tool, you can see the Service Workers registered on the current page.

Figure 3.12.1 View the Service Workers registered on the current page

{{o.name}}
{{m.name}}

Guess you like

Origin my.oschina.net/u/3620858/blog/5514292