关于前端:在命令行里也能用-React

1次阅读

共计 6877 个字符,预计需要花费 18 分钟才能阅读完成。

用过 React 的同学都晓得,React 作为一个视图库,在进行 Web 开发的时候须要装置两个模块。

npm install react --save
npm install react-dom --save

react 模块次要提供了组件的生命周期、虚构 DOM Diff、Hooks 等能力,以及将 JSX 转换为虚构 DOM 的 h 办法。而 react-dom 次要对外裸露一个 render 办法,将虚构 DOM 转化为实在 DOM。

import React from 'react'
import ReactDOM from 'react-dom'
/* import ReactDOM from 'react-dom/server' // 服务的渲染 */

class Hello extends React.component {render() {return <h1>Hello, world!</h1>,}
}

ReactDOM.render(
  <Hello />,
  document.getElementById('root')
)

如果咱们将 react-dom 换成 react-native 就能够将虚构 DOM 转换为安卓或 iOS 的原生组件。我在之前的文章中介绍过,虚构 DOM 最大的劣势并不是其 Diff 算法,而是将 JSX 转换为对立的 DSL,通过其形象能力实现了跨平台的能力。除了官网提供的 react-domreact-native,甚至能够渲染到命令行上,这也是咱们明天介绍的 ink

🔗 npm ink: https://www.npmjs.com/package…

ink 外部应用 facebook 基于 C++ 开发的一款跨平台渲染引擎 yoga,反对 Flex 布局,性能非常弱小。另外,React Native 外部应用了该引擎。

初始化

这里有一个官网提供的脚手架,咱们能够间接通过这个脚手架来创立一个我的项目。

$ mkdir ink-app
$ cd ink-app
$ npx create-ink-app

如果你想应用 TypeScript 来编写我的项目,你也能够应用如下命令:

$ npx create-ink-app --typescript

生成的代码如下:

// src/cli.js
#!/usr/bin/env node
const ink = require('ink')
const meow = require('meow')
const React = require('react')
const importJsx = require('import-jsx')

const ui = importJsx('./ui')

const cli = meow(`
    Usage
      $ ink-cli
    Options
        --name  Your name
`)

ink.render(React.createElement(ui, cli.flags))
// src/ui.js
const App = (props) => (
  <Text>
    Hello, <Text color = "green">
          {props.name || 'UserName'}
      </Text>
  </Text>
)

module.exports = App;

除了 inkreact,脚手架我的项目还引入了 meowimport-jsx 两个库。

meow 的次要作用是运行命令时,对参数进行解析,将解析的参数放到 flags 属性中,其作用与 yargscommander 一样,是构建 CLI 工具的必备利器。

const meow = require('meow')
// 传入的字符串,作为 help 信息。const cli = meow(`
    Options
        --name  Your name
        --age   Your age
`)
console.log('flags:', cli.flags)

另一个 import-jsx 的次要作用,就是将 jsx 字符串转化为 createElement 办法的模式。

// ui.js
const component = (props) => (
  <Text>
    Hello, <Text color = "green">
          {props.name || 'UserName'}
      </Text>
  </Text>
)

// cli.js
const importJsx = require('import-jsx')
const ui = importJsx('./ui')

console.log(ui.toString()) // 输入转化后的后果 
// 转化后果:props => /*#__PURE__*/React.createElement(
  Text,
  null,
  "Hello,",
  /*#__PURE__*/React.createElement(
    Text, {color: "green"},
    props.name || 'UserName'
     )
)

这一步的工作个别由 babel 实现,如果咱们没有通过 babel 本义 jsx,应用 import-jsx 就相当于是运行时本义,对性能会有损耗。然而,在 CLI 我的项目中,自身对性能要求也没那么高,通过这种形式,也能更疾速的进行我的项目搭建。

内置组件

因为是非浏览器的运行环境,inkreact-native 一样提供了内置的一些组件,用于渲染终端中的特定元素。

\<Text\>

<Text> 组件用于在终端渲染文字,能够为文字指定特定的色彩、加粗、斜体、下划线、删除线等等。

DEMO:

// ui.js
const React = require('react')
const {Text} = require('ink')
moudle.exports = () => (<>
  <Text>I am text</Text>
  <Text bold>I am bold</Text>
  <Text italic>I am italic</Text>
  <Text underline>I am underline</Text>
  <Text strikethrough>I am strikethrough</Text>
  <Text color="green">I am green</Text>
  <Text color="blue" backgroundColor="gray">I am blue on gray</Text>
</>)

// cli.js
const React = require('react')
const importJsx = require('import-jsx')
const {render} = require('ink')

const ui = importJsx('./ui')
render(React.createElement(ui))

其次要作用就是设置渲染到终端上的文本款式,有点相似于 HTML 中的 <font> 标签。

除了这种常见的 HTML 相干的文本属性,还反对比拟非凡的 wrap 属性,用于将溢出的文本进行截断。

长文本在超出终端的长度时,默认会进行换行解决。

<Text>loooooooooooooooooooooooooooooooooooooooong text</Text>

如果加上 wrap 属性,会对长文本进行截断。

<Text wrap="truncate">
  loooooooooooooooooooooooooooooooooooooooong text
</Text>

除了从尾部截断文本,还反对从文本两头和文本开始处进行截断。

<Text wrap="truncate">
  loooooooooooooooooooooooooooooooooooooooong text
</Text>
<Text wrap="truncate-middle">
  loooooooooooooooooooooooooooooooooooooooong text
</Text>
<Text wrap="truncate-start">
  loooooooooooooooooooooooooooooooooooooooong text
</Text>

\<Box\>

<Box> 组件用于布局,除了反对相似 CSS 中 marginpaddingborder 属性外,还能反对 flex 布局,能够将 <Box> 了解为 HTML 中设置了 flex 布局的 div(<div style="display: flex;">)。

上面咱们先给一个 <Box> 组件设置高度为 10,而后主轴方向让元素两端对齐,穿插轴方向让元素位于底部对齐。

而后在给外部的两个 <Box> 组件设置一个 padding 和一个不同款式的边框。

const App = () => <Box
  height={10}
  alignItems="flex-end"
  justifyContent="space-between"
>
    <Box borderStyle="double" borderColor="blue" padding={1} >
    <Text>Hello</Text>
  </Box>
    <Box borderStyle="classic"  borderColor="red" padding={1} >
      <Text>World</Text>
  </Box>
</Box>

最终成果如下:

比拟非凡的属性是边框的款式:borderStyle,和 CSS 提供的边框款式有点出入。

<Box borderStyle="single">
  <Text>single</Text>
</Box>
<Box borderStyle="double">
  <Text>double</Text>
</Box>
<Box borderStyle="round">
  <Text>round</Text>
</Box>
<Box borderStyle="bold">
  <Text>bold</Text>
</Box>
<Box borderStyle="singleDouble">
  <Text>singleDouble</Text>
</Box>
<Box borderStyle="doubleSingle">
  <Text>doubleSingle</Text>
</Box>
<Box borderStyle="classic">
  <Text>classic</Text>
</Box>

<Box> 组件提供的其余属性和原生的 CSS 基本一致,具体介绍能够查阅其文档:

🔗 ink#Box:https://www.npmjs.com/package/ink#box

\<Newline\>

<NewLine> 组件相当于间接在终端中增加一个 \n 字符,用于换行(PS:只反对插入在 <Text> 元素之间);

const App = () => (<>
  <Text>Hello</Text>
  <Text>World</Text>
</>)

const App = () => (<>
  <Text>Hello</Text>
  <Newline />
  <Text>World</Text>
</>)

\<Spacer\>

<Spacer> 组件用于隔开两个元素,应用后,会将距离开两个元素隔开到终端的两边,成果有点相似于 flex 布局的两端对齐(justify-content: space-between;

const App1 = () => <Box>
  <Text>Left</Text>
  <Spacer />
  <Text>Right</Text>
</Box>;

const App2 = () => <Box justifyContent="space-between">
  <Text>Left</Text>
  <Text>Right</Text>
</Box>;

下面两段代码的表现形式统一:

内置 Hooks

ink 除了提供一些布局用的组件,还提供了一些 Hooks。

useInput

可用于监听用户的输出,useInput 承受一个回调函数,用户每次按下键盘的按键,都会调用 useInput 传入的回调,并传入两个参数。

useInput((input: string, key: Object) => void)

第一个参数:input,示意按下按键对应的字符。第二个参数:key,为一个对象,对应按下的一些功能键。

  • 如果按下回车,key.return = true
  • 如果按下删除键,key.delete = true
  • 如果按下 esc 键,key.escape = true

具体反对哪些性能按键,能够参考官网文档:

🔗ink#useInput:https://www.npmjs.com/package/ink#useinputinputhandler-options

上面通过一个 DEMO,展现其具体的应用形式,在终端上记录用户的所有输入,如果按下的是删除键,则删除最近记录的一个字符。

const React = require('react')
const {useInput, Text} = require('ink')

const {useState} = React
module.exports = () => {const [char, setChar] = useState('')
  useInput((input, key) => {if (key.delete) {
      // 按下删除键,删除一个字符
      setChar(char.slice(0, -1))
      return
    }
    // 追加最新按下的字符
    setChar(char + input)
  })
  return <Text>input char: {char}</Text>
}

useApp

对外裸露一个 exit 办法,用于退出终端。

const React = require('react')
const {useApp} = require('ink')

const {useEffect} = React
const App = () => {const { exit} = useApp()

    // 3s 后退出终端
    useEffect(() => {setTimeout(() => {exit();
        }, 3000);
    }, []);

    return <Text color="red">3s 后退出终端……</Text>
}

useStdin

用于获取命令行的输出流。这里用一个简略的案例,来模仿用户登录。

const React = require('react')
const {useStdin} = require('ink')
const {useState, useEffect} = React
module.exports = () => {const [pwd, setPwd] = useState('')
  const {stdin} = useStdin()
  
  useEffect(() => {
    // 设置明码后,终止输出
    if (pwd) stdin.pause()}, [pwd])
  
  stdin.on('data', (data) => {
    // 提取 data,设置到 pwd 变量中
    const value = data.toString().trim()
    setPwd(value)
  })
  // pwd 为空时,提醒用户输出明码
  if (!pwd) {return <Text backgroundColor="blue">password:</Text>}

  return pwd === 'hk01810'
    ? <Text color="green"> 登录胜利 </Text>
    : <Text color="red"> 有内鬼,终止交易 </Text>
}

useStdout

用于获取命令行的输入流。会裸露 stdout 的写入流,还会裸露一个 write 办法,用于在终端进行输出。

const React = require('react')
const {useStdout} = require('ink')
const {useEffect} = React
module.exports = () => {const { write} = useStdout()
  useEffect(() => {
    // 在终端进行写入
        write('Hello from Ink to stdout')
    }, [])
  return null
}

第三方组件

除了内置的这些组件和 Hooks 外,还有丰盛的第三方生态。比方:Loading 组件、超链接组件、表格组件、高亮组件、多选组件、图片组件……

🔗 ink# 第三方组件:https://www.npmjs.com/package/ink#useful-components

ink-spinner

ink-link

ink-table

ink-syntax-highlight

ink-muti-select

调试工具

ink 属于 React 生态,天然可能反对 React 官网提供的调试工具 React Devtools

$ npm install react-devtools # 装置调试工具 
$ npx react-devtools # 启动调试工具 

而后,在启动利用时,在后面设置 DEV 全局变量。

DEV=true node src/cli

运行后的成果如下:

总结

React 的确是视图开发的一把利器,再加上 Hooks 的加持,其形象能力失去了进一步的晋升,对立的 DSL 加上 虚构 DOM,照理来说,是能够在任何平台进行渲染的。甚至,微软官网都开发了一个 React Native for Windows,要害是这个货色不仅仅能开发 Windows 的桌面软件,还能够开发 mac 的桌面软件。

有点跑题,说回 ink,大家熟知的 Gatsby 的命令行工具也是通过 ink 进行开发的。如果大家后续有本地的 CLI 工具须要实现,能够思考这款工具,至多不用懊恼如何在命令行进行文本对齐。

正文完
 0