掌握聚合最新动态了解行业最新趋势
API接口,开发服务,免费咨询服务

在生产中的Progressive Web App – Dev Channel – Medium

本文由二方土君在众成翻译平台上翻译。

两年前,我们团队在google开始研究如何用JavaScript 库来减少构建Progressive Web Apps应用的障碍。

我们首先发布了像sw-precache 和 sw-toolboxService Worker 的工具,目前有1000多个商业用户在使用这两个工具来离线缓存数据和实时加载(第二次浏览的情况下)它们的产品网站。

在2017年,如果你没有充分利用Service Workers优势的话,当用户从首页回到你的应用的时候,你的应用将会在性能上失去优势。

让我们对比一下 CNet’s Tech Today 和Housing.com PWAs这两个网站前后加载的速度。在3G网络的情况下,一共花了几秒钟我的时候我们才可以看到第一屏的内容。再看看通过Service Worker缓存了框架外壳和数据后的应用,平均加载的时间比之前快了3到4秒。

未命名1507527488.png

哇!它们几乎是瞬间打开。使用Service Worker使网站加载和进入可交互状态的时间更快。这个复制了原生应用渴望性能的特点:一旦(web)应用被安装,应用前期重新加载的时间就会被分销并且没有任何延迟。

Service Workers 是解决所需资源加载性能问题的,而不单单只是解决“离线功能支持”的问题 —— Alex Russell, Chrome

Large sites like Twitter.com, who recently shipped 100% of their mobile web traffic to their PWAwith Service Worker, an Application Shell architecture and the PRPL pattern are also seeing similar wins:

像Twitter.com那些大型的网站,最近已经用Service Worker技术100%地把他们的移动端网站转变成PWA,也就是壳结构应用PEPL 模式也能达到差不到的优势。

Service Worker不是只能优化移动端和PWAs应用,它也可以提升桌面网站的加载性能。

比如,Flipkart这个网站会在你第一次访问的时候缓存它所需要的资源,这样当你再次访问的时候就会比第一次访问的时间快1.5倍。

Flipkart.com 桌面端使用了 sw-precache 来缓存静态资源,从而使再次访问的时间加快了好几秒。

就像JavaScript 启动性能里面提到,Serveice Worker也能优化你第一次执行的JavaScript V8代码,这样你就可以更快地去启动你的JS脚本。

Service Workers除了在缓存上的优化外,其它地方也可以优化。

比如当一个网站引用了Google的离线分析功能,因为这个功能是基于Service Worker和IndexedDB技术的,所以当一个用户在离线或者弱网络的情况下来访问的时候,我们就可以把这个分析功能进行挂起,等待这个用户恢复了正常网络的时候再去请求Google的分析服务。这个情景就像eBay在墨西哥的分类广告一样,这样就可以减少用户因为缺少必要信息而无法进行下一步操作的情况。

PWA这个概念在Google I/O 2015网站内部测试并获得成功之后,我们发现它真的很好并且想要去推广出去让所有人可以用得到。

Autotrack是一个广受好评的离线分析库,它致力于帮助人们更容易地去追踪和分析他们所关心的事情。现在它的插件已经支持PWA和SPA的URL变化,以及元素的可视性,还有用户的滚动操作,媒体查询,页面显示等等。这些插件就像 1Password 那样可以轻松地跟踪重要的事件,使得你不再为此而烦恼。

接下来我们本打算开始造一个网页通知推送的库,但是有一个机会让我们可以和Firebase合作,并做出了一个更好的方案,也就是Firebase Cloud Messaging。它是一个跨平台的,能够基于基本通信来发送信息或者数据的消息解决方案,并且它能够很好地在PWAs上运行。

目前,阿里巴巴也是众多在PWAs产品中使用FCM公司的其中一个。

We also contributed to the web-push library by Mozilla, an alternative folks can look at in this space.

我们也针对了人们在日常中常用到的火狐浏览器开发了一个web推送库

随着Service Worker逐渐成为我们很多库的核心部分,我们就需要一些工具来对他们做单元测试。于是我们就创造了一个叫作selenium-assistant的基于Selenium上端对端的跨平台库。我们也开发了sw-testing-helpers来在测试中管理Service Workers。

开始使用我们的JavaScript库

现在sw-precachesw-toolbox & offline-analytics在 Google Developer Codelabs 上已经是免费的了。

Service Worker 脚手架

sw-precache (可以很好地和 Webpack搭配一起) 为你构建一个Service Worker。在最简单的例子中,你可以为工程创建一个“dist”目录,然后它就可以产生一份缓存静态离线资源的默认配置,然后用户就可以他们重复访问的时候实时地从Cache Storage API读取数据:

`$ sw-precache --root=dist`

你可以使用Chrome DevTools Application panel来检验文件是否正确地被缓存下来。在你加载完页面后找到‘Cache Storage’,然后你就可以看到所有可以从缓存中读取的文件目录。

sw-precache还支持通过 --config 来传递复杂的参数作为配置。所有文件上的配置项会被命令行的配置覆盖。我们建议通过使用module.exports的单独JavaScript文件来定义配置。例如现在假设有个文件叫 path/to/sw-precache-config.js ,里面包含:

module.exports = {
  staticFileGlobs: [
    'app/css/**.css',
    'app/**.html',
    'app/images/**.*',
    'app/js/**.js'
  ],
  stripPrefix: 'app/',
  runtimeCaching: [{
    urlPattern: /this\\.is\\.a\\.regex/,
    handler: 'networkFirst'
  }]
};

我们可以将文件里的配置转成命令行里的一长串选项设置。

`sw-precache --config=path/to/sw-precache-config.js --verbose`

通过这两种方式就可以很灵活地进行配置,比如为 runtimeCaching.urlPattern 添加一个常规的配置。在成功执行sw-precache之后,它将会计算被缓存下来的静态资源的总大小,这样就可以帮助你意识到用户将会使用多少的流量来加载。

如果你在使用Webpack 插件,一个典型的预缓存静态资源的配置应该如下:

const SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin');
module.exports = {
  // ...
  plugins: [
    // ...
    new SWPrecacheWebpackPlugin({
      cacheId: 'my-cache',
      filename: 'service-worker.js',
      staticFileGlobs: [
        './public/images/**/*.{png,jpg,gif}',
        './public/scripts/**/*.js',
        './public/styles/**/*.css',
        './public/partials/**/*.html'
      ],
      stripPrefix: './public/'
    })
  ]
};

将sw-precade整合到gulp打包机制中

要在gulp中使用 sw-precache,首先要把插件引入到gulpfile文件的顶部位置:

`const swPrecache = require('sw-precache');`

然后我们会在 swPrecahe 上添加一个叫 write 的gulp任务:

`swPrecache.write(filePath, options, callback)`

参数filePath指的是我们要指明的service worker。参数options是一个在创建service worker时所定义的配置对象(详细参考在 Github上文档 列出的所有配置)。参数callback总是会执行。这是为了让gulp知道这个异步的操作什么时候完成。如果其中发生了错误,将会把错误传递到这个callback。如果没有发生错误,即会传null。

我们来看一个例子:

gulp.task('generate-service-worker', function(callback) {
  swPrecache.write('app/service-worker.js'), {
    //1
    staticFileGlobs: [
       'app/index.html',
       'app/js/bundle.js',
       'app/css/bundle.css',
       'app/img/**/*.{svg,png,jpg,gif}'
     ],
    // 2
    importScripts: [
       'app/node_modules/sw-toolbox/sw-toolbox.js',
       'app/js/toolbox-script.js'
     ],
    // 3
    stripPrefix: 'app/'
  }, callback);
});

我们定义了一个gulp任务叫'generate-service-worker'并且传递了一个异步函数作为callback。

swPrecache.write 会通过下面的选项来创建一个service worker:

  • 在staticFileGlobs里的资源将会被预缓存,这意味着在创建service worker的时候会包含一个 install 的勾子去缓存资源。

  • importScripts里的脚本会包含在被创建出来的service worker里的importScripts方法中。在例子中我们将包含sw-toolbox模块和一个包括路由的脚本。

  • 在staticFileGlobs下的所有路径将会移除app/前缀,这样在创建service worker 时所使用的会是相对路径。

实时缓存

sw-toolbox是一个令人称赞的库,它可以让你去监听Service Worker的网络请求和对根据缓存策略作出响应。它会像fetch()监听事件一样去监听路由。

一个路由会根据URL匹配规则和HTTP请求来监听网络请求。然后会根据规则去响应所请求的处理方法。sw-toolbox有大概5种内置的处理方法,涵盖了最常用的缓存策略

如果你熟悉Express,sw-toolbox支持用类似的语法写的URL规则来对应它的路由语法。

toolbox.router.get('img/**/*.{png,jpg}', global.toolbox.cacheFirst);

上面代码将会监听通过GET请求的在 img 目录下的任何PNG/JPG文件。这个请求将会根据缓存第一的策略,先去检查有没有缓存可以响应。如果没有,请求就会发送出去网络。如果找到缓存,将会把缓存响应回去。

域名的全称也可以在这里使用,例如:下面将会缓存你的Google字体:

toolbox.router.get('https://fonts.googleapis.com/', toolbox.cacheFirst);

我们也可以通过Express-style路由来监听另一个域名的GET请求。我们只需要在配置中定义一个叫‘origin’ 的属性(可以是一个字符串或者正则表达式),这样我们可以不用写URL的全称也能匹配得到。

toolbox.router.get('/(.*)', global.toolbox.cacheFirst, {
  origin: /\.googleapis\.com$/
});

正则表达则对象也可以在这里使用。下面我们定义了一个以“https://www.googleapis.com ”开头的POST请求的路由。

`toolbox.router.post(/^https://www.googleapis.com\//, global.toolbox.networkFirst);`

提示: 在监听缓存的时候,你可以通过$$toolbox-cache$ 命名空间为区分哪些资源是在sw-toolbox下缓存的。

更细化的控制

sw-toolbox也给我们提供了细化控制缓存特性的能力。除了可以指明域名,我们也可以像下面一样地自定义缓存:

  • 我们起个名字(“products”)

  • 我们控制最大不超过12个项(使用maxEntries参数)

  • 我们设置过期的时候为一天(24小时等于86400秒)

toolbox.router.get('/(.*)', global.toolbox.cacheFirst, {
  cache: {
   name: 'products',
   maxEntries: 12,
   maxAgeSeconds: 86400
  },
  origin: /\.products\.com$/
});

你可以发现这个sw-precache和sw-toolbox的教程是在我们的Progressive Web Apps Instructor Led 里的训练材料

Google离线分析

就像之前提到的,Google离线分析可以根据用户网络重新正常的情况下再发起用户离线所需的请求。实现这个功能要在Service Worker中加入下面两行代码:

// Import offline analytics into the SW global scope:
importScripts('path/to/offline-google-analytics-import.js');
// initialize it
goog.offlineGoogleAnalytics.initialize();

Boom. 就是这样!

它也支持包含多个重复请求的自定义参数的对象:

goog.offlineGoogleAnalytics.initialize({
  parameterOverrides: {
    cd1: 'Guacamole',
    cd2: 'So much cheese'
  }
});

注意:传递一个对象来覆盖参数这种使用方式主要用于监听网页的正常访问(相对于Serivce Worker的优化)

Autotrack.js

配置Autotrack是相当轻松的。为了在你的页面包含analytics.js,它将会在Autotrack库是异步加载到你的页面。然后引入任何需要的Autotrack插件来更新一下你的默认追踪代码

<script>
window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
ga('create', '<!-- your google_analytics_tracking_id -->', 'auto');
// Autotrack plugins available
ga('require', 'urlChangeTracker');
ga('require', 'cleanUrlTracker');    
ga('require', 'eventTracker');
ga('require', 'maxScrollTracker');
ga('require', 'outboundLinkTracker');  
ga('require', 'pageVisibilityTracker');    

ga('send', 'pageview');
</script>
``<script async src='[https://www.google-analytics.com/analytics.js'](https://www.google-analytics.com/analytics.js%27)>``</script>
``<script async src='/public/js/autotrack.js'>``</script>

注意:有一些Autotrack.js的插件是不需要特别的配置就可以工作的(例如: outboundLinkTracker),而有一些则不是(例如: clearUrlTracker)。查询 文档 来确保各类的插件需要哪些配置

Selenium Assistant

上面提到, Selenium Assistant帮助我们得到一个在我们机器上可以运行哪些浏览器的列表,并根据列表获取一个它们的web驱动实例来然后再执行一些测试。

你可以为你想测试的浏览器安装web驱动模块。也可以把列表上的全部浏览器测试一遍并且在有需要的时候控制它们。当你需要的测试浏览器没有安装在你的本地系统上的时候,assistant也可以在Sauce Labs上正常运行。

Firebase Cloud Messaging

添加 Firebase 到一个已经上线的项目之后,还有几步操作才能支持 Web Push notifications:

1. 添加FCM gcm_sender_id 到你的 Web Application Manifest (manifest.json) 文件:

"gcm_sender_id": "103953800507"

2. 创建一个新的firebase-messaging-service-worker.js文件。我们会通过在这份文件引入Firebase Messaging 库来连接FCM。

importScripts('[https://www.gstatic.com/firebasejs/3.6.10/firebase-app.js'](https://www.gstatic.com/firebasejs/3.6.10/firebase-app.js%27))
importScripts('[https://www.gstatic.com/firebasejs/3.6.10/firebase-messaging.js'](https://www.gstatic.com/firebasejs/3.6.10/firebase-messaging.js%27))

然后在Service Worker中初始化Firebase app。传递你(在Firebase Project设置)的messagingSenderId,就像这样:

firebase.initializeApp({
  'messagingSenderId': '<-- your sender ID goes here -->'
});

接着,检查Firebase Messaging 实例从而处理后台消息:

const messaging = firebase.messaging();

然后向用户询问显示通知的权限。你应该等待一个合适的时机来做这个事情而不是当页面加载完后马上去询问权限。

messaging.requestPermission()
.then(function() {
  console.log('Notification permissions granted.');
  // ...
})
.catch(function(err) {
  console.log('Permission denied', err);
});

现在,在我们拿到权限的情况下,当用户接收到来自FCM的消息后,通知就会显示出来。

下一步要做什么?

我们目前正开发Service Worker 库的下一个大版本.下一个版本将会包括Background Sync,为PWAs提供基于HiDPI 图片切换和更智能的分析技术的Service-Worker。我们期望能够分享更多这些库正式的版本。

我们也正在计划在 Sustainable Loading 频道上发布一个新的类目来讲述关于在Goolge.com上的Service Workers。

截止到目前,不管是你正在创建一个PWA或者尝试提升你网站的性能,我们希望我们的库可以对你产生帮助:)。

谢谢我们优秀的团队成员— Jeff Posnick, Matt Gaunt, Taylor Savage, Joe Medley, Prateek Bhatnagar, Lucas Mullens, Phil Walton, Alex Russell and former member Mat Scales为我们开源小家庭所做出的贡献.

相关资源

原文来自:众成翻译

声明:所有来源为“聚合数据”的内容信息,未经本网许可,不得转载!如对内容有异议或投诉,请与我们联系。邮箱:marketing@think-land.com

  • 营运车判定查询

    输入车牌号码或车架号,判定是否属于营运车辆。

    输入车牌号码或车架号,判定是否属于营运车辆。

  • 名下车辆数量查询

    根据身份证号码/统一社会信用代码查询名下车辆数量。

    根据身份证号码/统一社会信用代码查询名下车辆数量。

  • 车辆理赔情况查询

    根据身份证号码/社会统一信用代码/车架号/车牌号,查询车辆是否有理赔情况。

    根据身份证号码/社会统一信用代码/车架号/车牌号,查询车辆是否有理赔情况。

  • 车辆过户次数查询

    根据身份证号码/社会统一信用代码/车牌号/车架号,查询车辆的过户次数信息。

    根据身份证号码/社会统一信用代码/车牌号/车架号,查询车辆的过户次数信息。

  • 风险人员分值

    根据姓名和身份证查询风险人员分值。

    根据姓名和身份证查询风险人员分值。

0512-88869195
数 据 驱 动 未 来
Data Drives The Future