3

tl; 博士 我创建了一个 React 包装器来将一组日志消息呈现到终端中,但是调整大小会给出一个奇怪的输出(参见屏幕截图)。(NPM 上有一个 React-Wrapper,但这不适用于我的用例 - 导致屏幕闪烁)

我正在为 Guppy 开发一项功能,我正在为终端输出添加Xterm.js。PR 可以在这里找到。

由于超链接扫描/解析,我添加了 xterm 并且它正在工作。

但我坚持调整大小工作。如果我在应用程序中启动 devServer 并等待一些文本,它将以正确的字母宽度显示。如果我减小尺寸,我会得到一个字母宽度不正确的输出。就像在下面的屏幕截图中一样: 混乱的输出

它在未调整大小的状态下总是看起来正确,但在调整大小后会出现错误的显示 - 所以这将发生在放大和缩小屏幕宽度时。

输出应该类似于以下屏幕截图(可能带有一些换行): 正确的输出

我认为这是由Fit 插件或我使用调整大小观察者处理调整大小的方式引起的,但我不确定。

NaNpxxterm letter 的 span 样式在以下屏幕截图中 的宽度类似于:宽度为 NaNpx 的 CSS 这是由我正在使用的媒体查询引起的吗?我还没有看到,也许我必须暂时禁用所有媒体查询,看看这是否导致了这种行为。

到目前为止我已经尝试过:

  • 包裹this.xterm.fit()成一个setTimeout(func, 0)但没有效果
  • 修改了一些我正在使用的样式,但我没有找到原因。

代码

我正在使用的代码可以在Github 分支 feature-terminal-links上找到,但在这里我想提取我添加的部分以使 Xterm 与 React 一起工作:

  1. 我创建了一个样式组件XtermContainer作为 div,因此我可以添加 Xterm 样式和自己的样式。以下代码在里面render,将成为我们的 xterm.js 容器(innerRef稍后将用于使用ComponentDidMount该容器初始化 Xterm):
<XtermContainer
    width={width}
    height={height}
    innerRef={node => (this.node = node)}
/>
  1. componentDidMount使用上面的容器初始化 xterm :
componentDidMount() {
    Terminal.applyAddon(webLinks);
    Terminal.applyAddon(localLinks);
    Terminal.applyAddon(fit);

    this.xterm = new Terminal({
      convertEol: true,
      fontFamily: `'Fira Mono', monospace`,
      fontSize: 15,
      rendererType: 'dom', // default is canvas
    });

    this.xterm.setOption('theme', {
      background: COLORS.blue[900],
      foreground: COLORS.white,
    });

    this.xterm.open(this.node);
    this.xterm.fit();

    /* ... some addon setup code here (not relevant for the problem) ... */
}
  1. 在也包含终端容器的包装器内添加了react-resize-observerthis.xterm.fit() ,因此我可以在大小更改时触发(在 repo 中有一个setTimeout用于测试的包装器)。
<ResizeObserver onResize={() => this.xterm && this.xterm.fit()} />
  1. 如果组件正在获取新日志,则componentDidUpdate(prevProps, prevState)用于更新终端并将终端滚动到底部:
componentDidUpdate(prevProps, prevState) {
    if (prevProps.task.logs !== this.state.logs) {
      if (this.state.logs.length === 0) {
        this.xterm.clear();
      }
      for (const log of this.state.logs) {
        /*
        We need to track what we have added to xterm - feels hacky but it's working.
        `this.xterm.clear()` and re-render everything caused screen flicker that's why I decided to not use it.
        Todo: Check if there is a react-xterm wrapper that is not using xterm.clear or 
              create a wrapper component that can render the logs array (with-out flicker).
        */
        if (!this.renderedLogs[log.id]) {
          this.writeln(log.text);
          this.xterm.scrollToBottom();
          this.renderedLogs[log.id] = true;
        }
      }
    }
}

我必须找到原因的想法:

  • 检查 ResizeObserver 代码。(见下面的更新)
  • 尝试找出 xterm css 获得 NaN 宽度的原因。Xterm.js 是否使用容器的样式宽度?如果是,则可能未正确设置。

更新

好的,可能不需要调整大小观察器,因为在注释掉<ResizeObserver/>渲染后我得到了相同的行为。所以我认为这是由 xterm.js 或 Guppy 中的 css 引起的。

4

1 回答 1

4

我有一个解决这个问题的方法。它现在在上述功能分支中工作。不确定是否有更好的解决方案,但它对我有用。

我想解释一下我是如何解决调整大小问题的:

问题出OnlyOnDevelopmentServerPane. 它总是渲染两个TerminalOutput组件。一个终端被隐藏,display: none另一个被显示display: inline- 样式更改是通过样式组件内的媒体查询处理的。

在替换OnlyOnReact-responsive并使用渲染道具检查mdMin断点后,它按预期工作。React-responsive 正在从 DOM 中删除未显示的 mediaquery 组件,因此在 DOM 中同时只有一个终端。

我仍然不知道为什么字母宽度有问题,但可能这两个实例以某种方式发生了碰撞。我无法创建最小的复制。我试图在这个Codesandbox中重新创建问题,但我一次只调整了一个终端的大小,所以我没有在那里遇到问题。

解决问题的代码(来自上述存储库的简化版本):

import MediaQuery from 'react-responsive';

const BREAKPOINT_SIZES = {
  sm: 900,
};

const BREAKPOINTS = {
  mdMin: `(min-width: ${BREAKPOINT_SIZES.sm + 1}px)`,
};

const DevelopmentServerPane = () => (
  <MediaQuery query={BREAKPOINTS['mdMin']}>
    {matches =>
      matches ? (
        <div>{/* ... render Terminal for matching mdMin and above */}</div>
      ) : (
        <div> {/* ... render Terminal for small screens */}</div>
      )
    }
  </MediaQuery>
);

于 2019-04-24T21:22:02.100 回答