xterm.js 使用及问题【新】

xterm.js 使用及问题【新】

版本

xterm:4.15.0

xterm-addon-fit:0.5.0

问题

  1. 如果初始化的时候就使用fit,那么屏幕缩小小于初始的宽度。

issues3564

a

在初始化的时候不能使用fit

  mounted() {
    this.term = this.initTerm()
    this.onTerminalResize()
    this.onTerminalKeyPress()
  },
 methods:{
	initTerm() {
    const term = new Terminal(this.option)
    this.fitAddon = new FitAddon()
    term.loadAddon(this.fitAddon)
    term.open(this.$refs.terminal)
    term.write(this.initText)
    // this.fitAddon.fit() // 初始化的时候不要使用fit

    return term
	},
}

使用fit的时机

  mounted() {
    this.term = this.initTerm()
    this.onTerminalResize()
    this.onTerminalKeyPress()
    
    setTimeout(() => {
      this.fitAddon.fit()
    }, 60) // 而且不能是0 ,xterm 生成时间在2ms左右
  },

⚠️不能使用 nextTick 替代setTimeout

⚠️setTimeout的时间⏰,xterm 生成时间在2ms左右,如果设置成5,初始进入好使,刷新浏览器的时候,就又不好使了。经测试,有可能是刷新浏览器时,xterm销毁并重建,设置成11时,是好使的。结论:setTimeout的时间要设置的大一点

代码

<template>
  <div class="terminal-cantainer">
    <div ref="terminal" style="width: 100%; height: 100%; display: block"></div>
  </div>
</template>

<script>
import { Terminal } from 'xterm'
import { FitAddon } from 'xterm-addon-fit'
import 'xterm/css/xterm.css'
import { debounce } from 'lodash'

const packStdin = data =>
  JSON.stringify({
    Op: 'stdin',
    Data: data
  })

const packResize = (col, row) =>
  JSON.stringify({
    Op: 'resize',
    Cols: col,
    Rows: row
  })
export default {
  name: 'Terminal',
  data() {
    return {
      initText: '连接中...',
      first: true,
      term: null,
      fitAddon: null,
      ws: null,
      socketUrl:'ws://localhost:8081',
      option: {
        lineHeight: 1.2,
        cursorBlink: true,
        cursorStyle: 'underline',
        fontSize: 12,
        fontFamily: "Monaco, Menlo, Consolas, 'Courier New', monospace",
        theme: {
          background: '#181d28'
        },
        cols: 10 // 初始化的时候不要设置fit,设置col为较小值(最小为可展示initText初始文字即可)方便屏幕缩放
      }
    }
  },
  computed: {
    isWsOpen() {
      return this.ws && this.ws.readyState === 1
    }
  },
  mounted() {
    this.term = this.initTerm()
    this.initSocket()

    this.onTerminalResize()
    this.onTerminalKeyPress()

    setTimeout(() => {
      this.fitAddon.fit()
    }, 60) // ⚠️
  },
  beforeDestroy() {
    this.removeResizeListener()
    this.term && this.term.dispose()
  },
  methods: {
    initTerm() {
      const term = new Terminal(this.option)
      this.fitAddon = new FitAddon()
      term.loadAddon(this.fitAddon)
      term.open(this.$refs.terminal)
      term.write(this.initText)
      // this.fitAddon.fit() // 初始化的时候不要使用fit
      return term
    },
    onTerminalKeyPress() {
      this.term.onData(data => {
        this.isWsOpen && this.ws.send(packStdin(data))
      })
    },

    // resize 相关
    resizeRemoteTerminal() {
      const { cols, rows } = this.term
      console.warn('cols, rows', cols, rows)
      // 调整后端终端大小 使后端与前端终端大小一致
      this.isWsOpen && this.ws.send(packResize(cols, rows))
    },
    onResize: debounce(function () {
      this.fitAddon.fit()
    }, 500),
    onTerminalResize() {
      window.addEventListener('resize', this.onResize)
      this.term.onResize(this.resizeRemoteTerminal)
    },
    removeResizeListener() {
      window.removeEventListener('resize', this.onResize)
    },

    // socket
    initSocket() {
      this.ws = new WebSocket(this.socketUrl)
      this.openSocket()
      this.closeSocket()
      this.errorSocket()
      this.messageSocket()
    },
    // 打开连接
    openSocket() {
      this.ws.onopen = () => {
        console.log('打开连接')
      }
    },
    // 关闭连接
    closeSocket() {
      this.ws.onclose = () => {
        console.log('关闭连接')
      }
    },
    // 连接错误
    errorSocket() {
      this.ws.onerror = () => {
        this.$message.error('websoket连接失败,请刷新!')
      }
    },
    // 接收消息
    messageSocket() {
      this.ws.onmessage = res => {
        const data = JSON.parse(res.data)
        const term = this.term
        console.warn('data', data)
        // 第一次连接成功将 initText 清空
        if (this.first) {
          this.first = false
          term.reset()
          term.element && term.focus()
          this.resizeRemoteTerminal()
        }
        term.write(data.Data)
      }
    }
  }
}
//
</script>
<style lang="scss">
.terminal-cantainer {
  height: 100%;
  border-radius: 4px;
  background: rgb(24, 29, 40);
  padding: 12px;
  color: rgb(255, 255, 255);
  .xterm-scroll-area::-webkit-scrollbar-thumb {
    background-color: #b7c4d1; /* 滚动条的背景颜色 */
  }
}
</style>

问题

Vue 监听不到ws.readyState变化

https://segmentfault.com/q/1010000023263992

解决方法: 在onopen、onclose、onerror、onmessage 手动修改状态。

Xterm.js是一个基于Web技术的终端模拟器库,可以实现在浏览器中运行终端。如果你想通过它连接到远程的虚拟机,你需要几个步骤: 1. **安装xterm.js**:首先,在项目中引入xterm.js和其他必要的库,如`@xterm/models` 和 `webix Terminal`。 ```html <script src="https://cdn.jsdelivr.net/npm/xterm@4.17.0/dist/xterm.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/@xterm/models@4.17.0/index.min.js"></script> ``` 2. **创建终端实例**:在JavaScript中初始化一个xterm.js终端,并设置其连接信息,这通常包括SSH或VNC服务器地址、用户名和密码(如果是SSH,可能是公钥认证)。 ```javascript const term = new Terminal(); term.open(document.getElementById('terminal')); term.write(`ssh user@your-virtual-machine.example.com\n`); ``` 3. **配置连接**:如果需要,你可以使用第三方库,如`ssh2`(用于SSH连接)或`node-xvnc`(用于VNC),在后台处理实际的连接建立过程。这里需要了解网络通信的知识以及相应的API。 4. **事件监听**:处理连接状态变化和输入输出,比如`data`事件可以读取终端接收的数据,而`resize`事件则用于调整终端视口大小。 5. **错误处理**:确保捕获并处理可能的连接失败或其他错误,提供友好的用户反馈。 ```javascript term.on('error', (e) => { console.error('Terminal error:', e); }); ``` 6. **安全注意事项**:在生产环境中,尤其是涉及敏感数据时,务必确保对用户的输入进行验证和过滤,防止注入攻击。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值