首页
/ tts-vue开发者指南:Electron+Vue项目的最佳实践

tts-vue开发者指南:Electron+Vue项目的最佳实践

2026-02-05 05:22:09作者:卓艾滢Kingsley

1. 项目架构概览

tts-vue是一个基于Electron + Vue 3 + TypeScript构建的微软语音合成工具,采用现代化前端技术栈实现跨平台桌面应用开发。项目架构遵循清晰的分层设计,确保代码可维护性和扩展性。

1.1 技术栈选型

技术/框架 版本 作用
Electron 19.1.9 跨平台桌面应用框架
Vue 3.2.37 前端UI框架
TypeScript 4.7.4 类型安全支持
Vite 2.9.13 构建工具
ElementPlus 2.2.9 UI组件库
Pinia 2.0.17 状态管理
fluent-ffmpeg 2.1.2 音频处理

1.2 项目目录结构

tts-vue/
├── electron/           # Electron主进程代码
│   ├── main/           # 主进程入口
│   ├── preload/        # 预加载脚本
│   └── utils/          # 主进程工具函数
├── src/                # Vue渲染进程代码
│   ├── assets/         # 静态资源
│   ├── components/     #  Vue组件
│   ├── global/         # 全局配置
│   ├── store/          # 状态管理
│   └── types/          # 类型定义
├── package.json        # 项目依赖
└── vite.config.ts      # Vite配置

1.3 核心模块交互流程

sequenceDiagram
    participant 渲染进程 as Renderer (Vue)
    participant 预加载脚本 as Preload
    participant 主进程 as Main (Electron)
    participant 外部API as External APIs
    
    渲染进程->>预加载脚本: IPC调用 (语音合成请求)
    预加载脚本->>主进程: 转发请求
    主进程->>外部API: 调用语音合成API
    外部API-->>主进程: 返回音频数据
    主进程-->>预加载脚本: 返回结果
    预加载脚本-->>渲染进程: 返回结果
    渲染进程->>渲染进程: 播放/保存音频

2. 开发环境搭建

2.1 环境要求

  • Node.js: >=14.17.0
  • npm/yarn: 最新稳定版
  • Git: 版本控制工具

2.2 项目初始化

# 克隆仓库
git clone https://gitcode.com/gh_mirrors/tt/tts-vue.git
cd tts-vue

# 安装依赖
npm install

# 启动开发服务器
npm run dev

2.3 开发配置解析

vite.config.ts中的核心配置:

export default defineConfig({
  resolve: {
    alias: {
      "@": join(__dirname, "./src"),
      "@components": join(__dirname, "./src/components"),
    }
  },
  plugins: [
    vue(),
    electron({
      main: {
        entry: "electron/main/index.ts",
        vite: withDebug({
          build: {
            outDir: "dist/electron/main",
          },
        }),
      },
      preload: {
        input: {
          index: join(__dirname, "electron/preload/index.ts"),
        }
      },
      renderer: {}
    }),
  ],
  server: {
    host: pkg.env.VITE_DEV_SERVER_HOST,
    port: pkg.env.VITE_DEV_SERVER_PORT,
  },
});

3. 核心功能实现详解

3.1 主进程与渲染进程通信

Electron主进程与渲染进程通过IPC (Inter-Process Communication) 进行通信。项目中封装了多种API调用方式:

主进程API注册 (electron/main/index.ts):

// 语音合成API
ipcMain.handle("speech", async (event, ssml) => {
  const res = api.speechApi(ssml);
  return res;
});

// Edge API调用
ipcMain.handle("edgeApi", async (event, ssml) => {
  const res = edgeApi(ssml)
  return res;
});

// Azure API调用
ipcMain.handle("azureApi", async (event, ssml, key, region) => {
  const res = azureApi(ssml, key, region)
  return res;
});

渲染进程调用 (src/store/play.ts):

async function getTTSData(
  inps: any,
  voice: string,
  express: string,
  role: string,
  rate = 0,
  pitch = 0,
  api: number,
  key: string,
  region: string,
  retryCount: number,
  retryInterval = 1,
) {
  // 构建SSML (Speech Synthesis Markup Language)
  let SSML = "";
  if (inps.activeIndex == "1" && (api == 1 || api == 3)) {
    SSML = `
    <speak xmlns="http://www.w3.org/2001/10/synthesis" xmlns:mstts="http://www.w3.org/2001/mstts" version="1.0" xml:lang="en-US">
        <voice name="${voice}">
            <mstts:express-as ${express != "" ? 'style="' + express + '"' : ""} ${role != "" ? 'role="' + role + '"' : ""}>
                <prosody rate="${rate}%" pitch="${pitch}%">
                ${inps.inputValue}
                </prosody>
            </mstts:express-as>
        </voice>
    </speak>
    `;
  }
  
  // 根据选择的API调用不同的IPC通道
  if (api == 1) {
    return await retrySpeechInvocation(SSML, retryCount, retryInterval * 1000);
  } else if (api == 2) {
    return await ipcRenderer.invoke("edgeApi", SSML);
  } else {
    return await ipcRenderer.invoke("azureApi", SSML, key, region);
  }
}

3.2 状态管理设计

项目使用Pinia进行状态管理,采用模块化设计:

store结构:

// src/store/store.ts
import { defineStore } from "pinia";
import { getTTSData, getDataGPT } from "./play";

export const useTtsStore = defineStore("ttsStore", {
  state: () => ({
    inputs: {
      inputValue: "你好啊\n今天天气怎么样?",
      ssmlValue: "你好啊\n今天天气怎么样?",
    },
    formConfig: store.get("FormConfig.默认"),
    page: {
      asideIndex: "1",
      tabIndex: "1",
    },
    // 其他状态...
  }),
  actions: {
    async start() {
      // 开始语音合成流程
    },
    async audition(val: string) {
      // 试听功能
    },
    // 其他方法...
  }
});

状态管理最佳实践:

  1. 按功能模块划分store
  2. 复杂逻辑封装为actions
  3. 使用localStorage/electron-store持久化配置
  4. 避免在组件中直接修改状态,通过actions操作

3.3 语音合成核心逻辑

项目实现了多API支持的语音合成功能,包括:

  1. 微软Azure语音服务
  2. Edge语音服务
  3. 自定义API服务

核心实现:

// electron/utils/api.ts
const speechApi = (ssml: string) => {
  var data = JSON.stringify({
    ssml,
    ttsAudioFormat: "audio-24khz-160kbitrate-mono-mp3",
    offsetInPlainText: 0,
    properties: {
      SpeakTriggerSource: "AccTuningPagePlayButton",
    },
  });

  var config = {
    method: "post",
    url: "https://southeastasia.api.speech.microsoft.com/accfreetrial/texttospeech/acc/v3.0-beta1/vcg/speak",
    responseType: "arraybuffer",
    headers: {
      "authority": "southeastasia.api.speech.microsoft.com",
      "accept": "*/*",
      "content-type": "application/json",
      // 其他必要头信息...
    },
    data: data,
  };

  return new Promise((resolve, reject) => {
    axios(config)
      .then(response => resolve(response.data))
      .catch(error => reject(error));
  });
};

错误处理与重试机制:

async function retrySpeechInvocation(SSML: string, retryCount: number, delay: number) {
  let retry = 0;
  while (retry < retryCount) {
    try {
      console.log("语音调用尝试:", retry + 1);
      const result = await ipcRenderer.invoke("speech", SSML);
      return result; // 执行成功,返回结果
    } catch (error) {
      console.error("Speech invocation failed:", error);
      await sleep(delay); // 暂停一段时间后再重试
    }
    retry++;
  }
  throw new Error(`${retryCount} 次重试后仍转换失败。`); // 重试次数用尽,抛出异常
}

3.4 组件设计与复用

项目采用组件化设计,主要组件结构如下:

核心组件:

  1. Main.vue: 主界面组件,包含文本输入和文件管理
  2. MainOptions.vue: 语音合成选项配置
  3. ConfigPage.vue: 应用设置页面
  4. Aside.vue: 侧边导航栏

组件复用示例 - MainOptions.vue:

<template>
  <div class="options">
    <el-form :model="formConfig" label-width="120px" label-position="top">
      <el-form-item :label="t('options.api')">
        <el-select
          v-model="formConfig.api"
          :placeholder="t('options.selectApi')"
          @change="apiChange"
          >
          <el-option
            v-for="item in oc.apiSelect"
            :key="item.value"
            :label="item.label"
            :value="item.value"
            />
        </el-select>
      </el-form-item>
      <!-- 其他配置项 -->
    </el-form>
  </div>
</template>

组件设计最佳实践:

  1. 提取重复逻辑为组合式API或mixin
  2. 使用slot实现组件内容分发
  3. Props定义明确的类型和默认值
  4. 组件状态通过事件向外传递

4. 高级功能与优化

4.1 大文本分片合成

对于超过API限制的大文本,实现了自动分片合成功能:

// src/store/store.ts
if (datastr.length > 400 && this.formConfig.api == 1) {
  const delimiters = ",。?,.? ".split("");
  const maxSize = 300;
  
  // 文本分片逻辑
  const textHandler = datastr.split("").reduce(
    (obj: any, char: any, index: any, arr: any) => {
      obj.buffer.push(char);
      if (delimiters.indexOf(char) >= 0) obj.end = index;
      if (obj.buffer.length === maxSize) {
        obj.res.push(
          obj.buffer.splice(0, obj.end + 1 - obj.offset).join("")
        );
        obj.offset += obj.res[obj.res.length - 1].length;
      }
      return obj;
    },
    { buffer: [], end: 0, offset: 0, res: [] }
  );
  
  // 分片合成
  for (let index = 0; index < tasks.length; index++) {
    try {
      const element = tasks[index];
      inps.inputValue = element;
      const buffers: any = await getTTSData(...);
      buffer = Buffer.concat([buffer, buffers]);
    } catch (error) {
      // 错误处理
    }
  }
  
  // 合并结果
  fs.writeFileSync(filePath, buffer);
}

4.2 音频格式转换

使用fluent-ffmpeg实现多种音频格式转换:

// src/store/store.ts
else {
  // 将buffer转换为可读流
  const inputStream = new Readable();
  inputStream.push(this.currMp3Buffer);
  inputStream.push(null); // 结束流
  
  // 使用fluent-ffmpeg进行转码
  ffmpeg(inputStream)
    .output(filePath)
    .audioCodec('pcm_s16le')
    .audioChannels(2)
    .audioFrequency(44100)
    .on('end', () => {
      console.log('转码完成!音频已保存为文件:', filePath);
    })
    .on('error', (err: any) => {
      console.error('转码出错:', err);
    })
    .run();
}

4.3 国际化实现

项目使用vue-i18n实现多语言支持:

// src/assets/i18n/i18n.ts
import { createI18n } from 'vue-i18n'
import zhCN from './zh-CN'
import enUS from './en-US'

const i18n = createI18n({
  locale: 'zh-CN', // 默认语言
  fallbackLocale: 'zh-CN',
  messages: {
    'zh-CN': zhCN,
    'en-US': enUS
  }
})

export default i18n

在组件中使用:

<template>
  <el-form-item :label="t('options.api')">
    <el-select
      v-model="formConfig.api"
      :placeholder="t('options.selectApi')"
      @change="apiChange"
      >
      <!-- 选项 -->
    </el-select>
  </el-form-item>
</template>

<script setup lang="ts">
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
</script>

5. 打包与分发

5.1 构建命令

# 构建生产版本
npm run build

5.2 打包配置

electron-builder配置:

// package.json
{
  "main": "dist/electron/main/index.js",
  "scripts": {
    "build": "vue-tsc --noEmit && vite build && electron-builder"
  },
  "build": {
    "appId": "com.example.tts-vue",
    "productName": "tts-vue",
    "directories": {
      "output": "release/${version}"
    },
    "files": [
      "dist/**/*",
      "package.json"
    ],
    "win": {
      "target": [
        {
          "target": "nsis",
          "arch": [
            "x64"
          ]
        }
      ]
    }
  }
}

5.3 跨平台构建注意事项

  1. 文件路径处理: 使用path模块处理路径,避免硬编码
  2. 资源访问: 打包后资源路径变化,使用正确的路径获取方式
  3. 依赖管理: 原生模块可能需要额外配置
  4. 权限处理: 不同平台的文件系统权限差异

6. 最佳实践与常见问题

6.1 性能优化建议

  1. 状态管理优化:

    • 避免存储过大数据在状态中
    • 合理使用计算属性缓存结果
  2. 渲染优化:

    • 使用v-show替代v-if处理频繁切换的元素
    • 长列表使用虚拟滚动
  3. 网络优化:

    • 实现请求缓存
    • 批量处理API请求

6.2 常见问题解决

Q: 开发环境中Electron主进程修改不生效?
A: 主进程代码修改需要重启开发服务器,或配置主进程热重载:

// vite.config.ts
export default defineConfig({
  plugins: [
    electron({
      main: {
        entry: "electron/main/index.ts",
        vite: withDebug({
          build: {
            outDir: "dist/electron/main",
          },
        }),
      },
    }),
  ],
});

Q: 打包后应用无法加载资源?
A: 确保资源路径使用正确的获取方式:

// 获取应用内资源
const getAssetPath = (...paths: string[]): string => {
  if (process.env.NODE_ENV === 'production') {
    return path.join(process.resourcesPath, ...paths);
  }
  return path.join(__dirname, '../../public', ...paths);
};

6.3 扩展开发指南

添加新的语音服务提供商:

  1. 在electron/utils/目录下创建新的API封装文件
  2. 在主进程中注册新的IPC处理函数
  3. 在渲染进程中添加对应的API选择项
  4. 更新状态管理逻辑支持新API的参数

7. 总结与展望

tts-vue项目展示了如何利用Electron和Vue构建功能完善的跨平台桌面应用。通过合理的架构设计和模块化开发,实现了语音合成、文件处理、格式转换等核心功能。

7.1 项目亮点

  1. 多API支持: 灵活切换不同的语音合成服务
  2. 用户体验优化: 直观的界面设计和流畅的交互流程
  3. 性能优化: 大文本分片处理,请求重试机制
  4. 可扩展性: 模块化设计便于功能扩展

7.2 未来改进方向

  1. 离线语音合成: 集成本地语音合成引擎
  2. 批量处理优化: 支持更多格式和批量任务管理
  3. 插件系统: 允许第三方扩展功能
  4. 云同步: 配置和历史记录云同步

通过本指南,开发者可以快速掌握tts-vue项目的架构设计和实现细节,为二次开发和功能扩展提供基础。项目遵循现代前端开发最佳实践,对于Electron+Vue技术栈的学习也具有参考价值。

登录后查看全文
热门项目推荐
相关项目推荐