? ? ? ? Terminal是web終端技術,類似cmd命令窗口,Webcontainer 中推薦使用的是Xterm.js,這里就不細說Xterm.js 的使用了,我們使用第三方庫來實現(原生確實有點難用)。
?vue-web-terminal
? ? ? ? 一個由 Vue 構建的支持多內容格式顯示的網頁端命令行窗口插件,支持表格、json、代碼等多種消息格式,支持自定義消息樣式、命令行庫、鍵入搜索提示等,模擬原生終端窗口支持 ← → 光標切換和 ↑ ↓ 歷史命令切換。其完善的API、中文文檔、拖拽、縮放、實時回顯等功能是我選擇其的原因之一。
在線體驗地址
github 地址
中文文檔
npm install vue-web-terminal@3 --save
import Terminal from 'vue-web-terminal'
// 3.2.0 及 2.1.13 以后版本需要引入此樣式,之前版本無需引入主題樣式
import 'vue-web-terminal/lib/theme/dark.css'// for vue3
const app = createApp(App).use(Terminal)
<template><terminal name="my-terminal" @exec-cmd="onExecCmd"></terminal>
</template><script setup>
function onExecCmd(cmd) {console.log(cmd);
}
</script>
切換主題
????????主題的核心是導入的style 文件里面自定義的css變量,因此,我們可以通過控制導入的文件實現主題切換:?
? ? ? ? 經過分析觀察,兩個文件的節點是一樣的,無法通過js控制,因此,復制文件到外部文件夾,并添加標識,main.js 中引入外部文件:?
function changeTheme() {document.querySelector("html").setAttribute("t-theme", dark.value ? "dark" : "lignt");dark.value = !dark.value;
}
????????這樣更利于我們自定義主題,直接在原有基礎上進行拓展即可。
個性化配置
show-header 是否顯示頭部
context?上下文內容(Root)
context-suffix?上下文后綴符號(@)
...更多配置項,大家去官網看下,比較簡單,就不細說了。
事件列表
? ? ? ? 事件這塊我就說一個?exec-cmd:執行自定義命令時觸發。success
和failed
為回調函數,必須調用兩個回調其中之一才會回顯!failed
回調參數為一個string,exec-cmd的success
回調參數支持多種數據類型,不同數據類型執行邏輯也會不同:
- 不傳任何參數,立即結束本次執行
- 傳入一個消息對象,將會向記錄中追加一條消息,并立即結束本次執行
- 傳入一個消息對象數組,將會向記錄中追加多條消息,并立即結束本次執行
- 傳入一個
TerminalFlash
對象,將會進入實時回顯處理邏輯,本次執行并不會結束,直到調用finish()
- 傳入一個
TerminalAsk
對象,將會進入用戶詢問輸入處理邏輯,本次執行并不會結束,直到調用finish()
function onExecCmd(cmdKey, command, success, failed, name) {}
function onExecCmd(cmdKey, command, success, failed, name) {success(); // 什么都不傳,直接結束
}
function onExecCmd(cmdKey, command, success, failed, name) {success({ content: "hello world" }); // 傳一個消息對象
}
function onExecCmd(cmdKey, command, success, failed, name) {success([{ content: "hello world" }, { content: "hello javascript" }]); // 傳一個消息對象數組
}
import { TerminalFlash } from "vue-web-terminal";
const flash = new TerminalFlash();
function onExecCmd(cmdKey, command, success, failed, name) {let count = 0;success(flash); // 傳一個 TerminalFlashlet flashInterval = setInterval(() => {// 顯示數據調用 flag.flush(string)flash.flush(`This is flash content: ${count}`);if (++count >= 20) {clearInterval(flashInterval);// 結束回顯調用 flag.finish()flash.finish();}}, 200);
}
import { TerminalFlash, TerminalAsk } from "vue-web-terminal";
const flash = new TerminalFlash();
const asker = new TerminalAsk();
function onExecCmd(cmdKey, command, success, failed, name) {success(asker); // 傳一個 TerminalAsk// 詢問賬號asker.ask({question: "Please input user name:",callback: askPassword,});function askPassword(value) {console.log("輸入的username ==>", value);asker.ask({question: "Please input password: ",autoReview: true,isPassword: true,callback: (pass) => {// do somethingconsole.log("輸入的password ==>", pass);asker.finish();},});}
}
? ? ? ? ?后面兩種復雜的情況在構建應用時常用到,大家多練習下。還支持插槽,這塊大家自己看下文檔,應該也能學會,就不細說了。
Terminal API
? ? ? ? API無非就是主動向終端發送消息,具體api 可以看官網哈
TerminalApi.textEditorOpen('my-terminal', {content: 'This is the preset content',onClose: (value, options) => {console.log('Final content: ', value, "options:", options)}
})
????????在xshell中,可以在終端直接編輯 nginx配置文件,vue-web-terminal 支持調用textEditorOpen方法,打開編輯器:
?與WebContainer結合
? ? ? ? terminal只是網頁端命令行窗口插件,并不具備實際的命令執行能力。當我輸入 node -v 的時候,terminal的顯示值,還需要通過 success 決定。
? ? ? ? 因此,需要與webcontainer 結合,使得 terminal的輸入,轉化為 spawn 的參數,獲取到output 輸出流后,再回顯到 terminal上。
async function onExecCmd(cmdKey, command, success, failed, name) {// 將命令行拆分為數組let argus = command.split(" ");let res = await wcInstance.spawn(argus[0], argus.slice(1));// 讀取流res.output.pipeTo(new WritableStream({write(data) {success(data);},}));
}
? ? ? ? ?這只是一個簡單的示例哈,并不是所有的回顯都是 success,也并不是所有的回顯都是直接顯示文本,例如 ls 查看文件列表,使用表格更合適,npm install 使用實時回顯flash 更合適。?
總結
? ? ? ? 確實vue-web-terminal使用起來更加方便,封裝的API、事件列表為我們省去了很多麻煩。使用起來也不難,大家多看文檔即可學會。但是大家要記住哈,terminal僅是終端展示用,實際的執行能力還是依靠webContainer,因此,兩者的參數傳遞、數據回顯需要做精細化處理。