长话短说 我创建了一个 React 包装器来将一组日志消息呈现到终端中,但调整大小会产生奇怪的输出(请参见屏幕截图)。 (NPM 上有一个 React-Wrapper,但它不适用于我的用例 - 导致屏幕闪烁)
我正在为 Guppy 开发一项功能,我在其中添加了 Xterm.js用于终端输出。 可以找到PR here .
由于超链接扫描/解析,我已经添加了 xterm,并且可以正常工作。
但我一直坚持调整大小才能工作。如果我在应用程序中启动 devServer 并等待一些文本,它将以正确的字母宽度显示。 如果我减小尺寸,我得到的输出字母宽度不正确。 就像下面的截图:
它在未调整大小的状态下总是看起来正确,但在调整大小后它会得到错误的显示 - 所以这会发生在放大和缩小屏幕宽度时。
输出应该类似于下面的截图(可能有一些换行):
我认为这是由 Fit addon 引起的或者我使用调整大小观察器处理调整大小的方式,但我不确定。
xterm 字母的跨度样式的宽度为 NaNpx
就像下面的截图:
这是由我使用的媒体查询引起的吗?我还没有看到,也许我必须暂时禁用所有媒体查询以查看是否是导致该行为的原因。
到目前为止我尝试了什么:
- 包装
this.xterm.fit()
进入setTimeout(func, 0)
但没有效果 - 修改了一些我正在使用的样式,但我还没有找到原因。
代码
我正在使用的代码可以在 Github branch feature-terminal-links 上找到但在这里我想提取我为使 Xterm 与 React 一起工作而添加的部分:
- 我创建了一个样式组件
XtermContainer
作为一个 div,所以我可以添加 Xterm 样式和自己的样式。以下代码在render
里面并将成为我们的 xterm.js 容器(innerRef
稍后将在ComponentDidMount
中使用以使用该容器初始化 Xterm):
<XtermContainer
width={width}
height={height}
innerRef={node => (this.node = node)}
/>
- 在
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) ... */
}
- 已添加 react-resize-observer在也包含终端容器的包装器内部,因此我可以触发
this.xterm.fit()
如果大小发生变化(在 repo 协议(protocol)中有一个setTimeout
包装用于测试)。
<ResizeObserver onResize={() => this.xterm && this.xterm.fit()} />
- 使用
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 引起的。
最佳答案
我已经解决了这个问题。它现在在上述功能分支中工作。不确定是否有更好的解决方案,但它对我有用。
我想解释一下我是如何解决调整大小问题的:
问题出在 DevelopmentServerPane
中使用的 OnlyOn
组件。它总是呈现两个 TerminalOutput
组件。一个终端使用 display: none
隐藏,另一个终端使用 display: inline
显示 - 样式更改是通过 styled-component 中的媒体查询处理的。
将 OnlyOn
替换为 React-responsive 后并使用渲染 Prop 检查 mdMin
断点是否按预期工作。 React-responsive 正在从 DOM 中删除未显示的媒体查询组件,因此 DOM 中同时只有一个终端。
我仍然不知道为什么字母宽度有问题,但可能这两个实例以某种方式发生了冲突。我无法创建最小的复制品。我试图在此 Codesandbox 中重现该问题但我一次只调整了一个终端的大小,所以我没有遇到问题。
修复问题的代码(上述 repo 的简化版本):
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>
);
关于javascript - 如何正确调整 Xterm.js 的大小?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55322629/