Service worker not caching https responses - vue.js

I have a service worker for a vue pwa. Its able to cache assets but I cant figure out how to cache http responses. According to this tutorial I am supposed to see api-cache in Cache Storage list
vue.config.vue
module.exports = {
pwa: {
workboxPluginMode: "InjectManifest",
workboxOptions: {
swSrc: "./src/sw.js",
swDest: "service-worker.js",
navigateFallback: "/index.html",
runtimeCaching: [
{
urlPattern: new RegExp(
"^https://<api>.<domain>.co.za/"
),
handler: "networkFirst",
options: {
networkTimeoutSeconds: 2000,
cacheName: "api-cache",
cacheableResponse: {
statuses: [0, 200]
}
}
}
]
}
}
};
registerServiceWorker.js
/* 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.'
)
},
registered (registration) {
setInterval(() => {
registration.update();
}, 1000 * 60 * 60);
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 (registration) {
document.dispatchEvent(
new CustomEvent('swUpdated', { detail: registration })
);
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)
}
})
}

It might well be that the package #ionic/vue is the reason.
If you can, I would suggest to try to implement your PWA with workbox. It is a set of node libraries, therefore you can embed in your Vue app.
In the same article suggested in the comments, I give also an Overview about Workbox.
Some articles related to using Workbox with Vue PWA:
https://www.blog.plint-sites.nl/adding-workbox-to-a-vue-cli-pwa/
https://www.telerik.com/blogs/offline-first-vue-apps-with-hoodie-workbox

Related

rtmp video does not play under electron: build command

I use electron-builder to package my vue projects. In my project I have a video page that uses rtmp video stream.
However, the video plays fine when I use electron:serve and browser, but the video does not play when I use electron:build.
My version is as follows.
os : windows10
"vue": "2.6.10",
"electron": "^13.6.9",
"electron-builder":"#22.14.13"
"vue-aliplayer-v2": "^1.3.0",
My configuration is as follows.
'use strict'
import { app, protocol, BrowserWindow } from 'electron'
import { createProtocol } from 'vue-cli-plugin-electron-builder/lib'
import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer'
const isDevelopment = process.env.NODE_ENV !== 'production'
protocol.registerSchemesAsPrivileged([
{ scheme: 'app', privileges: { secure: true, standard: true, stream: true } }
])
async function createWindow() {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
enableRemoteModule: !!process.env.IS_TEST,
webSecurity: false,
nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION,
contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION
}
})
if (process.env.WEBPACK_DEV_SERVER_URL) {
await win.loadURL(process.env.WEBPACK_DEV_SERVER_URL)
if (!process.env.IS_TEST) win.webContents.openDevTools()
} else {
createProtocol('app')
win.loadURL('app://./index.html')
}
}
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
app.on('ready', async () => {
if (isDevelopment && !process.env.IS_TEST) {
// Install Vue Devtools
try {
await installExtension(VUEJS_DEVTOOLS)
} catch (e) {
console.error('Vue Devtools failed to install:', e.toString())
}
}
createWindow()
})
if (isDevelopment) {
if (process.platform === 'win32') {
process.on('message', (data) => {
if (data === 'graceful-exit') {
app.quit()
}
})
} else {
process.on('SIGTERM', () => {
app.quit()
})
}
}
video page:
components: {
VueAliplayerV2
},
data() {
return {
options: {
isLive: true, //切换为直播流的时候必填
format: 'm3u8' //切换为直播流的时候必填
},
source: '//localhost:9600/live/uav0.m3u8',
show: true,
isShowMultiple: false,
};
},
I want to know how I need to solve the problem of unplayable video in electron.

The vue pwa offline event doesn't fire

I just create a new vue application with pwa from the #vue/cli.
When I switch to offline mode or refresh the page in offline mode,
I do expect to see the message: "No internet connection found. App is running in offline mode." because in registerServiceWorker.ts has the event.
/* 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'
)
},
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)
}
})
}

Show updated PWA created in Vue

I uasing Vue.js with Vuetify and creating a PWA.
I have service-worker.js in /public folder
A snippet from vue.config.js:
pwa: {
// configure the workbox plugin
workboxPluginMode: 'InjectManifest',
workboxOptions: {
// swSrc is required in InjectManifest mode.
swSrc: 'public/service-worker.js',
// ...other Workbox options...
}
}
This looks too be working good and caching the shell etc.
I run build and serve up the project
npm run build
The problem i have is when i update any files, i can't see the updated changes.
when i navigate to the url in my android device the page remains as the old one (cached).
How can i get it to update?
I tried including this code in index.html, but no success:
https://developers.google.com/web/tools/workbox/guides/advanced-recipes#offer_a_page_reload_for_users
service-worker.js
importScripts("/precache-manifest.8812c20b1b3401cbe039782d13cce03d.js", "https://storage.googleapis.com/workbox-cdn/releases/3.6.3/workbox-sw.js");
console.log(`Hello from service worker`);
if (workbox) {
console.log(`Workbox is loaded`);
self.__precacheManifest = [].concat(self.__precacheManifest || []);
workbox.precaching.suppressWarnings();
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
skipWaiting();
}
});
}
else {
console.log(`Workbox didn't load`);
}
Not sure what exactly your setup setup is, but it should be similar. Using the #vue/cli-plugin-pwa and with minimal setup below.
This will show a dialog when a new version of your app is available. Clicking yes will update your app. You will have to refresh the page somehow to actually show the new version, but that is up to you on how solve that.
vue.config.js:
module.exports = {
pwa: {
name: "name-of-your-app",
short_name: "noya",
themeColor: "#000000",
workboxPluginMode: "InjectManifest",
workboxOptions: {
swSrc: "src/service-worker.js" // CHECK CORRECT PATH!
}
}
};
src/main.js:
import Vue from "vue";
import App from "./App.vue";
import "./registerServiceWorker";
// whatever other imports...
new Vue({
render: h => h(App)
}).$mount("#app");
src/registerServiceWorker.js:
import { register } from "register-service-worker";
if (process.env.NODE_ENV === "production") {
register(`${process.env.BASE_URL}service-worker.js`, {
updated(registration) {
if (window.confirm("A new version is available, update now?")) {
const worker = registration.waiting;
worker.postMessage({ action: "SKIP_WAITING" });
// refresh the page or trigger a refresh programatically!
}
}
});
}
src/service-worker.js:
self.__precacheManifest = [].concat(self.__precacheManifest || []);
workbox.precaching.suppressWarnings();
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
self.addEventListener("message", (event) => {
if (event.data.action == "SKIP_WAITING") self.skipWaiting();
});
I get it work by following the offer_a_page_reload_for_users. The original registerServiceWorker.js seems redundant though.
src/registerServiceWorker.js
import { Workbox } from "workbox-window";
if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) {
const wb = new Workbox("/service-worker.js");
wb.addEventListener("waiting", () => {
const result = window.confirm("refresh now?");
if (result) {
wb.messageSW({ type: "SKIP_WAITING" });
}
});
wb.addEventListener("controlling", () => {
window.location.reload();
});
wb.register();
}

How to make Axios use Fetch instead of XMLHttpRequest

I am beginner in the PWA and I try to get my code to call an API and then store it in the browser's cache. But I see that axios uses the XMLHttpRequest and not the fetch API, so I can't cache my API call.
I use workbox for the service worker and vue cli.
my service-worker.js :
workbox.setConfig({
debug: false,
});
workbox.precaching.precacheAndRoute([]);
//image in cache
workbox.routing.registerRoute(
/\.(?:png|gif|jpg|jpeg|svg)$/,
workbox.strategies.staleWhileRevalidate({
cacheName: 'images',
plugins: [
new workbox.expiration.Plugin({
maxEntries: 60,
maxAgeSeconds: 30 * 24 * 60 * 60, // 30 Days
}),
],
}),
);
//network request in cache
workbox.routing.registerRoute(
new RegExp('http://api.giphy.com/v1/gifs/'),
workbox.strategies.networkFirst({
cacheName: 'api',
}),
);
//js and css in cache
workbox.routing.registerRoute(
/\.(?:js|css)$/,
workbox.strategies.staleWhileRevalidate(),
);
//webfont in cache
workbox.routing.registerRoute(
new RegExp('https://fonts.(?:googleapis|gstatic).com/(.*)'),
workbox.strategies.cacheFirst({
cacheName: 'googleapis',
plugins: [
new workbox.expiration.Plugin({
maxEntries: 30,
}),
],
}),
);
my registerServiceWorker.js :
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'
)
},
registered () {
console.log('Service worker has been registered.')
},
cached () {
console.log('content in cached');
},
updatefound () {
console.log('New content is downloading.')
},
updated () {
console.log('New content is available; please refresh.')
},
offline () {
},
error (error) {
console.error('Error during service worker registration:', error)
}
})
}
and my call API :
import Vue from 'vue';
import CONSTANTS from '../constants/constants';
import exception_manager from 'exception_manager';
export default {
getGiphy() {
return Vue.axios.get(`${CONSTANTS.SERVER_ADDRESS}search?q=cat&api_key=${CONSTANTS.API_KEY}&limit=9`).catch(error => {
exception_manager.handleException(error, 'GiphyService.js', 8, window, CONSTANTS.ERROR_SERVER_ADDRESS);
});
}
}
I think that's really the story with the xmlhttprequest, but I'm not sure.
On the other hand, js, css and images files are well cached
Your RegExp route inside of the service worker looks for http://api.giphy.com/v1/gifs/, which is an http: URL. Service workers will only intercept secure requests, which means https: (or http://localhost).
Make sure that you're using https: in your client-side Vue code, and adjust your Workbox configuration to use https: as well.

how to fix blank page if i am using vue router with electron js?

I'm trying to use vue router with an application on an Electron JS. If I use a router on the render page, then the router work done. But I do not understand how to make the transition to the page, for example,- 'Settings' using the Tray. At attempt of transition the empty page opens.
I have prepared a working example of the project. This problem exists only build project. In development mode all work well.
This is my work example on github. Please need help.
git clone https://github.com/DmtryJS/electron-vue-example.git
cd electron-vue-example
npm install
npm run build
and run dist\win-unpacked\example_for_stackoverflow.exe
my main.js file
'use strict'
import { app, protocol, BrowserWindow, Menu, ipcMain, Tray } from 'electron'
import { format as formatUrl } from 'url'
const electron = require('electron');
const path = require('path');
const isDevelopment = process.env.NODE_ENV !== 'production';
let imgBasePath;
if(isDevelopment) {
imgBasePath = path.join('src','assets', 'img');
} else {
imgBasePath = path.join(path.dirname(__dirname), 'extraResources', 'img');
}
let win;
let tray;
protocol.registerStandardSchemes(['app'], { secure: true })
const trayIcon = path.join(__static, 'img', 'icon.png');
function createWindow () {
win = new BrowserWindow({
width: 800,
height: 600,
icon: trayIcon
})
routeTo(win, "")
win.on('closed', () => {
win = null
})
//убрать меню
win.setMenuBarVisibility(true)
win.on('show', function() {
tray.setHighlightMode('always')
})
win.on('hide', function() {
tray.setHighlightMode('never')
})
}
// Quit when all windows are closed.
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
app.on('activate', () => {
if (win === null) {
createWindow()
}
})
app.on('ready', () => {
createWindow()
win.webContents.openDevTools(); //открыть dev tools
createTray()
})
// Exit cleanly on request from parent process in development mode.
if (isDevelopment) {
if (process.platform === 'win32') {
process.on('message', data => {
if (data === 'graceful-exit') {
app.quit()
}
})
} else {
process.on('SIGTERM', () => {
app.quit()
})
}
}
function createTray()
{
let traiIconPath = path.join(imgBasePath, 'preloader_tray_icon.png')
tray = new Tray(traiIconPath)
const contextMenu = Menu.buildFromTemplate(
[
{
label: 'Settings',
type: 'normal',
click: function()
{
routeTo(win, "/settings")
let contents = win.webContents
contents.on('dom-ready', function()
{
if(!win.isVisible())
{
showWindow()
}
})
}
},
{
label: 'Exit',
type: 'normal',
click: function()
{
win = null
app.quit()
}
}
])
tray.setContextMenu(contextMenu)
tray.on('click', function() {
toggleWindow();
})
}
function showWindow() {
var position = getPosition();
win.setPosition(position.x, position.y, false)
win.show()
win.focus()
}
ipcMain.on('routerEvent', function(event, arg) {
routeTo(win, arg)
})
function routeTo(win, to) {
if (isDevelopment) {
win.loadURL(`http://localhost:${process.env.ELECTRON_WEBPACK_WDS_PORT}` + to)
} else {
win.loadURL(formatUrl({
pathname: path.join(__dirname, 'index.html' + to);,
protocol: 'file',
slashes: true
}))
}
}
And
router.js
import Vue from 'vue'
import Router from 'vue-router'
import Main from './../views/Main.vue'
import Settings from './../views/Settings.vue'
Vue.use(Router)
export default new Router({
//mode: 'history',
routes: [
{
path: '/',
name: 'home',
component: Main
},
{
path: '/settings',
name: 'settings',
component: Settings
}
]
})
You need to add created to the main Vue app check docs
// src/main.js
new Vue({
router,
render: h => h(App),
created() {
// Prevent blank screen in Electron builds
this.$router.push('/')
}
}).$mount('#app')
The solution for me was to remove the history mode in the vue router.
Sorry, but after one day of googling, I just found a solution. The case turned out to be
win.loadURL(formatUrl({
pathname: path.join(__dirname, 'index.html' + to);,
protocol: 'file',
slashes: true
}))
I delete formaUrl and everything works well
win.loadURL(path.join(__dirname, 'index.html' + to));
For me solution was this:
Check if app is running at addresses like this:
Local: http://x86_64-apple-darwin13.4.0:8080/
Network: http://localhost:8080/
Check if you can access x86_64..url in browser. If you are not seeing a webpage but can see it from localhost, then map 127.0.0.1 to x86_64-apple-darwin13.4.0 in hosts file. in mac its located in /etc/hosts in windows its in system32/drivers/etc.