前端圈

分享与交流前端开发相关知识

创建一个React代码编辑器和语法荧光笔

在工作场所关于寻找一种可靠,有效的方法来完成简单的工作而又无需引入另一个依赖的沉重负担的推动下,我最近创建了一个基于React的代码语法突出显示器。

当然那里已经有其他东西了吗?

是的,是的。那里有一些突出显示代码语法的组件,但是到处购买第三方组件时需要考虑以下几点:

  1. 添加额外的依赖项会增加更多的代码权重,并给您的项目带来潜在的安全问题。如果可以避免这种情况,则应该这样做。
  2. 如果从工作的角度来看任务很小或不太繁重,那么在可能的情况下构建内部解决方案是值得的。
  3. 现有的第三方产品可能已经过期,也可能是付费期权(而且付费期权通常很昂贵)。

使用有用的Lea Verou制作的有用的Prism JS,我们构建了一个简单的点语法突出显示器,可以跟踪其自身状态并根据需要动态交换语言突出显示。

事不宜迟,这是如何做

从头开始构建React代码语法荧光笔

首先,启动并运行一个React项目,然后安装Prism JS

npm i prismjs

// or

yarn add prismjs

接下来,我们需要将CodeEditor组件添加到App.js主文件中,以启动其他所有功能。

import React, { useState } from "react";

// Styles
import "./styles.css";

// Components
import CodeEditor from "./CodeEditor";

export default function App() {
  const [editorLanguage, setEditorLanguage] = useState("javascript");

  return (
    <div className="App">
      <h1>React code syntax hightlighter</h1>

      <fieldset>
        <legend>Choose language:</legend>
        <input
          type="radio"
          id="javascript"
          name="language"
          value="javascript"
          checked={editorLanguage === "javascript"}
          onChange={() => setEditorLanguage("javascript")}
        />
        <label htmlFor="javascript">JavaScript</label>
        <input
          type="radio"
          id="xml"
          name="language"
          value="markup"
          checked={editorLanguage === "markup"}
          onChange={() => setEditorLanguage("markup")}
        />
        <label htmlFor="xml">XML</label>
        <input
          type="radio"
          id="css"
          name="language"
          value="css"
          checked={editorLanguage === "css"}
          onChange={() => setEditorLanguage("css")}
        />
        <label htmlFor="css">CSS</label>
      </fieldset>

      <CodeEditor language={editorLanguage} />
    </div>
  );
}

这里没有什么棘手的事情。我们useState从React 添加,以跟踪我们的语言选择。说到这,我们还获得了一些简单的单选按钮元素,这些元素将我们的语言选择更新为状态。

当用户选择其他语言时,我们会更新其状态,然后将其传递给我们的CodeEditor组件,该组件最终将调用Prism来更新语法突出显示。

需要注意的一个警告是,确保将checked属性添加到单选按钮,并将该单选按钮的语言与当前状态值进行比较。状态值和表单字段之间的这种关系将普通表单字段转换为受控组件

现在,尽管我们还没有创建CodeEditor组件(接下来将进行创建),但我们已经完成了App主体组件的所有必需的工作。

创建CodeEditor组件

现在我们来看一下主要事件:语法突出显示器本身,CodeEditor组件。

这是完整的:

import React, { useState, useEffect } from "react";
import Prism from "prismjs";

const CodeEditor = props => {
  const [content, setContent] = useState(props.content);

  const handleKeyDown = evt => {
    let value = content,
      selStartPos = evt.currentTarget.selectionStart;

    console.log(evt.currentTarget);

    // handle 4-space indent on
    if (evt.key === "Tab") {
      value =
        value.substring(0, selStartPos) +
        " " +
        value.substring(selStartPos, value.length);
      evt.currentTarget.selectionStart = selStartPos + 3;
      evt.currentTarget.selectionEnd = selStartPos + 4;
      evt.preventDefault();

      setContent(value);
    }
  };

  useEffect(() => {
    Prism.highlightAll();
  }, []);

  useEffect(() => {
    Prism.highlightAll();
  }, [props.language, content]);

  return (
    <div className="code-edit-container">
      <textarea
        className="code-input"
        value={content}
        onChange={evt => setContent(evt.target.value)}
        onKeyDown={handleKeyDown}
      />
      <pre className="code-output">
        <code className={`language-${props.language}`}>{content}</code>
      </pre>
    </div>
  );
};

export default CodeEditor;

它不是一个组件太大或太复杂,但让我们对其进行分解。

首先,我们从React 导入useEffectand useState钩子,以及导入PrismJS模块。

我们正在使用useState文本区域元素来跟踪输入的更新。根据Prism JS的文档,我们还将Prism样式的输入输出pre块中。

<pre className="code-output">
  <code className={`language-${props.language}`}>{content}</code>
</pre>

useEffect取代了许多React生命周期功能,例如componentDidMount()。就我们的目的而言,我们实际上是在观察通过道具传递的语言和输入更改的变化。如果发生任何一种情况,我们将触发Prism的highlightAll函数来更新样式。

useEffect(() => {
  Prism.highlightAll();
}, [props.language, content]);

这是非常整洁和有效的。React Hooks的好处之一!

最有趣的部分是onKeyDown事件发生的情况:

const handleKeyDown = evt => {
    let value = content,
      selStartPos = evt.currentTarget.selectionStart;

    console.log(evt.currentTarget);

    // handle 4-space indent on
    if (evt.key === "Tab") {
      value =
        value.substring(0, selStartPos) +
        " " +
        value.substring(selStartPos, value.length);
      evt.currentTarget.selectionStart = selStartPos + 3;
      evt.currentTarget.selectionEnd = selStartPos + 4;
      evt.preventDefault();

      setContent(value);
    }
  };

简而言之,每当用户按下某个键时,我们都会检查它是否为Tab键。如果是的话,我们从输入中更改当前状态值,并增加一些间距,从而沿途更新光标的选择点。这几乎使它像一个真正的代码编辑器。

就是这样。全做完了。但是,等等,事情看起来有点怪异。

《创建一个React代码编辑器和语法荧光笔》

让我们创建一些漂亮的样式来连接点。

添加样式

对于我们的样式,没有什么太过闪光的了,但在这里是:

/** ---------------------------- */
/** --- Code editor ------------ */
/** ---------------------------- */
.code-edit-container {
  position: relative;
  height: 500px;
  border: 1px solid hsl(0, 0%, 60%);
  background-color: hsl(212, 35%, 95%);
  margin: 1em 0;
}

.code-input,
.code-output {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  padding: 1rem;
  border: none;
  font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;
  font-size: 0.8rem;
  background: transparent;
  white-space: pre-wrap;
  line-height: 1.5em;
  word-wrap: break-word;
  font-size: 1rem;
}

.code-input {
  opacity: 1;
  margin: 0;
  color: hsl(0, 0%, 40%);
  resize: none;
}

.code-output {
  pointer-events: none;
  z-index: 3;
  margin: 0;
  overflow-y: auto;
}

code {
  position: absolute;
  top: 0;
  left: 0;
  margin: 0;
  padding: 1rem;
  display: block;
  color: hsl(0, 0%, 40%);
  font-size: 0.8rem;
  font-family: "PT Mono", monospace;
}

/* overrides */
.code-edit-container :not(pre) > code[class*="language-"],
.code-edit-container pre[class*="language-"] {
  background: transparent;
  margin: 0;
}

主要的优点是,我们在文本区域输入和代码输出之间创建了比较文本样式(字体大小,行高等),然后在文本区域输入上分层放置 Prism样式的输出。

最后,我们必须添加一些Prism替代项,以将所有内容整理整齐。

反应代码语法突出显示

有用的网址

就是这样。如果您希望看到它的实际效果,请在下面找到一个“代码沙箱”以及一些其他有用的链接。

点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注