zoukankan      html  css  js  c++  java
  • electron 开发拆坑总结

    electron 总结

    前言

    有一个web项目需要用客户端来包装一下 项目的主要业务都在服务器上 所以项目的大多数功能都用url 地址来访问; 客户端登陆界面在本地 打包客户端的本地登陆界面 做为登陆入口;
    
    electron 开发中 有一个概念需要理解 我暂且叫主进程吗 main,  这个进程的控制 在项目的 配置 package.json  的main 字段中定义;
    
    main.js 作为electron 控制的后端入口, 基本的主要控制都是在这个文件里面定义  eletron 控制系统 的接口很多也在这里面定义,详细可以查看官方API 文档; 
    
    
    {
      "name": "linksame",
      "version": "1.0.4",
      "description": "邻盛管家客户端",
      "main": "main.js",  // 主进程控制定义
      "scripts": {
        "start": "electron .",
        "dist": "electron-builder -wm",
        "build": "electron-builder",
        "test": "echo "Error: no test specified" && exit 1"
      },
    
    

    模块

    使用到的nodejs 模块:

    
       "electron-main-notification": "^1.0.1",  // 右下角消息提醒 组件
        "electron-notify": "^0.1.0",// 消息提醒组件
        "electron-packager": "^10.1.2",// 项目打包 组件  打包为独立的绿色运行程序
        "electron-squirrel-startup": "^1.0.0",// 升级包工具  用处不详
        "electron-updater": "^2.20.1",// 一个不错的升级包 制作组件 我们用它做的升级包 提醒
        "gulp": "^3.9.1", // 工具链工具  本地开发
        "nedb": "^1.8.0",// 一个数据库组件  对象性质 nodejs  
        "request": "^2.83.0" // 一个请求库
    
    

    项目用到 eletron 主要的模块 说明

    const electron = require('electron') //
    //import { autoUpdater } from "electron-updater"
    const autoUpdater=require('electron-updater').autoUpdater // 升级包检测 不同于官方默认的那个
    autoUpdater.autoDownload = false // 配置取消自动下载
    // Module to control application life.
    const app = electron.app // 项目基础控制
    // Module to create native browser window.
    const BrowserWindow = electron.BrowserWindow  // 开窗口控制
    const BrowserView = electron.BrowserView  // 窗口控制另外一种方式 
    const dialog = electron.dialog // 系统弹出地址框
    const Tray = electron.Tray // 右下角任务栏
    const Menu = electron.Menu //右下角任务栏 菜单控制
    const Notification = electron.Notification // 消息提醒
    const window = electron.window 
    const ipcMain = require('electron').ipcMain  // 重点 主进程通信专用  发通道信息  监听通道
    const ipcRenderer = require('electron').ipcRenderer // 重点 子窗口 通信专用 可以在子窗口中 和 主进程通信
    //const storage = require('electron-json-storage')
    const {shell} = require('electron') // electron 基础功能接口  例如  打开浏览器网页  ; 打开文件  运行程序等等;
    const notify = require('electron-main-notification') // 也是一个消息对话框
    const {session} = require('electron')  // seesion  cookie 控制
    const fs = require('fs');  // nodejs 模块 文件文本操作等等 io
    const nedb = require('nedb'); // 数据库 一个nodejs 中可以用的数据库
    
    

    通信

    const ipcMain = require('electron').ipcMain // 重点 主进程通信专用 发通道信息 监听通道
    const ipcRenderer = require('electron').ipcRenderer // 重点 子窗口 通信专用 可以在子窗口中 和 主进程通信

    // 例如 主穿裤中监听 界面发来的命令 主进程中执行相应操作
    ipcMain.on('window-min',function(){
    mainWindow.minimize();
    })
    // 主进程发起送消息 其他页面会收到数据domianObj
    mainWindow.webContents.send('islogin',domainObj)

    本地数据库

    打包

    npm run build
    
    

    升级包制作

    参考 :https://www.electron.build/auto-update

    const autoUpdater=require('electron-updater').autoUpdater // 升级包检查模块 包含检查升级包 下载 安装等接口封装 都在里面

    autoUpdater.setFeedURL('http://www.linksame.com/release/');

    升级包检测 比较

    app.getVersion() // 系统当前版本
    // 检查升级包接口 返回 Promise 对象
    let checkinfo=autoUpdater.checkForUpdates();
    //console.log('checkinfo:',checkinfo)
    checkinfo.then(function(data){
    console.log('datav:::',data.versionInfo.version)
    console.log('datav2:::',data)
    // 返回的data 为网络上版本最新版信息 可获取系统版本号 和 最新版本好比较 确定要部要升级
    if(data.versionInfo.version!=app.getVersion()){
    updateHandle();
    //const UpdateInfo=require('electron-updater').UpdateInfo
    //console.log('UpdateInfo:',)
    }
    })

    Promise 应用 对象

    异步变成 新规范 一些功能中用到了 例如 autoUpdater 升级管理 模块的的一些接口 就是 该类型

    参考

    升级参考

    main.js 参考

    项目组控制 介绍说明

    
    if (require('electron-squirrel-startup')) return;
    const electron = require('electron')
    //import { autoUpdater } from "electron-updater"
    const autoUpdater=require('electron-updater').autoUpdater
    autoUpdater.autoDownload = false // 配置取消自动下载
    // Module to control application life.
    const app = electron.app
    // Module to create native browser window.
    const BrowserWindow = electron.BrowserWindow
    const BrowserView = electron.BrowserView
    const dialog = electron.dialog
    const Tray = electron.Tray
    const Menu = electron.Menu
    const Notification = electron.Notification
    const window = electron.window
    const ipcMain = require('electron').ipcMain
    const ipcRenderer = require('electron').ipcRenderer
    //const storage = require('electron-json-storage')
    const {shell} = require('electron')
    const notify = require('electron-main-notification')
    const {session} = require('electron')
    const fs = require('fs');
    const nedb = require('nedb'); // 数据库
      
    const path = require('path')
    const url = require('url')
    
    const icologo=__dirname+'\ioc\ls.ico'
    // 域名
    const domain="http://www.linksame.com";
    const loginpath=domain+"/index.php?app=Core&m=Pcdlogin&network=1&ip=0"
    const request = require('request')
    // 判断网络配置
    const  options = {
           method: 'post',
                          url:domain+'/release/network.json',
                          form: "content",
                          headers: {
                            'Content-Type': 'application/x-www-form-urlencoded'
                          }
                        };
    
    const message={
          error:'check version error',
          checking:'check updateing ......',
          updateAva:'find a New Version,downloading ......',
          updateNotAva:'now it New best',
        };
    const os = require('os');
    
    // init obj
    let mainWindow
    let tray = null
    const app_data=app.getPath('userData')
    console.log('app_data',app_data)
    
    // 设置共享运行目录
    global.linksame = {
      runpath: app_data
    }
    
    // 实例化连接对象(不带参数默认为内存数据库) 
    
    const db = new nedb({
      filename: path.join(app_data,'\Cache\downloadfile.db'),
      autoload: true
    });
    let date=new Date();
    
    const initobj={
      1024,
      height:760,
    }
    
    
    function createWindow() {
        // Create the browser window.
        mainWindow = new BrowserWindow({ 
           712, 
          height: 588, 
          icon:icologo,
          title:'邻盛企业管家',
          frame: false,
          transparent: true,
          webPreferences:{
            nodeIntegration:true,
            nodeIntegrationInWorker:true,
          }
        })
    
    //Menu.setApplicationMenu(null);
    
    
        
     const mainindex = path.join('file://', __dirname, 'index.html')
     mainWindow.loadURL(mainindex)
    
    
        mainWindow.once('ready-to-show', () => {
            mainWindow.show()
        })
    
        // Open the DevTools.
        // mainWindow.webContents.openDevTools()
    
        // Emitted when the window is closed.
        mainWindow.on('closed', function() {
           
            mainWindow = null
        })
    
        //捕获新打开窗口事件 定制新窗口
        mainWindow.webContents.on('new-window', (event, url) => {
          event.preventDefault()
          const win = new BrowserWindow({ 
             1024, 
            height: 760,
            icon:__dirname+'./ioc/ls.ico',
            title:'邻盛企业管家',
            frame: false,
            transparent: true,
            backgroundColor:'#4385F4',
    
          })
          
          win.once('ready-to-show', () => win.show())
          win.loadURL(url)
          //event.newGuest = win
    
          console.log('windowID:',win.id)
    
          ipcMain.on('sub-close',function(d){
            console.log('d',d)
            win.hide()
          })
    
        })
    
        // download 
    
        mainWindow.webContents.session.on('will-download', (event, item, webContents) => {
            // Set the save path, making Electron not to prompt a save dialog.
    
            item.on('updated', (event, state) => {
    
                if (state === 'interrupted') {
                    console.log('Download is interrupted but can be resumed')
                } else if (state === 'progressing') {
                    // set loading progressBar
                     mainWindow.setProgressBar(item.getReceivedBytes() / item.getTotalBytes());
    
                    if (item.isPaused()) {
                        console.log('Download is paused')
                    } else {
    
                      if(Math.ceil(item.getReceivedBytes()/1024/1024)===1){
                          let title='稍等 正在下载';
                          let body= item.getSavePath();
                          let ico=getico(item.getSavePath());
                          notifly(title,body,ico)
                      }
    
                        let download = `Received bytes: ${Math.ceil(item.getReceivedBytes()/1024/1024)} M / ${Math.ceil(item.getTotalBytes()/1024/1024)}M`
    
                        console.log(`Received bytes: ${Math.ceil(item.getReceivedBytes()/1024/1024)} M / ${Math.ceil(item.getTotalBytes()/1024/1024)}M`)
                    }
                }
            })
            item.once('done', (event, state) => {
    
                if (state === 'completed') {
                    const filepath=item.getSavePath();
                    var arr = filepath.split('\');
                    let filename=arr[arr.length-1];
    
                    let title=filename+' 下载完成!';
                    let body= item.getSavePath()+'    打开';
                    let ico=getico(item.getSavePath());
    
                    //notifly(title,body,ico)
                    notiflyclick(title,body,function(){ shell.openItem(filepath) })
                    console.log('Download successfully')
                
                    db.insert({
                        name: filename,
                        path:item.getSavePath(),
                        datetime:date.toLocaleDateString(),
                        sizes:Math.ceil(item.getTotalBytes()/1024/1024),
                      }, (err, ret) => {
                         console.log('insert successfully',err,ret)
                      });
                   
    
                } else {
                    console.log(`Download failed: ${state}`)
                }
            })
    
    
    
        })
    
        //  任务栏图标菜单 A 
        tray = new Tray(icologo)
        const contextMenu = Menu.buildFromTemplate([{
                label: '帮助中心',
                type: 'normal',
                icon:  __dirname+'\ioc\help.png',
                click: function() {
                      shell.openExternal('http://help.linksame.com/')
                 }
            },
            { label: '官网', type: 'normal', icon: __dirname+'\ioc\web.png',click:function(){
                shell.openExternal('http://www.linksame.com')
            }},
            { label: '移动端', type: 'normal', icon:__dirname+'\ioc\phone.png',click:function(){
                //shell.openExternal('http://www.linksame.com/index.php?app=Core&m=V7&a=download')
                
                const modalPath = path.join('file://', __dirname, 'qcode.html')
                //let win = new BrowserWindow({  705, height: 250,resizable:false,autoHideMenuBar:true,type: 'desktop', icon: './ioc/download2.png' })
                let win = new BrowserWindow({ 
                  title:'移动设备软件下载', 
                   250, 
                  height: 250,
                  autoHideMenuBar:true,
                  type: 'desktop', 
                  icon:__dirname+'\ioc\phone.png',
                  resizable:false,
                  maximizable:false,
    
                   })
                //win.setApplicationMenu(null);
                win.on('close', function() { win = null })
                win.loadURL(modalPath)
                win.show()
    
            } },
            {
                label: '下载管理',
                type: 'normal',
                icon:__dirname+'\ioc\down.png',
                click: function() {
                    const modalPath = path.join('file://', __dirname, 'download.html')
                    //let win = new BrowserWindow({  705, height: 250,resizable:false,autoHideMenuBar:true,type: 'desktop', icon: './ioc/download2.png' })
                    let win = new BrowserWindow({ 
                       705, 
                      height: 250,
                      autoHideMenuBar:true,
                      type: 'desktop', 
                      icon: __dirname+'\ioc\download2.png',
                      resizable:false,
                      maximizable:false,
                       })
                    //win.setApplicationMenu(null);
                    win.on('close', function() { win = null })
                    win.loadURL(modalPath)
                    win.show()
                }
            },
            {
                label: '设置',
                type: 'submenu',
                icon:__dirname+'\ioc\setting.png',
                submenu: [
                    { label: '开机启动', type: 'normal', icon:__dirname+'\ioc\sysset.png' },
                    { label: '更新缓存', type: 'normal', icon:__dirname+'\ioc\clear-2.png',click:function(){
                               let cachepath=app_data+'/Cache'
                               let dir=fs.readdir(cachepath,(err,file)=>{
                                                              
                                  for(v in file){
                                      console.log('file',v)
                                      let rmnum=shell.moveItemToTrash(path.join(cachepath,file[v]))
                                      console.log('remove',rmnum)
                                  }
                                  notifly('缓存清理','缓存清理完成!',__dirname+'\ioc\ok.png')
    
                               })
                    }},
                  
                ]
            },
            { label: '升级', type: 'normal', icon: __dirname+'\ioc\upgrate.png',click:function(){
              updateHandle();
            } },
            { label: '注销', type: 'normal', icon: __dirname+'\ioc\zx.png', role: 'close',click:function(){
                
                // console.log('siht',console.log(ses.getUserAgent()))
                // 查询与指定 url 相关的所有 cookies.
                
                /*session.defaultSession.cookies.remove('http://www.linksame.com','sns_shell',function(cookies) {
                              console.log('remove~~~~')
                            });*/
                session.defaultSession.cookies.get({url:domain}, function(error, cookies) {
    
                           let  domainObj=cookies
                           for (var i in domainObj){
                             console.log(i,':',domainObj[i])
                             session.defaultSession.cookies.remove(domain,domainObj[i].name, function(data) {
                                           console.log('remove',data);
                              });
                             
                           }
                           //console.log('ddd',domainObj)
                 });
    
                 let newobj=session.defaultSession.cookies.get({ url : domain }, function(error, cookies) {
                    console.log('login out coockie:',newobj)
                  });
               
                  app.quit();  
                  //session.cookies.remove("http://www.linksame.com", name, callback)
                              
            }},
            { label: '退出', type: 'normal', icon:__dirname+'\ioc\loginout.png', role: 'close',click:function(){
                  const dopt={
                    type:'question',
                    title:'你确定要退出吗?',
                    buttons:['确定','取消'],
                    defaultId:1,
                    message:'退出后 会关闭邻盛企业管家。',
                    //icon:'./ioc/ls.ico',
                    noLink:true,
                  }
                  dialog.showMessageBox(dopt,function(e){
                     console.log('e:',e)
                     if(e==0){
                        app.quit();  
                     }else{
                        console.log('nat close')
                     }
                    //   
                  })
                        
            }}
        ])
        tray.setToolTip('邻盛企业管家 你的企业好帮手!')
        tray.setContextMenu(contextMenu)
    
    }
    
    // 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', createWindow)
    
    // Quit when all windows are closed.
    app.on('window-all-closed', function() {
        // On OS X 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', function() {
        // On OS X 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 (mainWindow === null) {
            createWindow()
        }
    })
    
    
    //登录窗口最小化
    ipcMain.on('window-min',function(){
      mainWindow.minimize();
    })
    
    //重新设置大小
    ipcMain.on('window-reset',function(){
      console.log(initobj.width, initobj.height)
    
      mainWindow.setSize(initobj.width, initobj.height)
      mainWindow.center()
    })
    
    //登录窗口最大化
    ipcMain.on('window-max',function(data){
    
      console.log('data',data)
    
      if(mainWindow.isMaximized()){
          mainWindow.restore();  
      }else{
          mainWindow.maximize(); 
      }
    })
    
    ipcMain.on('window-close',function(){
      mainWindow.close();
    })
    
    // 检查登陆
    ipcMain.on('islogin',function(){
               session.defaultSession.cookies.get({url:domain,name:'sns_username'}, function(error, cookies) {
                     let  domainObj=cookies                             
                     // console.log('ddd',domainObj[0].value)
                     mainWindow.webContents.send('islogin',domainObj)
               });
    })
    // 检查网络
    ipcMain.on('testNetwork',function(){
               request(options, function (err, res, body) {
                if (err) {
                  console.log(err)
                }else {
                  mainWindow.webContents.send('testNetwork',body)
                }
              })
    })
    // 注销登陆
    ipcMain.on('loginout',function(){
        session.defaultSession.cookies.get({url:domain}, function(error, cookies) {
    
                           let  domainObj=cookies
                           for (var i in domainObj){
                             console.log(i,':',domainObj[i])
                             session.defaultSession.cookies.remove(domain,domainObj[i].name, function(data) {
                                           console.log('remove',data);
                              });
                             
                           }
                           //console.log('ddd',domainObj)
          });
        //app.quit();
        //mainWindow.loadURL(loginpath)
    })
    
    
    
    
    
    // 消息通知 函数
    function notifly(title,body,ico){
      const opt = {
          icon: ico,
          title: title,
          body: body,              
      }
      const m = new Notification(opt);
      m.show()
      
    }
    
    // 可点击事件的通知
    function notiflyclick(title,body,callback){
       notify(title, { body: body }, () => {
                        console.log('The notification got clicked on!')
                        callback()
        })
    }
    
    //自动获取图标
    function getico(path){
      let str = path.substring(path.lastIndexOf(".")+1);
      switch (str) {
        case 'doc':
          return __dirname+'\ioc/format/doc.png';
          break;
        case 'docx':
          return __dirname+'\ioc/format/doc.png';
          break;
        case 'xls':
          return __dirname+'\ioc/format/excel.png';
          break;
        case 'xlsx':
          return __dirname+'\ioc/format/excel.png';
          break;
        case 'csv':
          return __dirname+'\ioc/format/excel.png';
          break;
        case 'exe':
          return __dirname+'\ioc/format/exe.png';
          break;
        case 'html':
          return __dirname+'\ioc/format/file_html.png';
          break;
        case 'htm':
          return __dirname+'\ioc/format/file_html.png';
          break;
        case 'pptx':
          return __dirname+'\ioc/format/ppt.png';
          break;
        case 'ppx':
          return __dirname+'\ioc/format/ppt.png';
          break;
        case 'rar':
          return __dirname+'\ioc/format/rar.png';
          break;
        case 'zip':
          return __dirname+'\ioc/format/zip.png';
          break;
        case 'gz':
          return __dirname+'\ioc/format/zip.png';
          break;
        case 'tar':
          return __dirname+'\ioc/format/zip.png';
          break;
        case 'pdf':
          return __dirname+'\ioc/format/pdf.png';
          break;    
         case 'png':
          return __dirname+'\ioc/format/png.png';
          break;    
         case 'jpg':
          return __dirname+'\ioc/format/jpg.png';
          break;    
         case 'gif':
          return __dirname+'\ioc/format/gif.png';
          break;    
        default:
           return __dirname+'\ioc/format/file.png';
          break;
      }
    }
    //================  升级
    
    autoUpdater.setFeedURL('http://www.linksame.com/release/');
    // 检测更新,在你想要检查更新的时候执行,renderer事件触发后的操作自行编写
    function updateHandle(){
        
        
        autoUpdater.on('error', function(error){
          sendUpdateMessage(message.error)
        });
        autoUpdater.on('checking-for-update', function(info) {
          console.log('checking for update:::',info)
          sendUpdateMessage(message.checking)
        });
        autoUpdater.on('update-available', function(info) {
            console.log('update-available:::',info)
            upwin.webContents.send('checkinfo', info)
            sendUpdateMessage(message.updateAva)
        });
        autoUpdater.on('update-not-available', function(info) {
            sendUpdateMessage(message.updateNotAva)
        });
        
        // 更新下载进度事件
        autoUpdater.on('download-progress', function(progressObj) {
            console.log('downloading:',progressObj)
            upwin.webContents.send('downloadProgress', progressObj)
        })
        autoUpdater.on('update-downloaded',  function (event, releaseNotes, releaseName, releaseDate, updateUrl, quitAndUpdate) {
            ipcMain.on('isUpdateNow', (e, arg) => {
                //some code here to handle event
                autoUpdater.quitAndInstall();
            })
            console.log(releaseNotes, releaseName, releaseDate, updateUrl, quitAndUpdate)
            //upwin.webContents.send('isUpdateNow')
        });
        
        //执行自动更新检查
        //autoUpdater.checkForUpdates();
         ipcMain.on("checkForUpdate",()=>{
              //执行自动更新检查 appUpdater.autoDownload = false
              autoUpdater.checkForUpdates();
              //console.log('checkinfo:',checkinfo)
              
          })
         //执行下载
        ipcMain.on("download",()=>{
              autoUpdater.downloadUpdate()          
          })
         console.log('now check updateing ~~~~')
         // autoUpdater.checkForUpdates();
    
          //open upgroud dialog
          const modalPath = path.join('file://', __dirname, 'upgroud.html')
          //let win = new BrowserWindow({  705, height: 250,resizable:false,autoHideMenuBar:true,type: 'desktop', icon: './ioc/download2.png' })
          let upwin = new BrowserWindow({ 
             705, 
            height: 250,
            autoHideMenuBar:true,
            type: 'desktop', 
            icon: __dirname+'\ioc\upgrate2.png',
            resizable:false,
            maximizable:false,
             })
          //win.setApplicationMenu(null);
          upwin.on('close', function() { 
              
               upwin.webContents.send('close') 
               
               //ipcMain.removeAllListeners(["checkForUpdate", "download", "isUpdateNow"])
               upwin = null
               //ipcRenderer.removeAll(["checkForUpdate", "download", "isUpdateNow"]);
           })
          upwin.loadURL(modalPath)
          upwin.show()
    }
    
    // 通过main进程发送事件给renderer进程,提示更新信息
    // mainWindow = new BrowserWindow()
    function sendUpdateMessage(text){
        console.log('text:',text)
        //mainWindow.webContents.send('message', text)
    }
    
    // 第一次运行软件 判断网络
    function initview(){
                          
    }
    // 自动随机检查升级包
    function checkUp(){
         let checkinfo=autoUpdater.checkForUpdates();
         //console.log('checkinfo:',checkinfo)
         checkinfo.then(function(data){
            console.log('data:::',data.versionInfo.version)
            if(data.versionInfo.version){
                updateHandle();
            }
         })
    
    }
    //checkUp();
    //随机时间执行检查升级
    let randoms=Math.round(Math.random()*9+1)*60000;
    console.log('randoms=',randoms)
    setTimeout(function(){
      checkUp();
    },randoms)
    // hi i'm a vision 1.0.2 hahah
    
    
    
  • 相关阅读:
    CI框架用cookie实现用户自动登录
    php预定义常量
    ubuntu root密码
    jQuery的form中ajaxSubmit参数
    优酷视屏播放去广告链接id_XXX.html内容替换红色
    linux 重启apache:apachectl -k graceful
    python深copy与浅copy的区别
    python3中pathlib库的Path类方法汇总
    unittest使用discover加载不同目录下的case失败,提示Path must be within the project
    自定义HTMLTestRunner报告case名称
  • 原文地址:https://www.cnblogs.com/freefei/p/8397409.html
Copyright © 2011-2022 走看看