当前位置: 首页 > 知识库问答 >
问题:

前端 - 参考electron-trpc的示例代码:(做事件订阅/事件触发)的时候:如何使用(订阅和触发)呢?

牛嘉谊
2024-12-03

您好,在参考electron-trpc的示例代码:(做事件订阅/事件触发)的时候:
https://github.com/jsonnull/electron-trpc/blob/main/examples/basic-react/electron/api.ts

代码如下:

import z from 'zod';
import { initTRPC } from '@trpc/server';
import { observable } from '@trpc/server/observable';
import { EventEmitter } from 'events';

const ee = new EventEmitter();

const t = initTRPC.create({ isServer: true });

export const router = t.router({
  greeting: t.procedure.input(z.object({ name: z.string() })).query((req) => {
    const { input } = req;

    ee.emit('greeting', `Greeted ${input.name}`);
    return {
      text: `Hello ${input.name}` as const,
    };
  }),
  subscription: t.procedure.subscription(() => {
    return observable((emit) => {
      function onGreet(text: string) {
        emit.next({ text });
      }

      ee.on('greeting', onGreet);

      return () => {
        ee.off('greeting', onGreet);
      };
    });
  }),
});

export type AppRouter = typeof router;

请问下,应该如何使用呢?

1、在主进程如何使用(订阅和触发)?
2、在渲染进程如何使用(订阅和触发)?

共有2个答案

蒋斯伯
2024-12-03

1.设置项目结构

创建一个新的项目文件夹,并在其中创建以下文件和文件夹结构:

my-electron-trpc-app/
├── main.ts
├── preload.ts
├── renderer.ts
├── package.json
├── tsconfig.json
└── webpack.config.js

2.安装依赖

在项目根目录下运行以下命令来安装必要的依赖:

npm init -y
npm install electron @trpc/server @trpc/client electron-trpc zod typescript ts-loader webpack webpack-cli

3. 配置 TypeScript

在项目根目录下创建 tsconfig.json 文件,并添加以下内容:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "outDir": "./dist"
  },
  "include": ["./**/*.ts"]
}

4. 配置 Webpack

在项目根目录下创建 webpack.config.js 文件,并添加以下内容:

const path = require('path');

module.exports = {
  entry: {
    main: './main.ts',
    preload: './preload.ts',
    renderer: './renderer.ts',
  },
  target: 'electron-main',
  module: {
    rules: [
      {
        test: /\.ts$/,
        include: /src/,
        use: [{ loader: 'ts-loader' }],
      },
    ],
  },
  resolve: {
    extensions: ['.ts', '.js'],
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].js',
  },
};

5. 添加 Electron 脚本

package.json

"scripts": {
  "start": "webpack && electron ."
}

6. 编写代码

main.ts(主进程代码)

 import { app, BrowserWindow } from 'electron';
import { createIPCHandler } from 'electron-trpc/main';
import { initTRPC } from '@trpc/server';
import { observable } from '@trpc/server/observable';
import { EventEmitter } from 'events';
import { z } from 'zod';

const ee = new EventEmitter();
const t = initTRPC.create({ isServer: true });

export const router = t.router({
  greeting: t.procedure.input(z.object({ name: z.string() })).query((req) => {
    const { input } = req;
    ee.emit('greeting', `Greeted ${input.name}`);
    return { text: `Hello ${input.name}` };
  }),
  subscription: t.procedure.subscription(() => {
    return observable((emit) => {
      function onGreet(text: string) {
        emit.next({ text });
      }
      ee.on('greeting', onGreet);
      return () => {
        ee.off('greeting', onGreet);
      };
    });
  }),
});

app.on('ready', () => {
  const win = new BrowserWindow({
    webPreferences: {
      preload: 'path/to/preload.js',
    },
  });
  createIPCHandler({ router, windows: [win] });

  // 示例:在主进程中触发事件
  setTimeout(() => {
    ee.emit('greeting', 'Hello from main process');
  }, 5000); // 5秒后触发事件
});

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

preload.ts (预加载脚本)

import { exposeElectronTRPC } from 'electron-trpc/main';

process.once('loaded', async () => {
  exposeElectronTRPC();
});

renderer.ts (渲染进程代码)

 import { createTRPCProxyClient } from '@trpc/client';
import { ipcLink } from 'electron-trpc/renderer';

export const client = createTRPCProxyClient({
  links: [ipcLink()],
});

// 触发事件
client.greeting({ name: 'YourName' }).then(response => {
  console.log(response.text); // 输出: Hello YourName
});

// 订阅事件
client.subscription('subscription', {
  next(data) {
    console.log(data.text);
  },
  error(err) {
    console.error('Subscription error:', err);
  },
});

7. 运行项目

在项目根目录下运行以下命令来构建并启动 Electron 应用:

npm start
华易安
2024-12-03

1. 在主进程如何使用(订阅和触发)?

在主进程中,你需要通过 electron-trpc 提供的 API 来订阅事件和触发事件。以下是如何在主进程中实现这些功能的示例:

订阅事件

const { createClient } = require('@trpc/client');
const { app, BrowserWindow } = require('electron');
const path = require('path');

let mainWindow;

function createWindow() {
  mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
      contextIsolation: true,
      enableRemoteModule: false,
      nodeIntegration: false,
    },
  });

  mainWindow.loadFile('index.html');

  // Create TRPC client
  const trpcClient = createClient({
    url: 'http://localhost:3000/api', // 替换为你的 TRPC 服务器 URL
  });

  // 订阅事件
  const subscription = trpcClient.subscription.subscription({});

  subscription.subscribe({
    next: (data) => {
      console.log('Received greeting:', data.text);
    },
    error: (err) => {
      console.error('Subscription error:', err);
    },
    complete: () => {
      console.log('Subscription completed');
    },
  });
}

app.on('ready', createWindow);

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

app.on('activate', () => {
  if (BrowserWindow.getAllWindows().length === 0) {
    createWindow();
  }
});

// 触发事件
const ee = require('events').EventEmitter;
const eventEmitter = new ee();

// 在适当的地方(例如响应某个操作)
eventEmitter.emit('greeting', 'John Doe');

注意:这里的 eventEmitter.emit('greeting', 'John Doe'); 是为了模拟触发事件,但在实际场景中,你应该使用你的 TRPC 服务器中的 ee.emit('greeting', ...) 来触发事件。

2. 在渲染进程如何使用(订阅和触发)?

在渲染进程中,由于 Electron 的安全策略,直接访问主进程的 EventEmitter 实例是不可能的。你应该通过 ipcRenderer 与主进程通信,然后在主进程中处理这些请求。

在渲染进程中订阅事件

你可以通过 preload.js 文件来暴露一个接口给渲染进程,以便订阅和触发事件。但是,由于 TRPC 已经处理了 API 的路由,你通常会在渲染进程中直接使用 TRPC 客户端来订阅事件。

// 渲染进程中的 JavaScript (例如:renderer.js)
const { createClient } = require('@trpc/client/react');

const trpcClient = createClient({
  url: 'http://localhost:3000/api', // 替换为你的 TRPC 服务器 URL
});

function useGreetingSubscription() {
  const subscription = trpcClient.subscription.subscription({});

  return React.useMemo(() => {
    const [data, setData] = React.useState(null);
    const [error, setError] = React.useState(null);

    React.useEffect(() => {
      const unsubscribe = subscription.subscribe({
        next: (res) => setData(res),
        error: (err) => setError(err),
      });

      return () => unsubscribe();
    }, [subscription]);

    return { data, error };
  }, [subscription]);
}

function App() {
  const { data, error } = useGreetingSubscription();

  if (error) {
    return <div>Error: {error.message}</div>;
  }

  return (
    <div>
      {data && <div>Received greeting: {data.text}</div>}
    </div>
  );
}

export default App;

在渲染进程中触发事件

由于安全原因,渲染进程不应该直接触发主进程中的事件。相反,你应该通过 ipcRenderer 发送一个消息到主进程,然后让主进程处理这个请求并触发相应的事件。但是,在这个场景中,由于你已经在使用 TRPC,你应该通过 TRPC 的 greeting 过程来触发事件,而不是直接操作 EventEmitter

// 渲染进程中的 JavaScript (例如:通过某个按钮触发)
async function triggerGreeting() {
  try {
    const res = await trpcClient.query('greeting', { name: 'John Doe' });
    console.log('Greeting response:', res);
  } catch (err) {
    console.error('Error triggering greeting:', err);
  }
}

// 例如,在某个按钮点击事件中调用 triggerGreeting()

在这个例子中,当 triggerGreeting 函数被调用时,它会通过 TRPC 服务器触发 greeting 事件,然后订阅这个事件的客户端(无论是在主进程还是渲染进程)都会收到这个事件并处理它。

 类似资料:
  • Node.js应用程序可以使用composer-client.BusinessNetworkConnection.onAPI调用从业务网络订阅事件。事件在业务网络模型文件中定义,并由交易处理函数文件中的指定交易处理。有关发布事件的更多信息,请参阅发布事件。 在你开始之前 在应用程序可以订阅事件之前,你必须定义一些事件和发送它们的交易。还必须部署业务网络,并且必须具有可连接到该业务网络的连接配置文件

  • 我在WPF中编写代码,其中有两个视图模型。在一个视图模型中,我正在关闭一个prism弹出窗口,在关闭时,我将调用我的发布方法,如: 而我的subscribe事件如下所示: 以下是这两个类接收事件聚合器的方式: 这是我的第二堂课: 每次取消交互时,发布都会被发布,但不会被调用,因为订阅。 在具有subscribe方法的其他类之前被调用。会有问题吗?我只想在交互完成时初始化我的集合视图,而不破坏MVV

  • 基本上,我试图使用ARM部署一个事件网格订阅来收集订阅中的特定事件(主题类型= Azure订阅)。我已经有一个创建了事件网格触发功能的功能应用程序,只需要将该功能与事件网格订阅绑定为webhook。 我正在使用Azure DevOps中的发布管道来自动化整个工作流。 以下是我使用的一个示例: 这最终部署了事件网格主题,而不是事件网格订阅。 然后,有人建议我尝试以下操作: 但是这最终以这个错误而失败

  • 问题内容: 我的整个项目都使用(Bluebird)Promises,但是有一个使用EventEmitter的特定库。 我想要实现以下目标: 我在Promises链中读了EventEmitter的答案。这给了我一种执行’connect’事件的回调的方法。这是我到目前为止所到之处 现在如何进一步链接“ eventB”? 问题答案: 我假设您想为每个事件做不同的事情。即使由的动作触发,您也可以将其视为另

  • 本文向大家介绍JS代码触发事件代码实例,包括了JS代码触发事件代码实例的使用技巧和注意事项,需要的朋友参考一下 这篇文章主要介绍了js代码触发事件代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持呐喊教程。

  • 我编写了一个Spring RestController,它返回一个SseEmitter(对于服务器发送的事件),并向每个事件添加HATEOAS链接。以下是该控制器的一个简化但有效的示例: 问候语课程如下: 此代码运行完美。如果我尝试使用Web浏览器访问REST服务,我会看到显示正确内容和链接的事件。 结果如下所示(每个事件出现在前一个事件之后5秒): 现在我需要调用这个REST服务并从另一个Spr

  • Tendermint 会发出不同的事件,您可以通过Websocket订阅这些事件。这对于第三方应用程序(如 analysys)或检查状态非常有用。 事件列表 您可以通过 Websocket 调用 subscribe RPC 方法订阅上面的任何事件。 { "jsonrpc": "2.0", "method": "subscribe", "id": "0", "para

  • 调用: myContract.events.allEvents([options][, callback]) 与events相同,只是可以接收合约的全部事件。可以使用options.filter属性进行过滤。