electron+vue3全家桶+vite项目搭建【14】electron多窗口,多语言切换不同步更新问题

news2024/11/15 6:51:29

文章目录

    • 引入
    • 问题演示
    • 补充逻辑
      • 注意
      • 封装缓存工具类
      • 补充状态管理
      • 调整多语言初始化
      • 调整多语言切换组件
    • 解决方案
      • 思路整理
      • 渲染进程监听语言切换
      • 主进程创建多语言切换处理
      • 语言切换组件通知主进程语言切换
    • 最终实现效果演示

引入

我们之前在这篇文章中集成了 多语言切换,但随着我们项目越来越复杂,单页面已经无法满足我们的需求,我们需要多个窗口去进行页面展示,此时会暴露很多问题,例如多窗口时,某个窗口切换了语言,其他窗口并不会同步切换

demo项目地址

问题演示

我们在src\components\demo\Index.vue页面中也显示一个多语言文本:

<template>
	<h1>{{ $t(langMap.app_title) }}</h1>
</template>
<script setup lang="ts">
	import langMap from "@/locales/langMap";
</script>

问题如下:

请添加图片描述

补充逻辑

注意

这里主要是对之前多语言文章的补充,之前写的是个极简的整合,没有考虑很多,如只需要解决多窗口同步问题可直接跳过!!

封装缓存工具类

首先为了记住我们当前所选语言,避免每次重启重新选择,我们将语言持久化在本地,这里参考这篇博客,封装一个缓存工具类:

  • src\utils\cacheUtils.ts
/* localstorage封装,缓存工具类  参考:https://blog.csdn.net/w544924116/article/details/120906411

/** key前缀 */
const keyPrefix = 'app_cahce$_';

/**
 * @param value 内容
 * @param addTime 存入时间
 * @param expires 有效时间
 */
interface valObjParams {
  value: any;
  addTime: number;
  expires: number;
}

interface StorageInterface {
  /**
   * 设置localStorage
   * @param value 内容
   * @param expires 有效时间 单位 s
   */
  set: (key: string, value: any, expires?: number) => void;
  /** 获取localStorage,会自动转json */
  get: (key: string) => any;
  /** 是否含有key */
  has: (key: string) => boolean;
  /** 移除 */
  remove: (key: string) => void;
  /** 移除全部缓存 */
  clear: () => void;
  /** 移除自己前缀的全部缓存 */
  clearSelf: () => void;
}

const storage: StorageInterface = {
  set: () => {},
  get: () => '',
  has: () => false,
  remove: () => {},
  clear: () => {},
  clearSelf: () => {}
};

/**
 * 是否过期
 */
const isFresh = (valObj: valObjParams) => {
  const now = new Date().getTime();
  return valObj.addTime + valObj.expires >= now;
};

/* 给key值添加前缀 */
const addPrefix = (key: string) => {
  return `${keyPrefix}${key}`;
};

/* 加方法 */
const extend = (s: Storage) => {
  return {
    set(key: string, value: any, expires?: number) {
      const skey = addPrefix(key);
      if (expires) {
        expires *= 1000; // 将过期时间从微秒转为秒
        s.setItem(
          skey,
          JSON.stringify({
            value,
            addTime: new Date().getTime(),
            expires
          })
        );
      } else {
        const val = JSON.stringify(value);
        s.setItem(skey, val);
      }
      if (value === undefined || value === null) {
        s.removeItem(skey);
      }
    },
    get(key: string) {
      const skey = addPrefix(key);
      const item = JSON.parse(s.getItem(skey) as any);
      // 如果有addTime的值,说明设置了失效时间
      if (item && item.addTime) {
        if (isFresh(item)) {
          return item.value;
        }
        /* 缓存过期,清除缓存,返回null */
        s.removeItem(skey);
        return null;
      }
      return item;
    },
    has(key: string) {
      const skey = addPrefix(key);
      return !!s.getItem(skey);
    },
    remove: (key: string) => {
      const skey = addPrefix(key);
      s.removeItem(skey);
    },
    clear: () => {
      s.clear();
    },
    clearSelf: () => {
      const arr = Array.from({ length: s.length }, (_, i) => s.key(i)).filter(
        str => str?.startsWith(keyPrefix)
      );
      arr.forEach(str => s.removeItem(str as string));
    }
  };
};

Object.assign(storage, extend(window.localStorage));

export default storage;

补充状态管理

我们可以创建一个appStore用来保存整个应用的全局状态:

import { defineStore } from "pinia";
import cacheUtils from "@/utils/cacheUtils";

/**应用相关状态管理 */
export const useAppStore = defineStore("appStore", {
  state() {
    return {
      lang: cacheUtils.get("lang") || "zhCn", // app的语言
    };
  },
});

调整多语言初始化

  • 补充从缓存中取初始值
  • src\locales\index.ts
import { createI18n } from "vue-i18n";
import en from "./packages/en";
import zhCn from "./packages/zh-cn";
import cacheUtils from "@/utils/cacheUtils";

// 初始化i18n
const i18n = createI18n({
  legacy: false, // 解决Not available in legacy mode报错
  globalInjection: true, // 全局模式,可以直接使用 $t
  locale: cacheUtils.get("lang") || "zhCn", // 从本地缓存中取语言,如果没有 默认为中文
  fallbackLocale: "en", // set fallback locale
  messages: {
    en,
    zhCn,
  },
});

export default i18n;

调整多语言切换组件

  • 我们在语言切换的时候补充状态更新、缓存设置
import cacheUtils from "@/utils/cacheUtils";
import { useAppStore } from "@/store/modules/appStore";

const appStore = useAppStore();

// 切换语言
function handleCommand(lang: string) {
  i18n.locale.value = lang;
  // 设置缓存的值
  cacheUtils.set("lang", lang);
  // 更新全局状态
  appStore.lang = lang;
}

解决方案

思路整理

我们可以在多语言初始化的时候,让渲染进程监听多语言改变消息,然后主进程创建一个多语言改变handle,然后在语言切换组件中当语言切换时告知主进程语言切换了,并传参当前的语言,接着主进程的handle遍历所有窗口,除通知主进程的窗口外的其他窗口都触发 多语言改变通知,然后窗口自行更新即可。

渲染进程监听语言切换

1.我们在多语言初始化时监听多语言切换通知:

  • 调整src\locales\index.ts代码:
import { useAppStore } from "@/store/modules/appStore";
import { ipcRenderer } from "electron";

// ......

// 注意,因为 pinia还没初始化就进行取值会有问题,所以这里我们单独暴露一个方法,在 src/main.ts中的 app.mount("#app").$nextTick 中调用
// 初始化语言监听
export function initLangListener() {
  const appStore = useAppStore();
  // 监听语言切换时,同步本窗口更新
  ipcRenderer.on("lang:change", (event, lang: string) => {
    i18n.global.locale.value = lang;
    appStore.lang = lang;
  });
}

2.在src/main.ts中执行初始化监听:

import { initLangListener } from "@/locales";
// .....

app.mount("#app").$nextTick(() => {
  postMessage({ payload: "removeLoading" }, "*");
  // 初始化多语言切换监听
  initLangListener();
});

主进程创建多语言切换处理

主进程中创建多语言处理监听,我们在electron\main\index.ts中补充代码:

/**语言修改同步 */
ipcMain.handle("lang:change", (event, lang) => {
  // 通知所有窗口同步更改语言
  for (const currentWin of BrowserWindow.getAllWindows()) {
    const webContentsId = currentWin.webContents.id;
    // 这里排除掉发送通知的窗口
    if (webContentsId !== event.sender.id) {
      currentWin.webContents.send("lang:change", lang);
    }
  }
});

语言切换组件通知主进程语言切换

当语言切换时,我们需要通知主进程告诉其他窗口同步修改,所以我们调整 多语言切换组件:

import { ipcRenderer } from 'electron';

// 多语言切换时
const handleCommand = (lang: string) => {
  // ...
  // 主进程通知其他窗口同步修改语言
  ipcRenderer.invoke('lang:change', lang);
};

请添加图片描述

最终实现效果演示

请添加图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/459215.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

各种加法器介绍——真值表、表达式、电路图、verilog代码实现

文章目录 前言一、半加器二、全加器三、串行/行波进位加法器&#xff08;Ripple-Carry Adder/RCA&#xff09;四、超前进位加法器&#xff08;Lookahead Carry Adder/LCA&#xff09;五、进位保存加法器&#xff08;Carry Save Adder/CSA&#xff09; 前言 2023.4.25 一、半加…

Stable Diffusion人工智能图像合成

AI 图像生成大有来头。新发布的开源图像合成模型称为Stable Diffusion&#xff0c;它允许任何拥有 PC 和像样的 GPU 的人想象出他们能想象到的几乎任何视觉现实。它几乎可以模仿任何视觉风格&#xff0c;如果你给它输入一个描述性的短语&#xff0c;结果就会像魔术一样出现在你…

HTML5 <span> 标签

实例 HTML5 <span>标签内的内容独立于文档&#xff0c;当对该标签应用样式时&#xff0c;包含在其中的内容会呈现出不同的视觉效果。请参考下述示例&#xff1a; 使用 <span> 元素对文本中的一部分进行着色&#xff1a; <p>我的母亲有 <span style&quo…

Redis缓存预热、雪崩、击穿和穿透

文章目录 1、Redis缓存预热2、Redis缓存雪崩3、Redis缓存击穿4、Redis缓存穿透 1、Redis缓存预热 Redis需要缓存预热是因为Redis是基于内存的缓存系统&#xff0c;当Redis启动时&#xff0c;它的内存是空的&#xff0c;需要在实际使用前将需要缓存的数据提前加载到内存中。这个…

DevExpress:报表中XRPictureBox控件绑定数据库(SQlite)中的图片数据

一.需求描述 1.使用XRPictureBox控件显示图片&#xff1b; 2.因为报表需求&#xff0c;显示的图片在指定条件下需要改变&#xff0c;比如&#xff1a;指定生成小明的报表时&#xff0c;加载小明的头像&#xff1b;指定生成小红的报表时&#xff0c;加载小红的头像&#xff1b;…

midjourney关键词总结

Midjourney是一个自主研发的实验室&#xff0c;用户可以通过Discord与Midjourney bot交互&#xff0c;并提交“Prompt”来快速获取所需的图像。 Midjourney具有易上手、难精通的特点&#xff0c;想要绘制出自己满意的图片&#xff0c;需要掌握各种命令和参数&#xff0c;并花费…

web 应用常用功能 -文件上传下载以及原理分析图和注意事项和细节

目录 web 应用常用功能 -文件上传下载 基本介绍 文件上传 文件上传应用实例 upload.jsp 实现步骤 文件上传注意事项和细节 文件下载 文件下载的原理分析图 文件下载应用实例 ● 需求&#xff1a;演示文件下载&#xff0c;如图 download.jsp FileDownloadServlet …

牛客网Verilog刷题——VL28

牛客网Verilog刷题——VL28 题目答案 题目 请编写一个序列检测模块&#xff0c;输入信号端口为data&#xff0c;表示数据有效的指示信号端口为data_valid。当data_valid信号为高时&#xff0c;表示此刻的输入信号data有效&#xff0c;参与序列检测&#xff1b;当data_valid为低…

【手把手做ROS2机器人系统开发五】使用C++实现编写简单的服务器和客户端

使用C实现编写简单的服务器和客户端 目录 使用C实现编写简单的服务器和客户端 一、程序编写 1、创建软件包 2、编译软件包 3、软件配置 4、服务器程序编写 5、客户端程序编写 6、软件包设置 7、设置编译选项 二、程序测试 1、编译程序 2、开启节点测试运行 3、执行…

verilog语言中的门级描述、行为描述及测试验证

描述D型主从触发器模块的门级结构建模&#xff1a; module flop(data, clock, clear, q, qb);input data, clock, clear; output q, qb;// 批量定义门电路 nand #10 nd1(a, data, clock, clear)&#xff0c;nd2(b, ndata, clock)&#xff0c;nd4(d, c, b, clear)&#xf…

算法刷题|1049.最后一个块石头的重量||、494.目标和、474.一和零

最后一个块石头的重量|| 题目&#xff1a;有一堆石头&#xff0c;用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。 每一回合&#xff0c;从中选出任意两块石头&#xff0c;然后将它们一起粉碎。假设石头的重量分别为 x 和 y&#xff0c;且 x < y。那么粉…

HBase的表设计笔记

HBase的RowKey设计 HBase是三维有序存储的&#xff0c;通过rowkey&#xff08;行键&#xff09;&#xff0c;column key&#xff08;column family和qualifier&#xff09;和TimeStamp&#xff08;时间戳&#xff09;这个三个维度可以对HBase中的数据进行快速定位。 HBase中row…

中国能源网络安全大会举办,腾讯安全曹文炎分享助力能源企业安全建设的实践经验

2023年4月20日-21日&#xff0c;由中国能源研究会主办的“2023年中国能源网络安全大会”&#xff08;以下简称“大会”&#xff09;在南京召开。大会以“新形势新安全”为主题&#xff0c;围绕提升我国能源行业网络安全水平&#xff0c;增强和维护我国能源网络安全能力&#xf…

Pytorch的CNN,RNNLSTM

CNN 拿二维卷积举例&#xff0c;我们先来看参数 卷积的基本原理&#xff0c;默认你已经知道了&#xff0c;然后我们来解释pytorch的各个参数&#xff0c;以及其背后的计算过程。 首先我们先来看卷积过后图片的形状的计算&#xff1a; 参数&#xff1a; kernel_size &#xff…

【AI炼丹术】写深度学习代码的一些心得体会

写深度学习代码的一些心得体会 体会1体会2体会3总结内容来源 一般情况下&#xff0c;拿到一批数据之后&#xff0c;首先会根据任务先用领域内经典的Model作为baseline跑通&#xff0c;然后再在这个框架内加入自己设计的Model&#xff0c;微调代码以及修改一些超参数即可。总体流…

Matlab进阶绘图第18期—相关性气泡热图

相关性气泡热图是一种特殊的气泡热图。 与一般的气泡热图相比&#xff0c;其数值位于[-1 1]区间&#xff0c;其颜色用于表示正负&#xff0c;而其气泡的大小用于表示数值绝对值的大小&#xff0c;可以十分直观地对两个变量的相关性进行分析。 由于Matlab中未收录相关性气泡热…

In-Context Learning中的示例选择及效果

一. ICL的背景 大型语言模型&#xff08;LLM&#xff09;如GPT-3是在大规模的互联网文本数据上训练&#xff0c;以给定的前缀来预测生成下一个token&#xff08;Next token prediction&#xff09;。这样简单的训练目标&#xff0c;大规模数据集以及高参数量模型相结合&#x…

国内可直接使用的OpenAI DALL*E 图片AI体验站,可通过自然语言生成图片

体验站最终演示效果 国内可直接使用的图片AI体验站&#xff1a;https://zizhu888.cn/text2img/index.html ChatGPT3.5 Turbo国内体验站: https://zizhu888.cn/chatgpt/index.html OpenAI DALL*E可以通过自然语言生成图片&#xff0c;内容创作者的福音&#xff0c;大大降低了创…

基于飞桨 PaddleVideo 的骨骼行为识别模型 CTR-GCN

main.pysame_seedsparse_argsmain ensemble.pyconfigs 文件夹Joint&#xff08;J&#xff09;的配置文件ctrgcn_fsd_J_fold0.yamlctrgcn_fsd_J_fold1.yaml Joint Angle&#xff08;JA&#xff09;的配置文件ctrgcn_fsd_JA_fold0.yaml paddlevideo 文件夹utils 文件夹__init__.p…

【Python 协程详解】

0.前言 前面讲了线程和进程&#xff0c;其实python还有一个特殊的线程就是协程。 协程不是计算机提供的&#xff0c;计算机只提供&#xff1a;进程、线程。协程是人工创造的一种用户态切换的微进程&#xff0c;使用一个线程去来回切换多个进程。 为什么需要协程&#xff1f; …