使用marked和highlight.js对GPT接口返回的代码块渲染,高亮显示,支持复制,和选择不同的高亮样式

1,857 阅读1分钟

最近随着chatGPT越来越火,各大公司都基于gpt衍生了一些业务,其中比较多的是使用openAI搭建自己公司内部使用的问答项目,搭配回答问题中,嵌入公司自身的产品等。

openAI接口,返回的回答通过div中dangerouslySetInnerHTML属性嵌入,能够有较好的显示样式,但是对越代码块部分,显示不够显眼,对其中的代码进行高亮和支持代码复制功能变得必不可少。

使用效果: image.png

marked 是一个流行的 JavaScript 库,用于将 Markdown 格式的文本转换为 HTML。它提供了简单易用的 API,使得在网页中渲染和展示 Markdown 内容变得非常方便。

要使用 marked 插件,首先需要引入 marked 库。你可以通过以下几种方式之一来获取并引入 marked

  1. 通过 CDN 引入:
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
  1. 使用 NPM 安装:
npm install marked

然后在你的 JavaScript 文件中导入 marked

import marked from 'marked';
  1. 使用marked对代码块进行高亮显示和增加复制功能:
<div className={styles["content-items"]}
     dangerouslySetInnerHTML={{ __html: 
     marked(item.content, {
        highlight: function (code, language) {
        // 通过时间戳生成唯一标识
        const codeIndex = parseInt(Date.now() + "") + Math.floor(Math.random() * 10000000);
        // 格式化第一行是右侧language和 “复制” 按钮;
        let html = ` 
            <div class=${styles["code-block-header"]}>
                <span>${language}</span>
                <span id='copy-btn'data-clipboard-action="copy" data-clipboard-target="#copy${codeIndex}">复制代码</span>
            </div>
            `;
        
        //代码部分
        if (code) {
           try {
           // 使用 highlight.js 对代码进行高亮显示
              const preCode = hljs.highlightAuto(code).value;
              // 将代码包裹在 textarea 中,由于防止textarea渲染出现问题,这里将 "<" 用 "&lt;" 代替,不影响复制功能
              return `<pre class='${styles["hljs-customer"]} hljs'>
                          <code>${preCode}</code>
                      </pre>
                      <textarea style="position: absolute;top: -9999px;left: -9999px;z-index: -9999;" id="copy${codeIndex}">${code.replace(/<\/textarea>/g,"&lt;/textarea>")}</textarea>`;
            } catch (error) {
               console.log(error);
              }
          }
        }}) as string,
      =}}
    />
  </div>
  1. 复制部分代码:
const clipboard = new Clipboard("#copy-btn");
    // 复制成功失败的提示
    clipboard.on("success", (e) => {
      message.success(
        intl.formatMessage({
          id: "contentPage.复制成功",
        })
      );
    });
    clipboard.on("error", (e) => {
      message.error(
        intl.formatMessage({
          id: "contentPage.复制失败",
        })
      );
    });

放在项目组件初始化的生命周期函数中,react放在useEffect中,vue放在mounted中。

5.根据喜好,选择不同的highlight样式: highlight.js提供了多种高亮显示样式,官网链接

image.png

引入样式方式:

import "highlight.js/styles/paraiso-light.css";