Vue electron automatic update / electron manual update
Uncaught TypeError: fs.existsSync is not a function
All posts are the same. I don't want to try. I walked through all the holes in this partial article.
Direct code
The main problem is const electron = require('electron ') ipcRenderer can be used without reference
When this problem is solved, the latter will be simple
So many posts didn't say this question
The code is on gitee, and the address is at the bottom
Direct reference will report an error
#Uncaught TypeError: fs.existsSync is not a function
Causes of problems:
Firstly, the rendering process belongs to the browser side and there is no integrated Node environment, so the basic package of nodes such as fs can not be used.
Because there is no Node environment, the require keyword cannot be used.
The method found on this stackoverflow
Create a preload js
const electron = require('electron'); window.ipcRenderer = require('electron').ipcRenderer; // Just go ahead, electron The ipcrenderer method is mounted on windows window.remote = require('electron').remote;
In main Set preload in webpreference in JS file:
const win = new BrowserWindow({ width: 1920, height: 1040, frame:true, webPreferences: { nodeIntegration:true, contextIsolation:false, webSecurity: false, preload:__dirname+'\\preload.js' , } })
Let's focus on preload JS storage location is really important, otherwise it will be undefined
Local preload JS is placed after packaging
Preload after packaging JS under public and index HTML same as directory
Before I put two, I only put one. All local operations are OK. After packaging, an error is reported
Really, there is no post saying this problem. Maybe only I met this problem
Otherwise, I can't find it
Now start the installation electron-updater
// Recommend yarn yarn add electron-updater --save-dev // *I don't recommend npm. Anyway, I haven't successfully installed it once **npm i electron-updater --save-dev
This must exist, otherwise the package will not generate latest YML file
Every time you update, just throw yml and exe to the back end
//This must exist, otherwise the package will not generate latest YML file publish:[ { "provider":"generic", url: "http://192.168.0.191/electron/ "/ / update the file server address } ],
New updata js
import { autoUpdater } from 'electron-updater' import { ipcMain } from 'electron' let mainWindow = null; autoUpdater.autoDownload = false; let canQuit = false; export function updateHandle(window, feedUrl) { mainWindow = window; let message = { error: 'Error checking for updates', checking: 'Checking for updates', updateAva: 'New version detected, downloading', updateNotAva: 'The latest version is used now, and there is no need to update it', }; //Set the address of the update package autoUpdater.setFeedURL(feedUrl); //Listening for upgrade failure events autoUpdater.on('error', function (error) { sendUpdateMessage({ cmd: 'error', message: error }) }); //Listen to start detecting update events autoUpdater.on('checking-for-update', function (message) { sendUpdateMessage({ cmd: 'checking-for-update', message: message }) }); //Listening for available update events autoUpdater.on('update-available', function (message) { sendUpdateMessage({ cmd: 'update-available', message: message }) }); //Listen for no update events available autoUpdater.on('update-not-available', function (message) { sendUpdateMessage({ cmd: 'update-not-available', message: message }) }); // Update download progress event autoUpdater.on('download-progress', function (progressObj) { sendUpdateMessage({ cmd: 'download-progress', message: progressObj }) }); autoUpdater.on('close', (event) => { if (!canQuit) { mainWindow.hide(); mainWindow.setSkipTaskbar(true); event.preventDefault(); } }); //Listening for download completion events autoUpdater.on('update-downloaded', function (event, releaseNotes, releaseName, releaseDate, updateUrl) { sendUpdateMessage({ cmd: 'update-downloaded', message: { releaseNotes, releaseName, releaseDate, updateUrl } }) //Exit and install the update package if (process.platform !== 'darwin') { canQuit = true; autoUpdater.quitAndInstall(); } // autoUpdater.quitAndInstall(); }); //Receive the rendering process message and start checking for updates ipcMain.on("checkForUpdate", (e, arg) => { //Perform automatic update check // sendUpdateMessage({cmd:'checkForUpdate',message:arg}) if(arg){ autoUpdater.autoDownload = true; } autoUpdater.checkForUpdates(); }) } //Send a message to the rendering process function sendUpdateMessage(text) { mainWindow.webContents.send('message', text) }
backgrounnd.ts I use ts this and you main JS is the same
import { app, protocol, BrowserWindow,Menu } from 'electron' import { createProtocol } from 'vue-cli-plugin-electron-builder/lib' // import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer' // Automatic update introduces the updater created above js import {updateHandle} from './updater.js' ; const isDevelopment = process.env.NODE_ENV !== 'production' // Scheme must be registered before the app is ready protocol.registerSchemesAsPrivileged([ { scheme: 'app', privileges: { secure: true, standard: true } } ]) async function createWindow() { // Create the browser window. const win = new BrowserWindow({ width: 1920, height: 1040, frame:true, webPreferences: { // Use pluginOptions.nodeIntegration, leave this alone // See nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration for more info // nodeIntegration: (process.env // .ELECTRON_NODE_INTEGRATION as unknown) as boolean, // contextIsolation: !(process.env // .ELECTRON_NODE_INTEGRATION as unknown) as boolean, nodeIntegration:true, contextIsolation:false, webSecurity: false, // preload:'./src/preload.js' , preload:__dirname+'\\preload.js' , Here's the point // Preload. XML is introduced here js } }) Menu.setApplicationMenu(null) if (process.env.WEBPACK_DEV_SERVER_URL) { // Load the url of the dev server if in development mode await win.loadURL(process.env.WEBPACK_DEV_SERVER_URL as string) if (!process.env.IS_TEST) win.webContents.openDevTools() } else { createProtocol('app') // Load the index.html when not in development win.loadURL('app://./index.html') win.webContents.openDevTools() // win.loadURL(process.env.WEBPACK_DEV_SERVER_URL as string) } updateHandle(win, 'http://192.168.0.191/electron/'); } // Quit when all windows are closed. app.on('window-all-closed', () => { // On macOS it is common for applications and their menu bar // to stay active until the user quits explicitly with Cmd + Q if (process.platform !== 'darwin') { app.quit() } }) app.on('activate', () => { // On macOS it's common to re-create a window in the app when the // dock icon is clicked and there are no other windows open. if (BrowserWindow.getAllWindows().length === 0) createWindow() }) // This method will be called when Electron has finished // initialization and is ready to create browser windows. // Some APIs can only be used after this event occurs. 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() }) // 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() }) } }
If Updater JS reference error
Put Updater. In the same directory JS copy. Change the name to Updater I'll be fine
app.vue setup update page
This page contains manual and automatic updates
<template> <div id="app"> <a-config-provider :locale="zh_CN"> <router-view /> </a-config-provider> <a-modal v-model="modalVisible" width="300PX" :footer="null" :closable="false" :keyboard="false" :maskClosable="false" wrapClassName="supplement-detail-modal" :title="isUpdatte?'Check for new version...':'The system is being updated...'"> <div style="width:120px;margin:30px auto;" v-if="isUpdatte"> <a-progress type="circle" :percent="percent" :format="percent => `${percent} %`" /> </div> <div style="display:flex;justify-content: space-between;" v-if="!isUpdatte"> <a-button type="primary" @click="isUpdatte=true" style="margin:30px auto;"> Update new version </a-button> </div> </a-modal> </div> </template> <script> import zh_CN from 'ant-design-vue/lib/locale-provider/zh_CN'; import moment from 'moment'; import 'moment/locale/zh-cn'; import { ConfigProvider, progress, button } from 'ant-design-vue' // const electron = require('electron').ipcRenderer; const ipcRenderer = window.ipcRenderer moment.locale('zh-cn'); export default { components: { AConfigProvider: ConfigProvider, AProgress: progress, AButton: button, }, data() { return { zh_CN, modalVisible: false, percent: 0, isUpdatte: false, }; }, mounted() { if (ipcRenderer) { const _this=this ipcRenderer.on("message", (event, arg) => { // for (var i = 0; i < arg.length; i++) { console.log(arg); if ("update-available" == arg.cmd) { //The upgrade dialog box is displayed _this.modalVisible = true; if(!_this.isUpdatte)return; } else if ("download-progress" == arg.cmd) { //Update upgrade progress /** * * message{bytesPerSecond: 47673 delta: 48960 percent: 0.11438799862426002 total: 42801693 transferred: 48960 } */ console.log(arg.message.percent); let percent = Math.round(parseFloat(arg.message.percent)); _this.percent = percent; } else if ("error" == arg.cmd) { _this.modalVisible = false; _this.$message.error("Update failed"); } // } }); } }, created() { //Test once every 1 hour alert(ipcRenderer) let _this=this window.setInterval(() => { // _ this.isUpdatte == true means that the new version will be updated automatically when it is obtained. It is recommended to update the installer manually // _ this.isUpdatte == false is automatic update ipcRenderer.send("checkForUpdate",_this.isUpdatte); }, 10000); } }; </script> <style lang="less"> .ant-popover-message { * { font-size: 22px !important; } .ant-popover-buttons { .ant-btn { font-size: 22px !important; } } } @font-face { font-family: AGENCYB; src: url('./assets/AGENCYB.TTF'); } @font-face { font-family: AGENCYR; src: url('./assets/AGENCYR.TTF'); } html, body, #app { height: 100%; width: 100%; padding: 0; margin: 0; } div { padding: 0; margin: 0; box-sizing: border-box; } .box-title { position: relative; line-height: 32px; height: 32px; font-size: 18px; padding-left: 15px; display: flex; align-items: center; justify-content: space-between; font-weight: bold; &.border { border-bottom: 1px solid #A7B0C1; } &:before { position: absolute; left: 0; top: calc(16px - 8px); content: ''; display: inline-block; width: 4px; height: 18px; background: #2c3e50; } } .box-content { height: calc(100% - 32px); } .second-title { position: relative; line-height: 24px; height: 24px; font-size: 16px; padding-left: 15px; display: flex; align-items: center; justify-content: space-between; color: #939393; &:before { position: absolute; left: 0; /*top: calc(12px - 3px);*/ content: ''; display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: #5190eb; } } .ant-modal-body { padding-top: 0; } </style>
Now get the new version and directly report the error cmd: "update not available"
I can't find this problem alive and dead. There's no problem with it
The original version number is the same. The program judges that it does not need to be updated! There is already a latest YML also needs a version number. Really
Change the package when packing Just version in JSON
This book can be updated automatically
You think it's over
He will now automatically update TM. The automatic update installation will fail
The reason is that the appid is the same
When modifying the version number, you should also modify the appid
Every time you pack, you have to modify the appid and version number. There is no language
Here is the package to automatically modify the version and appid code. My configuration is in Vue config. JS, you can also put the code in build JS inside
const path = require('path') var webpack = require('webpack'); const fs = require('fs'); const demoName="app" function resolve(dir) { return path.join(__dirname, dir) } //------------------------Automatically modify the version number and use the version number as appid function AddZero(time){ if(time<10){ return "0"+time }else{ return time } } let packageTxt=fs.readFileSync('./package.json','utf8'); let versionData = packageTxt.split('\n'); let packageJson = JSON.parse(packageTxt); let VersionArr = packageJson.version.split('.'); let date = new Date(); let today = date.getFullYear()+""+AddZero((date.getMonth()+1))+""+AddZero(date.getDate()) if(today == VersionArr[1]){ VersionArr[2] = parseInt(VersionArr[2])+1 }else{ VersionArr[1] = date.getFullYear()+""+AddZero((date.getMonth()+1))+""+AddZero(date.getDate()) VersionArr[2] = 1; } let versionLine = VersionArr.join('.'); for(let i= 0; i<versionData.length;i++){ if(versionData[i].indexOf('"version":')!=-1){ versionData.splice(i,1,' "version": "'+versionLine+'",'); break; } } fs.writeFileSync('./package.json',versionData.join('\n'),'utf8'); //------------------------ module.exports = { outputDir: 'dist', publicPath: './', productionSourceMap: false, // Simple configuration of webpack configureWebpack: { // provide the app's title in webpack's name field, so that // it can be accessed in index.html to inject the correct title. resolve: { alias: { '@': resolve('src') } }, }, // More granular configuration of webpack // From Vue admin template chainWebpack: config => { config.module .rule('svg') .exclude.add(resolve('src/svg-icon')) .end() config.module .rule('icons') .test(/\.svg$/) .include.add(resolve('src/svg-icon')) .end() .use('svg-sprite-loader') .loader('svg-sprite-loader') .options({ symbolId: 'icon-[name]' }) .end() }, pluginOptions: { electronBuilder: { // List native deps here if they don't work // The native package must be declared here externals: ["serialport"], // If you are using Yarn Workspaces, you may have multiple node_modules folders // List them all here so that VCP Electron Builder can find them nodeModulesPath: ["../../node_modules", "./node_modules"], nodeIntegration: true, // Packaging configuration builderOptions: { "appId": demoName+versionLine, // Publisher name productName: demoName, // Installation package name, which can be configured by yourself artifactName: demoName+'.exe', nsis: { // One click installation. If it is set to true, the nsis setting is meaningless. Please delete the nsis configuration directly oneClick: false, // true all user installation [Directory: C:\Program Files (x86)], false installation to the current user perMachine: true, // Allow request for promotion. If false, the user must restart the installer with elevated privileges. allowElevation: true, // Allows you to modify the installation directory allowToChangeInstallationDirectory: true, // Create desktop icon createDesktopShortcut: true, // Create start menu icon createStartMenuShortcut: true, // The name of the shortcut. The default is the application name shortcutName: demoName, // Installation Icon // installerIcon: "./logo.png", / / installation Icon // uninstallerIcon: "./logo.png", / / uninstall Icon // installerHeaderIcon: "./logo.png", / / header icon during installation }, // files: [ // { // 'filter': ['**/*'] // } // ], // extraFiles: ['./extensions/'], // asar: false, publish:[ { "provider":"generic", url: "http://192.168.0.191/electron/ "/ / update the file server address } ], } } }, }
If the packaged software reports an error
This is because of the electron ic version
yarn add electron // Directly yarn install it again. Do not use npm
Finally, put the code
I know what you're trying to say. Dependent download failed
I put node_modules have been uploaded
Just ask you for details
Project address: Vue eletron auto update / manual update .
Would you like it, tiezi!