Vue electron自动更新/ electron手动更新

Vue electron自动更新 /electron手动更新

Uncaught TypeError: fs.existsSync is not a function

所有的帖子都是一样的,都不去试一下 ,这偏文章我把坑都走了一遍。
直接上代码

最主要的就是这个问题 const electron = require(‘electron’).ipcRenderer 能不用引用
这个问题解决了 后面的就简单了
这么多帖子 居然没有一个说这个问题
代码在gitee上 地址在最下面
Vue electron自动更新/ electron手动更新_第1张图片

直接引用就会报错
#Uncaught TypeError: fs.existsSync is not a function
Vue electron自动更新/ electron手动更新_第2张图片

产生问题的原因:

首先在渲染进程属于浏览器端,没有集成Node的环境,所以类似 fs 这样的Node的基础包是不可以使用。

因为没有Node环境,所以require关键词是不可以使用的。

这个 stackoverflow 上找的方法
新建一个 preload.js

const electron = require('electron');
window.ipcRenderer = require('electron').ipcRenderer;   //  直接吧 electron.ipcRenderer 方法挂载在windows上面
window.remote = require('electron').remote;

在main.js文件中的 webPreferen中设置预加载preload:

  const win = new BrowserWindow({
    width: 1920,
    height: 1040,
    frame:true,
    webPreferences: {
      nodeIntegration:true,
      contextIsolation:false,
      webSecurity: false,
      preload:__dirname+'\\preload.js' ,
    }
  })

Vue electron自动更新/ electron手动更新_第3张图片

这个重点提一下preload.js 存放位置 位置真的很重要 不然就 undefined

本地的 preload.js 放在 打包之后的位置

Vue electron自动更新/ electron手动更新_第4张图片

打包之后的 preload.js 放在 public 下 和index.html 同目录

Vue electron自动更新/ electron手动更新_第5张图片

放了两个 我之前就是 只放了一个 所有 本地运行没问题 打包之后就报错了
真特么坑 居然没有一个帖子说这个问题 也有可能只有我遇见了这个问题
不然就是这样 找不到
Vue electron自动更新/ electron手动更新_第6张图片

下面开始 安装 electron-updater
// 推荐yarn
yarn add electron-updater --save-dev  

// *不推荐 npm  反正我是一次都没装成功过  
**npm i electron-updater --save-dev

这个必须有 不然打包不会生成 latest.yml 文件

每次更新把 yml 和 exe 丢给后端就行了

//这个必须有  不然打包不会生成 latest.yml 文件
 publish:[
      {
     "provider":"generic", 
       url: "http://192.168.0.191/electron/" // 更新文件服务器地址
      }
    ],

新建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: '检查更新出错',
        checking: '正在检查更新……',
        updateAva: '检测到新版本,正在下载……',
        updateNotAva: '现在使用的就是最新版本,不用更新',
    };
    //设置更新包的地址
    autoUpdater.setFeedURL(feedUrl);
    //监听升级失败事件
    autoUpdater.on('error', function (error) {
        sendUpdateMessage({
            cmd: 'error',
            message: error
        })
    });

    //监听开始检测更新事件
    autoUpdater.on('checking-for-update', function (message) {
        sendUpdateMessage({
            cmd: 'checking-for-update',
            message: message
        })
    });
    //监听发现可用更新事件
    autoUpdater.on('update-available', function (message) {
        sendUpdateMessage({
            cmd: 'update-available',
            message: message
        })
    });
    //监听没有可用更新事件
    autoUpdater.on('update-not-available', function (message) {
        sendUpdateMessage({
            cmd: 'update-not-available',
            message: message
        })
    });
 
    // 更新下载进度事件
    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();
        }
    });

    //监听下载完成事件
    autoUpdater.on('update-downloaded', function (event, releaseNotes, releaseName, releaseDate, updateUrl) {
        sendUpdateMessage({
            cmd: 'update-downloaded',
            message: {
                releaseNotes,
                releaseName,
                releaseDate,
                updateUrl
            }
        })
        //退出并安装更新包
        if (process.platform !== 'darwin') {
            canQuit = true;
            autoUpdater.quitAndInstall();
        }
        // autoUpdater.quitAndInstall();
    });
 
    //接收渲染进程消息,开始检查更新
    ipcMain.on("checkForUpdate", (e, arg) => {

        //执行自动更新检查

        // sendUpdateMessage({cmd:'checkForUpdate',message:arg})
        if(arg){
            autoUpdater.autoDownload = true;
        }
        autoUpdater.checkForUpdates();
    })
}
//给渲染进程发送消息
function sendUpdateMessage(text) {
    mainWindow.webContents.send('message', text)
}

backgrounnd.ts 我用的ts 这个和你们main.js 是一样


import { app, protocol, BrowserWindow,Menu } from 'electron'
import { createProtocol } from 'vue-cli-plugin-electron-builder/lib'
// import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer'

// 自动更新    引入上面创建的 updater.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' ,   这里是重点  //  这里引入 preload.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()
    })
  }
}

如果 updater.js 引用错误

Vue electron自动更新/ electron手动更新_第7张图片

在同目录 把updater.js copy 一份 吧名字改成 updater.ts 就好了

Vue electron自动更新/ electron手动更新_第8张图片

app.vue 设置更新页面

这个页面包含手动更新 和自动更新

<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?'检查到新版本...':'系统正在更新...'">
            <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;">
                    更新新版本
                </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) {
                        //显示升级对话框
                        _this.modalVisible = true;
                        if(!_this.isUpdatte)return;
                    } else if ("download-progress" == arg.cmd) {
                        //更新升级进度
                        /**
                         * 
                         * 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("更新失败");
                    }
                    // }
                });
            }
        },
        created() {
            //间隔1小时检测一次
            alert(ipcRenderer)
            let _this=this
            window.setInterval(() => {
            	// _this.isUpdatte == true 就是自动更新 获取到新版会自动更新 重新安装程序  推荐手动更新  
            	//  _this.isUpdatte == false 就是自动更新 
                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>

现在获取新版本 直接报错 cmd: “update-not-available”

死活找不到这个问题 全带全部没有问题

原来是 版本号一样 程序判断不需要更新! 明明已经有了 latest.yml 还需要版本号 真的坑

打包的时候 就改一下 package.json 里面 version 就可以了
Vue electron自动更新/ electron手动更新_第9张图片

到这一本就可以实现 自动更新

你以为完了

他现在是会自动 更新了 TM的 自动更新 安装会失败
原因是因为 appid 是一样
所有修改版本号的同时也要修改 appid

Vue electron自动更新/ electron手动更新_第10张图片

每次打包都要修改appid 和版本号 无语了

下面放打包 自动修改 version 和appid代码 我的配置在vue.config.js 里面 你也可以把代码放在 build.js 里面

 const path = require('path')
var webpack = require('webpack');
const fs = require('fs');

const demoName="app"
function resolve(dir) {
    return path.join(__dirname, dir)
}

//------------------------ 自动修改 版本号   用版本号 做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,

    // 简单配置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')
            }
        },
    },
    //  更加细粒化的配置webpack
    //  取自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
      // 原生包必须这里声明下
      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,
            // 打包配置
            builderOptions: {
                "appId": demoName+versionLine,
               
                // 发布者名称
                productName: demoName,

                // 安装包名称,可自行配置
                artifactName: demoName+'.exe',
                
                nsis: {
                    // 一键安装,如果设为true,nsis设置就无意义请直接删除 nsis 配置
                    oneClick: false,
                    // true全用户安装【目录为:C:\Program Files (x86)】,false安装到当前用户
                    perMachine: true,
                    // 允许请求提升。 如果为false,则用户必须使用提升的权限重新启动安装程序。
                    allowElevation: true,
                    // 允许修改安装目录
                    allowToChangeInstallationDirectory: true,
                    // 创建桌面图标
                    createDesktopShortcut: true,
                    // 创建开始菜单图标
                    createStartMenuShortcut: true,
                    // 快捷方式的名称,默认为应用程序名称
                    shortcutName: demoName,
                    // 安装图标
                    // installerIcon: "./logo.png",// 安装图标
                    // uninstallerIcon: "./logo.png",//卸载图标
                    // installerHeaderIcon: "./logo.png", // 安装时头部图标
                },
                // files: [
                //     {
                //       'filter': ['**/*']
                //     }
                //   ],
                //   extraFiles: ['./extensions/'],
                //   asar:  false,
                publish:[
                    {
                        "provider":"generic",
                        url: "http://192.168.0.191/electron/" // 更新文件服务器地址
                    }
                ],
            }
        }
    },
  
    
}

如果打包软件报错

Vue electron自动更新/ electron手动更新_第11张图片
这个因为electron 版本问题

yarn add   electron    //  直接yarn  在安装一遍  不要用npm  

最后放上代码

我知道你想说什么 依赖下载失败
我把 node_modules 都上传了
就问你细不细节

项目地址: vue-eletron 自动更新/手动更新 .

点个赞好吗 铁子!

你可能感兴趣的:(vue,vue.js,electron,javascript)