Qt 与 React 混合开发
问题与探索
最近的项目遇到了一个问题:如何在 Qt 里去渲染 AI 对话并实现打字的效果。要在 Qt 里边实现相同的功能一种方案是首先用 C++
对 Markdown
进行解析,然后将解析的 HTML 交由 Qt WebEngine 进行渲染。另外一种就是将解析、渲染的功能完全交由 Qt WebEngine 处理。
权衡之下,显然第二种方案更有优势,用 Web 处理 Markdown
和实现动画有很多成熟的解决方案。Ant Design X 提供了完整的解决方案,同时配合 markdown-it 也能处理 AI 返回的 Markdown
。
根据需求,有两种方式让 Qt WebEngine 加载 HTML 文件,一种是将页面发布到服务器,从远程加载,另外一种是使用 Qt 的资源系统将 HTML 文件嵌入到生成的二进制可执行文件中。如果选择远程加载,下文中探讨的一些问题将不复存在,但是需要准备服务器等。这里以第二种方式进行探讨。
Web App 配置
完成了技术选型之后,接下来就要进行项目的开发了。要开始一个 React Web App 的开发,有几种选择:1. 使用诸如 Vite 之类的工具创建项目,2. 使用 Next.js 等框架提供的脚手架创建项目,3. 使用 webpack 从零定制项目。这里选择了第 3 个方案,理由如下:
- 需要对编译打包的输出进行定制,例如去掉文件
hash
,以防止每次文件生成之后都要更新CMakeLists.txt
文件。 - 项目足够简单,不需要使用第三方框架。
由于不需要 hash
,webpack.config.js
的配置可以跟下边类似。
module.exports = {
...
output: {
filename: 'app.js',
publicPath: './',
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx'],
},
plugins: [
new MiniCssExtractPlugin({
filename: 'style.css',
}),
],
...
};
值得注意的是,需要在 html
文件中插入 Qt
提供的 qwebchannel.js
文件,这个文件是 Qt
自动嵌入到资源系统里的。
<script src="qrc:/qtwebchannel/qwebchannel.js"></script>
去掉了 文件 hash, Code Splitting 之后,打包输出的文件只有 index.html
, app.js
和 style.css
。接下来就可以打包进资源了。
qt_add_resources(App "html"
PREFIX "/"
FILES
html/index.html
html/app.js
html/style.css
)
资源加载与渲染
接下来的流程与文章 WebEngine Markdown Editor Example 类似。鉴于 APP 既需要支持历史消息,又需要支持用户输入和 AI 返回的消息。那么需要向 Qt WebChannel 注册两个对象
channel->registerObject(QStringLiteral("histories"), &m_histories);
channel->registerObject(QStringLiteral("message"), &m_message);
对于历史消息、用户输入的消息直接进行渲染,对于 AI 返回的消息,则添加打字效果。
useEffect(() => {
new QWebChannel(qt.webChannelTransport, function (channel) {
const histories = channel.objects.histories;
const message = channel.objects.message;
if (histories.text) {
setMessages(JSON.parse(histories.text));
}
history.textChanged.connect((histories: string) => {
if (histories.length > 0) {
setMessages(JSON.parse(histories));
}
});
message.textChanged.connect((message: string) => {
if (message.length > 0) {
setMessages((prev) => [
...prev,
{
...JSON.parse(message),
typing: true,
},
]);
}
});
});
}, [setMessages]);
效果如上图所示。
总结
Qt WebChannel 提供了 JS Bridge 类似的功能,使得 JavaScript
代码能够与 Native
代码进行交互。方便编写跨平台的 Hybrid App。
评论已关闭