Electron project template: how to create a new vue3+electron project and add infrastructure

1. Use npm creat vite@latest to create a vue3 project

The specific selection configuration is as follows:
image.png
and run the steps prompted
image.png

2. Join electron

Official reference steps: https://www.electronjs.org/zh/docs/latest/tutorial/tutorial-first-app
a. Install dependencies

npm install electron --save-dev

b. Create the main entry file of electron and add the running script command.
Add main in package.json to tell the main entry file of electron.
Add the running command of electron as: npm run start
image.png
Create the main.js file and write the following code ( official website code ):

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

const createWindow = () => {
    
    
  const win = new BrowserWindow({
    
    
    width: 800,
    height: 600,
  })

  // 下面的url为自己启动vite项目的url。
  win.loadURL('http://127.0.0.1:5173/')
  // 打开electron的开发者工具
  win.webContents.openDevTools({
    
     mode: 'detach' })
}

app.whenReady().then(() => {
    
    
  createWindow()
  app.on('activate', () => {
    
    
    if (BrowserWindow.getAllWindows().length === 0) createWindow()
  })
})

app.on('window-all-closed', () => {
    
    
  if (process.platform !== 'darwin') {
    
    
    app.quit()
  }
})

Run npm run start to successfully run your first electron project.
image.png
Note that your vite project should also be running at this time, win.loadURL(' http://127.0.0.1:5173/') ), where the url is the url of your vite project. It is equivalent to running two projects at this time, one is an electron project and the other is a vite project, and then electron is equivalent to a window/mac window, and then the URL is stuffed into this window. To go online in the later stage is to replace the url address during development with the url address that has already been launched.
image.png
At this time, there will be an error: Reason: It is an alarm for the setting of electron security policy, which means that the content security policy is not set, or the security setting of unsafe
-eval is used. Solution: https://blog.csdn.net/hwytree/article /details/121287531 We can solve it by adding the following meta to index.html:

image.png

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

3. Add the electron preload script, and use it to interact with electron and vue

Note: Here you must have a deep understanding of the role of this preloading script, otherwise you will be very confused about this preloading script. Here is an explanation of the function of this preloading script,
conceptual understanding:
main process: refers to your electron process
preloading script: a direct bridge between electron process and vue project communication
rendering process: your vue project or react project
according to electron official website ( https://www.electronjs.org/zh/docs/latest/tutorial/tutorial-preload ) said, for security and isolation environment, the communication between the rendering process and the main process needs to be carried out through a preload.js . (In general, the function of this preload.js is to serve as a communication bridge between the rendering process and the main process. What it does is: 1. Give the rendering process (it can be said to be the .vue file and .js file under your src) Expose the method for the rendering process to call 2. The rendering process calls this method, which can send events to the main process, and then generally listen to the event in the main process, and then call electron's api to change the state of the application (such as changing the status of the window shell size, let the window minimize))

a. Create a preload folder in the same directory as src, write index.js below, and add the following code:

const {
    
     contextBridge, ipcRenderer } = require('electron')
const handleSend = async (vue_params) => {
    
    
  let fallback = await ipcRenderer.invoke('sent-event', vue_params)
  return fallback
}
contextBridge.exposeInMainWorld('myApi', {
    
    
  handleSend: handleSend
  // 能暴露的不仅仅是函数,我们还可以暴露变量
})

image.png

b. Introduce the preload script in main.js, and add the listening event of the main process:

 webPreferences: {
    
    
      preload: path.join(__dirname, './preload/index.js')
    }
ipcMain.handle('sent-event', (event,params) => {
    
    
  console.log(params)
  return '1111'
})

image.png

c. Called in vue, you can see the result returned by the main process

import {
    
     onMounted } from 'vue'
onMounted(async () => {
    
    
  let res = await window.myApi.handleSend('liaoruiruirurirui')
  console.log(res)
})

image.png
image.png
Explanation: Here is mainly to tell you how to introduce electron's preloading script and a simple use. For the specific communication process, you can refer to this article: https://blog.csdn.net/weixin_43239880/article/details/129563632?spm=1001.2014.3001.5501
You can also see the tutorial on the official website: https://www.electronjs. org/en/docs/latest/tutorial/ipc

d. Handle ts error reporting: add the following code to env.d.ts:

declare interface Window {
    
    
  myApi: any
}

image.png
At this point we have successfully created a vite+vue3+electron project! ! !

4. Optimize the development experience

Because we modify the vue code at this time, vite will help us to restart it. But when we modify the code of the electron file, there is no scaffolding to help us restart it, so we need to add nodemon to help us restart it.
a. Install nodemon dependencies:

npm i nodemon -D

Add this sentence to the scripts in package.json to start the electron project. When nodemon detects that there is a change in the file ending with .js or .html, it will restart the window (if you need to change .vue and .css, start at the same time , you can continue to add later, but it is not necessary, because these files change, vite will automatically restart hot.)

"start": "nodemon --exec electron . --watch ./ --ext .js,.html"

image.png

5. Add some necessary development tools for the vite project

Such as: add commitLint, add element-plus, configure aliases, configure agents, add axios and packaging, add pinia, etc.
How to add please check this blog , each step has detailed steps and usage. I won't go into details here.

6. System Tray

It refers to this, as shown in the picture:
image.png
Basically, all desktop projects have trays, so a simple version is written here. You can refer to:
a. Create a controller directory in the same level directory as src, and create a tray.js file in it.

// 创建系统托盘
const {
    
     Tray, Menu } = require('electron')
// const { ipcRenderer } = require('electron')
const path = require('path')

function createTray(app, win) {
    
    
  let tray = new Tray(path.join(__dirname, '../public/favicon.ico'))
  // if (process.env.NODE_ENV === 'development') {
    
    
  // tray = new Tray(path.join(__dirname, '../public/favicon.ico'))
  // } else {
    
    
  // tray = new Tray(path.join(path.dirname(app.getPath('exe')), '/resources/public/logo.ico'))
  // }
  tray.setToolTip('示例平台') // 鼠标放在托盘图标上的提示信息
  tray.on('click', (e) => {
    
    
    if (e.shiftKey) {
    
    
      app.quit()
    } else {
    
    
      win.show()
    }
  })
  tray.setContextMenu(
    Menu.buildFromTemplate([
      {
    
    
        label: '退出',
        click: () => {
    
    
          // 先把用户的登录状态和用户的登录信息给清楚掉,再退出
          app.quit()
        }
      }
    ])
  )
}
module.exports = createTray

image.png
b. Import in main.js and use the modified function to create the tray.
image.png

6. Customize the window (do not use the native window)

Why do you need a custom window? The reason is because the native window is ugly and ugly. And it cannot be modified, resulting in the effect of not satisfying the UI, so many desktop programs are custom windows.
This is mainly added when creating a window in main.js

const win = new BrowserWindow({
    
    
    width: 800,
    height: 600,
    frame: false, // 不要自带的窗口
    webPreferences: {
    
    
      preload: path.join(__dirname, './preload/index.js')
    }
  })

image.png
At this point, our program becomes like this, which looks ugly, but we are free from the limitations of the native window and can play freely:
image.png

The events here mainly use the communication between the rendering process and the main process. Simply put:
modify the style in HomeView.vue, and bind events to each button. This event needs to call the exported in the preloading script. event.

<script setup lang="ts">
// import TheWelcome from '../components/TheWelcome.vue'
import { onMounted } from 'vue'
onMounted(async () => {
  let res = await window.myApi.handleSend('liaoruiruirurirui')
  console.log(res)
})
const toMin = () => {
  window.myApi.windowMin()
}
const toBig = () => {
  window.myApi.windowMax()
}
const toClose = () => {
  window.myApi.windowClose()
}
</script>

<template>
  <main>
    <div class="hearder">
      <span @click="toMin">最小化</span>
      <span @click="toBig">最大化</span>
      <span @click="toClose">关闭</span>
    </div>
    <div class="main">主要内容</div>
    <!-- <TheWelcome /> -->
  </main>
</template>

<style scoped>
.hearder {
  -webkit-app-region: drag;
  background-color: #ccc;
  height: 40px;
  width: 100%;
  display: flex;
  justify-content: flex-end;
  align-items: center;
}
.hearder span {
  margin: 0 16px;
  border: 1px solid rgb(35, 34, 34);
  cursor: pointer;
  -webkit-app-region: no-drag;
}
.main {
  height: calc(100vh - 40px);
  display: flex;
  align-items: center;
  justify-content: center;
}
</style>

Details: The top of the window is draggable because of this style: -webkit-app-region: drag. But because of this style, the click event of the button cannot respond, so we need to add -webkit-app-region: no-drag; to the button.
The simple style is:
image.png
Then we add events to the preload script (the added events are all sent to the main process):

const { contextBridge, ipcRenderer } = require('electron')
const handleSend = async (vue_params) => {
  let fallback = await ipcRenderer.invoke('sent-event', vue_params)
  return fallback
}

contextBridge.exposeInMainWorld('myApi', {
  handleSend: handleSend,
  // 能暴露的不仅仅是函数,我们还可以暴露变量
   // 最小化
   windowMin: () => {
    ipcRenderer.send('window-min')
  },
  // 最大化
  windowMax: () => {
    ipcRenderer.send('window-max')
  },
  // 关闭窗口
  windowClose: () => {
    ipcRenderer.send('window-close')
  },
})

Create the changeWindowSize.js file in the controller folder, add the following code, and import it in the main.js of the main process:
the code here is actually the event that calls electron in the main process. Because electron things cannot be used in the rendering process, events need to be sent through preloading scripts, and the main process listens to this event and responds accordingly.

const { ipcMain, BrowserWindow } = require('electron')
// 最小化
ipcMain.on('window-min', event => {
  const webContent = event.sender
  const win = BrowserWindow.fromWebContents(webContent)
  win.minimize()
})

// 最大化
ipcMain.on('window-max', event => {
  const webContent = event.sender
  const win = BrowserWindow.fromWebContents(webContent)
  if (win.isMaximized()) {
    win.restore()
  } else {
    win.maximize()
  }
})

// 关闭
ipcMain.on('window-close', event => {
  const webContent = event.sender
  const win = BrowserWindow.fromWebContents(webContent)
  win.close()
})

image.png
image.png

7. Basic development

I won’t talk about basic development here. Everyone is different. The main thing is to use inter-process communication. If you don’t understand, read this blog or official website . After setting up the framework, just develop according to your own business.
Another thing to mention is that electron's api should also be used during development https://www.electronjs.org/zh/docs/latest/api/crash-reporter
image.png

8. Project packaging: use electron-builder

a. Installation dependencies:

npm install electron-builder -D

b. Configure in package.json

For configuration item information, please refer to the official document: https://www.electron.build/configuration/configuration
You can also read this blog: https://juejin.cn/post/6844903693683261453#comment
Here I will directly post our side Configured (this build is at the same level as scripts)

"build": {
    "appId": "com.police.desktop",//包名  
    "productName": "测试平台", //项目名 这也是生成的exe文件的前缀名
    "asar": true,
    "copyright": "Copyright © 2022 electron",//版权  信息
    "publish": {
      "provider": "generic",// 服务器提供商 也可以是GitHub等等
      "url": ""// 服务器地址
    },
    "directories": { // 输出文件夹
      "output": "electron-build/"
    },
    "extraResources": [
      {
        "from": "./public",
        "to": "./public"
      }
    ],
    "files": [ // 打包的electron需要包含的文件,一般就是与electron的有关的打包进去
      "main.js", // electron主入口文件
      "controller", // 也是主入口文件,只不过拆成了两个文件
      "preload" //预加载文件
    ],
    "mac": {
      "artifactName": "${productName}_${version}.${ext}",
      "target": [
        "dmg"
      ]
    },
    "win": {
      "icon": "public/logoTemplate.ico",
      "target": [
        {
          "target": "nsis",
          "arch": [
            "ia32"
          ]
        }
      ],
      "artifactName": "${productName}_${version}.${ext}"
    },
    "nsis": {
      "oneClick": false,// 是否一键安装
      "perMachine": false,
      "allowToChangeInstallationDirectory": true,// 允许修改安装目录
      "deleteAppDataOnUninstall": false,
      "installerIcon": "public/favicon.ico",// 安装图标
      "uninstallerIcon": "public/favicon.ico",// 创建桌面图标
      "createDesktopShortcut": true,// 创建桌面图标
      "createStartMenuShortcut": true,// 创建开始菜单图标
      "shortcutName": "测试平台" // 图标名称
    },
    "releaseInfo": {
      "releaseNotes": "版本更新的具体内容"
    }
  }

c. To package, use the command: npm run app:dist

Add the run command to package.son:

"app:dist": "electron-builder"

image.png

d. Solve the problem that the download package fails due to the network when packaging.

You can read my other blog: https://blog.csdn.net/weixin_43239880/article/details/129532978?spm=1001.2014.3001.5502
image.png
Log in to this website: https://registry.npmmirror.com/binary.html? path=electron/24.1.3/ , find the corresponding package, download it and
image.png
put it in the path: C:\Users\DELL\AppData\Local\electron\Cache (this is the path of my computer, please refer to the blog content for your path Find out where it is by yourself)
image.png
Run it again to package successfully.
image.png
image.png

9. Solve the problem that the pallet disappears after packing:

At this point, we can see that our desktop project is running normally, but we will find that the tray in the lower right corner has a space when it is running, but there is no picture on it! ! ! !
image.png
Please read this article for detailed steps to solve it: https://blog.csdn.net/weixin_43239880/article/details/129495602?spm=1001.2014.3001.5502

10. Package the vite project and put the project online

Run the command:

npm run build

image.png
The dist file after packaging the vite project is online. Change the url in main.js to the ip address after going online
image.png
How to put the project online?
Please check out my blog: https://blog.csdn.net/weixin_43239880/article/details/129434402?spm=1001.2014.3001.5501

other:

Project template address: https://github.com/rui-rui-an/how_to_create_electron_vue

Guess you like

Origin blog.csdn.net/weixin_43239880/article/details/130437490