【electron】验证渲染URL

场景

electron和h5文件分离

需求

  1. electron部分添加配置h5URL页面
    通过配置winURL(h5的路径)打开最终渲染页面,给electron部分增加一个默认页(与electron同级src下的404页面)用来输入winURL。这里添加两种检验:①通过fetch网络状态判断winURL是否有效,②通过electron的隐藏窗口触发send事件。

  2. 本地配置文件
    通过electron提供的app.getPath('documents')获取用户的目录路径,在这个路径下写入验证成功的winURL,每次进入electron都先在本地检查下是否有该配置文件,在有配置文件情况下,获取文件内winURL,然后检验该winURL是否还有效,若无效则返回配置URL页面,反之进入渲染页面。

electron部分

图1.1-electron部分目录

  1. 配置URL404页面中保存按钮的方法
图1.2-配置URL404页面

envCheck 为electron创建的事件这里用于初始化 winURL

<template>
    <div class="container flex-row-start">
        <div class="problemBox flex-col">
            <img class="logo" src="/images/common/company.png" />
            <div v-if="curUrlState == URLState.noelectron" class="tip flex-row">请勿使用非法客户端访问服务</div>
            <div v-if="curUrlState == URLState.input || curUrlState == URLState.isSuccess" class="url-input flex-row">
                <input v-model="urlValue" placeholder="请输入有效服务地址" />
                <div class="save flex-row" @click="saveUrl">
                    保存
                </div>
            </div>
            <div class="tip2" v-if="curUrlState == URLState.input && !isInvalid">
                无效地址,请检查地址</div>
            <div class="tip2" v-if="curUrlState == URLState.isSuccess">
                请输入正确的测绘渲染地址</div>
            <el-text v-loading="loading" element-loading-text="Loading..." />
        </div>
    </div>
</template>
  
  
<script lang="ts" setup>
import {
    
     onMounted, ref } from "vue";

enum URLState {
    
    
    loading = '0',
    noelectron = '1',
    input = '2',
    isSuccess = '3'
};

const curUrlState = window.location.hash.split('=')?.[1];
const loading = ref(curUrlState == URLState.loading);
const isInvalid = ref(true);//输入的url是否有效
const urlValue = ref('');

onMounted(() => {
    
    
    isInvalid.value = true;
    urlValue.value = '';
})

function saveUrl() {
    
    
    if (!urlValue.value) return;
    loading.value = true;
    // 检查url是否有效
    fetch(urlValue.value)
        .then(response => {
    
    
            loading.value = false;
            if (response.ok) {
    
    
                console.log('Path is correct');
                isInvalid.value = true;
                //@ts-ignore
                window.api.send("envCheck", {
    
     url: urlValue.value });
            } else {
    
    
                console.error('Path is incorrect');
                isInvalid.value = false;
            }
        })
        .catch(() => {
    
    
            loading.value = false;
            isInvalid.value = false;
        });
}

</script>
  1. electron下的main.ts
let winURL = "http://localhost:5173/"; // 默认配置URL页面的路径
let mainWin;
let checkWin;
let timer; //返回输入url页面的计时器
//404?status=0是包含loading的页面
//404?status=2是只包含url输入框
//404?status=3是包含url输入框和错误渲染地址提示

/* 打开主窗体 */
const createMainWindow = () => {
    
    
  mainWin = new BrowserWindow({
    
    
    width: 1920,
    height: 1080,

    webPreferences: {
    
    
      //@ts-ignore
      nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION,
      contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION,
      nodeIntegrationInWorker: true,
      webSecurity: false,
      preload: path.join(__dirname, "preload.ts"),
    },
    title: 测试",
    show: false,
  });
  mainWin.loadURL(winURL);
  mainWin.webContents.openDevTools(); //调试工具

  mainWin.once("ready-to-show", () => {
    
    
    mainWin.show();
    mainWin.maximize();
  });
};

// 创建隐藏验证渲染窗口进程
const createcheckWindow = () => {
    
    
  checkWin = new BrowserWindow({
    
    
    webPreferences: {
    
    
      //@ts-ignore
      nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION,
      contextIsolation: !process.env.ELECTRON_NODE_INTEGRATION,
      nodeIntegrationInWorker: true,
      webSecurity: false,
      preload: path.join(__dirname, "preload.ts"),
    },
    show: false, // 设置为 false,不显示窗口
  });
  checkWin.loadURL(winURL);
};

app.whenReady().then(() => {
    
    
  mainWin = null;
  checkWin = null;
  checkConfigFile().then((res) => {
    
    
    createMainWindow();
    mainWin.loadURL("http://localhost:5173/#/404?status=0");
  });
});

// 检查配置文件
function checkConfigFile() {
    
    
  // 要检查的文件路径
  const filePath = path.join(app.getPath("documents"), "mk_config.txt");

  return new Promise((resolve) => {
    
    
    // 检查文件是否存在
    fs.access(filePath, fs.constants.F_OK, (err) => {
    
    
      if (!err) {
    
    
        // 文件已经存在
        console.log("File already exists.");
        //读取文件内容
        fs.readFile(filePath, "utf8", (err, data) => {
    
    
          if (err) {
    
    
            console.error("Error reading file:", err);
            resolve(false);
          } else {
    
    
            console.log("File content:", data);
            //验证配置文件里的路径
            if (data) {
    
    
              winURL = data;
              // 打开检验进程,去checkWinOpenSuccess看是否正常执行
              createcheckWindow();
              // 10s之后如果检验没反应,返回默认页
              timer = setTimeout(() => {
    
    
                mainWin.loadURL("http://localhost:5173/#/404?status=3");
                checkWin = null;
              }, 10000);
            }
            resolve(true);
          }
        });
      } else {
    
    
        resolve(false);
      }
    });
  });
}

// 写入配置文件
function createAndWriteConfig(data) {
    
    
  const filePath = path.join(app.getPath("documents"), "mk_config.txt");
  fs.writeFile(filePath, data, (err) => {
    
    
    if (err) {
    
    
      console.error("Error writing file:", err);
    } else {
    
    
      console.log("File created and content written.");
    }
  });
}

//校验权限,html是否在electron内打开和初始化winURL 
const envCheck = (event, res) => {
    
    
  console.log("envCheck", res);
  if (res && res.url) {
    
    
    winURL = res.url;
    mainWin.loadURL("http://localhost:5173/#/404?status=0");
    // 打开检验进程,去checkWinOpenSuccess看是否正常执行
    createcheckWindow();
    // 10s之后如果检验没反应,返回默认页
    timer = setTimeout(() => {
    
    
      mainWin.loadURL("http://localhost:5173/#/404?status=3");
      checkWin = null;
    }, 10000);
  }
  event.reply("envCheckCallback", {
    
    
    result: true,
  });
};

ipcMain.on("envCheck", envCheck);

// 校验渲染进程路径是否可以正常打开
const checkWinOpenSuccess = (event) => {
    
    
  console.log("checkWinOpenSuccess", winURL);
  if (checkWin) {
    
    
    // 检验成功,写入地址
    createAndWriteConfig(winURL);
    mainWin.loadURL(winURL);
  }
  clearTimeout(timer);
  checkWin = null;
};

ipcMain.on("checkWinOpenSuccess", checkWinOpenSuccess);
  1. 与electron同级下默认页面入口文件main.ts

(1)envCheck 这里用于验证electron环境
(2)checkWinOpenSuccess 用于给electron部分发送信息,验证electron部分输入的url是正确的

router.beforeEach((to, from, next) => {
    
    
  //校验是否在electron的环境中
  try {
    
    
    //@ts-ignore
    window.api.send("envCheck", {
    
     test: 11 });
    //@ts-ignore
    window.api.receive("envCheckCallback", (res: boolean) => {
    
    
      if (!res) return;
      if (to.path == "/") {
    
    
        next("/404?status=2");
      } else {
    
    
        // 当前hash路径status为0或3时(0:输入url,3:提示无效路径),需要刷新页面
        const isLoading =
          from.fullPath == "/404?status=2" &&
          to?.query &&
          to?.query?.status == "0";
        const isInvalid =
          from.fullPath == "/404?status=0" &&
          to?.query &&
          to?.query?.status == "3";
        if (isLoading || isInvalid) {
    
    
          window.location.reload();
        }
        next();
      }
    });
  } catch (error) {
    
    
    if (to.path == "/") {
    
    
      next("/404?status=1");
    } else {
    
    
      next();
    }
  }
});

winURL下H5

  1. 入口文件main.ts

(1)envCheck 这里用于验证electron环境
(2)checkWinOpenSuccess 用于给electron部分发送信息,验证electron部分输入的url是正确的

router.beforeEach((to, from, next) => {
    
    
  try {
    
    
    //@ts-ignore
    window.api.send("envCheck");//发给electron,envCheckCallback接收成功表示在electron的环境中
    //@ts-ignore
    window.api.send("checkWinOpenSuccess");

    //@ts-ignore
    window.api.receive("envCheckCallback", (res: boolean) => {
    
    
      if (!res) return;
        next();
    });
  } catch (error) {
    
    
    if (to.path == "/") {
    
    
        next("/warn");
    } else {
    
    
      next();
    }
  }
});

注意:
(1)envCheck同方法不同作用, 在404页面的savaUrl方法中用于初始化winURL,在两个main.ts中是验证是否在electron环境
(2)在electron切换地址只能通过hash模式,history模式找不到路径,同时在全局路由守卫需要通过判断to,from控制hash刷新