网站建设需求原型,黑龙江城乡建设厅网站,宣传网页,网站后台制作用的软件初始化
npm init vue # 全选 yes
npm i # 进入项目目录后使用
npm install electron electron-builder -D
npm install commander -D # 额外组件增加文件
新建 plugins 文件夹
src/background.ts
属于主进程
ipcMain.on、ipcMain.handle 都用于主进程监听 ipc#xff0c;…初始化
npm init vue # 全选 yes
npm i # 进入项目目录后使用
npm install electron electron-builder -D
npm install commander -D # 额外组件增加文件
新建 plugins 文件夹
src/background.ts
属于主进程
ipcMain.on、ipcMain.handle 都用于主进程监听 ipcipcMain.on 用于监听 ipcRenderer.sendipcMain.handle 用于监听 ipcRenderer.invoke 并 return xxx
ipc 单向 从渲染进程发向主进程ipcRenderer.send 从主进程发向渲染进程window.webContents.send
ipc 双向 从渲染进程发向主进程主进程还会返回发向渲染进程ipcRenderer.invoke 从主进程发向渲染进程渲染进程还会返回发向主进程没有类似于 ipcRenderer.invoke 的需要间接实现。主进程使用 window.webContents.send渲染进程使用 ipcRenderer.send
import { app, BrowserWindow, screen, ipcMain } from electron
import path from path
import { Command } from commander;app.whenReady().then(() {const command new Commandlet width, heightlet optionscommand.option(-m, --maximize, maximize window).option(-l, --location , location of load index page, index.html).option(-d, --dev, openDevTools).option(--no-sandbox, other).parse()options command.opts()if (options.maximize) {width screen.getPrimaryDisplay().workAreaSize.widthheight screen.getPrimaryDisplay().workAreaSize.height}else {width 800height 600}const window new BrowserWindow({width: width,height: height,autoHideMenuBar: true,webPreferences: {preload: path.join(__dirname, preload.js)}})if (options.location.indexOf(:) 0)window.loadURL(options.location)elsewindow.loadFile(options.location)if (options.dev)window.webContents.openDevTools()ipcMain.on(rtm, () {console.log(rtm)window.webContents.send(mtr)})ipcMain.on(rtm_p, (e, p) {console.log(p)window.webContents.send(mtr_p, mtr_p ${p})})ipcMain.handle(rtmmtr_p, (e, p) {console.log(p)return rtmmtr_p_return})
})src/preload.ts
预加载脚本用来给渲染进程提供使用 ipcRenderer 的方法 rtm 是渲染进程发向主进程rtmmtr 是从渲染进程发向主进程主进程还会返回发向渲染进程mtr 是主进程发向渲染进程
import { contextBridge, ipcRenderer } from electroncontextBridge.exposeInMainWorld(electronAPI, {rtm: () ipcRenderer.send(rtm),rtm_p: (p: any) ipcRenderer.send(rtm_p, p),rtmmtr_p: (p: any) ipcRenderer.invoke(rtmmtr_p, p),mtr: (p: any) ipcRenderer.on(mtr, p),mtr_p: (p: any) ipcRenderer.on(mtr_p, p),
})src/renderer.d.ts
给渲染进程用的 preload.ts 里的方法的类型声明
export interface IElectronAPI {rtm: () Promiseany,rtm_p: (p: any) Promiseany,rtmmtr_p: (p: any) Promiseany,mtr: (p: any) Promiseany,mtr_p: (p: any) Promiseany,
}declare global {interface Window {electronAPI: IElectronAPI}
}plugins/vite.electron.dev.ts
自定义 dev 方法用于启动 vite 后带起 electron
// 导入需要使用的类型和库
import type { Plugin } from vite
import type { AddressInfo } from net
import { spawn } from child_process
import fs from fs// 导出Vite插件函数
export const viteElectronDev (): Plugin {return {name: vite-electron-dev,// 在configureServer中实现插件的逻辑configureServer(server) {// 定义初始化Electron的函数const initElectron () {// 使用esbuild编译TypeScript代码为JavaScriptrequire(esbuild).buildSync({entryPoints: [src/background.ts, src/preload.ts],bundle: true,outdir: dist,platform: node,external: [electron]})}// electron 运行let electron_run (ip: string) {initElectron()// 启动Electron进程let electronProcess spawn(require(electron), [dist/background.js, -l, ip, -d])// 监听Electron进程的stdout输出electronProcess.stdout?.on(data, (data) {console.log(${data});});return electronProcess}// 监听Vite的HTTP服务器的listening事件server?.httpServer?.once(listening, () {// 获取HTTP服务器的监听地址和端口号const address server?.httpServer?.address() as AddressInfoconst ip http://localhost:${address.port}let electronProcess electron_run(ip)// 监听主进程代码的更改fs.watch(src, () {// 杀死当前的Electron进程electronProcess.kill()electronProcess electron_run(ip)})})}}
}
plugins/vite.electron.build.ts
自定义 build 方法这里打包了 linux 的 x64、arm64 的包
import type { Plugin } from vite
import * as electronBuilder from electron-builder
import path from path
import fs from fs// 导出Vite插件函数
export const viteElectronBuild (): Plugin {return {name: vite-electron-build,// closeBundle是Vite的一个插件钩子函数用于在Vite构建完成后执行一些自定义逻辑。closeBundle() {// 定义初始化Electron的函数const initElectron () {// 使用esbuild编译TypeScript代码为JavaScriptrequire(esbuild).buildSync({entryPoints: [src/background.ts, src/preload.ts],bundle: true,outdir: dist,platform: node,external: [electron]})}// 调用初始化Electron函数initElectron()// 修改package.json文件的main字段不然会打包失败const json JSON.parse(fs.readFileSync(package.json, utf-8))json.main background.jsfs.writeSync(fs.openSync(dist/package.json, w), JSON.stringify(json, null, 2))// 创建一个空的node_modules目录 不然会打包失败fs.mkdirSync(path.join(process.cwd(), dist/node_modules));// 使用electron-builder打包Electron应用程序electronBuilder.build({config: {appId: com.example.app,productName: vite-electron,directories: {output: path.join(process.cwd(), release), //输出目录app: path.join(process.cwd(), dist), //app目录},linux: {target: [{target: AppImage,arch: [x64, arm64]}]}}})}}
}
修改源文件
src/App.vue
属于渲染进程
window.electronAPI.xxx() 就是预加载脚本preload.ts给渲染进程提供的使用 ipcRenderer 的方法 window.electronAPI.mtr 和 …mtr_p mtrmain to renderer用于监听主进程发过来的消息 由于 window.electronAPI.rtmmtr_p 使用 ipcRenderer.invoke这是异步方法如果不在其前面加 await 而直接获取会得到一个用于异步执行的对象Promise其内容包含了需要异步执行的东西await 就是等待该对象运行结束从而获取正确值而 await 需要其调用者是异步的所以 increment() 也加上了 async异步标志
script setup langts
import { RouterLink, RouterView } from vue-router
import HelloWorld from ./components/HelloWorld.vue
import { ref } from vue// 响应式状态
const count ref(0)// 用来修改状态、触发更新的函数
async function increment() {count.valuewindow.electronAPI.rtm()window.electronAPI.rtm_p(rtm_p ${count.value})const rtmmtr_p await window.electronAPI.rtmmtr_p(rtmmtr_p ${count.value})console.log(rtmmtr_p)
}window.electronAPI.mtr(() {console.log(mtr)
})window.electronAPI.mtr_p((e: any, p: any) {console.log(p)
})
/scripttemplatebutton clickincrementhhh: {{ count }}/buttonheaderimg altVue logo classlogo src/assets/logo.svg width125 height125 /div classwrapperHelloWorld msgYou did it! /navRouterLink to/Home/RouterLinkRouterLink to/aboutAbout/RouterLink/nav/div/headerRouterView /
/templatestyle scoped
header {line-height: 1.5;max-height: 100vh;
}.logo {display: block;margin: 0 auto 2rem;
}nav {width: 100%;font-size: 12px;text-align: center;margin-top: 2rem;
}nav a.router-link-exact-active {color: var(--color-text);
}nav a.router-link-exact-active:hover {background-color: transparent;
}nav a {display: inline-block;padding: 0 1rem;border-left: 1px solid var(--color-border);
}nav a:first-of-type {border: 0;
}media (min-width: 1024px) {header {display: flex;place-items: center;padding-right: calc(var(--section-gap) / 2);}.logo {margin: 0 2rem 0 0;}header .wrapper {display: flex;place-items: flex-start;flex-wrap: wrap;}nav {text-align: left;margin-left: -1rem;font-size: 1rem;padding: 1rem 0;margin-top: 1rem;}
}
/style修改配置文件
tsconfig.node.json
{extends: tsconfig/node18/tsconfig.json,include: [vite.config.*,vitest.config.*,cypress.config.*,nightwatch.conf.*,playwright.config.*,plugins/**/*.ts],compilerOptions: {composite: true,module: ESNext,types: [node]}
}
vite.config.ts
import { fileURLToPath, URL } from node:urlimport { defineConfig } from vite
import vue from vitejs/plugin-vue
import vueJsx from vitejs/plugin-vue-jsx
import { viteElectronDev } from ./plugins/vite.electron.dev
import { viteElectronBuild } from ./plugins/vite.electron.build// https://vitejs.dev/config/
export default defineConfig({plugins: [vue(),vueJsx(),viteElectronDev(),viteElectronBuild()],base: ./, //默认绝对路径改为相对路径 否则打包白屏resolve: {alias: {: fileURLToPath(new URL(./src, import.meta.url))}}
})使用
启动npm run dev 打包npm run build
npm run dev启动后桌面出现应用界面并自动打开开发者工具。修改 src 下的任何文件都会自动编译并重启应用 打包后启动可以添加 -m 全屏-d 打开开发者工具
其他
https://xiaoman.blog.csdn.net/article/details/131713875?spm1001.2014.3001.5502https://www.electronjs.org/zh/docs/latest/tutorial/context-isolationhttps://www.electronjs.org/zh/docs/latest/tutorial/ipc