阅读 549

PWA在nuxt中的简单应用

PWA(Progressive Web Apps,渐进式 Web 应用) 运用现代的 Web API 以及传统的渐进式增强策略来创建跨平台 Web 应用程序。这些应用无处不在、功能丰富,使其具有与原生应用相同的用户体验优势。

通常用户使用pwa应用的方式是将H5页面添加到手机主屏幕(桌面),什么叫添加到主屏幕?

添加到主屏幕(Add to Home Screen,简称 A2HS)是现代智能手机浏览器中的一项功能,使开发人员可以轻松便捷地将自己喜欢的 Web 应用程序(或网站)的快捷方式添加到主屏幕中,以便用户随后可以通过单击访问它。

Mobile Chrome / Android Webview 从 31 版开始支持 A2HS,Opera for Android 从 32 版开始支持,Firefox for Android 从 58 版 开始支持。

如何使我们的应用程序支持 A2HS 呢?

  • 应用通过 HTTPS 提供服务——Web 正朝着更加安全的方向发展,包括 A2HS 在内的许多现代 Web 技术都将仅工作在安全的环境中。
  • 从 HTML 头链接具有正确字段的 manifest 文件。
  • 有合适的图标可显示在主屏幕上。
  • Chrome 浏览器还要求该应用程序注册一个 Service Worker(这样在离线状态下就也可以运行)。

详细说明可以参考: PWA Docs

按照上面的方法,我们讲解下如何使我们的nuxt应用支持A2HS:

  • 首先,我们的应用应该部署在 https 服务上。(这点大部分网站都是https的)
  • 在nuxt中创建 manifest 文件:

因为我们要在app.html中加入

<link rel="manifest" href="/manifest.webmanifest">
复制代码

按照nuxt访问路径,我们应该把manifest这种静态文件加到nuxt的static文件夹里,所以我们在static文件夹中创建manifest.webmanifest文件,这个文件的内容是定义我们的PWA桌面应用的配置。

{
  "background_color": "#ffffff", // 安装应用时或启动应用时的背景色
  "theme_color": "#ffffff", // UI的颜色,操作系统使用的
  "description": "test pwa", // 应用描述
  "display": "standalone", // 桌面应用的壳中h5显示方式,全屏、独立、最小UI或浏览器
  "icons": [
    {
      "src": "icon/36-min.png",
      "sizes": "36x36",
      "type": "image/png"
    },
    {
      "src": "icon/48-min.png",
      "sizes": "48x48",
      "type": "image/png"
    },{
      "src": "icon/72-min.png",
      "sizes": "72x72",
      "type": "image/png"
    },{
      "src": "icon/96-min.png",
      "sizes": "96x96",
      "type": "image/png"
    },{
      "src": "icon/144-min.png",
      "sizes": "144x144",
      "type": "image/png"
    },{
      "src": "icon/192-min.png",
      "sizes": "192x192",
      "type": "image/png"
    },{
      "src": "icon/256-min.png",
      "sizes": "256x256",
      "type": "image/png"
    },{
      "src": "icon/512-min.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ], // icon图标,图标尽量多种尺寸,可以支持用户不同的设备
  "name": "test", // 网站应用的全名
  "short_name": "test", // 显示在桌面的短名
  "start_url": "/?from=pwa", // 桌面应用的入口页面链接,可以带上参数来区分pwa应用打开
  "related_applications": [{ 
    "platform": "play",
    "id": "xxx",
    "url": "xxx"
  }] // 关联google应用
}
复制代码
  • 接着在nuxt应用中注册一个 serviceworker

我们可以在static文件夹中简单创建一个sw.js,可以在里面定义两个事件监听

self.addEventListener('install', (e) => {
    e.waitUntil(caches.open('test-store').then((cache) => cache.addAll([
        // 可以加入你想缓存的文件列表
        '/xxxx/xxxx'
    ]))
})

self.addEventListener('fetch', (e) => {
    e.respondWith(
        caches.match(e.request).then((response) => response || fetch(e.request))
    )
})
复制代码

有了sw.js后,我们需要在window.onload的时候注册一下

window.addEventListener('load',function(){
  if ('serviceWorker' in window.navigator) {
    window.navigator.serviceWorker
      .register('sw.js')
      .then(() => { console.log('Service Worker Registered'); })
      .catch(() => { console.log('Service Worker Registered Failed'); });
  }
})
复制代码

这样我们在上面支持A2HS的浏览器中打开我们的H5时,比如在安卓手机的chrome浏览器,点击右上角菜单,就能看到安装应用这个选项,便可以安装PWA应用到手机桌面啦。

2901630325820_.pic.jpg

但实际上我们要做打包后js和静态图片文件缓存的话,应该在构建的时候将缓存的文件的路径打入到serviceworker里,对此google提供了一个插件叫 workbox-webpack-plugin ,我们在nuxt.config.js的插件配置他:

build: {
    extend(config, { isDev, isClient }) {
        const worboxWebpackPlugin = require("workbox-webpack-plugin")
        if (isClient) {
            config.plugins.push(
                new worboxWebpackPlugin.GenerateSW({
                  cleanupOutdatedCaches: true,
                  clientsClaim: true,
                  skipWaiting: true,
                  maximumFileSizeToCacheInBytes: 10 * 1024 * 1024,
                  swDest: path.join(__dirname, '/src/static/sw.js'),
                  manifestTransforms: [
                    async (manifestEntries) => {
                      const manifest = manifestEntries.filter(entry => {
                        return entry.url.indexOf('../server') === -1;
                      });
                      return {manifest, warnings: []};
                    }
                  ]
                })
            )
        }
    }
}

复制代码

因为nuxt构建是包含服务端server的文件,所以我们需要剔除他们,实际缓存的是client端的文件。 swDest是重新定义sw.js生成的路径,同样和前面demo的sw.js一样放到static文件夹里。

插件具体配置参数和作用可以参考: developers.google.com/web/tools/w…

这样我们nuxt构建后,就会在static文件夹里生成sw.js和workbox-{hash}.js。

PWA在nuxt中的简单应用就实现了。 实际上,我们是希望用户点击H5页面的某个按钮或者提示条来触发PWA应用的安装,而不是需要用户自主点击浏览器菜单中的安装应用,这怎么实现呢? 这需要我们监听并使用浏览器的 beforeinstallprompt 事件。

在nuxt中,我们是通过写一个vue组件来实现一个提示条或安装按钮的,所以我们会在组件的mounted里这样写道:

window.addEventListener('beforeinstallprompt', (e) => {
    e.preventDefault(); // 防止 Chrome 67 及更早版本自动显示安装提示
    this.deferredPrompt = e; // 保存event,点击唤起安装需要用到
    this.show(); // 触发事件时展示该组件
})
复制代码

然后给该组件绑定一个点击事件:

handleClick() {
    if (this.deferredPrompt) {
        this.deferredPrompt.prompt();
        this.deferredPrompt.userChoice.then((choiceResult) => {
          if (choiceResult.outcome === 'accepted') {
            console.log('User accepted the A2HS prompt');
          } else {
            console.log('User dismissed the A2HS prompt');
          }
          this.deferredPrompt = null;
        });
    }
    this.hide(); // 点击完后隐藏该组件
}
复制代码

完成后,我们发现组件有很大概率没有展示,也就是说我们监听的这个beforeinstallprompt事件并不能成功触发,这是为什么呢? 因为,beforeinstallprompt事件触发的时机过早,可能会比组件mounted还要早,所以如果浏览器已经触发了beforeinstallprompt事件,而我们才在组件mounted中去监听,那这个监听就不会再触发了,所以我们需要改进一下。

正常情况下,beforeinstallprompt事件会在window.onload后触发,我们可以在 app.html 里的script中 window.onload 的时候加个监听:

window.addEventListener('beforeinstallprompt',(e) => {
  console.log(e);
  e.preventDefault();
  window.deferredPrompt = e;
});
复制代码

然后把这个event存储在全局变量里,再修改下组件mounted里的展示判断:

// 如果beforeinstallprompt这个事件在window.onload后 组件mounted前就触发,那window.deferredPrompt就会是pwa的事件
if (window.deferredPrompt) {
  this.deferredPrompt = window.deferredPrompt;
  this.show();
} else {
  // 如果beforeinstallprompt这个事件在window.onload后 组件mounted时未触发,则重新建立监听
  window.addEventListener('beforeinstallprompt', (e) => {
    console.log('bar', e);
    e.preventDefault();
    this.deferredPrompt = e;
    this.show();
  });
}
复制代码

这样就大功告成啦!

文章分类
前端
文章标签