vue3 + vite + ts + pinia + electron 搭建项目

479 阅读4分钟

vue3 + vite + ts + pinia + electron

构建vite项目

npm

npm init vite@latest

yarn

yarn create vite

选择 vue + 自定义选择

安装依赖

cd aaa
npm install
npm run dev

package.json 解析

{
  "scripts": {
    "dev": "vite", // 启动开发服务器,别名:`vite dev`,`vite serve`
    "build": "vite build", // 为生产环境构建产物
    "preview": "vite preview" // 本地预览生产构建产物
  }
}

安装脚手架

npm install @vue/cli -g

安装electron

npm install electron --save-dev
npm install vite-plugin-electron --save-dev
npm install electron-builder --save-dev

添加文件 electron/index.ts

index.ts创建window,相当于之前的main.js

import { app, BrowserWindow } from 'electron'
import path from 'path'
//app 控制应用程序的事件生命周期。
//BrowserWindow 创建并控制浏览器窗口。

let win: BrowserWindow | null
//定义全局变量获取 窗口实例

const createWindow = () => {
  win = new BrowserWindow({
    //
    webPreferences: {
      devTools: true,
      contextIsolation: false,
      nodeIntegration: true
      //允许html页面上的javascipt代码访问nodejs 环境api代码的能力(与node集成的意思)
    }
  })
  console.log(app.isPackaged)

  if (app.isPackaged) {
    // win.loadFile(`file://${path.join(__dirname, '../dist/index.html')}`)
    win.loadFile(`${path.join(__dirname, '../dist/index.html')}`)
  } else {
    console.log(
      process.env['VITE_DEV_SERVER_HOSTNAME'],
      process.env['VITE_DEV_SERVER_PORT'],
      process.env['VITE_DEV_SERVER_URL'],
      `http://${process.env['VITE_DEV_SERVER_HOSTNAME']}:${process.env['VITE_DEV_SERVER_PORT']}`
    )

    //VITE_DEV_SERVER_HOST 如果是undefined 换成  VITE_DEV_SERVER_HOSTNAME
    win.loadURL(
      `${process.env['VITE_DEV_SERVER_URL']}`
      //   `http://${process.env['VITE_DEV_SERVER_HOST']}:${process.env['VITE_DEV_SERVER_PORT']}`
      //   `http://localhost:5173`
    )
  }
}
//isPackage 不好使换下面的
//  if(process.env.NODE_ENV != 'development'){
//  win.loadFile(path.join(__dirname, "../index.html"));
//  }else{
//win.loadURL(`http://${process.env['VITE_DEV_SERVER_HOSTNAME']}:${process.env['VITE_DEV_SE//RVER_PORT']}`)
// }
//在Electron完成初始化时被触发
app.whenReady().then(createWindow)

或者electron/index.js

如果 __dirname 报错则 添加

import path from 'path'
import {fileURLToPath} from 'url'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
import { app, BrowserWindow } from 'electron'
import path from 'path'
//app 控制应用程序的事件生命周期。
//BrowserWindow 创建并控制浏览器窗口。

let win //: BrowserWindow | null
//定义全局变量获取 窗口实例

const createWindow = () => {
  win = new BrowserWindow({
    //
    webPreferences: {
      devTools: true,
      contextIsolation: false,
      nodeIntegration: true,
      // eslint-disable-next-line no-undef
      // preload: path.join(__dirname, '../dist-electron/preload.js')
      // eslint-disable-next-line no-undef
      preload: path.join(__dirname, './preload.js')
      // preload: '../electron/preload.js'
      //允许html页面上的javascipt代码访问nodejs 环境api代码的能力(与node集成的意思)
    }
  })
  console.log(app.isPackaged)

  win.setMenuBarVisibility(false) // 隐藏菜单栏

  if (app.isPackaged) {
    // win.loadURL(`file://${path.join(__dirname, '../dist/index.html')}`)
    // win.loadFile(`file://${path.join(__dirname, '../dist/index.html')}`)
    // eslint-disable-next-line no-undef
    win.loadFile(path.join(__dirname, '../dist/index.html'))
  } else {
    //VITE_DEV_SERVER_HOST 如果是undefined 换成  VITE_DEV_SERVER_HOSTNAME
    win.loadURL(
      // eslint-disable-next-line no-undef
      `${process.env['VITE_DEV_SERVER_URL']}`
      //   `http://${process.env['VITE_DEV_SERVER_HOST']}:${process.env['VITE_DEV_SERVER_PORT']}`
      //   `http://localhost:5173`
    )
  }
}
//isPackage 不好使换下面的
//  if(process.env.NODE_ENV != 'development'){
//  win.loadFile(path.join(__dirname, "../index.html"));
//  }else{
//win.loadURL(`http://${process.env['VITE_DEV_SERVER_HOSTNAME']}:${process.env['VITE_DEV_SE//RVER_PORT']}`)
// }
//在Electron完成初始化时被触发
app.whenReady().then(createWindow)

添加electron/preload.js

package.json中删除

{
	"type": "module",
	  "main": "electron/index.js",
}

添加

"main": "dist-electron/index.js",
// All of the Node.js APIs are available in the preload process.
// It has the same sandbox as a Chrome extension.

// eslint-disable-next-line no-undef
const { ipcRenderer } = require('electron')

window.addEventListener('DOMContentLoaded', () => {
  console.log('loaded', window, ipcRenderer) // //   const replaceText = (selector, text) => {
  window.ipcRenderer = ipcRenderer
})

// /** docoment 加载完成 */
function domReady(...args) {
  const condition = args.length ? [...args] : ['complete', 'interactive']
  return new Promise((resolve) => {
    if (condition.includes(document.readyState)) {
      resolve(true)
    } else {
      document.addEventListener('readystatechange', () => {
        if (condition.includes(document.readyState)) {
          resolve(true)
        }
      })
    }
  })
}

;(async function () {
  await domReady()

  console.log('domReady', window, ipcRenderer)
  // window.ipcRenderer = require('electron').ipcRenderer;
  window.ipcRenderer = ipcRenderer
})()

添加 vite.config.electron.ts

import { fileURLToPath, URL } from 'node:url'

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

import electron from 'vite-plugin-electron'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    vue(),
    vueJsx(),
    electron({ entry: ['electron/index.ts', 'electron/preload.ts'] })
  ],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    }
  }
})

修改package.json

{
     "main": "dist-electron/index.js",
  "scripts": {
    "dev": "vite",
    "build": "run-p type-check \"build-only {@}\" --",
    "build-client": "vue-tsc --noEmit && vite build --config vite.config.electron.ts && electron-builder",
    "dev-client": "vue-tsc --noEmit && vite --config vite.config.electron.ts && electron .",
    "preview": "vite preview",
    "test:unit": "vitest",
    "build-only": "vite build",
    "type-check": "vue-tsc --noEmit -p tsconfig.vitest.json --composite false",
    "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
    "format": "prettier --write src/"
  },
}

package.json 中build配置

{
"build": {
    "productName": "package",
    "copyright": "",
    "directories": {
      "output": "release/"
    },
    "files": [
      "dist",
      "dist-electron",
      "release"
    ],
    "mac": {
      "artifactName": "${productName}_${version}.${ext}",
      "target": [
        "dmg"
      ]
    },
    "win": {
      "target": [
        {
          "target": "nsis",
          "arch": [
            "x64"
          ]
        }
      ],
      "artifactName": "${productName}_${version}.${ext}"
    },
    "nsis": {
      "oneClick": false,
      "perMachine": false,
      "allowToChangeInstallationDirectory": true,
      "deleteAppDataOnUninstall": false,
      "installerIcon": "./electron/induSec-logo.ico",
      "uninstallerIcon": "./electron/induSec-logo.ico"
    },
    "publish": [
      {
        "provider": "generic",
        "url": "http://127.0.0.1:8080"
      }
    ],
    "releaseInfo": {
      "releaseNotes": "版本更新的具体内容"
    }
  }
}

nsis(桌面应用程序安装过程)配置

{   
	// 一键安装程序、或者辅助安装程序(默认是一键安装)  
	"oneClick": false,
    // 是否允许请求提升,如果为 false,则用户必须使用提升的权限重新启动安装程序(仅作用于辅助安装程序)  
    "allowElevation": true,
    // 是否允许修改安装目录(仅作用于辅助安装程序)  
    "allowToChangeInstallationDirectory": true,
    // 安装程序图标的路径  
    "installerIcon": "public/timg.ico",
    // 卸载程序图标的路径  
    "uninstallerIcon": "public/timg.ico",
    // 安装时头部图片路径(仅作用于辅助安装程序)  
    "installerHeader": "public/timg.ico",
    // 安装时标题图标(进度条上方)的路径(仅作用于一键安装程序)  
    "installerHeaderIcon": "public/timg.ico",
    // 安装完毕界面图片的路径,图片后缀.bmp,尺寸 164*314(仅作用于辅助安装程序)  
    "installerSidebar": "public/installerSiddebar.bmp",
    // 开始卸载界面图片的路径,图片后缀.bmp,尺寸 164*314(仅作用于辅助安装程序)  
    "uninstallerSidebar": "public/uninstallerSiddebar.bmp",
    // 控制面板中的卸载程序显示名称  
    "uninstallDisplayName": "${productName}${version}",
    // 是否创建桌面快捷方式  
    "createDesktopShortcut": true,
    // 是否创建开始菜单快捷方式  
    "createStartMenuShortcut": true,
    // 用于快捷方式的名称,默认为应用程序名称  
    "shortcutName": "TestApp",
    // NSIS 包含定制安装程序脚本的路径,安装过程中自行调用 (可用于写入注册表 开机自启动等操作)  
    "include": "script/installer.nsi",
    // 用于自定义安装程序的 NSIS 脚本的路径  
    "script": "script/installer.nsi",
    // 是否在卸载时删除应用程序数据(仅作用于一键安装程序)  
    "deleteAppDataOnUninstall": false,
    // 完成后是否运行已安装的应用程序(对于辅助安装程序,应删除相应的复选框)  
    "runAfterFinish": true,
    // 是否为开始菜单快捷方式和程序文件目录创建子菜单,如果为 true,则使用公司名称  
    "menuCategory": false,
   }

electron 镜像下载保存位置

根据需要的版本下载

macOS: ~/Library/Caches/electron
Linux: ~/.chche/electron
windows: %/LOCALAPPDATA%/electron/cache

window C:/Users/用户名/AppData/local/electron

nsis 保存到 C:/Users/用户名/AppData/local/electron-builder/Cache

cross-env

安装 cross-env,这个包用来设置环境变量

修改package.json, "dev": "cross-env NODE_ENV=development vite",这样就指定开发时的环境变量,

修改electron/index.ts, process.env.NODE_ENV !='development' 判断生产环境

默认白屏问题

路由地址为空,或者需要点击跳转路由才可看到页面

修改vue路由类型改为hash

const router = createRouter({
  history: createWebHashHistory(), // 由 createWebHistory() 改为hash
  routes: [
    {
      path: '/',
      name: 'login',
      component: () => import('@/views/login/index.vue')
    }
  ]
})