pnpm add -g asar
注意,必须使用 cmd
better_sqlite3_binary_host=https://registry.npmmirror.com/-/binary/better-sqlite3
better_sqlite3_binary_host_mirror=https://registry.npmmirror.com/-/binary/better-sqlite3
better-sqlite3_binary_host=https://registry.npmmirror.com/-/binary/better-sqlite3
better-sqlite3_binary_host_mirror=https://registry.npmmirror.com/-/binary/better-sqlite3
set ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/
set NSIS_MIRROR=https://npmmirror.com/mirrors/nsis/
set NSIS_RESOURCES_MIRROR=https://npmmirror.com/mirrors/nsis-resources/
set WIN_CODE_SIGN_MIRROR=https://npmmirror.com/mirrors/win-code-sign/
https://blog.csdn.net/zhaihmj/article/details/139630124
1. 进入工作目录
npx electron-rebuild -f -w better-sqlite3 --arch=ia32 # 32位安装包
npx electron-rebuild -f -w better-sqlite3 --arch=x64 # 64位安装包
node-gyp configure --arch=ia32
pip install setuptools
npm install better-sqlite3 --build-from-source --runtime=electron --target=31.0.0 --dist-url=https://electronjs.org/headers
请将 --target=31.0.0 替换为你实际使用的 Electron 版本。
sudo apt-get update
sudo apt-get install -y python3 python3-pip build-essential libssl-dev
终端执行以下命令: chcp 65001
netstat -aon|findstr "3001"
杀死进程命令:taskkill /PID 53176 -T -F (其中-T 是包括了子进程,-F 是强制) taskkill /PID 15712 -T -F
# ar 是一个标准的工具,通常在 binutils 软件包中包含。你可以使用以下命令来安装 binutils,从而获取 ar 工具。
sudo apt-get update
sudo apt-get install binutils
sudo apt-get install dpkg-dev
sudo apt-get install libgtk-3-0 libnotify4 libnss3 libxss1 libxtst6 xdg-utils libatspi2.0-0 libuuid1 libsecret-1-0
--deb-no-default-config-files
"build": {
"linux": {
"target": {
"target": "deb",
"arch": [
"x64"
]
},
"deb": {
"fpm": [
"--deb-no-default-config-files"
]
}
}
}
sudo chmod -R 755 /home/zy/Desktop/hyt-exam-cliten/
如果文件所有者不属于你当前的用户,可以使用 chown 命令更改文件所有者。
sudo chown -R $(whoami) /home/zy/Desktop/hyt-exam-cliten/
sudo apt install ./your-package-name.deb
sudo apt remove package-name // package 的 name
export http_proxy=http://10.5.4.120:10809
export https_proxy=http://10.5.4.120:10809
sudo apt-get install firwalld firewall-config
systemctl stop firewalld
sudo sysctl fs.inotify.max_user_watches=524288
[来源](https://zhuanlan.zhihu.com/p/599156184)
childWindow?.webContents.on('did-finish-load', () => {
childWindow?.setAlwaysOnTop(true) //置顶逻辑一定要放到这里
// childWindow?.webContents.send(IpcMessageType.sendRenderConnect, data) // 发送渲染进程内容至少要要延迟40ms才能行,因为渲染进程还没启动好
const rendererProcessId = winInfo.webContents.getProcessId()
console.log('窗口渲染进程的 ID:', rendererProcessId)
})
const ipc = require('electron').ipcMain
ipc.on('save-dialog', (event, src) => {
/* dialog.showSaveDialog({
title: '导出',
filters: [
{name: 'All', extensions: ['*']},
]
}, res => {
//直接下载url
mainWindow.webContents.downloadURL(res)
}) */
dialog.showErrorBox('报错标题', '报错内容')
// mainWindow.webContents.downloadURL(src.imageSrc)
})
const { shell } = require('electron')
shell.openExternal(url)
const winURL = is.dev
? `${process.env['ELECTRON_RENDERER_URL']}`
: join(__dirname, '../renderer/index.html')
const pageUrl = mainWindows.webContents.getURL() //获取当前页面url
pageUrl = 'info' //指定路由;如http://localhost:5172/#/info ;根路径为 '/';路由模式必须是hash模式,history模式需要做特殊处理,不建议使用
if (is.dev) {
mainWindows.loadURL(winURL, { hash: pageUrl })
} else {
mainWindows.loadFile(winURL, { hash: pageUrl })
}
访问 http://localhost:3800/my 页面;dist 为静态资源目录,index.html 为渲染进程的入口文件;
// xx.js
mainWindows.loadURL('http://localhost:3800/dist/#/my')
// router.js
const express = require('express')
const app = express()
const path = require('path')
import mimeTypes from 'mime-types'
router.get('/dist', async (req, res, next) => {
try {
const concatUrl = await getProjectInfoUrl()
const staticPath = path.join(concatUrl, 'index.html')
const reqUrl = req.url
res.sendFile(staticPath)
} catch (error) {
res.send('404')
}
})
router.get('/dist/static/* || /dist/assets/*', async (req, res, next) => {
if (req.url.startsWith('/static/') || req.url.startsWith('/assets/')) {
const extname: string = path.extname(req.url)
const mime: string = mimeTypes.lookup(extname)
const staticPath = await getProjectInfoUrl()
const lastPath = join(staticPath, req.url)
readFile(lastPath, (err, data) => {
if (err) {
res.status(404).send('assets资源未找到')
} else {
res.setHeader('Content-Type', mime)
res.send(data)
}
})
} else {
next()
}
})
/**
* 窗口移动计算,让其不能超出屏幕边界
* @param winInfo - child window, including methods to get and set its position.
* @param width - The width of the screen.
* @param height - The height of the screen.
* @param childWidth - The width of the child window.
* @param slider - Threshold value to determine the adjustment limits (default is 100).
*
* repositioning it within bounds if necessary.
*/
export function getWindowWidthAndHeight(params): void {
const {
winInfo, //子窗口信息
width, //屏幕宽度
height, //屏幕高度
childWidth, //子窗口宽度
slider = 100 //临界值
} = params
const [positonsX, positonsY] = winInfo.getPosition()
let newX = positonsX || 0
let newY = positonsY || 0
/**
* x轴左方
* x轴点位置最小为屏幕宽度 - 临界值
*/
let crisisLX = childWidth - Math.abs(newX)
if (newX < 0 && crisisLX < slider) {
newX = -childWidth + slider
}
/**
* x轴右方
* x轴点位置最小为屏幕宽度 - 临界值
*/
const crisisRX = newX + slider
if (crisisRX >= width) {
newX = width - slider
}
/**
* y轴下方
* y轴点位置最小为屏幕高度 - 临界值
*/
const crisisY = newY + slider
if (crisisY >= height) {
newY = height - slider
}
/**
* y轴上方
* y轴点位置最大为0
*/
if (newY <= 0) {
newY = 0
}
if (newX !== positonsX || newY !== positonsY) {
console.log('位置', newX === positonsX, '位置', newY === positonsY)
// 设置窗口位置
winInfo?.setBounds({ x: newX, y: newY })
}
}
openCalculate() {
const windowInfo = new WindowInfo()
const primaryDisplay = screen.getPrimaryDisplay()
const { width, height } = primaryDisplay.workAreaSize
const childWidth = 320
const childHeight = 540
calacWin = windowInfo.createWindows({
route: '/calculator',
parentId: 1,
minimizable: true,
width: childWidth,
height: childHeight,
resizable: false,
title: '计算器',
modal: false,
titleBarStyle: 'default',
skipTaskbar: true // 不显示在任务栏
})
if (calacWin) {
calacWin?.webContents.on('did-finish-load', () => {
calacWin?.setAlwaysOnTop(true)
})
calacWin?.on('close', () => {
calacWin = null
})
calacWin?.on(
'move',
throttle(() => {
getWindowWidthAndHeight({
winInfo: childrenWin,
width,
height,
childWidth
})
}, 50)
)
}
}
User\AppData\Local\electronName-updater #electronName 是package.json的name字段
powershell 命令行必须是终端管理员模式,否则会提示权限不足。 正常使用 cmd 行运行如下命令:
安装程序.exe /S
未验证
your-electron-app/
├── resources/
│ └── redis/ # 你新建
│ ├── redis-server.exe
│ ├── redis.conf # 可选(自己配置)
│ └── data/ # 存放 rdb/aof(新建)
├── src/
├── package.json
const { app, BrowserWindow } = require('electron')
const { spawn } = require('child_process')
const path = require('path')
const fs = require('fs')
let redisProcess = null
// 1. 获取 Redis 路径(开发/生产兼容)
function getRedisPath() {
if (app.isPackaged) {
// 生产:resources/redis
return path.join(process.resourcesPath, 'redis')
} else {
// 开发:项目/resources/redis
return path.join(__dirname, '../resources/redis')
}
}
// 2. 启动 Redis
async function startRedis() {
const redisDir = getRedisPath()
const redisExe = path.join(redisDir, 'redis-server.exe')
const redisConf = path.join(redisDir, 'redis.conf')
const dataDir = path.join(redisDir, 'data')
// 确保data目录存在
if (!fs.existsSync(dataDir)) fs.mkdirSync(dataDir, { recursive: true })
// 启动参数(绑定本地、自定义端口、数据目录)
const args = [
redisConf,
'--bind', '127.0.0.1',
'--port', '6380', // 避开默认6379
'--dir', dataDir,
'--maxmemory', '256mb',
'--maxmemory-policy', 'allkeys-lru'
]
// 启动子进程
redisProcess = spawn(redisExe, args, {
cwd: redisDir,
stdio: 'ignore', // 不输出日志
windowsHide: true // 隐藏黑窗口
})
redisProcess.on('error', (err) => {
console.error('Redis启动失败:', err)
})
console.log('Redis 已启动 (port:6380)')
}
// 3. 停止 Redis(应用退出时)
function stopRedis() {
if (redisProcess && !redisProcess.killed) {
redisProcess.kill()
console.log('Redis 已停止')
}
}
// 4. 应用生命周期
app.whenReady().then(async () => {
await startRedis()
createWindow()
})
app.on('window-all-closed', () => {
stopRedis()
app.quit()
})
app.on('before-quit', stopRedis)
// 5. 窗口
function createWindow() {
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
contextIsolation: true,
nodeIntegration: false,
preload: path.join(__dirname, 'preload.js')
}
})
win.loadFile('index.html')
}
// 主进程里连接自己打包的Redis
const Redis = require('ioredis')
const redis = new Redis({
host: '127.0.0.1',
port: 6380 // 跟上面一致
})
// BullMQ 同样用这个连接
const { Queue } = require('bullmq')
const queue = new Queue('myQueue', {
connection: { host: '127.0.0.1', port: 6380 }
})
bind 127.0.0.1
port 6380
daemonize no
dir ./data
dbfilename dump.rdb
appendonly yes
appendfilename "appendonly.aof"
maxmemory 256mb
maxmemory-policy allkeys-lru
protected-mode no
!include "MUI2.nsh"
!define APP_NAME "YourApp"
!define REDIS_SUBPATH "redis"
;--------------------------------
; 安装完成后
Function .onInstSuccess
DetailPrint "安装完成,Redis 已内置到应用目录"
FunctionEnd
;--------------------------------
; 卸载开始前
Function .onUnInit
DetailPrint "尝试关闭应用相关进程..."
nsExec::ExecToLog "taskkill /F /IM YourApp.exe /T"
nsExec::ExecToLog "taskkill /F /IM redis-server.exe /T"
Sleep 500
FunctionEnd
;--------------------------------
; 卸载时删除 Redis 数据
Function un.onUninstSuccess
RMDir /r "$INSTDIR\${REDIS_SUBPATH}"
DetailPrint "Redis 数据已清理"
FunctionEnd