浅析如何使用前端终端组件Xterm.js制作一个web terminal及遇到的元素自适应、字符删除与上下键切换命令等问题

时间:2024-03-12 08:37:56

一、Xterm.js 介绍

  xterm 是一个使用 TypeScript 编写的前端终端组件,可以直接在浏览器中实现一个命令行终端应用。Xterm.js 适用于大多数终端应用程序,如 bash,vim 和 tmux,这包括对基于curses的应用程序和鼠标事件的支持。Xterm.js 非常快,它甚至还包括一个GPU加速的渲染器。

  这里有一些 api 介绍,可以看下先:Class: Terminal  ——  https://xtermjs.org/docs/api/terminal/classes/terminal/

  这里有篇博客对这个 api 的一些翻译:初窥Xterm.js  ——  https://juejin.cn/post/6844903809035010055

  还有一篇博客可以看看:用 xterm.js 实现一个简易的 web-terminal !—— https://juejin.cn/post/6918911964009725959

  还有一篇博客有具体使用介绍:xterm.js + vue + websocket实现终端功能(xterm 3.x+xterm 4.x) ——  https://blog.csdn.net/weixin_38318244/article/details/103908129

二、使用 xterm.js 开发终端流程

1、安装依赖

// 1、安装 xterm
npm install --save xterm

// 2、安装xterm-addon-fit
// xterm.js的插件,使终端的尺寸适合包含元素。
npm install --save xterm-addon-fit

// 3、安装xterm-addon-attach(这个你不用就可以不装)
// xterm.js的附加组件,用于附加到Web Socket
npm install --save xterm-addon-attach

2、创建xterm实例并挂载到dom上

3、xterm全屏调整,让终端的尺寸包含元素,要不然会有溢出的隐藏掉了,这样包含让终端内元素自适应。

4、具体代码如下:

<template>
  <div id="terminal" ref="terminal"></div>
</template>
<script>
import { Terminal } from "xterm"
import { FitAddon } from \'xterm-addon-fit\'
import "xterm/css/xterm.css"
export default {
  data() {
    return {
      term: "", // 保存terminal实例
      rows: 40,
      cols: 100
    }
  },
  mounted() {
    this.initXterm()
  },
  methods: {
    initXterm() {
      let _this = this
      let term = new Terminal({
        rendererType: "canvas", //渲染类型
        rows: _this.rows, //行数
        cols: _this.cols, // 不指定行数,自动回车后光标从下一行开始
        convertEol: true, //启用时,光标将设置为下一行的开头
        // scrollback: 50, //终端中的回滚量
        disableStdin: false, //是否应禁用输入
        // cursorStyle: "underline", //光标样式
        cursorBlink: true, //光标闪烁
        theme: {
          foreground: "#ECECEC", //字体
          background: "#000000", //背景色
          cursor: "help", //设置光标
          lineHeight: 20
        }
      })
      // 创建terminal实例
      term.open(this.$refs["terminal"])
      // 换行并输入起始符 $
      term.prompt = _ => {
        term.write("\r\n\x1b[33m$\x1b[0m ")
      }
      term.prompt()
      // canvas背景全屏
      const fitAddon = new FitAddon()
      term.loadAddon(fitAddon)
      fitAddon.fit()

      window.addEventListener("resize", resizeScreen)
      function resizeScreen() {
        try { // 窗口大小改变时,触发xterm的resize方法使自适应
          fitAddon.fit()
        } catch (e) {
          console.log("e", e.message)
        }
      }
      _this.term = term
      _this.runFakeTerminal()
    },
    runFakeTerminal() {
      let _this = this
      let term = _this.term
      if (term._initialized) return
      // 初始化
      term._initialized = true
      term.writeln("Welcome to \x1b[1;32m墨天轮\x1b[0m.")
      term.writeln(\'This is Web Terminal of Modb; Good Good Study, Day Day Up.\')
      term.prompt()
      // 添加事件监听器,支持输入方法
      term.onKey(e => {
        const printable = !e.domEvent.altKey && !e.domEvent.altGraphKey && !e.domEvent.ctrlKey && !e.domEvent.metaKey
        if (e.domEvent.keyCode === 13) {
          term.prompt()
        } else if (e.domEvent.keyCode === 8) { // back 删除的情况
          if (term._core.buffer.x > 2) {
            term.write(\'\b \b\')
          }
        } else if (printable) {
          term.write(e.key)
        }
        console.log(1,\'print\', e.key)
      })
      term.onData(key => {  // 粘贴的情况
        if(key.length > 1) term.write(key)
      })
    }
  }
}
</script>

  效果如下:

  这个就是纯前端的输入与write展示,在实际应用是需要xterm与websocket结合发送数据并显示在屏幕上的

5、xterm与websocket结合发送数据并显示在屏幕上

  这个再写篇单独的博客记录吧,见下篇博客。

三、遇到的问题

1、有2个问题见这篇博客:使用xterm报错:Error: Terminal requires a parent element、及删除时报错:xterm.js: Parsing error 的问题

2、关于全屏

  在这里我要吐槽一下这个xterm.js的官网,真是辣鸡的很,什么都不写清楚,就实例一下就完事了,好多参数和方法api里也不给个例子,还得自己到处去找,真的很辣鸡!一开始以为4.0中不需要设置 fit 及 fullscreen,引入css后直接设置行数和列数就能实现背景铺满全屏。

  但遇到一个问题是,xterm默认代码不会占满一整行,而是会直接换行,所以这里我们需要使用到官网首页给到的插件。

    import { FitAddon } from "xterm-addon-fit";

    // canvas背景全屏-默认
    var fitAddon = new FitAddon();
    term.loadAddon(fitAddon);
    fitAddon.fit();

    // 内容全屏显示-窗口大小发生改变时
    function resizeScreen(size) {
      console.log("size", size);
      try {
        fitAddon.fit();

        // 窗口大小改变时触发xterm的resize方法,向后端发送行列数,格式由后端决定
        term.onResize(size => {
          _this.onSend({ Op: "resize", Cols: size.cols, Rows: size.rows });
        });
      } catch (e) {
        console.log("e", e.message);
      }
    }
    window.addEventListener("resize", resizeScreen);

3、关于字符删除与上下键切换命令等

  值得注意的是,在我们使用 xterm 实现仿终端功能时,不需要对输入字符进行判断,也不需要在输入事件中把输入的字符打出来。因为在输入事件中执行的 websocket 连接中,每输入一个字符都会自动传到后端,而后端会根据你输入的回车符来判断是否要为你换行及返回何种数据。

  也就是说:我们不必关心用户输入与想做的操作,只需要向后台传递参数就好。