DEV Community

Cover image for Vue 3 + Vite + TypeScript + ELECTRON (My Full Setup)
Jenuel Oras Ganawed
Jenuel Oras Ganawed

Posted on

Vue 3 + Vite + TypeScript + ELECTRON (My Full Setup)

I did create a simple post about making vue 3 + vite + electron. But it does not support hot reload if their is a change in electron files you have to close and rerun everything.

In this post, I will show the full setup I am using when building an electron app. I will do it in step by step that way its a lot easier to follow the setup. I will paste the codes without explanation but I encourage you to take a good understanding on why I put things.

In this project we are going to use yarn.
If you don't have yarn you can install it by running.

npm install --global yarn
Enter fullscreen mode Exit fullscreen mode

STEP 1 - Create a project

Lets start by creating a folder and running init to setup our package.json.

mkdir electron-vue # make a new folder `electron-vue`. 
cd electron-vue # redirect to electron-vue folder
yarn init # run and just press enters for every question.
Enter fullscreen mode Exit fullscreen mode

STEP 2 - let us install Dev Dependencies that we need.

Install This packages as Dev Dependencies

yarn add -D concurrently electron electron-builder electron-devtools-installer nodemon typescript wait-on json
Enter fullscreen mode Exit fullscreen mode

Install This packages as dependencies

yarn add electron-updater electron-store electron-log
Enter fullscreen mode Exit fullscreen mode
  • concurrently - we are going to use this to run concurrent commands.
  • electron - we will need this for developing our electron app.
  • electron-builder - we need this to build our electron app.
  • electron-devtools-installer - for adding vue devtools extension that we will need when develping.
  • nodemon - we are going to need this to watch our files and reload electron whenever their is a change.
  • typescript - we are going to need this for compiling TypeScript to JavaScript.
  • wait-on - were going to need this to wait on frontend server before rendering our electron app.
  • electron-updater - we are going to use this for auto updating our application.
  • electron-store - for storing some configurations or any data we want to save.
  • electron-log - your going to need this to log some important errors or warnings in your app.
  • json - for modifying our package.json file

STEP 3: Setup Our Front End App (Vue3 + vite + script)

yarn create vite
Enter fullscreen mode Exit fullscreen mode

run this and make the project name FrontEndApp and select vue and then select typescript.
After Doing that you should have a new Folder called FrontEndApp
BroJenuel Screen Shot

After Doing that, cd FrontEndApp and install the dependencies by running yarn install

STEP 4: Setup FrontEndApp

Setup This file FrontEndApp\vite.config.ts

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

export default defineConfig(({ mode }) => ({
    build: {
        outDir: './../dist',
        chunkSizeWarningLimit: 1000,
    },
    base: mode == 'development' ? '' : './',
    plugins: [vue()],
    server: {
        port: 3000,
    },
}));
Enter fullscreen mode Exit fullscreen mode

This will configure the ouput folder to ./../dist whenever we run build, and also going to make the server default to 3000 this is important.

Go to This file FrontEndApp\package.json and edit the build script and lets add some options. --emptyOutDir will remove the folder whenever we build the FrontEndApp.

"scripts": {
        "dev": "vite",
        "build": "vue-tsc --noEmit &&  vite build --emptyOutDir",
        "preview": "vite preview"
    },
Enter fullscreen mode Exit fullscreen mode

In your root project. add and edit .gitignore

dist
node_modules
Enter fullscreen mode Exit fullscreen mode

After doing all that you can try running yarn build in /FrontEndApp directory if build command works properly.

Like in this image, it successfully built the FrontEndApp to this dist folder.

showing vue js dist folder

I think that the setup in our FrontEndApp.

STEP 5: Setup Electron

Lets Create a Folder Electron where we can put all our Electron Typescript Files.

Create a file tsconfig.json in the root folder with this content.

{
    "compilerOptions": {
        "target": "ESNext",
        "module": "CommonJS",
        "outDir": "./dist",
        "removeComments": true,
        "esModuleInterop": true,
        "forceConsistentCasingInFileNames": true,
        "strict": true,
        "skipLibCheck": true
    },
    "include": ["Electron/**/*.ts"]
}
Enter fullscreen mode Exit fullscreen mode

Let us create this files.
\Electron\main.ts

import { app, BrowserWindow, BrowserWindowConstructorOptions, ipcMain, screen } from "electron";
import path from "path";
import { isDev } from "./config";
import { appConfig } from "./ElectronStore/Configuration";
import AppUpdater from "./AutoUpdate";

async function createWindow() {
    const { width, height } = screen.getPrimaryDisplay().workAreaSize;
    const appBounds: any = appConfig.get("setting.appBounds");
    const BrowserWindowOptions: BrowserWindowConstructorOptions = {
        width: 1200,
        minWidth: 900,
        height: 750,
        minHeight: 600,

        webPreferences: {
            preload: __dirname + "/preload.js",
            devTools: isDev,
        },
        show: false,
        alwaysOnTop: true,
        frame: true,
    };

    if (appBounds !== undefined && appBounds !== null) Object.assign(BrowserWindowOptions, appBounds);
    const mainWindow = new BrowserWindow(BrowserWindowOptions);

    // auto updated
    if (!isDev) AppUpdater();

    // and load the index.html of the app.
    // win.loadFile("index.html");
    await mainWindow.loadURL(isDev ? "http://localhost:3000" : `file://${path.join(__dirname, "./index.html")}`);

    if (appBounds !== undefined && appBounds !== null && appBounds.width > width && appBounds.height > height) mainWindow.maximize();
    else mainWindow.show();

    // this will turn off always on top after opening the application
    setTimeout(() => {
        mainWindow.setAlwaysOnTop(false);
    }, 1000);

    // Open the DevTools.
    if (isDev) {
        mainWindow.webContents.openDevTools();
    }


    ipcMain.handle('versions', () => {
        return {
            node: process.versions.chrome,
            chrome: process.versions.chrome,
            electron: process.versions.electron,
            version: app.getVersion(),
            name: app.getName(),
        };
    });
}

// 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.whenReady().then(async () => {
    // if dev
    if (isDev) {
        try {
            const { installExt } = await import("./installDevTool");
            await installExt();
        } catch (e) {
            console.log("Can not install extension!");
        }
    }

    createWindow();
    app.on("activate", function () {
        // On macOS 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 (BrowserWindow.getAllWindows().length === 0) createWindow();
    });
});

// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on("window-all-closed", () => {
    if (process.platform !== "darwin") {
        app.quit();
    }
});


Enter fullscreen mode Exit fullscreen mode

Electron\installDevTool.ts

import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer';

export async function installExt() {
    await installExtension(VUEJS_DEVTOOLS)
        .then(() => {
            console.log('Added Extension');
        })
        .catch((err) => {
            console.log('Extension Error: ', err);
        });
}

Enter fullscreen mode Exit fullscreen mode

Electron\config.ts

export const isDev = process.env.APP_IS_DEV ? true : false;
Enter fullscreen mode Exit fullscreen mode

Electron\AutoUpdate.ts

import { dialog, app } from 'electron';
import { autoUpdater, UpdateInfo } from 'electron-updater';

export default () => {
    if (app.isPackaged) {
        autoUpdater.autoInstallOnAppQuit = true;
        autoUpdater.checkForUpdates();
        autoUpdater.addListener('update-downloaded', (info: UpdateInfo) => {
            dialog
                .showMessageBox({
                    title: 'Restart Believers Sword?',
                    type: 'question',
                    message: `New version "Believers Sword ${info.version}" has been successfully downloaded.`,
                    buttons: ['Yes', 'Later', 'Yes, Update'],
                    cancelId: 1,
                })
                .then(({ response }) => {
                    if (response == 0 || response == 2) {
                        autoUpdater.quitAndInstall();
                    }
                });
        });
    }
};

Enter fullscreen mode Exit fullscreen mode

Electron\ElectronStore\Configuration.ts

import ElectronStore from 'electron-store';

export const appConfig = new ElectronStore({
    name: 'appConfig',
    defaults: {
        setting: {},
    },
    schema: {
        setting: {
            type: 'object',
        },
    },
});

Enter fullscreen mode Exit fullscreen mode

Electron/preload.ts

import { contextBridge, ipcRenderer } from "electron";

contextBridge.exposeInMainWorld("browserWindow", {
    versions: () => ipcRenderer.invoke("versions"),
});

Enter fullscreen mode Exit fullscreen mode

package.json

{
  "name": "electron-vue",
  "version": "1.0.0",
  "description": "This is a sample",
  "main": "./dist/main.js",
  "license": "MIT",
  "author": "BroJenuel",
  "scripts": {
    "build": "tsc",
    "watch": "nodemon --exec yarn serve:electron",
    "start": "yarn nightly:rename && concurrently -k \"yarn serve:front\" \"yarn watch\"",
    "front:setup": "cd FrontEndApp && yarn",
    "electron:setup": "yarn",
    "setup": "yarn front:setup && yarn electron:setup",
    "serve:front": "cd FrontEndApp && yarn dev",
    "serve:electron": "set APP_IS_NIGHTLY=yes && set APP_IS_DEV=yes && yarn build && wait-on tcp:3000 && electron .",
    "build:front": "cd FrontEndApp && yarn build",
    "electron:build": "yarn build",
    "electron:builder": "electron-builder",
    "app:build": "yarn prod:rename && yarn build:front && yarn electron:build && yarn electron:builder",
    "app:build:nightly": "set APP_IS_NIGHTLY=yes && set APP_IS_DEV=no && yarn nightly:rename && yarn build:front && yarn electron:build && yarn electron:builder",
    "nightly:rename": "json -I -f package.json -e \"this.name='electron-vue'\" && json -I -f package.json -e \"this.build.productName='Electron Vue Nightly'\" && json -I -f package.json -e \"this.build.appId='com.official-electron-vue.app'\"",
    "prod:rename": "json -I -f package.json -e \"this.name='electron-vue'\" && json -I -f package.json -e \"this.build.productName='Electron Vue'\" && json -I -f package.json -e \"this.build.appId='com.official-electron-vue.app'\"",
    "electron-rebuild": "./node_modules/.bin/electron-rebuild",
    "sqlite-rebuild": "cd node_modules/better-sqlite3 && npm run build-release",
    "postinstall": "electron-builder install-app-deps",
    "up:win": "up_using_window.cmd"
  },
  "devDependencies": {
    "concurrently": "^7.6.0",
    "electron": "^22.0.0",
    "electron-builder": "^23.6.0",
    "electron-devtools-installer": "^3.2.0",
    "json": "^11.0.0",
    "nodemon": "^2.0.20",
    "typescript": "^4.9.4",
    "wait-on": "^7.0.1"
  },
  "dependencies": {
    "electron-log": "^4.4.8",
    "electron-store": "^8.1.0",
    "electron-updater": "^5.3.0"
  },
  "build": {
    "appId": "com.official-electron-vue.app",
    "productName": "Electron Vue Nightly",
    "copyright": "Copyright © 2022 ${author}",
    "publish": [
      {
        "provider": "github",
        "owner": "Bible-Projects"
      }
    ],
    "nsis": {
      "oneClick": true,
      "allowToChangeInstallationDirectory": false,
      "deleteAppDataOnUninstall": true
    },
    "files": [
      "dist/**/*"
    ],
    "extraResources": [
      "defaults/**/*"
    ],
    "directories": {
      "buildResources": "assets",
      "output": "dist_electron"
    }
  }
}

Enter fullscreen mode Exit fullscreen mode

Next Create a Nodemon file to configure the files to watch nodemon.json

{
    "watch": ["Electron/*"],
    "ext": "js,css,ts,json"
}
Enter fullscreen mode Exit fullscreen mode

STEP 6: Start Up Development Mode

You can start development mode by running. For more scripts you can review the script property in the package.json

yarn start
Enter fullscreen mode Exit fullscreen mode

Run yarn app:build for production or you can run yarn app:build:nightly for nightly version.

when running build commands make sure to add github repository property on your package.json, and add your repository link as the value.


Source Code is in my Repo: https://github.com/BroJenuel/vue-3-vite-electron-typescript


Buy me coffee 😁😁😁 Thanks 💖💖

bmc-button

Oldest comments (1)

Collapse
 
javierabosch profile image
javierabosch

Really nice! Great job! Do you believe this could work with docker?