🥝FANMR.CN热爱,追求
Electron

安装

npm init
cnpm i electron

package.json中指定启动命令

"scripts": {
  "dev": "electron ."
},

然后根据入口文件名称创建对应的文件,写入最简单的代码

const { app, BrowserWindow } = require('electron')

app.on('ready', () => {
  new BrowserWindow({
    width: 500,
    height: 300
  })
})

加载资源

// 加载网络资源
mainWindow.loadURL('http://xxxx')
// 加载本地资源
mainWindow.loadFile('src/index.html')

控制台

打开调试控制台配置

// 打开控制台
mainWindow.webContents.openDevTools()

关闭控制台警告

index.html中加入

<meta http-equiv="Content-Security-Policy" content="default-src 'self' 'unsafe-inline';">

自定义菜单

const { app, BrowserWindow, Menu } = require('electron')

app.on('ready', () => {
  const mainWindow = new BrowserWindow({
    width: 800,
    height: 500,
  })

  // 加载本地资源
  mainWindow.loadFile('src/index.html')

  Menu.setApplicationMenu(Menu.buildFromTemplate([
    {
      label: '同步云端',
      submenu: [
        {
          label: '同步到Gitee',
          click() {
            console.log('点击事件');
          }
        }
      ]
    },
    {
      label: '发布笔记'
    }
  ]))

})

完全自定义头部

先关闭菜单栏

const { app, BrowserWindow } = require('electron')

app.on('ready', () => {
  const mainWindow = new BrowserWindow({
    width: 800,
    height: 500,
     frame: false // 完全关闭菜单栏
  })

  // 加载本地资源
  mainWindow.loadFile('src/index.html')
})

然后自己写菜单栏并实现可拖拽

<style>
  body {
    margin: 0;
  }
  .header {
    width: 100%;
    height: 60px;
    background-color: aquamarine;
    /* 窗口可拖拽 */
    -webkit-app-region: drag;
  }
  .header span {
    /* 关闭拖拽防止影响点击事件 */
    -webkit-app-region: no-drag;
  }
</style>
<body>
  <div class="header">
    <span>保存</span>
    <span>新建</span>
  </div>
</body>
</html>

渲染进程

除了入口js文件外都属于渲染进程,一个程序只有一个主进程,而渲染进程要使用Node相关的命令和语法,需要进行配置

const mainWindow = new BrowserWindow({
  width: 800,
  height: 500,
  webPreferences: {
    // 开启渲染进程使用Node
    nodeIntegration: true,
    // 控制上下文隔离
    contextIsolation: false,
  }
})

然后就可以在子进程中使用

const { shell } = require('electron')

// 所有a标签使用浏览器打开
let allA = document.querySelectorAll('a')
allA.forEach(item => {
  item.onclick = function (e) {
    e.preventDefault()
    shell.openExternal(item.href)
  }
})

使用fs

const fs = require('fs')

渲染进程与主进程通信

// 自定义事件
ipcMain.on('event-name', (event, arg) => {
  console.log(event);
  console.log(arg);
  mainWindow.maximize()
})

// 触发事件
ipcRenderer.send('event-name', '传递参数')

主进程可以使用回调给渲染进程传数据

// 主进程
ipcMain.on('marked', (e, a) => {
  mainWindow.webContents.send('back-html', marked.parse(a))
})

// 渲染进程
ipcRenderer.on('back-html', (e, a) => {
  console.log(a);
})

打包

下载打包依赖

cnpm i electron-packager

然后配置打包命令

"build": "electron-packager ./ my-app --platform=win32 --arch=x64 --out ./target --overwrite --icon=./favicon.ico --electron-zip-dir=D:/Bb_work/electron"

最后指定了打包源文件的位置,防止因网络问题无法打包,点击前往下载地址

注意:如果打包报错一下信息:

Failed to locate module "cross-spawn" from "D:\La_temp\900\node_modules\cross-env"

        This normally means that either you have deleted this package already somehow (check your ignore settings if using electron-packager).  Or your module installation failed.

代表npm在下载依赖时只是下载了第一层的依赖,内部依赖并没有下载,进入一级依赖内执行npm i为其下载才能解决

使用JQ

<script src="lib/jquery.min.js"></script>
<script>if (typeof module === 'object') { window.jQuery = window.$ = module.exports; };</script>

窗口大小控制

// 关闭窗口
ipcMain.on('win-close', (e, a) => {
  mainWindow.close()
})
// 最大化窗口
ipcMain.on('win-max', (e, a) => {
  mainWindow.maximize()
})
// 恢复窗口大小
ipcMain.on('win-res', (e, a) => {
  mainWindow.restore()
})
// 最小化窗口
ipcMain.on('win-min', (e, a) => {
  mainWindow.minimize()
})

如果不想让可拖拽的元素双击最大化可以关闭,在主文件窗口配置中

// 禁用双击最大化窗口,自己控制
maximizable: false

结合Vite

学习文章地址

创建vite项目

# npm 7+, extra double-dash is needed:
npm create vite@latest my-vue-app -- --template vue
npm i

安装各种依赖

cnpm i electron concurrently wait-on cross-env electron-packager

其中:concurrently用于多命令执行,加入wait-on实现热更新,cross-env用于指定开发环境和生产环境,electron-packager用于打包

配置

第一步:创建electron文件夹,加入两个js文件

preload.js用于隔离electron和vue的环境

window.addEventListener('DOMContentLoaded', () => {
  const replaceText = (selector, text) => {
    const element = document.getElementById(selector)
    if (element) element.innerText = text
  }

  for (const dependency of ['chrome', 'node', 'electron']) {
    replaceText(`${dependency}-version`, process.versions[dependency])
  }
})

main.js为程序的主要入口

// 控制应用生命周期和创建原生浏览器窗口的模组
const { app, BrowserWindow } = require('electron')
const path = require('path')

const NODE_ENV = process.env.NODE_ENV

function createWindow() {
  // 创建浏览器窗口
  const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js')
    }
  })

  // 通过环境加载不同的资源
  mainWindow.loadURL(NODE_ENV === 'development' ? 'http://localhost:5173' : `file://${path.join(__dirname, '../dist/index.html')}`);

  // 开发中打开控制台
  if (NODE_ENV === 'development') {
    // 打开控制台
    mainWindow.webContents.openDevTools()
  }
}

// 这段程序将会在 Electron 结束初始化
// 和创建浏览器窗口的时候调用
// 部分 API 在 ready 事件触发后才能使用。
app.whenReady().then(() => {
  createWindow()
  app.on('activate', function () {
    // 通常在 macOS 上,当点击 dock 中的应用程序图标时,如果没有其他
    // 打开的窗口,那么程序会重新创建一个窗口。
    if (BrowserWindow.getAllWindows().length === 0) createWindow()
  })
})

// 除了 macOS 外,当所有窗口都被关闭的时候退出程序。 因此,通常对程序和它们在
// 任务栏上的图标来说,应当保持活跃状态,直到用户使用 Cmd + Q 退出。
app.on('window-all-closed', function () {
  if (process.platform !== 'darwin') app.quit()
})

第二步:配置package.json中的脚本,加入以下内容

"scripts": {
  "dev": "vite",
  "build": "vite build",
  "preview": "vite preview",
  // 加入下列内容
  "electron": "wait-on tcp:5173 && cross-env NODE_ENV=development electron .",
  "electron:serve": "concurrently -k \"npm run dev\" \"npm run electron\"",
  "electron:preview": "vite build && electron .",
  "electron:build": "vite build && electron-packager ./ my-app --platform=win32 --arch=x64 --out ./target --overwrite --icon=./favicon.ico --electron-zip-dir=D:/Bb_work/electron"
}

同时删除"type": "module",新增"main": "electron/main.js"

第三步:配置vite.config.js

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default defineConfig({
  base: "./", // 新增
  plugins: [vue()],
})

此操作是让Vue打包后的资源以相对路径进行访问,否则无法加载资源