如何在tauri中创建多窗口(rust + react + typescript + html + css)?

rekjcdws  于 12个月前  发布在  React
关注(0)|答案(1)|浏览(244)

我对tauri和它的后端rust还很陌生。我开始在visual studio上开发,到目前为止,开发进展顺利,直到我需要创建物理窗口时,用户需要被引导到应用程序的其他部分,例如设置或从应用程序的前端访问人工智能模型。
从我对tauri的这个设置的理解来看,前端由Typescript,CSS,HTMl和React处理。后端由rust处理。
根据Tauri官方文档,窗口打开有三种方式(参见Tauri文档https://tauri.app/v1/guides/features/multiwindow#accessing-a-window-at-runtime):

1.静态

窗口可以静态添加,方法是在最初创建tauri项目时,在windows-tauri-app命令提供的tauri.conf.json文件的**“windows:“**部分或块中添加条目。
tauri.conf.json:

{
  "tauri": {
    "windows": [
      {
        "label": "external",
        "title": "Tauri Docs",
        "url": "https://tauri.app"
      },
      {
        "label": "local",
        "title": "Tauri",
        "url": "index.html"
      }
    ]
  }
}

字符串

用户接近

2.前端:JavaScript/Typescript

窗口可以通过在前端创建webview示例打开。
JavaScript:

import { WebviewWindow } from '@tauri-apps/api/window'
const webview = new WebviewWindow('theUniqueLabel', {
  url: 'path/to/page.html',
})
// since the webview window is created asynchronously,
// Tauri emits the `tauri://created` and `tauri://error` to notify you of the creation response
webview.once('tauri://created', function () {
  // webview window successfully created
})
webview.once('tauri://error', function (e) {
  // an error occurred during webview window creation
})

打印文本:

import { WebviewWindow } from '@tauri-apps/api/window';

const webview = new WebviewWindow('theUniqueLabel', {
  url: 'path/to/page.html',
});

// since the webview window is created asynchronously,
// Tauri emits the `tauri://created` and `tauri://error` to notify you of the creation response
webview.once('tauri://created', () => {
  // webview window successfully created
  console.log('Webview window successfully created');
});

webview.once('tauri://error', (e) => {
  // an error occurred during webview window creation
  console.error('Error creating webview window:', e);
});

3.后端:Rust

通过App示例App句柄tauri命令,使用AppBuilder::结构体创建窗口

应用示例:

tauri::Builder::default()
  .setup(|app| {
    let docs_window = tauri::WindowBuilder::new(
      app,
      "external", /* the unique window label */
      tauri::WindowUrl::External("https://tauri.app/".parse().unwrap())
    ).build()?;
    let local_window = tauri::WindowBuilder::new(
      app,
      "local",
      tauri::WindowUrl::App("index.html".into())
    ).build()?;
    Ok(())
  })
  .run(tauri::generate_context!())
  .expect("error while running app");

应用句柄:

tauri::Builder::default()
  .setup(|app| {
    let handle = app.handle();
    std::thread::spawn(move || {
      let local_window = tauri::WindowBuilder::new(
        &handle,
        "local",
        tauri::WindowUrl::App("index.html".into())
      ).build()?;
    });
    Ok(())
  })

tauri命令:

\#\[tauri::command\]
async fn open_specific_window(handle: tauri::AppHandle) {
let specific_window = tauri::WindowBuilder::new(
&handle,
"external", /\* the unique window label \*/
tauri::WindowUrl::External("https://tauri.app/".parse().unwrap())
).build().unwrap();
}

我的方法

我使用了第三种方法,因为直观上,它看起来像是我最简单的方法,并且它以直接的方式优化性能。其他两种方法工作得很好,但第三种方法需要额外的步骤,我将强调这一点作为这个堆栈的答案。再次,我想使用第三种方法,因为我相信它应该具有优化的性能和易于实现。一个我会贴到这个堆栈上的?
另外,这是我的第一个堆栈上堆栈溢出。我想打开这个问题,以帮助其他人在不浪费他们的时间像我一样。任何建议,欢迎。
我尝试了第三种方法,正如我在问题的细节中所规定的那样。

Rust命令

我创建了一个rust命令来创建窗口:

#[tauri::command]
async fn open_settings_window(app: tauri::AppHandle) {
    
    let file_path = "src-tauri/src/Views/settings.html";
    
    let _settings_window = tauri::WindowBuilder::new(
        &app,
        "Settings", /* the unique window label */
        tauri::WindowUrl::App(file_path.into()),
    )
    .title("Settings")
    .build()
    .unwrap();

}

  • 注意:* 我花了很长时间才弄清楚tauri::WindowUrl::App()只加载html,请使用html文件。

前端调用rust命令

当用户点击按钮(id = settings-button)时,我调用了rust命令:
下面是App.tsx文件:

import { useState } from "react";
import reactLogo from "./assets/react.svg";
import { invoke } from "@tauri-apps/api/tauri";
import "./App.css";

function App() {
  const [greetMsg, setGreetMsg] = useState("");
  const [name, setName] = useState("");

  async function open_settings() {
    await invoke("open_settings_window") ; 
    console.log("Window was opened") ; 
  }
  async function greet() {
    // Learn more about Tauri commands at https://tauri.app/v1/guides/features/command
    setGreetMsg(await invoke("greet", { name }));
  }

  async function TerminateApplication() {
    await invoke("terminate_application") ; 
  }

  return (
    <div className="container">
      <h1>Welcome to SSLT!</h1>

      <div className="eye-container align-justify-center">
       <div className="eye align-justify-center">
        <div className="iris align-justify-center">
         <div className="pupil align-justify-center"></div>
        </div>
       </div>
      </div>

      <form
        id="form"
        className="row"
        onSubmit={(e) => {
          e.preventDefault();
          greet();
        }}
      >
        <input
          id="greet-input"
          onChange={(e) => setName(e.currentTarget.value)}
          placeholder="Enter a name..."
        />
        <button type="submit">Greet</button>
      </form>

      <p>{greetMsg}</p>

      <div className="options-container align-justify-center">
        <button type="submit" className="option">AI model</button>
        <button className="option" onClick={open_settings} id="settings-button" >
        Settings
        </button>
        <button className="option" onClick={TerminateApplication}>Exit</button>
      </div>
    </div>
  );
}

export default App;


它正确地调用了rust命令,并按照rust命令中的编程创建了一个文件。唯一的问题是它实际上并没有在指定的html文件中显示连接的类型脚本的内容。

html文件内容:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Tauri + React + TS</title>
  </head>

  <body>
    <div>Hello</div>
    <!-- Include the compiled TypeScript file -->
    <script type="module" src="settings-main.tsx"></script>
    
  </body>
</html>


如果有人有这个问题,请帮助。我最终得到了解决方案...

vojdkbi0

vojdkbi01#

我终于找到了解决办法...

    • 第三种**方法使用rust中的tauri命令,它正确地创建了窗口。它确实呈现了html内容,但没有呈现连接到html脚本的typescript内容。

好吧,我找到了一个解决方案。 typescript 应该使用divReact重新呈现。

  • 注意:* 如果您打算使用它,这可以作为第三种方法的模板:
    1.创建一个额外的文件,{specific window}-main.tsx
import React from "react";
    import ReactDOM from "react-dom/client";
    import Settings_View from "./settings";
    
    ReactDOM.createRoot(document.getElementById("settings-root") as HTMLElement).render(
        <React.StrictMode>
          <Settings_View/>
        </React.StrictMode>,
      );

字符串

  • 注意:* 请确保您的{指定}视图或屏幕(在本例中为Setting_View)与您的{指定}.tsx文件的导出组件匹配,并且组件已正确导入。document.getElementById("settings-root")通过其id获取容器div,并相应地将上述组件渲染到该div中。
    2.连接html文件中的新脚本(tauri窗口创建命令中调用的文件)。
<!doctype html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <link rel="icon" type="image/svg+xml" href="/vite.svg" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Tauri + React + TS</title>
      </head>
    
      <body>
        <div id="settings-root">Hello</div>
        <!-- Include the compiled TypeScript file -->
        <script type="module" src="settings-main.tsx"></script>
                                  <!--Here ^ -->>
      </body>
    </html>


注意:确保div有一个可以在React部分中关联的id,请参阅步骤1的说明。

结论

这解决了我使用rust命令创建窗口的问题,最终将实际的 typescript 加载到容器中并显示其内容。
下面是两个窗口打开的示例:
Link to the result of the solution

使用方法3的简短指南

1.为特定窗口创建tauri命令

#[tauri::command]
    async fn open_a_window(app: tauri::AppHandle) {
        
        let file_path = "path/window.html";
        
        let _settings_window = tauri::WindowBuilder::new(
            &app,
            "Window", /* the unique window label */
            tauri::WindowUrl::App(file_path.into()),
        )
        .title("Window")
        .build()
        .unwrap();
    
    }

  • 注意:* 请确保函数是一个**的函数,以避免在Windows上出现死锁。
    2.创建包含窗口内容的window.tsx文件
import { useState } from "react";
    import { invoke } from "@tauri-apps/api/tauri";
    import React from "react";
    import "./window.css";
    
    function Window_View() {
     
      return (
        <div className="container">
          <h1>Window</h1>
    
          <button>Hello</button>
        </div>
      );
    }
    
    export default Window_View;

3.创建html文件

<!doctype html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <link rel="icon" type="image/svg+xml" href="/vite.svg" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Tauri + React + TS</title>
      </head>
    
      <body>
        <div id="window-root"></div>
        <!-- Include the compiled TypeScript file that handles the React render -->
        <script type="module" src="window-main.tsx"></script>
        
      </body>
    </html>

  • 注意:* 请确保divid,以便在下一步创建的window-main.tsx文件中引用。另外,请确保脚本模块类型。请...
    4.创建window-main.tsx文件
import React from "react";
    import ReactDOM from "react-dom/client";
    import Window_View from "./window";
    
    ReactDOM.createRoot(document.getElementById("window-root") as HTMLElement).render(
        <React.StrictMode>
          <Window_View/>
        </React.StrictMode>,
      );


注意事项:请检查组件(本例中为Window_View())是否正确导入,组件是否在过程ReactDOM.createRoot(document.getElementById("window-root") as HTMLElement).render();中呈现。另外,只需检查过程是否通过其id获取正确的div,它需要是我们创建的html文件中声明的div

总的来说

只要检查所有的导入,脚本,和任何带有路径的东西是否正确。请,你不想浪费你的时间...
谢谢你
感谢您的时间,我真的希望这有助于任何人有同样的问题.这是我很难甚至得到任何视频教程或官方文档,以帮助在这个问题.我希望这有助于:)

相关问题