使用 Prism.js 对代码进行语法高亮

简介: 通常我们在开发博客网站或者技术社区(类似掘金)这类网站的时候,就会有需求“对代码进行语法高亮”,本文主要记录笔者在开发的时候遇到的问题以及解决方案。

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第4天,点击查看活动详情

我正在参加 码上掘金体验活动,详情:show出你的创意代码块

前言

通常我们在开发博客网站或者技术社区(类似掘金)这类网站的时候,就会有需求“对代码进行语法高亮”,我在开发 mdx editor(微信排版编辑器) 的时候,也有这个功能。

社区对应语法高亮比较流行的有 highlight.jsPrism.jsPrism.js 使用非常简单,只需要引一行<script>就可以对文档中的代码进行高亮, 然而,它有一个比较严重的问题。文档虽然简单,而我的项目是 React 项目,当想要增加一种语法高亮就会变得有些麻烦了,下面介绍下我的实现方式。

Prism.js 的使用

通过官网可以下载页面进行配置,并且下载对应的 js 和 css

下载配置

可以看到 prism.js 非常轻量。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link href="prism.css" rel="stylesheet" />
  </head>
  <body>
    <pre><code class="language-css">p { color: red }</code></pre>
    <script>
        window.Prism = window.Prism || {};
        window.Prism.manual = false;
        </script>
    <script  src="prism.js"></script>
  </body>
</html>

只需要引入 prism.js 和 prism.css 就会对文档中的 code 代码块进行高亮,code 代码需要使用language-前缀,代码块需要使用 <pre> 标签,行内代码可以直接使用 <code class="language-css">就可以, prism.js 会在DOMContentLoaded 的时候,自动触发,对代码进行高亮.

如果我们的内容是异步加载的,这块时候就需要设置手动执行

<script src="prism.js" data-manual></script>

或者

<script>
window.Prism = window.Prism || {};
window.Prism.manual = true;
</script>
<script src="prism.js"></script>

在 react 中使用

首先我们可以安装

npm install --save prismjs
# 或者
yarn add prismjs

我们可以从 prism js 包中手动加载这些资源,但问题是 webpack 会将所有语言的支持都进行打包。如果只使用所提供几种语言支持,那么这可能会影响最终整个 bandle js 的大小。接下来我们需要做的是添加一个 babel 插件,负责加载对 Prism.js 的 CSS 和语言支持。

使用 npm 安装这个 babel 插件

npm install --save-dev babel-plugin-prismjs
# 或者
yarn add --dev babel-plugin-prismjs

然后在 .bablerc 配置需要支持的语言和插件

{
  "plugins": [
    ["prismjs", {
      "languages": ["javascript", "css", "html"],
      "plugins": ["line-numbers", "show-language"],
      "theme": "okaidia",
      "css": true
    }]
  ]
}

我们可以指定使用哪些主题和插件,以及支持哪些语言。babel 将自动添加的插件以及所需的 CSS 。如果将 CSS 设置为 true,请先确保已经配置了 css-loader,能够支持 import css

实现 react 组件

我们使用 hooks 来实现一个组件

import React, { useRef, useEffect } from 'react';

function PrismCode({ code, language, plugins = [] }) {
  const ref = useRef(null);
  useEffect(() => {
    if (ref && ref.current) {
      Prism.highlightElement(ref.current);
    }
  }, [code]);

  return (
    <pre className={plugins.join(" ")}>
      <code ref={ref} className={`prism-code language-${language}`}>
        {code}
      </code>
    </pre>
  );
}

上面代码中 Prism.highlightElement 对当前代码中的 code 进行高亮。

使用

import React from "react"
import {PrismCode} from "./component"
const code = `
const foo = 'foo';
const bar = 'bar';
console.log(foo + bar);
`
const Example = () => (
  <PrismCode
    code={code}
    language="js"
    plugins={["line-numbers"]}
  />
)

很简单,只需要指定插件和语言, 就可以对代码进行语法高亮了。

代码片段

高级技巧

我们知道 react 是采用虚拟 dom,通过虚拟 dom 的 diff 对比,将 dom 中修改的部分提交更新到页面上。

如果使用上面的组件,我们页面中的 code 是实时编辑的,那么整个代码块都会重新渲染,因为 Prism.js 库修改了 DOM,导致 React 丢失了标记,这就失去了使用 react 虚拟 DOM 的优势。

如果你也遇到这些问题,则需要使用 Prism.tokenise API。 这个函数是 Prism.js 底层用来为突出显示的代码部分构建 HTML 的。 关于这个函数的更多信息请查看官网

使用这个函数,可以在 React 组件中突出显示的代码标记。 这样,React 可以正确地跟踪 DOM 并且不会遇到任何错误。

prism-react-renderer

让人高兴的是社区已经有一个组件 prism-react-renderer 帮我们封装好了 Prism.tokenise API,当我们修改 code 的时候,不会让整个组件的 DOM 更新。

安装

# npm
npm install --save prism-react-renderer
# yarn
yarn add prism-react-renderer

使用

import React from "react";
import { render } from "react-dom";
import Highlight, { defaultProps } from "prism-react-renderer";

const exampleCode = `
(function someDemo() {
  var test = "Hello World!";
  console.log(test);
})();

return () => <App />;
`;

render((
  <Highlight {...defaultProps} code={exampleCode} language="jsx">
    {({ className, style, tokens, getLineProps, getTokenProps }) => (
      <pre className={className} style={style}>
        {tokens.map((line, i) => (
          <div {...getLineProps({ line, key: i })}>
            {line.map((token, key) => (
              <span {...getTokenProps({ token, key })} />
            ))}
          </div>
        ))}
      </pre>
    )}
  </Highlight>,
  document.getElementById('root')
);

同样也支持自定义语言和皮肤。

代码行高亮

我在mdx editor 中也使用了 prism-react-renderer,当我把代码开源后,本以为完成了这个功能, 在这里感谢@蓝色的秋风 提的 issues,就是要支持代码行高亮。

其实 Prism.js 本来就支持diff-highlight, 但我使用的 prism-react-renderer,还没支持,官方还有个 issues 没有 close,总不能等官方实现这个功能在来实现吧?接下来就得自己实现了。

const isDiff = language.startsWith('diff-')

  let highlightStyle = []

  let code = children
  if (isDiff) {
    code = []
    language = language.substr(5)
    highlightStyle = children.split('\n').map((line) => {
      if (line.startsWith('+')) {
        code.push(line.substr(1))
        return 'inserted'
      }
      if (line.startsWith('-')) {
        code.push(line.substr(1))
        return 'deleted'
      }
      code.push(line)
    })
    code = code.join('\n')
  }

主要方式是通过判断每一行的第一个字符是否是 +-,对应在遍历代码的是加上高亮的样式。

使用

在语法前面加 diff- 比如 diff-jsdiff-jsx, diff-ts,代码前面可以加 +,-,就可以高亮代码了。

效果

这里我就不贴代码了,感兴趣的小伙伴可以移步 github 查看源码


推荐下我的开源程序

若对你有帮助记得点个 star,感谢!

以上就是本文全部内容,希望这篇文章对大家有所帮助,也可以参考我往期的文章或者在评论区交流你的想法和心得,欢迎一起探索前端。

本文首发掘金平台,来源小马博客

相关文章
|
2月前
|
JavaScript 前端开发 算法
流量分发代码实战|学会用JS控制用户访问路径
流量分发工具(Traffic Distributor),又称跳转器或负载均衡器,可通过JavaScript按预设规则将用户随机引导至不同网站,适用于SEO优化、广告投放、A/B测试等场景。本文分享一段不到百行的JS代码,实现智能、隐蔽的流量控制,并附完整示例与算法解析。
82 1
|
3月前
|
JavaScript 前端开发
怀孕b超单子在线制作,p图一键生成怀孕,JS代码装逼娱乐
模拟B超单的视觉效果,包含随机生成的胎儿图像、医疗文本信息和医院标志。请注意这仅用于前端开发学习
|
3月前
|
JavaScript
JS代码的一些常用优化写法
JS代码的一些常用优化写法
67 0
|
5月前
|
存储 JavaScript 前端开发
在NodeJS中使用npm包进行JS代码的混淆加密
总的来说,使用“javascript-obfuscator”包可以帮助我们在Node.js中轻松地混淆JavaScript代码。通过合理的配置,我们可以使混淆后的代码更难以理解,从而提高代码的保密性。
425 9
|
6月前
|
前端开发 JavaScript
【Javascript系列】Terser除了压缩代码之外,还有优化代码的功能
Terser 是一款广泛应用于前端开发的 JavaScript 解析器和压缩工具,常被视为 Uglify-es 的替代品。它不仅能高效压缩代码体积,还能优化代码逻辑,提升可靠性。例如,在调试中发现,Terser 压缩后的代码对删除功能确认框逻辑进行了优化。常用参数包括 `compress`(启用压缩)、`mangle`(变量名混淆)和 `output`(输出配置)。更多高级用法可参考官方文档。
427 11
|
6月前
|
JavaScript 前端开发 算法
JavaScript 中通过Array.sort() 实现多字段排序、排序稳定性、随机排序洗牌算法、优化排序性能,JS中排序算法的使用详解(附实际应用代码)
Array.sort() 是一个功能强大的方法,通过自定义的比较函数,可以处理各种复杂的排序逻辑。无论是简单的数字排序,还是多字段、嵌套对象、分组排序等高级应用,Array.sort() 都能胜任。同时,通过性能优化技巧(如映射排序)和结合其他数组方法(如 reduce),Array.sort() 可以用来实现高效的数据处理逻辑。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
8月前
|
人工智能 程序员 UED
【01】完成新年倒计时页面-蛇年新年快乐倒计时领取礼物放烟花html代码优雅草科技央千澈写采用html5+div+CSS+JavaScript-优雅草卓伊凡-做一条关于新年的代码分享给你们-为了C站的分拼一下子
【01】完成新年倒计时页面-蛇年新年快乐倒计时领取礼物放烟花html代码优雅草科技央千澈写采用html5+div+CSS+JavaScript-优雅草卓伊凡-做一条关于新年的代码分享给你们-为了C站的分拼一下子
328 21
【01】完成新年倒计时页面-蛇年新年快乐倒计时领取礼物放烟花html代码优雅草科技央千澈写采用html5+div+CSS+JavaScript-优雅草卓伊凡-做一条关于新年的代码分享给你们-为了C站的分拼一下子
|
6月前
|
JavaScript 前端开发 API
JavaScript中通过array.map()实现数据转换、创建派生数组、异步数据流处理、复杂API请求、DOM操作、搜索和过滤等,array.map()的使用详解(附实际应用代码)
array.map()可以用来数据转换、创建派生数组、应用函数、链式调用、异步数据流处理、复杂API请求梳理、提供DOM操作、用来搜索和过滤等,比for好用太多了,主要是写法简单,并且非常直观,并且能提升代码的可读性,也就提升了Long Term代码的可维护性。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
8月前
|
前端开发 JavaScript
【02】v1.0.1更新增加倒计时完成后的放烟花页面-优化播放器-优化结构目录-蛇年新年快乐倒计时领取礼物放烟花html代码优雅草科技央千澈写采用html5+div+CSS+JavaScript-优雅草卓伊凡-做一条关于新年的代码分享给你们-为了C站的分拼一下子
【02】v1.0.1更新增加倒计时完成后的放烟花页面-优化播放器-优化结构目录-蛇年新年快乐倒计时领取礼物放烟花html代码优雅草科技央千澈写采用html5+div+CSS+JavaScript-优雅草卓伊凡-做一条关于新年的代码分享给你们-为了C站的分拼一下子
193 14
【02】v1.0.1更新增加倒计时完成后的放烟花页面-优化播放器-优化结构目录-蛇年新年快乐倒计时领取礼物放烟花html代码优雅草科技央千澈写采用html5+div+CSS+JavaScript-优雅草卓伊凡-做一条关于新年的代码分享给你们-为了C站的分拼一下子
|
7月前
|
人工智能 数据可视化 机器人
【通义灵码】三句话生成P5.js粒子特效代码,人人都可以做交互式数字艺术
我发掘出的通义灵码AI程序员新玩法:三句话生成P5.js粒子特效代码,人人都可以做交互式数字艺术
278 6