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 模塊:javascript

"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 主要的模塊 說明php

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 // 重點 子窗口 通訊專用 能夠在子窗口中 和 主進程通訊html

// 例如 主穿褲中監聽 界面發來的命令 主進程中執行相應操做
ipcMain.on('window-min',function(){
mainWindow.minimize();
})
// 主進程發起送消息 其餘頁面會收到數據domianObj
mainWindow.webContents.send('islogin',domainObj)java

本地數據庫

打包

npm run build

升級包製做

參考 :https://www.electron.build/auto-updatenode

const autoUpdater=require('electron-updater').autoUpdater // 升級包檢查模塊 包含檢查升級包 下載 安裝等接口封裝 都在裏面web

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

升級包檢測 比較數據庫

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:',)
}
})npm

Promise 應用 對象

異步變成 新規範 一些功能中用到了 例如 autoUpdater 升級管理 模塊的的一些接口 就是 該類型json

參考

升級參考

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={
  width:1024,
  height:760,
}


function createWindow() {
    // Create the browser window.
    mainWindow = new BrowserWindow({ 
      width: 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({ 
        width: 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({ width: 705, height: 250,resizable:false,autoHideMenuBar:true,type: 'desktop', icon: './ioc/download2.png' })
            let win = new BrowserWindow({ 
              title:'移動設備軟件下載', 
              width: 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({ width: 705, height: 250,resizable:false,autoHideMenuBar:true,type: 'desktop', icon: './ioc/download2.png' })
                let win = new BrowserWindow({ 
                  width: 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({ width: 705, height: 250,resizable:false,autoHideMenuBar:true,type: 'desktop', icon: './ioc/download2.png' })
      let upwin = new BrowserWindow({ 
        width: 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
相關文章
相關標籤/搜索