vue+xterm.js实现webssh踩坑之旅
最近在做的项目需要使用xterminal实现网页远程连接Linux终端,引了这个插件后发现问题很多,接下来一一记录问题所在。
一、如何在vue项目中使用xterm.js
- 安装xterm.js,博主使用的是3.x
npm i xterm --save
- 在项目中引用
- 新建组件Xterminal.vue
<template>
<div class="console" id="terminal'" v-loading="loading" element-loading-text="拼命连接中">
</div>
</template>
<script>
import { Terminal } from "xterm";
import * as fit from "xterm/lib/addons/fit/fit";
import * as attach from "xterm/lib/addons/attach/attach";
import "xterm/dist/xterm.css";
Terminal.applyAddon(attach);
Terminal.applyAddon(fit);
export default {
name: "xterminal",
data() {
return {
term: null,
terminalSocket: null,
copy: '',
loading: true,
cols: 80,
content: ''
};
},
methods: {
runRealTerminal(res) {
this.content.operate = 'connect'; // 根据后端需求调节建立连接时发送的数据
this.terminalSocket.send(
JSON.stringify(this.content)
);
this.loading = false;
console.log("webSocket is finished");
},
closeRealTerminal() {
console.log("close");
}
},
mounted() {
let terminalContainer = document.getElementById("terminal");
this.term = new Terminal({
// 光标闪烁
cursorBlink: true
});
this.term.open(terminalContainer, true);
// open websocket
this.terminalSocket = new WebSocket("ws://********"); // 填入服务器的websocket连接地址
this.terminalSocket.onopen = this.runRealTerminal;
this.terminalSocket.onclose = this.closeRealTerminal;
this.terminalSocket.onerror = this.errorRealTerminal;
this.term.attach(this.terminalSocket);
this.term._initialized = true;
console.log("mounted is going on");
this.term.on("data", (data) => {
console.log("data", data);
this.terminalSocket.send(
JSON.stringify({
operate: "command",
command: data
})
);
});
},
beforeDestroy() {
this.terminalSocket.close();
this.term.destroy();
},
};
</script>
<style lang="scss">
</style>
以上代码是我对webssh的初步实现,当代码编译通过,在浏览器上展示时,博主发现终端窗口的大小并没有占满容器。
页面布局类似于上图。主要问题有以下几点:
- 终端的高度无法自适应容器大小
- 终端可显示区域未占满终端的100%宽度及高度
- 浏览器进行缩放或放大时无法自适应
针对这些问题,最初想到的办法是过去container的宽高,在初始化terminal时对cols及rows参数进行设置,但是container的高度只在终端初始化后才会被撑开,因此获取到的高度为0。最终想到的办法是获取浏览器的innerWidth和innerHeight,计算终端容器的宽高。如上图所示,终端的高度为 window.innerHeight - 140
,宽度为window.innerWidth - 230px
(只是举个例子)。之后初始化时进行设置,代码如下:
const width = window.innerWidth - 230;
const height = window.innerHeight - 140;
this.cols = parseInt(width/9, 10); // 经过计算one col大约等同于9px
this.term = new Terminal({
// 光标闪烁
cursorBlink: true,
cols: this.cols,
rows: parseInt(height/17, 10), // one row = 17px
});
本以为到此开发结束,结果有一天心血来潮输入命令狂按a的时候,命令行过长不会换行而且把前面的内容覆盖了,淦!如下图所示。
百度了很多文章,却没有找到解决办法,转到github,在官方issue1359中找到了答案:
这只是第一种解决办法,删除term.fit()或者cols属性设置,但像他说的,这并不是一种好办法。
结合上面二者的回答原因是前后端设置的终端cols不同导致换行不正确。并且建议在设置cols后将其返回到后端,使后端设置成前端的cols。
所以博主在建立连接时,将cols也进行了传递,传递的方法很多,你可以在建立websocket连接时,将cols作为参数拼接到url后,而博主根据后端的需求将参数放在了content中进行了传递。
runRealTerminal(res) {
this.content.operate = 'connect';
this.content.cols = this.cols;
this.terminalSocket.send(
JSON.stringify(this.content)
);
this.loading = false;
console.log("webSocket is finished");
},
之后确实实现了可换行的效果,但是问题又出现了,就是在浏览器缩放或放大的时候,终端内容消失了而且经常无法输入。这是因为没有使用fit()函数使其自适应。解决办法:在浏览器resize的时候调用fit函数。
在mounted钩子函数中加入:window.addEventListener('resize',this.windowChange);
在methods中定义:
windowChange(){
const width = window.innerWidth - 230;
const height = window.innerHeight - 140;
this.cols = parseInt(width/9, 10);
this.term.fit();
this.term.resize(this.cols, parseInt(height/17, 10));
this.term.scrollToBottom();
}
同时需要向后端通过websocket发送最新的cols值。
至此,问题全部解决。end.
更多推荐
所有评论(0)