什么是pwa?
Progressive Web Apps,渐进式 Web 应用
pwa是我们在追求webapp便捷和原生应用良好体验结合的过程中的产物,目前兼容性是最大障碍
底层使用的是workBox (service worker的一个框架),封装了各种api和缓存策略,
vue-cli3.0集成的是workbox-webpack-plagin,我们可以通过vue.config.js的pwa配置项进行配置
pwa 特征?
具有可离线、添加到桌面(一级入口)、后台同步、服务端推送等特征
/* eslint-disable no-console */
import { register } from 'register-service-worker'
if (process.env.NODE_ENV === 'production') {
register(`${process.env.BASE_URL}service-worker.js`, {
ready () {
console.log(
'App is being served from cache by a service worker.\n' +
'For more details, visit https://goo.gl/AFskqB'
)
},
registered () {
console.log('Service worker has been registered.')
},
cached () {
console.log('Content has been cached for offline use.')
},
updatefound () {
console.log('New content is downloading.')
},
updated () {
console.log('New content is available; please refresh.')
},
offline () {
console.log('No internet connection found. App is running in offline mode.')
},
error (error) {
console.error('Error during service worker registration:', error)
}
})
}
其中register的 service-worker.js文件可以通过插件方式配置生成(具体操作百度);
也可以手动在/src路径下,新增service-worker.js文件,内容如下:
importScripts('https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js');
// set the prefix and suffix of our sw's name
workbox.core.setCacheNameDetails({
prefix: 'vue-pwa',
suffix: 'v1.0.1',
});
// have our sw update and control a web page as soon as possible.
// workbox.skipWaiting();
// workbox.clientsClaim();
// vue-cli3.0 supports pwa with the help of workbox-webpack-plugin, we need to get the precacheing list through this sentence.
workbox.precaching.precacheAndRoute(self.__precacheManifest || []);
// cache our data, and use networkFirst strategy.
workbox.routing.registerRoute(
// Cache CSS files
/.*\.css/,
// 使用缓存,但尽快在后台更新
workbox.strategies.staleWhileRevalidate({
// 使用自定义缓存名称
cacheName: 'css-cache'
})
);
// 缓存web的js资源
workbox.routing.registerRoute(
// 缓存JS文件
/.*\.js/,
// 使用缓存,但尽快在后台更新
workbox.strategies.staleWhileRevalidate({
// 使用自定义缓存名称
cacheName: 'js-cache'
})
);
// 缓存web的图片资源
workbox.routing.registerRoute(
/\.(?:png|gif|jpg|jpeg|svg)$/,
workbox.strategies.staleWhileRevalidate({
cacheName: 'images-cache',
plugins: [
new workbox.expiration.Plugin({
maxEntries: 60,
maxAgeSeconds: 1 * 24 * 60 * 60 // 设置缓存有效期为30天
})
]
})
);
注意:
importScripts(‘https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js’)
workbox-sw.js 文件 在vue.confg.js中配置pwa后,原本是可以不用手动引入的;但是由于程序默认导入的workbox-sw.js文件跨域了,无法正常访问,会导致报错 workbox is no undefined 而无法正常使用; 所以这里一定要手动引入!
接下来需要加vue.config.js中配置pwa的相关参数:
...
pwa: {
// 一些基础配置
name: 'vue-pwa',
themeColor: '#6476DB',
msTileColor: '#000000',
appleMobileWebAppCapable: 'yes',
appleMobileWebAppStatusBarStyle: 'black',
/*
* 两个模式,GenerateSW(默认)和 InjectManifest
* GenerateSW: build项目时候,每次都会新建一个service worker文件
* InjectManifest: 自定义的service worker文件,并且可以处理预缓存列表
*/
workboxPluginMode: 'InjectManifest',
workboxOptions: {
// 自定义的service worker文件的位置
swSrc: 'src/service-worker.js',
// ...other Workbox options...
importWorkboxFrom: "disabled" // 是否要引入线上的service-worker文件,我们只需要自己定义的文件,不需要谷歌提供的sw文件
}
}
...
/public路径下新增manifest.json文件,配置桌面图标相关的参数,如:
{
"name": "vue-pwa",
"short_name": "vue-pwa",
"theme_color": "#6476DB",
"icons": [
{
"src": "./img/icons/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "./img/icons/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
},
{
"src": "./img/icons/android-chrome-maskable-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "./img/icons/android-chrome-maskable-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
],
"start_url": ".",
"display": "standalone",
"background_color": "#000000"
}
注意图标路径是否正确,以及相应尺寸的图标是否存在
简写
vue add @vue/pwa
全名
vue add @vue/cli-plugin-pwa
简写
vue add pwa
全名
vue add vue-cli-plugin-pwa
其它配置同新项目扩展是一样,需要注意的是,老项目中资源缓存的配置需要根据实情调整处理, workbox.strategies.staleWhileRevalidate的使用方法与缓存类型说明,自行百度!
至此,涉及的相关代码已添加完毕,怎么启动调试呢?
注意: pwa默认只在https: (安全域名)与 localhsot下可用;
本地dev环境 可以通过启动node服务,来帮助开发调试查看效果
node start.js
start.js文件配置如下:
const express = require("express");
const cors = require("cors")
const fs = require("fs");
const { createProxyMiddleware } = require('http-proxy-middleware');
const app = express();
app.use(cors());
const proxyOption = {
// 代理跨域目标接口
target: 'http://192.168.1.61:30033',
// pathRewrite: {
// '^/api/' : '/' // 重写请求,api/解析为/
// },
changeOrigin: true,
// 修改响应头信息,实现跨域并允许带cookie
onProxyRes: function(proxyRes, req, res) {
res.header('Access-Control-Allow-Origin', '*');
},
// 修改响应信息中的cookie域名
// cookieDomainRewrite: '' // 可以为false,表示不修改
}
//服务器的代理请求地址
app.use('^/base/', createProxyMiddleware(proxyOption));
//app.use('^/passport/', createProxyMiddleware(proxyOption));
app.get("/", (req, res) => {
res.end(fs.readFileSync("./dist/index.html", "utf-8"));
});
app.use(express.static("./dist"));
app.listen(8092, () => {
console.log("server listening on 8092")
});