Vue + Electron Hands-on Project

In this chapter, we will useVue + ElectronCreate a note-taking app. This hands-on project is namedVueElectronNotes, with features covering multi-window, tray, file operations, and IPC communication. It is a very suitable project for beginners to practice on.

If you don't yet know Vue, you can first refer to:Vue Tutorial。


I. Project Features

  • Desktop note-taking app (Notes)
  • The main window displays the list of notes
  • A child window pops up for creating new notes
  • System tray icon, minimize to tray
  • Local file save/read
  • Secure communication between the renderer process and the main process

II. Project Initialization

1. Create a Vue project

vue create VueElectronNotes
cd VueElectronNotes

Select default preset (Babel + ESLint).

2. Install Electron and dependencies

npm install --save-dev electron electron-builder concurrently wait-on
npm install electron-store
  • electron-storeFor local data persistence

III. Main Process Configuration

electron-main.js:

Example

const { app, BrowserWindow, Tray, Menu, ipcMain } = require('electron')
const path = require('path')
const Store = require('electron-store')

let mainWindow, noteWindow, tray
const store = new Store()

function createMainWindow() {
  mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      contextIsolation: true,
      nodeIntegration: false
    }
  })

  if (process.env.NODE_ENV === 'development') {
    mainWindow.loadURL('http://localhost:8080')
  } else {
    mainWindow.loadFile('dist/index.html')
  }

  mainWindow.on('minimize', (e) => {
    e.preventDefault()
    mainWindow.hide()
  })

  createTray()
}

function createNoteWindow() {
  noteWindow = new BrowserWindow({
    width: 400,
    height: 300,
    parent: mainWindow,
    modal: true,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      contextIsolation: true,
      nodeIntegration: false
    }
  })

  noteWindow.loadFile('note.html')
}

function createTray() {
  tray = new Tray(path.join(__dirname, 'assets/icon.png'))
  const contextMenu = Menu.buildFromTemplate([
    { label: 'Show main window', click: () => mainWindow.show() },
    { label: 'Exit', click: () => app.quit() }
  ])
  tray.setToolTip('VueElectronNotes')
  tray.setContextMenu(contextMenu)
}

app.whenReady().then(createMainWindow)

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

ipcMain.handle('get-notes', () => {
  return store.get('notes', [])
})

ipcMain.on('save-note', (event, note) => {
  const notes = store.get('notes', [])
  notes.push(note)
  store.set('notes', notes)
  mainWindow.webContents.send('update-notes', notes)
})

Description:

  • mainWindow: Main window, displays the list of notes
  • noteWindow: Child window, used to create new notes
  • Tray: System tray icon and menu
  • ipcMain: The main process receives messages from the renderer process

4. Renderer Process and Vue

preload.js:

Example

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

contextBridge.exposeInMainWorld('electronAPI', {
  getNotes: () => ipcRenderer.invoke('get-notes'),
  saveNote: (note) => ipcRenderer.send('save-note', note),
  onUpdateNotes: (callback) => ipcRenderer.on('update-notes', callback)
})

src/App.vue:

Example

<template>
  <div>
    <h1>note list</h1>
    <button @click="openNoteWindow">New Note</button>
    <ul>
      <li v-for="(note, index) in notes" :key="index">{{ note }}</li>
    </ul>
  </div>
</template>

<script>
export default {
  data() {
    return { notes: [] }
  },
  methods: {
    openNoteWindow() {
window.open('note.html', 'New Note', 'width=400,height=300')
    }
  },
  mounted() {
    window.electronAPI.getNotes().then((notes) => {
      this.notes = notes
    })

    window.electronAPI.onUpdateNotes((event, notes) => {
      this.notes = notes
    })
  }
}
</script>

note.html + note.js:

note.html:

Example

<!DOCTYPE html>
<html>
  <head><meta charset="UTF-8"><title>New Note</title></head>
  <body>
    <h2>New Note</h2>
    <textarea id="note"></textarea><br>
    <button id="saveBtn">Save</button>
    <script src="note.js"></script>
  </body>
</html>

note.js:

Example

const saveBtn = document.getElementById('saveBtn')
saveBtn.addEventListener('click', () => {
  const note = document.getElementById('note').value
  if (note) {
    window.electronAPI.saveNote(note)
    window.close()
  }
})

Description:

  • note.htmlis a pop-up window for entering notes
  • note.jsCallelectronAPI.saveNoteSend to the main process
  • The main window will receive itupdate-notesThe message updates the list

V. Run the Project

Development mode:

npm run electron:serve
  • Start Vue Dev Server + Electron simultaneously
  • Can debug UI in real time

Packaging the application:

npm run electron:build
  • Output executable file todist_electron
  • Supports Windows/macOS/Linux
other extensions