vue-electron

2,605 阅读5分钟

先来说一个 vue-electron 的报错

C:\Users\user\Desktop\dome\my-im\node_modules\_electron-localshortcut@3.2.0@electron-localshortcut\index.js:20
                } catch {
  SyntaxError: Unexpected token {
      at createScript (vm.js:80:10)
      at Object.runInThisContext (vm.js:139:10)
      at Module._compile (module.js:606:28)
      at Object.Module._extensions..js (module.js:653:10)
      at Module.load (module.js:561:32)
      at tryModuleLoad (module.js:504:12)
      at Function.Module._load (module.js:496:3)
      at Module.require (module.js:586:17)
      at require (internal/module.js:11:18)
      at eval (webpack:///external_%22electron-localshortcut%22?:1:18)
//这个错误是因为electron-localshortcut 升级导致的,降级到electron-localshortcut@3.0.0即可 

前言注意

<!--  1、组件内,打开外部链接 -->
<script>
this.$electron.shell.openExternal(link)
</script>
<!--  引入CSS -->
<style>
@import url('https://www.baidu.com/index.css')
</style>

全局安装

npm install -g electron

npm install -g electron-forge

创建 项目

electron-forge init my-app

运行 项目

npm start

结合vue创建项目

vue cerate app
vue add vue-cli-plugin-electron-builder

//启动
yarn electron:serve
//打包
yarn electron:build

主进程

import { app, BrowserWindow, Menu} from 'electron'

/**
 * Set `__static` path to static files in production
 * https://simulatedgreg.gitbooks.io/electron-vue/content/en/using-static-assets.html
 */
if (process.env.NODE_ENV !== 'development') {
  global.__static = require('path').join(__dirname, '/static').replace(/\\/g, '\\\\')
}

let mainWindow
const winURL = process.env.NODE_ENV === 'development'
  ? `http://localhost:9080`
  : `file://${__dirname}/index.html`

function createWindow () {
  /**
   * Initial window options
   */
  mainWindow = new BrowserWindow({
    height: 763,
    useContentSize: true,
    fullscreenable: true,  //是否允许全屏
    width: 1200,
    frame: true,   //是否显示 菜单栏
    center: true ,  //是否在屏幕中间显示
    title: '云笔记',
    titleBarStyle: 'hidden',
    webPreferences: {
      backgroundThrottling: false // 当页面被置于非激活窗口的时候是否停止动画和计时器
    }
    
  })

  mainWindow.loadURL(winURL)

  mainWindow.on('closed', () => {
    mainWindow = null
  })

  // 开启渲染进程中的调试模式
  mainWindow.webContents.openDevTools()
}
// 当electron完成初始化后触发
app.on('ready', createWindow)


//所有窗口都关闭的时候触发,在windows和linux里,所有窗口都退出的时候通常是应用退出的时候
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

//
app.on('activate', () => {
  if (mainWindow === null) {
    createWindow()
  }
})

小试牛刀——读取本地文件

const fs = require('fs')
export default {
    methods:{
        btn:function(){
            //这里的路径 为根路径
            fs.readFile('package.json',(err,data)=>{
                console.log(data)   
                this.$refs.texts.innerHTML=data
            })
        }
    }
}

开启初始化的调试模式

 // 开启渲染进程中的调试模式
  mainWindow.webContents.openDevTools()

拖拽

<div v-on:drop.prevent="drop" 
	v-on:dragenter="dragenter" 
	v-on:dragover="dragenter" 
	v-on:dragleave="dragenter" 
	class="box" id="box"
	>
</div>
   
<script>
const fs = require('fs')
export default {
    methods:{
      dragenter:function(){
          return false;
      },
      drop:function(e){
          console.log(e)
      }
    }
}
</script>

隐藏菜单

  //隐藏 顶部 菜单
  mainWindow.setMenu(null)

去掉顶部 最大化、最下化

  mainWindow = new BrowserWindow({
    height: 763,
    useContentSize: true,
    fullscreen: false,  //是否允许全屏
    width: 1200,
    frame: false,   //是否显示 菜单栏 最大化、最小化
    center: true ,  //是否在屏幕中间显示
    title: '云笔记',
    titleBarStyle: 'hidden',
    webPreferences: {
      backgroundThrottling: false // 当页面被置于非激活窗口的时候是否停止动画和计时器
    }
    
  })

可以拖动 的CSS

.box{
	webkit-app-region: drag
}

自定义 最小化、最大化、关闭

 min:function(){
           this.$electron.ipcRenderer.send('window-min') 

        /***
         * 获取mainWindow(既:win)  BrowserWindow.getFocusedWindow()
         * 在主进程 执行操作
         * var {ipcMain,BrowserWindow} = require('election')
         * ipcMain.on('window-min',()=>{
         *      最大化 win.maximize()
         *      最小化 win.minimize()
         *      恢复   win.restore()
         *      关闭   win.close()
         * 
         *  最大化  和 恢复原状的判断
         *      if(win.isMaximized()){
         *          win.restore()  //还原
         *      }else{
         *          win.maximize()  //最大化
         *      }
         * 
         * })
         * 
         *  */   
        },
        max:function(){
             this.$electron.ipcRenderer.send('window-max') 
        },
        close:function(){
             this.$electron.ipcRenderer.send('window-close') 
        }

注册全局快捷键

const {app,globalShortcut,BrowserWindow} = require('electron')
let win=BrowserWindow.getFocusedWindow();

app.on('ready',function(){
    // 注册快捷键
    globalShortcut.register('ctrl+e',function(){
       // win.hide();
       console.log('123')
    })
    // 检测注册是否成功
    let glos=globalShortcut.isRegistered('ctrl+e')
    console.log(glos)
})

app.on('will-quit',function(){
    // 取消 全局快捷键
    globalShortcut.unregister('ctrl+e')
})

注册剪切板

// 复制
clipboard.writeText('机器码')

webview

//和应用运行的是不同的进程,无渲染进程
<webview src='http://www.baidu.com'></webview>

通知 window.Notification

//通知 采用的是H5的 通知API

// 通知
let options={
    title: '通知',
    body: '你有新短消息,请注意查收',
    // icon: path.join('../static/imgs/app2.ico')
}

let defaults=window.Notification.permission
//  "denied"  --- 为用户点击了禁用(拒绝打开推送功能)

// "default" ----推送功能默认关闭

// "granted" ----推送功能为开启状态(用户点击允许后的状态)


console.log(defaults)


var myNotification= new Notification(options.title,options);

myNotification.onclick=function(){
    console.log('点击了')
}

let bons =document.getElementById('bons')
//点击以后 发布通知
bons.onclick=function(){
    console.log('123')
    let myNotifications= new window.Notification(options.title,options);
    console.log(myNotifications)
    //点击通知栏的事件
        myNotifications.onclick=function(){
            console.log('点击了')
        }
}


// 监听 网络变化,最好在APP.vue 的mounted函数中
window.addEventListener('online',function(){
    console.log('有网络')
})
window.addEventListener("offline",function(){
    console.log('网络断开')
})

实践

	<body>
		<button id="btn">按钮</button>
	</body>

<script>
	let btn=document.getElementById('btn')

let options={
	title: '通知',
	body: '你有新短消息,请注意查收'
}
btn.onclick=function(){
	//console.log('123')
	let notifications=null
	 if (!window.Notification) {
                    alert("浏览器不支持通知!");
        }
	 if(window.Notification.permission != 'granted'){
	 	window.Notification.requestPermission(function(stauts){
	 		console.log(stauts)
	 		if(stauts=='granted'){
	 			notifications=new window.Notification(options.title,options);
	 		}else{
	 			alert('您为允许开启消息通知,将接收不到 本站的消息推送')
	 		}
	 		
	 	})
	 }
	notifications=new window.Notification(options.title,options);
	
	//推送窗口 关闭时
	notifications.onclose=function(){
		
	}
	//推送窗口显示时
	notifications.onshow=function(){
		
	}
}

	

</script>

dialog 窗口提示和打开、保存文件

<template>
<!-- 云笔记 主页 -->
    <div class="notebook">
        <button v-on:click="btn">点击l</button>
        <webview id="webs" src='http://www.bbaidu.com'></webview>

        <button v-on:click="btna">错误提示</button>
        <button v-on:click="btnb">选择提示</button>
        <button v-on:click="btnc">打开文件</button>
        <button v-on:click="btnd">另存为</button>
    </div>
</template>

<script>

// 
const {shell,ipcRenderer,remote} = require('electron')

export default {
    methods:{
        btn:function(){
            console.log('123')
            shell.openExternal('https://www.baidu.com/')
            
        },
        btna:function(){
            remote.dialog.showErrorBox('错误提示','错误了')
        },
        btnb:function(){
            remote.dialog.showMessageBox({
                title: '提示信息',
                message: '内容',
                type: 'info',
                buttons: ['确定','取消']
            },function(index){
                //index  为buttons的索引
                console.log(index)
            })
        },
        btnc:function(){
            //打开文件
             remote.dialog.showOpenDialog({
                 properties:['openFile']
             },function(data){
                 //可以使用 node.js读取文件内容
                 console.log(data)
             })
                /***
                 * properties: openFile 只允许选择文件
                 *              openDirectory 只允许选择文件夹
                 *              
                 * 
                 *  */
        },
        btnd:function(){
            //保存文件
            remote.dialog.showSaveDialog({
                title: 'save file', //保存窗口的 title
                defaultPath: 'abs.gif',//默认路径
                filters:[
                    {
                        name: 'Images', extensions:['jpg','png','gif']
                    },
                    {
                        name: 'Movies', extensions:['mkv','avi','mp4']
                    },
                    {
                        name: 'Custom File Type', extensions:['as']
                    },
                    {
                        name: 'All Files', extensions:['*']
                    }
                ]
            },function(data){
                //保存以后,返回保存路径,但是 没有真正保存文件,需要node.js保存文件
                    console.log(data)
            })
        }
    },
    mounted:function(){
        console.log('hhahha')
        ipcRenderer.on('webs',function(event,data){

           // 在软件内部 打开外部网页
           let webs=document.getElementById('webs')
                webs.src=data
        })
    }
}
</script>

shell模块

//在外部浏览器打开连接地址,也可以打开本地目录
<template>
<!-- 云笔记 主页 -->
    <div class="notebook">
        <button v-on:click="btn">点击l</button>
    </div>
</template>

<script>

// 
const {shell} = require('electron')

export default {
    methods:{
        btn:function(){
            console.log('123')  //打开外部浏览器
            this.$electron.shell.openExternal('https://www.baidu.com/')
            
        }
    }
}
</script>

在页面嵌套页面,类似iframe

<webview src='https://www.baidu.com/'></webview>

主进程:ipcMain,渲染进程:ipcRenderer 通讯

/**
* 1、ipcMain  :在主进程中使用,他处理从渲染进程发送出来的异步消息,和同步消息
*		当然也有可能处理从主进程向渲染进程发送消息
* 2、ipcRenderer: 使用它提供的一些方法从渲染进程 发送同步或异步消息到主进程,
*		也可以接收主进程的回复消息
*/ 
//主进程主动给渲染进程广播数据
var win= BrowserWindow.getFocusedWindow()
win.webContents.send('opens',data)

渲染进程 向主进程 发送数据

// 渲染进程
<template>
<!-- 云笔记 主页 -->
    <div class="notebook">
        <button v-on:click="btn">点击</button>
    </div>
</template>

<script>
const fs = require('fs')

// 让渲染进程 给主进程发消息
const {ipcRenderer} = require('electron')

export default {
    methods:{
        btn:function(){
            // console.log('123')
            //渲染进程给主进程广播数据
            ipcRenderer.send('sendM','this is home')
        }
    }
}
</script>

<scripr>
//  主进程
import { ipcMain} from 'electron'

ipcMain.on('sendM',function(event,data){
    console.log(data)
})
/*  在下面生命周期中执行
* 当electron完成初始化后触发
*   app.on('ready', createWindow)
*/
</scripr>

渲染进程 向主进程 发送数据,并返回处理结果给渲染进程

<template>
<!-- 云笔记 主页 -->
    <div class="notebook">
        <button v-on:click="btn">点击</button>
    </div>
</template>

<script>
const fs = require('fs')

// 让渲染进程 给主进程发消息,主进程 返回处理结果给渲染
const {ipcRenderer} = require('electron')

export default {
    methods:{
        btn:function(){
            // console.log('123')
            //渲染进程给主进程广播数据
            ipcRenderer.send('sendM','this is home')
        }
    },
    mounted:function(){
            ipcRenderer.on('replay',(event,data)=>{
                    console.log(data)
            }) 
    }
}
</script>

<!-- 主进程 -->
<script>
import { ipcMain} from 'electron'

//接收 渲染进程数据,并且返回处理数据
ipcMain.on('sendM',function(event,data){
    //console.log(data)
    // 返回处理结果
    event.sender.send('replay','ok hello')
})
</script>

同步广播

<template>
<!-- 云笔记 主页 -->
    <div class="notebook">
        <button v-on:click="btn">点击</button>
    </div>
</template>

<script>
const fs = require('fs')

// 让渲染进程 给主进程发消息,主进程 返回处理结果给渲染
const {ipcRenderer} = require('electron')

export default {
    methods:{
        btn:function(){
            // console.log('123')
            //渲染进程给主进程广播数据(同步模式)
            let src=ipcRenderer.sendSync('sendM','this is home')
                console.log(src)
        }
    },
    mounted:function(){
            
    }
}
</script>

<!-- 主进程  -->
<script>
import { ipcMain} from 'electron'

//接收 渲染进程数据,并且返回处理数据(同步)
ipcMain.on('sendM',function(event,data){
    //console.log(data)
    // 返回处理结果
    event.returnValue='this is sync main'
})
</script>

实例

//  在渲染进程,通讯到主进程  打开新窗口
import { ipcMain, BrowserWindow} from 'electron'
var win;

//接收 渲染进程数据,并且返回处理数据
ipcMain.on('sendM',function(event,data){
    //console.log(data)
    // 返回处理结果
   // event.returnValue='this is sync main'

    win= new BrowserWindow({
        width:300,
        height: 400
    })
    win.loadURL('https://www.baidu.com/')
    win.webContents.openDevTools()
    win.on('closed',()=>{
        win=null
    })
})

渲染进程 与 渲染进程 单项通讯

/*
*   1、首先  第一个渲染进程 发送数据  到主进程,
*   2、主进程  保存 第一个渲染进程,并打开新的渲染进程
*   3、 传递数据  和  第一个 渲染进程  到新的渲染进程
*   4、   新的 渲染进程  拿到 第一个 渲染进程 后  发送数据 给第一个渲染进程
*
*/


// 首先 接收到  前一个页面传递的数据,并打开新的 页面,再把数据发送到 新页面

import { ipcMain, BrowserWindow} from 'electron'
var win;

//接收 渲染进程数据,并且返回处理数据
ipcMain.on('sendM',function(event,data){
    //console.log(data)
    // 返回处理结果
   // event.returnValue='this is sync main'

    win= new BrowserWindow({
        width:300,
        height: 400
    })
    win.loadURL('https://www.baidu.com/')
    win.webContents.openDevTools()
    
    //在页面渲染完成以后传递数据
    win.webContents.on('did-finish-load',function(){
        win.webContents.send('Load','ha ha hehe')
    })

    win.on('closed',()=>{
        win=null
    })
})
//新页面接收数据
 ipcRenderer.on('Load',function(event,data){
                console.log(data)
    })

渲染进程 与渲染 进程 相互通讯

ipcMain.on('sendM',function(event,data){
    // 保存 发送数据的窗口
    var winId = BrowserWindow.getFocusedWindow().id

    win= new BrowserWindow({
        width:300,
        height: 400
    })
    win.loadURL('https://www.baidu.com/')
    win.webContents.openDevTools()

    //在页面渲染完成以后传递数据, 并把 winId  传递过去
    win.webContents.on('did-finish-load',function(){
        win.webContents.send('Load','ha ha hehe',winId)
    })

    win.on('closed',()=>{
        win=null
    })
})
const fs = require('fs')

// 让渲染进程 给主进程发消息,主进程 返回处理结果给渲染
const {ipcRenderer} = require('electron')
const BrowserWindow =require('electron').remote.BrowserWindow
export default {
  
    mounted:function(evemt,data,winId){
        // 获取上一个窗口的winId
         let firstWinid=  BrowserWindow.fromId(winId)
        //  向上一个窗口发送消息
        firstWinid.webContents.send('toIndex', 'this is news');
    }
}

Tray 模块

let {app,Menu,Tray,shell,ipcMain,BrowserWindow} = require('electron');
// 托盘图标设置
// console.log(__dirname)
var iconTray=new Tray(path.join(__dirname,'../static/imgs/app2.png'));

// 绑定托盘的右键菜单
var tpl=[
    {
        label: '设置',
        click:function(){
            console.log('123')
        }
    },
    {
        label: '退出',
        click:function(){
            console.log('123')
        }
    }
]

let rightIcon=Menu.buildFromTemplate(tpl)

iconTray.setContextMenu(rightIcon)

// 托盘图标 提示文字
iconTray.setToolTip('云应用')

// 点击关闭按钮,让应该保存在托盘里,双击托盘,打开应用
let win=BrowserWindow.getFocusedWindow();
// console.log(win)

win.on('close',(e)=>{
    /****
     * 如 是点击 托盘的 退出,需要直接退出,
     *  win.isFocused() //判断窗口 当前是否激活显示
     *  */
    if(!win.isFocused()){
       win=null
    }else{
         // 阻止窗口关闭
         e.preventDefault();
         // 隐藏窗口
         win.hide();
    }
    
})

// 双击 托盘图标 打开窗口
iconTray.on('double-click',function(){
    win.show()
})


// 闪烁图标
/***
 * 定时器 设置图标
 *  iconTray.setImage(path.join(__dirname,'../static/imgs/app1.png'))
 *  */

在渲染进程 打开 另外一个 渲染进程,remote模块

/**
*渲染进程,无法直接调用主进程中的模块,
*但是我们可以通过 electron中的remove模块,间接的调用主进程中的模块
*/
//监听渲染进程的 某个点击事件
let BrowserWindow = require('electron').remote.BrowserWindow

let win=null
	win=new BrowserWindow({
    		height: 763,
    		useContentSize: true,
    		fullscreen: true,  //是否允许全屏
    		width: 1200,
    		frame: true,   //是否显示 菜单栏
    		center: true ,  //是否在屏幕中间显示
    		title: '云笔记',
    		titleBarStyle: 'hidden',
		    autoHideMenuBar : true,  //隐藏菜单
    		webPreferences: {
      			backgroundThrottling: false // 当页面被置于非激活窗口的时候是否停止动画和计时器
    		}    
  })
//加载页面
let winURL=path.join("file:",__dirname,'news.html')
mainWindow.loadURL(winURL)

//关闭页面
mainWindow.on('closed'.()=>{
	win=null
})

自定义 顶部菜单 menu 模块,在主进程

menus.js
import { Menu} from 'electron'

//定义菜单
let template=[
    {
      label: '文件',  //主菜单
      submenu:[       //子菜单
        {
          label: '新建窗口',
          click:function(){  //绑定事件
              console.log('新建窗口')
          },
          accelerator: 'ctrl+n'  //绑定快捷键,PS : 必须 单引号
        },
        {
          type: 'separator'   //分割线
        },
        {
          label: '打开文件',
          accelerator: 'ctrl+x',
          click:()=>{
            console.log('打开文件')
          }
        }
      ]
    },
    {
      label: '编辑',
      submenu:[
        {
          role: 'cut',  //绑定角色(内置)
          label: '剪切'
        },
        {
          rele: 'copy', //绑定角色
          label: '复制'
        }
      ]
    }
  ]


  //注册菜单
  let m= Menu.buildFromTemplate(template)
  //挂载菜单
  Menu.setApplicationMenu(m)


//需要在主进程中执行
// 当electron完成初始化后触发
app.on('ready', {
	//创建菜单
  	require('./menu')
})

自定义 顶部菜单 menu模块,在渲染进程中

const {Menu} = require('electron').remote;

//定义菜单
let template=[
    {
      label: '文件',  //主菜单
      submenu:[       //子菜单
        {
          label: '新建窗口',
          click:function(){  //绑定事件
              console.log('新建窗口')
          },
          accelerator: 'ctrl+n'  //绑定快捷键,PS : 必须 单引号
        },
        {
          type: 'separator'   //分割线
        },
        {
          label: '打开文件',
          accelerator: 'ctrl+x',
          click:()=>{
            console.log('打开文件')
          }
        }
      ]
    },
    {
      label: '编辑',
      submenu:[
        {
          role: 'cut',  //绑定角色(内置)
          label: '剪切'
        },
        {
          rele: 'copy', //绑定角色
          label: '复制'
        }
      ]
    }
  ]


  //注册菜单
  let m= Menu.buildFromTemplate(template)
  //挂载菜单
  Menu.setApplicationMenu(m)


//在需要 引入菜单的  渲染进程中 执行 即可

渲染进程的 右键菜单

const remote= require('electron').remote;
const Menu = remote.Menu
//定义菜单
let template=[
    {
      label: '文件',  //主菜单
      submenu:[       //子菜单
        {
          label: '新建窗口',
          click:function(){  //绑定事件
              console.log('新建窗口')
          },
          accelerator: 'ctrl+n'  //绑定快捷键,PS : 必须 单引号
        },
        {
          type: 'separator'   //分割线
        },
        {
          label: '打开文件',
          accelerator: 'ctrl+x',
          click:()=>{
            console.log('打开文件')
          }
        }
      ]
    },
    {
      label: '编辑',
      submenu:[
        {
          role: 'cut',  //绑定角色(内置)
          label: '剪切'
        },
        {
          rele: 'copy', //绑定角色
          label: '复制'
        }
      ]
    }
  ]


  //注册菜单
  let m= Menu.buildFromTemplate(template)
  //挂载菜单
  Menu.setApplicationMenu(m)

  
//   右键菜单
window.addEventListener('contextmenu',function(e){
    e.preventDefault()
    m.popup({
        window:remote.getCurrentWindow()
    })
},false)

//remote.getCurrentWindow()//表示获取当前窗口

主进程 右键菜单

//渲染进程  给主进程广播事件
window.addEventListener('contextmenu',(e)=>{
    <!--this.$electron.ipcRenderder.emit('contextmenu')-->
    //给主进程广播事件
    this.$electron.ipcRenderer.send('contextmenu');
},false)

//主进程 
//新建 menu.js
const { Menu, ipcMain, BrowserWindow } = require('electron');
//右键菜单
const contextMenuTemplate=[
    {
        label: '复制',
        role: 'copy'
    },
    {
        label: '粘贴',
        role: 'paste'
    },
    {
        type: "separstor" //分割线
    },
    {
        label: '其他功能',
        click:()=>{
            console.log('自定义功能')
        }
    }
]
//创建菜单
const contextMenu = Menu.buildFromTemplate(contextMenuTemplate)

//监听渲染进程contextmenu事件,显示菜单
ipcMain.on('contextmenu',function(){
    //获取当前窗口
    contextMenu.popup(BrowserWindow.getFocusedWindow())
})

//在主进程引入menu.js (ready函数内)
require('./menu.js')

点击菜单栏 在 软件内部 或者 外部默认浏览器 打开网页

import { Menu,shell,BrowserWindow} from 'electron'
//定义菜单
let template=[
    {
      label: '加载网页',
      submenu: [
        {
          label: '优酷',
          click: function(){
            // 在 默认浏览器 打开
            shell.openExternal('http://www.baidu.com')
          }
        },
        {
          type: 'separator'
        },
        {
          label: '百度',
          click: function(){

            let win= BrowserWindow.getFocusedWindow();
			//通知 渲染进程  在软件内 打开特定网页
                win.webContents.send('webs','http://www.youku.com')
                
          }
        }
      ]
    },
    {
      label: '帮助',
      submenu: [
        {
          label:'关于我们',
          click:function(){

          }
         
        }
      ]
    }
  ]
<template>
<!-- 云笔记 主页 -->
    <div class="notebook">
        <button v-on:click="btn">点击l</button>
        <webview id="webs" src='http://www.baidu.com'></webview>
    </div>
</template>

<script>


const {shell,ipcRenderer} = require('electron')

export default {
    methods:{
        btn:function(){
            console.log('123')
		//在外部 默认浏览器 打开特定网页
            shell.openExternal('https://www.baidu.com/')
            
        }
    },
    mounted:function(){

        ipcRenderer.on('webs',function(event,data){
            
           // 在软件内部 打开外部网页
           let webs=document.getElementById('webs')
                webs.src=data
        })
    }
}
</script>