成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

他居然把 React 組件跑在命令行終端窗口里面!

開源
今天, 給大家分享一個非常有意思的開源項目: ink。它的作用就是將 React 組件渲染在終端窗口中,呈現出最后的命令行界面。

[[384219]]

 也許你之前聽說過前端組件代碼可以運行在瀏覽器,運行在移動端 App 里面,甚至可以直接在各種設備當中,但你有沒有見過: 前端組件直接跑在命令行窗口里面,讓前端代碼構建出終端窗口的 GUI 界面和交互邏輯?

今天, 給大家分享一個非常有意思的開源項目: ink。它的作用就是將 React 組件渲染在終端窗口中,呈現出最后的命令行界面。

本文偏重實戰,前面會帶大家熟悉基本使用,然后會做一個基于實際場景的實戰項目。

上手初體驗

剛開始上手時,推薦使用官方的腳手架創建項目,省時省心。

  1. npx create-ink-app --typescript 

然后運行這樣一段代碼:

  1. import React, { useState, useEffect } from 'react' 
  2. import { render, Text} from 'ink' 
  3.  
  4. const Counter = () => { 
  5.   const [count, setCount] = useState(0) 
  6.   useEffect(() => { 
  7.     const timer = setInterval(() => { 
  8.       setCount(count => ++count
  9.     }, 100) 
  10.     return () => { 
  11.       clearInterval(timer) 
  12.     } 
  13.      
  14.   }) 
  15.  
  16.   return ( 
  17.     <Text color="green"
  18.       {count} tests passed 
  19.     </Text> 
  20.   ) 
  21.  
  22. render(<Counter />); 

會出現如下的界面:


并且數字一直遞增!demo 雖小,但足以說明問題:

  1. 首先,這些文本輸出都不是直接 console 出來的,而是通過 React 組件渲染出來的。
  2. React 組件的狀態管理以及hooks 邏輯放到命令行的 GUI 當中仍然是生效的。

也就是說,前端的能力以及擴展到了命令行窗口當中了,這無疑是一項非常可怕的能力。著名的文檔生成工具Gatsby,包管理工具yarn2都使用了這項能力來完成終端 GUI 的搭建。

命令行工具項目實戰

可能大家剛剛了解到這個工具,知道它的用途,但對于具體如何使用還是比較陌生。接下來讓我們以一個實際的例子來進行實戰,快速熟悉。代碼倉庫已經上傳到 git,大家可以這個地址下面 fork 代碼: https://github.com/sanyuan0704/ink-copy-command。

下面我們就來從頭到尾開發這個項目。

項目背景

首先說一說項目的產生背景,在一個 TS 的業務項目當中,我們曾經碰到了一個問題:由于production模式下面,我們是采用先 tsc,拿到 js 產物代碼,再用webpack打包這些產物。

但構建的時候直接報錯了,原因就是 tsc 無法將 ts(x) 以外的資源文件移動到產物目錄,以至于 webpack 在對于產物進行打包的時候,發現有些資源文件根本找不到!比如以前有這樣一張圖片的路徑是這樣—— src/asset/1.png,但這些在產物目錄dist卻沒還有,因此 webpack 在打包 dist 目錄下的代碼時,會發現這張圖片不存在,于是報錯了。

解決思路

那如何來解決呢?

很顯然,我們很難去擴展 tsc 的能力,現在最好的方式就是寫個腳本手動將src下面的所有資源文件一一拷貝到dist目錄,這樣就能解決資源無法找到的問題。

一、拷貝文件邏輯

確定了解決思路之后,我們寫下這樣一段 ts 代碼:

  1. import { join, parse } from "path"
  2. import { fdir } from 'fdir'
  3. import fse from 'fs-extra' 
  4. const staticFiles = await new fdir()  
  5.   .withFullPaths()    
  6.   // 過濾掉 node_modules、ts、tsx 
  7.   .filter( 
  8.     (p) => 
  9.       !p.includes('node_modules') && 
  10.       !p.endsWith('.ts') && 
  11.       !p.endsWith('.tsx'
  12.   ) 
  13.   // 搜索 src 目錄 
  14.   .crawl(srcPath) 
  15.   .withPromise() as string[] 
  16.  
  17. await Promise.all(staticFiles.map(file => { 
  18.   const targetFilePath = file.replace(srcPath, distPath); 
  19.   // 創建目錄并拷貝文件 
  20.   return fse.mkdirp(parse(targetFilePath).dir) 
  21.     .then(() => fse.copyFile(file, distPath)) 
  22.    ); 
  23. })) 

代碼使用了fdir這個庫才搜索文件,非常好用的一個庫,寫法上也很優雅,推薦大家使用。

我們執行這段邏輯,成功將資源文件轉移到到了產物目錄中。

問題是解決掉了,但我們能不能封裝一下這個邏輯,讓它能夠更方便地在其它項目當中復用,甚至直接提供給其他人復用呢?

接著,我想到了命令行工具。

二、命令行 GUI 搭建

接著我們使用 ink,也就是用 React 組件的方式來搭建命令行 GUI,根組件代碼如下:

  1. // index.tsx 引入代碼省略 
  2. interface AppProps { 
  3.  fileConsumer: FileCopyConsumer 
  4.  
  5. const ACTIVE_TAB_NAME = { 
  6.  STATE: "執行狀態"
  7.  LOG: "執行日志" 
  8.  
  9. const App: FC<AppProps> = ({ fileConsumer }) => { 
  10.  const [activeTab, setActiveTab] = useState<string>(ACTIVE_TAB_NAME.STATE); 
  11.  const handleTabChange = (name) => { 
  12.   setActiveTab(name
  13.  } 
  14.  const WELCOME_TEXT = dedent` 
  15.   歡迎來到 \`ink-copy\` 控制臺!功能概覽如下(按 **Tab** 切換): 
  16.  ` 
  17.  
  18.  return <> 
  19.    <FullScreen> 
  20.     <Box> 
  21.      <Markdown>{WELCOME_TEXT}</Markdown> 
  22.     </Box> 
  23.     <Tabs onChange={handleTabChange}> 
  24.      <Tab name={ACTIVE_TAB_NAME.STATE}>{ACTIVE_TAB_NAME.STATE}</Tab> 
  25.      <Tab name={ACTIVE_TAB_NAME.LOG}>{ACTIVE_TAB_NAME.LOG}</Tab> 
  26.     </Tabs> 
  27.     <Box> 
  28.      <Box display={ activeTab === ACTIVE_TAB_NAME.STATE ? 'flex''none'}> 
  29.       <State /> 
  30.      </Box> 
  31.      <Box display={ activeTab === ACTIVE_TAB_NAME.LOG ? 'flex''none'}> 
  32.       <Log /> 
  33.      </Box> 
  34.     </Box> 
  35.    </FullScreen> 
  36.  </> 
  37. }; 
  38.  
  39. export default App; 

可以看到,主要包含兩大組件: State和Log,分別對應兩個 Tab 欄。具體的代碼大家去參考倉庫即可,下面放出效果圖:



三. GUI 如何實時展示業務狀態?

現在問題就來了,文件操作的邏輯開發完了,GUI 界面也搭建好了。那么現在如何將兩者結合起來呢,也就是 GUI 如何實時地展示文件操作的狀態呢?

對此,我們需要引入第三方,來進行這兩個模塊的通信。具體來講,我們在文件操作的邏輯中維護一個 EventBus 對象,然后在 React 組件當中,通過 Context 的方式傳入這個 EventBus。從而完成 UI 和文件操作模塊的通信。

現在我們開發一下這個 EventBus 對象,也就是下面的FileCopyConsumer:

  1. export interface EventData { 
  2.   kind: string; 
  3.   payload: any
  4.  
  5. export class FileCopyConsumer { 
  6.  
  7.   private callbacks: Function[]; 
  8.   constructor() { 
  9.     this.callbacks = [] 
  10.   } 
  11.   // 供 React 組件綁定回調 
  12.   onEvent(fn: Function) { 
  13.     this.callbacks.push(fn); 
  14.   } 
  15.   // 文件操作完成后調用 
  16.   onDone(event: EventData) { 
  17.     this.callbacks.forEach(callback => callback(event)) 
  18.   } 

接著在文件操作模塊和 UI 模塊當中,都需要做響應的適配,首先看看文件操作模塊,我們做一下封裝。

  1. export class FileOperator { 
  2.   fileConsumer: FileCopyConsumer; 
  3.   srcPath: string; 
  4.   targetPath: string; 
  5.   constructor(srcPath ?: string, targetPath ?: string) { 
  6.     // 初始化 EventBus 對象 
  7.     this.fileConsumer = new FileCopyConsumer(); 
  8.     this.srcPath = srcPath ?? join(process.cwd(), 'src'); 
  9.     this.targetPath = targetPath ?? join(process.cwd(), 'dist'); 
  10.   } 
  11.  
  12.   async copyFiles() { 
  13.     // 存儲 log 信息 
  14.     const stats = []; 
  15.     // 在 src 中搜索文件 
  16.     const staticFiles = ... 
  17.      
  18.     await Promise.all(staticFiles.map(file => { 
  19.         // ... 
  20.         // 存儲 log 
  21.         .then(() => stats.push(`Copied file from [${file}] to [${targetFilePath}]`)); 
  22.     })) 
  23.     // 調用 onDone 
  24.     this.fileConsumer.onDone({ 
  25.       kind: "finish"
  26.       payload: stats 
  27.     }) 
  28.   } 

然后在初始化 FileOperator之后,將 fileConsumer通過 React Context 傳入到組件當中,這樣組件就能訪問到fileConsumer,進而可以進行回調函數的綁定,代碼演示如下:

  1. // 組件當中拿到 fileConsumer & 綁定回調 
  2. export const State: FC<{}> = () => { 
  3.   const context = useContext(Context); 
  4.   const [finish, setFinish] = useState(false); 
  5.   context?.fileConsumer.onEvent((data: EventData) => { 
  6.     // 下面的邏輯在文件拷貝完成后執行 
  7.     if (data.kind === 'finish') { 
  8.       setTimeout(() => { 
  9.         setFinish(true
  10.       }, 2000) 
  11.     } 
  12.   }) 
  13.  
  14.   return  
  15.   //(JSX代碼) 

這樣,我們就成功地將 UI 和文件操作邏輯串聯了起來。當然,篇幅所限,還有一些代碼并沒有展示出來,完整的代碼都在 git 倉庫當中。希望大家能 fork 下來好好體會一下整個項目的設計。

總體來說,React 組件代碼能夠跑在命令行終端,確實是一件激動人心的事情,給前端釋放了更多想象的空間。本文對于這個能力的使用也只是冰山一角,更多使用姿勢等待你去解鎖,趕緊去玩一玩吧!

 

責任編輯:姜華 來源: 前端三元同學
相關推薦

2021-07-29 09:07:44

React視圖庫Web 開發

2022-01-04 09:02:24

瀏覽器命令行ttyd

2014-02-12 13:30:16

Linux命令行終端工具

2020-07-30 13:34:48

終端命令行Linux

2009-03-01 22:09:08

LinuxTerminal命令行終端

2023-03-28 08:40:22

命令行JSON用法

2011-09-05 15:09:07

Ubuntuw3m

2020-11-23 05:50:40

瀏覽器Web瀏覽器Linux

2021-07-15 13:32:12

Linux生成密碼

2019-12-09 09:23:04

Linux命令sort

2021-07-15 13:25:43

LinuxPDF

2020-02-17 11:05:27

GitHub代碼開發者

2013-01-29 14:08:58

UbuntuUbuntu手機Ubuntu手機操作系

2023-02-26 01:28:09

終端命令行工具

2020-12-10 16:16:08

工具代碼開發

2020-12-11 06:44:16

命令行工具開發

2021-07-12 14:53:27

LinuxGmail電子郵件

2019-05-21 10:38:17

Linux命令行

2009-02-17 23:21:12

autojump命令行下快速更改目錄

2010-03-24 14:08:10

CentOS命令行
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 午夜一级做a爰片久久毛片 精品综合 | 全免费a级毛片免费看视频免 | 国产成人免费视频 | 日韩一二三区视频 | av在线免费观看不卡 | 亚洲精品一区二区三区免 | re久久| 亚洲欧美久久 | 国产精品久久久久久久久久妞妞 | 中文字幕在线观看视频网站 | 99精品99| 欧美日本一区 | 在线一区 | 日韩中文不卡 | 亚洲视频中文字幕 | 欧美另类日韩 | 欧美日韩精品一区二区天天拍 | 久久国产精品久久 | 一区二区三区欧美 | 久久精品久久久 | 尹人av| 国产精品久久一区二区三区 | 麻豆久久久久 | 三级欧美| av中文字幕在线 | 91国产在线播放 | 成人免费一区二区三区视频网站 | 日韩av免费在线观看 | 91网在线播放| 日韩理论电影在线观看 | 久久91av | 亚洲av毛片成人精品 | 国产一区二区三区视频在线观看 | 国产一区黄色 | 四虎影院欧美 | 亚洲三级在线观看 | 国产一区二区在线看 | 日韩精品极品视频在线观看免费 | 欧美日韩不卡合集视频 | 欧美午夜精品 | 成人在线一级片 |