Vue electron ic automatic update / electronic manual update

Posted by rpanning on Wed, 12 Jan 2022 10:10:05 +0100

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!

Topics: Javascript Vue.js Electron