vue3+ts+vite+electron打包exe

本文详细介绍了如何将一个基于Vue3、TypeScript和Vite的项目打包为Electron应用,包括修改打包配置、处理TS代码检测、安装相关依赖、配置预加载模块。同时,文章还解决了使用ipcRenderer通信时遇到的问题,指出由于Electron的安全性更新,需注意contextIsolation的配置,并推荐使用@vueuse/electron库来简化通信操作。
摘要由CSDN通过智能技术生成


一. 前言

注意点: node的fs模块应当写入独立的js或ts文件中.
node相关路径以及获取返回值应当使用如下样例:

import path from 'path';
const fs = require('fs')
export function getLogData() {
    let rs = fs.readFileSync(path.join(__dirname, './log.json'), 'utf8');
    console.log(rs);

    return JSON.parse(rs);
};


export function writeLogData(currentLog = {}) {
    fs.writeFile(path.join(__dirname, './log.json'), JSON.stringify(currentLog), 'utf8', function (err) {
        if (err) {
            console.log(err);
        }
    });
}

export function deleteLogData() {
    fs.writeFile(path.join(__dirname, './log.json'), '', 'utf8', function (err) {
        if (err) {
            ElMessage.error('清空日志失败');
        }
    });
    ElMessage.success('清空日志成功');
}

二. 准备写好的vue项目打包

我使用的是vue3,ts,vite.

构建vue项目

首先使用vite构建工具构建vue3项目

2.1 修改ts打包代码检测.这个比较烦人. 在package.json

  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  }

2.2 配置打包参数

用vite+ts构建的配置是vite.config.ts 打包样例模板如下:
打包期间期间会报错,你需要按提示安装相应组件

import { defineConfig } from 'vite'
import { resolve } from "path"; // 主要用于alias文件路径别名
import vue from '@vitejs/plugin-vue'
import vueJsx from "@vitejs/plugin-vue-jsx"; // jsx插件
export default defineConfig({
    plugins: [vue(), vueJsx()], // 配置需要使用的插件列表
    base: './',   // 在生产中服务时的基本公共路径
    publicDir: './public',  // 静态资源服务的文件夹, 默认"public" 
    resolve: {
        alias: {
            "@": resolve(__dirname, './src'), // 这里是将src目录配置别名为 @ 方便在项目中导入src目录下的文件
        }
    },
    // 引入第三方的配置,强制预构建插件包
    optimizeDeps: {
        include: ['echarts', 'axios', 'mockjs'],
    },
    css: {
        preprocessorOptions: {
            scss: {
                charset: false, // 关闭编译时 字符编码 报错问题
                javascriptEnabled: true,
                additionalData: `@import "${resolve(__dirname, 'src/assets/css/var.scss')}";`,
            },
        },
    },
    json: {
        //是否支持从 .json 文件中进行按名导入 
        namedExports: true,
        //若设置为 true 导入的json会被转为 export default JSON.parse("..") 会比转译成对象字面量性能更好 
        stringify: false,
    },
    //继承自 esbuild 转换选项,最常见的用例是自定义 JSX 
    esbuild: {
        jsxFactory: "h",
        jsxFragment: "Fragment",
        jsxInject: `import Vue from 'vue'`
    },
    // 打包配置
    build: {
        target: 'modules', // 设置最终构建的浏览器兼容目标。modules:支持原生 ES 模块的浏览器
        outDir: 'dist', // 指定输出路径
        assetsDir: 'assets', // 指定生成静态资源的存放路径
        assetsInlineLimit: 4096, // 小于此阈值的导入或引用资源将内联为base64编码,设置为0可禁用此项。默认4096(4kb)
        cssCodeSplit: true, // 启用/禁用CSS代码拆分,如果禁用,整个项目的所有CSS将被提取到一个CSS文件中,默认true
        sourcemap: false, // 构建后是否生成 source map 文件
        minify: 'terser', // 混淆器,terser构建后文件体积更小
        write: true,   //设置为 false 来禁用将构建后的文件写入磁盘  
        emptyOutDir: true,  //默认情况下,若 outDir 在 root 目录下,则 Vite 会在构建时清空该目录。  
        chunkSizeWarningLimit: 500,  //chunk 大小警告的限制 
        terserOptions: {
            compress: {
                drop_console: false, // 打包时不删除console.log日志.否则无法调试
                drop_debugger: true,
            },
        }   //去除 console debugger
    },
    // 本地运行配置,及反向代理配置
    server: {
        host: 'localhost', // 指定服务器主机名
        port: 3000, // 指定服务器端口
        open: true, // 在服务器启动时自动在浏览器中打开应用程序
        strictPort: false, // 设为 false 时,若端口已被占用则会尝试下一个可用端口,而不是直接退出
        https: false, // 是否开启 https
        cors: true, // 为开发服务器配置 CORS。默认启用并允许任何源
        // proxy: { // 为开发服务器配置自定义代理规则
        //     // 字符串简写写法 
        //     '/foo': 'http://192.168.xxx.xxx:xxxx', 
        //     // 选项写法
        //     '/api': {
        //         target: 'http://192.168.xxx.xxx:xxxx', //代理接口
        //         changeOrigin: true,
        //         rewrite: (path) => path.replace(/^\/api/, '')
        //     }
        // }
    },

})

vite-env.d.ts

declare module '*.vue' {
    import { ComponentOptions } from 'vue'
    const componentOptions: ComponentOptions
    export default componentOptions
}

安装electron相关

npm install electron # 这是原生electron .网上也说可以用vue-cli-pugin-electron-build
npm install electron-packager --save-dev # electron打包插件

npm install terser

npm install @vitejs/plugin-vue-jsx

在这里插入图片描述

在package中新增如上配置

 "main": "main.ts",
   "scripts": {
    # 运行客户端命令
    "electron:serve": "vite build & electron . ",
    # exe打包命令
    "packager": "electron-packager ./ App --platform=win32 --arch=x64 --overwrite" 
  },

接着新建main.ts和src同级
在这里插入图片描述
main.ts内容

// Modules to control application life and create native browser window

const { app, BrowserWindow, ipcMain,Menu } = require('electron')
// import './src/store/index'
const path = require('path')
// const winURL = process.env.NODE_ENV === 'development' ? 'http://localhost:8080' : `file://${path.join(__dirname, './dist/index.html')}`

let mainWindow
let textdata
let textBox;
function createWindow() {
    // Create the browser window.
    mainWindow = new BrowserWindow({
        width: 1280,
        height: 1024,
        minWidth: 600,
        minHeight: 600,
        title: '局部放电监控中心',
        // autoHideMenuBar: true, // 自动隐藏菜单栏
        webPreferences: {
            // 是否启用Node integration
            nodeIntegration: true, // Electron 5.0.0 版本之后它将被默认false
            // 是否在独立 JavaScript 环境中运行 Electron API和指定的preload 脚本.默认为 true
            contextIsolation: false,  // Electron 12 版本之后它将被默认true
            nodeIntegrationInWorker: true,
            // 禁用同源策略,允许跨域
            webSecurity: false,
            preload: path.join(__dirname, 'preload.js'),

        }

    })
  

    // and load the index.html of the app.
    mainWindow.loadFile('./dist/index.html')
    // Open the DevTools.
    mainWindow.webContents.openDevTools({mode:'right'})

    // 监听窗口关闭
    mainWindow.on('window-close', function () {
        mainWindow.close();
    })
    mainWindow.webContents.on('did-finish-load', () => {
        console.log("主进程渲染,主窗口加载完毕")
      })

}
// 左上角菜单
const menu = Menu.buildFromTemplate([
    {
      label: app.name,
      submenu: [
        {
          click: () => textBox.webContents.send('customer-msg', textdata),
          label: 'Increment'
        },
      ]
    }
  ])
Menu.setApplicationMenu(menu)
  

// 监听textBox消息 子窗口
ipcMain.on('textBox', function (event, data) {
    console.log("接收")
    textBox = new BrowserWindow({
        width: 1280,
        height: 1024,
        minWidth: 600,
        minHeight: 600,
        parent: mainWindow, // mainWindow是主窗口
        frame: true, // 有边框
        title: '查看',
        autoHideMenuBar: true, // 自动隐藏菜单栏
        webPreferences: {
            // 是否启用Node integration
            nodeIntegration: true, // Electron 5.0.0 版本之后它将被默认false
            // 是否在独立 JavaScript 环境中运行 Electron API和指定的preload 脚本.默认为 true
            contextIsolation: false,  // Electron 12 版本之后它将被默认true
            nodeIntegrationInWorker: true,
            // 禁用同源策略,允许跨域
            webSecurity: false,
            preload: path.join(__dirname, 'preload.js'),
        }
    })
    // console.log(data,"---2323---")
    // textBox.loadURL('http://127.0.0.1:3000/#/add')  // 此处写 你要打开的路由地址
    textBox.loadFile('./dist/index.html', {
        hash: '#' + data
    });
    // 监听textBox窗口关闭
    textBox.on('closed', () => {
        textBox == null;
    })
    // Open the DevTools.
    textBox.webContents.openDevTools({ mode: 'right' })
    // textBox.webContents.on('did-finish-load', () => {
    //     console.log("渲染进程渲染,新窗口加载完毕")
    //     textBox.webContents.send('customer-msg', textdata)
    //   })
    textBox.webContents.on('did-stop-loading', () => {
        console.log("渲染进程渲染,新窗口加载完毕")
        textBox.webContents.send('customer-msg', textdata)
    })
})

// 监听 product-msg 信道消息
ipcMain.handle('product-msg', async (event, data) =>{
    console.log(data, "i am data")
    textdata = data
    return true;
});


// 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(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', function () {
    if (process.platform !== 'darwin') app.quit()
})

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.

新建 preload.ts预加载模块

/**
 * The preload script runs before. It has access to web APIs
 * as well as Electron's renderer process modules and some
 * polyfilled Node.js functions.
 *
 * https://www.electronjs.org/docs/latest/tutorial/sandbox
 */
const { contextBridge,  } = require('electron')

contextBridge.exposeInMainWorld('versions', {
  node: () => process.versions.node,
  chrome: () => process.versions.chrome,
  electron: () => process.versions.electron,
})


2.4 运行客户端vue

做完如上步骤那么我们就可以直接:

npm run electron:serve

在这里插入图片描述

至此我们的vue客户端可以正常运行


3.5 运行打包命令

npm run packager 

在这里插入图片描述
大功告成.

在这里插入图片描述

相关报错的坑

vite+vue3+ts中通信报错 使用 ipcRenderer 报错

request not undif
原因:为了安全性,官方将 electron v12.0.0 的 contextIsolation 的默认值改了

安装@vueuse/electron
npm install @vueuse/electron -D

import { useIpcRenderer } from "@vueuse/electron";
const ipcRenderer = useIpcRenderer();
const send = () => {
  ipcRenderer.send('openWindow','/index');
};
const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: true,
      contextIsolation: false,
    }
  })

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

最难不过坚持丶渊洁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值