惊蛰-electron里面优雅地发起请求

惊蛰的寓意是天气回暖、春雷始鸣,惊醒蛰伏于地下冬眠的昆虫,象征着自然界的生机勃勃和万物复苏

回顾

上一篇文章虽然做了简单的登录和登出,实际上是没有调接口

传统的web应用就是一个渲染页面,可以直接在里面进行http请求等等,而且是有域名的,后端也可以设置cros黑白名单。

但是electron如果在渲染进程操作是极为不安全的且会跨域的,所以请求方法都是放在主进程,渲染进程聚是一个纯渲染ui,请求以及桌面内部操作都是放到了主进程来实现

我们用上一篇文章的代码:electron-vue3-template进行增强调用请求,暂时用的apifox的mock接口

进入正题...

改造项目(主进程调用方式)

  1. 我们用最流行的aixos npm包
npm 复制代码
    npm install axios --save
  1. 在src/main/ 增加 http.ts 对axios进行简单拦截器封装和业务封装
ts 复制代码
import axios from 'axios'
import { ipcMain } from 'electron'
const settings = require('electron-settings')
import log from 'electron-log'

export interface ApiResponse<T> {
  success: boolean
  errorCode: string
  data: T
  message: string
}
// 创建 Axios 实例
const axiosInstance = axios.create({
  baseURL: 'https://m1.apifoxmock.com', // 这里可以改为你自己的api baseUrl
  timeout: 30000,
  headers: {
    'Content-Type': 'application/json; charset=UTF-8'
  }
  // withCredentials: true
})

// 请求拦截器
axiosInstance.interceptors.request.use(
  (config) => {
    const token = settings.getSync('token')
    // 在发送请求之前做些什么
    // 例如可以在这里添加 token 到请求头中
    // 登录在第一次是不需要的
    if (token) {
      config.headers.Authorization = `Bearer ${token}`
    }
    return config
  },
  (error) => {
    // 处理请求错误
    return Promise.reject(error)
  }
)

// 响应拦截器
axiosInstance.interceptors.response.use(
  (response) => {
    if (response.status !== 200) {
      log.error(response.headers, response.data.msg)
    }
    return response.data
  },
  (error) => {
    // 处理超时错误
    if (error.code === 'ECONNABORTED' && error.message.includes('timeout')) {
      log.error('请求超时:', error.config.url)
      return Promise.reject({
        success: false,
        message: '请求超时,请稍后重试',
        data: null
      })
    }
    // 确保 error.response 存在
    if (!error.response) {
      log.error(error)
      log.error('网络错误:', error.message)
      return Promise.reject({
        success: false,
        message: '网络连接错误,请检查网络设置',
        data: null
      })
    }

    // 这里是业务上token过期或者鉴权失败等等,根据你业务代码来
    if (error.response && error.response.status === 424) {
      // 如果响应状态码为 424,移除 token,并且退出
      settings.unsetSync('token')
      ipcMain.emit('main-logout-success') // 主进程调用自己用emit
    }
    if (error.response.status === 504) {
      log.info('error', '504 GATEWAY_TIMEOUT')
    }

    log.info('报错接口:', error.response.config.url)
    log.info('报错接口错误:', error.response.data)
    // 处理响应错误
    return Promise.reject({
      success: false,
      message: error.message,
      data: null
    })
  }
)

export default axiosInstance
  1. 我们对api接口进行统一管理,在src/下面增加apis文件夹
ts 复制代码
// src/apis/index.ts
export const authApis = {
  loginIn: '/m1/6017821-5706748-default/api/token' // apifox的mock接口
}

export const otherApis = {}
  1. 注意这里为了之后好引用改造下全局的tsconfig.node.json文件:
json 复制代码
    {
      "extends": "@electron-toolkit/tsconfig/tsconfig.node.json",
      "include": ["electron.vite.config.*", "src/main/**/*", "src/preload/**/*","src/apis"],
      "compilerOptions": {
        "composite": true,
        "types": ["electron-vite/node"]
      }
}
// 增加了"src/apis"

再改动一下全局的tsconfig.web.json文件

json 复制代码
  {
  "extends": "@electron-toolkit/tsconfig/tsconfig.web.json",
  "include": [
    "src/renderer/src/env.d.ts",
    "src/renderer/src/**/*",
    "src/renderer/src/**/*.vue",
    "src/preload/*.d.ts",
    "src/apis/*" // 增加这个
  ],
  "compilerOptions": {
    "composite": true,
    "baseUrl": ".",
    "paths": {
      "@renderer/*": [
        "src/renderer/src/*"
      ],
    "@apis/*": [
        "src/apis/*"  // 增加这个
      ],
    }
  }
}
// 增加@apis/* 以便后续在渲染进程用
  1. 我们要改造我们的登录方法src/main/index.ts
ts 复制代码
  import axiosInstance from './http'
  import { authApis } from '../apis'
  
  // 改造我们的登录方法
   ipcMain.handle('loginIn', async (event, params) => {
    log.info('login----params', params)
    try {
      const response: any = await axiosInstance.post(`${authApis.loginIn}`, params)

      if (response.success) {
        log.info('登录成功---返回', response)
        settings.setSync('token', response.data.access_token) // 存到应用本地数据库
        ipcMain.emit('main-login-success') // 通知登录成功了
      } else {
        log.info('登录失败---返回', response)
        return { message: '登录失败', success: false, data: null }
      }
      return response // 返回请求数据
    } catch (error: any) {
      log.error('Uncaught Exception:', error)
      return { message: error.message, success: false, data: null } // 返回错误信息
    }
  })

这时候重启项目进入登录界面:

账号用:admin 密码: 123 (才会正常登录)

上面在主进程已经可以正常调用了,那我们在渲染进程怎么调用请求呢。 不可能我们在渲染调用一个请求就要写很多ipcMain.handle 之类的吧

那我们接下来进行渲染进程的改造...

改造项目(渲染进程调用)

我们受ipcMain.handle的启发,其实我们可以重复封装一些方法给到渲染进程的

  1. 改动一下electron.vite.config.ts
json 复制代码
    alias: {
        '@renderer': resolve('src/renderer/src'),
        '@apis': resolve('src/apis') // 增加这个
      }
  1. 在src/main/index.ts setIpcHandlersForAxios函数里面增加2个通用型方法
ts 复制代码
// 统一的get-data
 ipcMain.handle("get-data", async (event, url, params) => {
    log.info("get-data", url, params)
    try {
      const response: any = await axiosInstance.get(url, { params })

      if (!response.success) {
        log.error("请求失败:url", `msg:${response.message}`, `errorCode:${response.errorCode}`, url)
      } else {
        log.info("get-data-success:", url)
      }
      return response // 返回请求数据
    } catch (error: any) {
      log.error("Uncaught Exception:url", error.message, url)
      return { success: false, message: error.message, error: error.message } 
    }
  })
    // 统一的post-data方法
  ipcMain.handle("post-data", async (event, url, params) => {
    log.info("post-data", url, params)
    try {
      const response: any = await axiosInstance.post(url, params)
      if (!response.success) {
        log.error("请求失败", `msg:${response.message}`, `errorCode:${response.errorCode}`, url)
      } else {
        log.info("post-data-success:", url)
      }
      return response // 返回请求数据
    } catch (error: any) {
      log.error("Uncaught Exception:url", error.message, url)
      return { success: false, message: error.message, error: error.message } // 返回错误信息
    }
  })
 

我们2个普通的get和post封装好了,我们可以在渲染进程用了

  1. 在renderder下面增加service文件夹且添加index.ts
ts 复制代码
const invokeData = window.electron.ipcRenderer.invoke
import { otherApis } from '@apis/index'


export async function fetchMockList() {
  return invokeData('get-data', otherApis.getList)
}
  1. 改造我们之前的views/home/index.vue页面发起请求
vue 复制代码
  <template>
  <div class="home">
    接口返回数据:
    <div>
      <div v-if="data && data.length">
        <div v-for="item in data" :key="item.id" class="item">
          <div>{{ item.text }}</div>
        </div>
      </div>
      <div v-else>
        <p>没有数据可显示。</p>
      </div>
    </div>
  </div>
</template>

<script lang="ts" setup>
import { onMounted, ref } from 'vue'
import { fetchMockList } from '@renderer/service/index'

let data = ref([])

onMounted(async () => {
  await fetchList()
})

const fetchList = async () => {
  const res = await fetchMockList()
  console.info('res', res)
  if (res.success) {
    data.value = res.data
  }
}
</script>

<style scoped>
.home {
  height: 100%;
  width: 100%;
  display: flex;
  align-items: center;
  flex-direction: column;
  justify-content: center;
}
</style>

最终效果如下:

上面完整的主进程调用http请求以及渲染进程调用已经基本实现了

希望对你有所帮助! 后续会更新更多实战项目的技术点...

最后附上源码地址electron-vue3-template

相关推荐
汪子熙32 分钟前
Angular i18n 资源加载利器解析: i18n-http-backend
前端·javascript·面试
天天扭码35 分钟前
在React项目中实现富文本编辑文章并发布
前端·react.js·github
Yehong38 分钟前
nuxt实现50个前端小创意(1)——前端基础学习
前端·vue.js
拉不动的猪38 分钟前
回顾vue3组件在运行过程中的编译提升
前端·vue.js·trae
zhuyasen39 分钟前
从Node.js到Go:如何从NestJS丝滑切换并拥抱Sponge框架
node.js·nestjs
天天扭码40 分钟前
前端必备 | 一文掌握React的Token管理
前端·javascript·react.js
烛阴40 分钟前
用Joi守住数据防线!Node.js/前端必备校验神器入门与进阶
前端·javascript
神秘敲码人2 小时前
前端面试题-HTML篇
前端·面试·html
杏仁海棠花饼2 小时前
杏仁海棠花饼的学习日记第十四天CSS
前端·css·学习
逃逸线LOF2 小时前
品优购项目(HTML\CSS)
前端·css·html