用开源源码做淘宝客网站wordpress不显示分类目录
用开源源码做淘宝客网站,wordpress不显示分类目录,帮别人做视频剪辑的网站,湖北专业网站建设公司一、技术背景在传统的 Web 应用中#xff0c;微信授权通常通过 window.open() 打开授权页面#xff0c;然后通过 postMessage 进行跨窗口通信。但在 Electron 应用中#xff0c;我们需要使用 IPC#xff08;进程间通信#xff09;机制来实现主窗口和授权窗口之间的消息传递…一、技术背景在传统的 Web 应用中微信授权通常通过window.open()打开授权页面然后通过postMessage进行跨窗口通信。但在 Electron 应用中我们需要使用 IPC进程间通信机制来实现主窗口和授权窗口之间的消息传递。二、架构设计我们的方案采用了主进程-渲染进程的通信架构渲染进程Login/Bind页面 ↓ 调用 window.api.auth.openWindow() 主进程ipc.ts ↓ 创建授权窗口 BrowserWindow 授权窗口微信授权页面 ↓ 监听重定向/导航事件 主进程ipc.ts ↓ 发送 IPC 消息到渲染进程 渲染进程Login/Bind页面 ↓ 接收授权结果并调用后端 API三、核心实现步骤1. Preload 脚本配置src/preload/index.ts首先我们需要在 preload 脚本中暴露安全的 API 给渲染进程const api { // 授权窗口 API auth: { openWindow: (authUrl: string, redirectUri: string) ipcRenderer.invoke(auth:open-window, authUrl, redirectUri) }, // 事件监听 API on: (channel: string, callback: (...args: any[]) void) { const validChannels [ webview-message, bind-callback-result, // 绑定回调 login-callback-result // 登录回调 ] if (validChannels.includes(channel)) { ipcRenderer.on(channel, (_, ...args) callback(...args)) } }, // 移除监听 off: (channel: string, callback: (...args: any[]) void) { const validChannels [ webview-message, bind-callback-result, login-callback-result ] if (validChannels.includes(channel)) { ipcRenderer.removeListener(channel, (_, ...args) callback(...args)) } } } // 通过 contextBridge 安全地暴露给渲染进程 contextBridge.exposeInMainWorld(api, api)关键点使用白名单机制validChannels限制可监听的频道确保安全性区分bind-callback-result和login-callback-result两种事件类型通过contextBridge而不是直接暴露 Node.js API2. 主进程授权窗口创建src/main/ipc.ts在主进程中注册 IPC 处理器负责创建授权窗口并监听回调ipcMain.handle(auth:open-window, (_event, authUrl: string, redirectUri: string) { console.log(auth:open-window called:, { authUrl, redirectUri }) return new Promise((resolve, reject) { try { // 创建授权窗口 const authWindow new BrowserWindow({ width: 600, height: 600, parent: mainWindow, modal: false, show: false, autoHideMenuBar: true, // 隐藏菜单栏 webPreferences: { nodeIntegration: false, contextIsolation: true, sandbox: true // 启用沙箱模式提高安全性 } }) // 窗口准备好后显示 authWindow.once(ready-to-show, () { authWindow.show() }) // 加载授权 URL authWindow.loadURL(authUrl).catch((error) { console.error(Failed to load URL:, error) authWindow.close() reject(error) }) // 监听重定向事件主要方案 authWindow.webContents.on(will-redirect, (event, url) { handleCallback(event, url, true) }) // 监听导航完成事件备用方案 authWindow.webContents.on(did-navigate, (event, url) { handleCallback(event, url, false) }) // 回调处理函数 function handleCallback(event: any, url: string, preventDefault: boolean) { try { const urlObj new URL(url) // 判断是绑定还是登录 const isCallbackUrl urlObj.pathname.includes(bind.html) || urlObj.pathname.includes(socialLogin.html) let type bind-callback-result if (urlObj.pathname.includes(bind.html)) { type bind-callback-result } else if (urlObj.pathname.includes(socialLogin.html)) { type login-callback-result } const code urlObj.searchParams.get(code) const state urlObj.searchParams.get(state) console.log(Callback check:, { pathname: urlObj.pathname, isCallbackUrl, hasCode: !!code, type }) if (isCallbackUrl code) { if (preventDefault) { event.preventDefault() } // 发送结果到主窗口渲染进程 if (mainWindow !mainWindow.isDestroyed()) { console.log(Sending to main window:, type, { code, state }) mainWindow.webContents.send(type, { type: type, code, state, status: success }) } // 关闭授权窗口 authWindow.close() resolve({ success: true }) } } catch (error) { console.error(Failed to handle callback:, error) } } // 窗口关闭事件 authWindow.on(closed, () { console.log(Auth window closed) }) // 处理用户手动关闭窗口的情况 authWindow.on(close, () { if (!authWindow.isDestroyed()) { resolve({ success: false, cancelled: true }) } }) } catch (error) { console.error(Failed to create auth window:, error) reject(error) } }) })关键点使用will-redirect和did-navigate双重监听确保回调被捕获根据 URL 路径动态判断事件类型bind.htmlvssocialLogin.html使用preventDefault()阻止重定向提高用户体验通过mainWindow.webContents.send()将结果发送到主窗口3. 渲染进程监听授权结果以登录为例src/core/ui/pages/useWechat.tsxconst useBindWechatDialog ({ handleLoginCallback }) { const [open, setOpen] useState(false) let currentItem: any {} // 打开微信授权窗口 function openWechatLoginWindow(url: any, redirectUri: string) { const wechatLoginUrl url // 检查是否在 Electron 环境 if (window.api window.api.auth) { // Electron 环境使用主进程创建授权窗口 console.log(调用 window.api.auth.openWindow:, { wechatLoginUrl, redirectUri }) window.api.auth .openWindow(wechatLoginUrl, redirectUri) .then((result) { console.log(授权窗口结果:, result) }) .catch((error) { console.error(打开授权窗口失败:, error) message.error(打开授权窗口失败) }) } else { // 浏览器环境使用 window.open let width 600, height 600 let top (window.screen.height - 30 - height) / 2 let left (window.screen.width - 30 - width) / 2 window.open( wechatLoginUrl, , width${width},height${height},top${top},left${left},toolbarno,menubarno ) } } // 触发授权流程 const handleDialogVisible async (value: any, item: any { type: 32 }) { if (value) { currentItem item // 构建回调地址 let redirectUri https://demo.jdyos.com/aa2_aios/socialLogin.html if (process.env.NODE_ENV ! development) { let fullDomain ${window.location.protocol}//${window.location.hostname}${ window.location.port ? : window.location.port : } redirectUri ${fullDomain}/aa2_aios/socialLogin.html } // 获取授权 URL let res: any await AuthApi.bindForCommonForLogin( ?type${item.type}redirectUri${encodeURIComponent(redirectUri)} ) // 打开授权窗口 openWechatLoginWindow(res.data, redirectUri) } } useEffect(() { // 处理授权回调的统一函数浏览器和 Electron 共用 const handleBindCallback async (data: any) { // 检查是否是登录回调且在登录页面 if (data data.type login-callback-result location.href.includes(login)) { console.log(成功接收到授权结果:, data) if (data.status error) { message.info(data.message) } else { // 构建回调地址用于后端验证 let redirectUri: any undefined if (currentItem.type 50) { redirectUri https://demo.jdyos.com/aa2_aios/socialLogin.html if (process.env.NODE_ENV ! development) { let fullDomain ${window.location.protocol}//${window.location.hostname}${ window.location.port ? : window.location.port : } redirectUri ${fullDomain}/aa2_aios/socialLogin.html } } if (currentItem.type) { // 调用登录 API let res: any await AuthApi.bindForCommonForLoginCodeState({ type: currentItem.type, code: data.code, state: data.state, redirectUri }) if (res.code 0) { handleLoginCallback(res) // 登录成功回调 } else { message.warning(res.msg) } } } } } // 浏览器环境监听 postMessage const messageHandler (event: MessageEvent) { handleBindCallback(event.data) } window.addEventListener(message, messageHandler, false) // Electron 环境监听 IPC 消息 const ipcHandler (data: any) { handleBindCallback(data) } if (window.api) { window.api.on(login-callback-result, ipcHandler) } // 清理函数 return () { window.removeEventListener(message, messageHandler) if (window.api) { window.api.off(login-callback-result, ipcHandler) } } }, []) return { wechatVisible: handleDialogVisible, wechatGetDom: getDom, } }关键点统一的handleBindCallback函数同时兼容浏览器和 Electron 环境在浏览器中监听postMessage在 Electron 中监听 IPC 事件使用location.href.includes(login)确保只在正确的页面响应组件卸载时清理事件监听防止内存泄漏4. 绑定场景的实现src/renderer/src/components/app/useBindWechatDialog.tsx绑定场景与登录场景的实现基本相同主要区别在于// 1. 监听的事件类型不同 window.api.on(bind-callback-result, ipcHandler) // 绑定用这个 // 2. 调用的 API 不同 AuthApi.bindForCommonCodeState({ // 绑定用这个 API type: currentItem.type, code: data.code, state: data.state, redirectUri }) // 3. 回调地址不同 redirectUri https://demo.jdyos.com/aa2_aios/bind.html // 绑定用 bind.html四、技术要点总结1. 安全性考虑✅ 使用contextBridge暴露 API避免直接暴露 Node.js 能力✅ 白名单机制限制可监听的 IPC 频道✅ 授权窗口启用沙箱模式sandbox: true✅ 禁用 Node 集成nodeIntegration: false2. 兼容性处理✅ 同时支持浏览器和 Electron 环境✅ 浏览器环境使用postMessageElectron 使用 IPC✅ 通过window.api检测运行环境3. 用户体验优化✅ 隐藏授权窗口菜单栏autoHideMenuBar: true✅ 窗口准备好后再显示ready-to-show事件✅ 使用will-redirect拦截重定向避免页面跳转✅ 授权完成后自动关闭窗口4. 健壮性保障✅ 双重监听机制will-redirectdid-navigate✅ 完善的错误处理和日志记录✅ 组件卸载时清理事件监听✅ 处理用户手动关闭窗口的情况五、流程图用户点击微信登录/绑定 ↓ 调用后端 API 获取授权 URL ↓ window.api.auth.openWindow(authUrl, redirectUri) ↓ 主进程创建 BrowserWindow 并加载授权 URL ↓ 用户在授权窗口中完成微信扫码/授权 ↓ 微信重定向到 socialLogin.html?codexxxstatexxx ↓ 主进程监听到 will-redirect 事件 ↓ 解析 URL提取 code 和 state ↓ mainWindow.webContents.send(login-callback-result, { code, state }) ↓ 渲染进程 window.api.on(login-callback-result, callback) ↓ 调用后端 API 验证 code/state 并完成登录 ↓ 登录成功更新 UI六、常见问题与解决方案Q1: 为什么需要同时监听will-redirect和did-navigateA:will-redirect在重定向发生前触发可以使用preventDefault()阻止跳转提供更好的用户体验。但某些情况下will-redirect可能不触发因此did-navigate作为备用方案。Q2: 为什么要区分bind-callback-result和login-callback-resultA:绑定和登录是两个不同的业务场景回调地址bind.htmlvssocialLogin.html和后端 API 都不同。区分事件类型可以让各自的页面只响应自己的授权结果。Q3: 如何确保只在正确的页面响应授权结果A:通过location.href.includes(login)或类似条件判断当前页面避免在错误的页面处理授权回调。Q4: 授权窗口为什么要设置parent: mainWindowA:设置父窗口后授权窗口会始终显示在主窗口前面且当主窗口最小化时授权窗口也会一起最小化提供更好的用户体验。七、完整的文件结构src/ ├── preload/ │ └── index.ts # Preload 脚本暴露安全的 API ├── main/ │ └── ipc.ts # 主进程 IPC 处理器 ├── core/ui/pages/ │ └── useWechat.tsx # 登录页面的微信授权 Hook └── renderer/src/components/app/ └── useBindWechatDialog.tsx # 绑定页面的微信授权 Hook八、总结这套方案充分利用了 Electron 的 IPC 机制实现了安全、健壮且用户体验良好的第三方授权功能。核心思路是在主进程中创建授权窗口利用BrowserWindow的导航事件监听能力通过 IPC 机制传递授权结果避免跨域和安全问题统一的回调处理函数同时兼容浏览器和 Electron 环境完善的安全机制使用白名单、沙箱模式等保护应用安全这个方案不仅适用于微信授权也可以轻松扩展到其他第三方登录如 GitHub、Google 等场景。希望这份技术总结对你有帮助如果有任何问题或需要补充的内容请随时告诉我。