最小示例:从react应用程序中打开电子窗口?

IT技术 javascript reactjs electron
2021-05-01 13:29:32

假设我正在使用 react/redux 和电子构建一个桌面应用程序。所以我在电子中的 index.html 文件看起来像这样:

<!DOCTYPE html>
<html>
 ...
  <body>

    <div id="content"></div>

  <script src="public/js/bundle.js"></script>
  </body>
</html>

我最大的 React 容器(称为 app.js)被加载到 'id=content' div 中。到目前为止,这工作正常,但现在我想知道当用户单击我的 React 应用程序中的按钮时如何打开一个新的文件对话框窗口(或任何与此相关的新窗口)。

我在这里这里找到了一些例子,但两个例子都只解释了如何从主电子进程(在渲染器或主进程中)加载文件对话框窗口。

但是,我希望用户使用我的 React 应用程序,然后,一旦他或她单击一个按钮,我的应用程序应该告诉电子生成一个新窗口,当然,这个新窗口应该以某种方式成为我的react的一部分应用。

如果有人能在这里提供一个关于这些如何协同工作的最小示例,我将非常感激。

3个回答

react 16 中的“createPortal”api 会帮助你。

首先假设我们有一个这样的组件:

<SubWindow>
  <h1>bar</h1>
</SubWindow>

然后在其生命周期方法中打开(或关闭)一个窗口,如下所示:

export class SubWindow extends React.Component {
  nativeWindowObject: null;

  componentWillMount() {
    this.nativeWindowObject = window.open('');
  }
  render() {
    return this.nativeWindowObject ? ReactDOM.createPortal(this.props.children, this.nativeWindowObject.document.body) : null;
  }
}

注意:您必须在主窗口中设置 webPreferences:{nativeWindowOpen: true} 以确保“window.open”返回本机 Window 对象。访问https://electronjs.org/docs/api/window-open了解更多信息。

在按钮单击时在新窗口中打开react组件并检测窗口何时关闭,因为该组件不会componentWillUnmount在您关闭窗口时简单地调用您的应用程序应如下所示

应用程序.js

export default class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      // To keep track of the new window if opened or closed
      isNewWindow: false,
    };
  }

  render() {
    return(
    // onClose will be fired when the new window is closed
    // everything inside NewWindowComponent is considered props.children and will be
    // displayed in a new window
    {(this.state.isNewWindow) &&
    <NewWindowComponent
      onClose={() => this.setState({ isNewWindow: false })>
      <h2>This will display in a new window</h2>
    </NewWindowComponent> }

    <button
      onClick={() => this.setState({ isNewWindow: true })}>
      Open in new window</button>
    )
  }
}

新窗口组件.js

export default class NewWindowComponent extends Component {
  // Create a container <div> for the window
  private containerEl = document.createElement('div');

  // This will keep a reference of the window
  private externalWindow = null;

  // When the component mounts, Open a new window
  componentDidMount() {
    // The second argument in window.open is optional and can be set to whichever
    // value you want. You will notice the use of this value when we modify the main
    // electron.js file
    this.externalWindow = window.open('', 'NewWindowComponent ');

    // Append the container div and register the event that will get fired when the
    // window is closed
    if (this.externalWindow)
    {
      this.externalWindow.document.body.appendChild(this.containerEl);
      this.externalWindow.onunload = () => this.props.onClose();
    }
  }

  render() {
    return ReactDOM.createPortal(this.props.children, this.containerEl);
  }
}

electron-main.js或者你的主电子文件被命名

...
function createWindow() {
  mainWindow = new BrowserWindow({
    ...
    // You need to activate `nativeWindowOpen`
    webPreferences: { nativeWindowOpen: true },
  });
  ...
  mainWindow.webContents.on('new-window',
    (event, url, frameName, disposition, options, additionalFeatures) =>
    {
      // This is the name we chose for our window. You can have multiple names for
      // multiple windows and each have their options
      if (frameName === 'NewWindowComponent ') {
        event.preventDefault();
        Object.assign(options, {
          // This will prevent interactions with the mainWindow
          parent: mainWindow,
          width: 300,
          height: 300,
          // You can also set `left` and `top` positions
        });
        event.newGuest = new BrowserWindow(options);
    }
  });
  ...
}
...

单击按钮 handleNewWindow 被调用。使 nodeIntegration 为 true 或预加载 js。

const handleNewWindow =()=> {
    const remote=require('electron').remote;
    const BrowserWindow=remote.BrowserWindow;
    const win = new BrowserWindow({
      height: 600,
      width: 800,
      frame:false,
      webPreferences: {
        nodeIntegration: true,
    }
    });
    win.loadURL(`file://${__dirname}/app.html#/login`);


  }

并在路由器文件上

<Route path="/login" component={Login} />

通过使用此代码,我们可以打开 reactjs 文件并路由到登录。app.html 是主文件,它在电子react样板代码的 main.dev.js 中加载。散列是打开 reactjs 文件的最简单方法。由于电子中的 loadURL 只加载 urls 和 htmls 文件,我们无法打开 js 文件。