prism-react-renderer 扩展语言模块

prism-react-renderer 扩展语言模块

github:github.com/FormidableL...

demo:github.com/FormidableL...

prism-react-renderer 是基于 Prism.js 的 React 封装库,解决了 Prism.js 在代码更新时直接操作 DOM、从而破坏 React Diff 算法的问题。 使用 prism-react-renderer 时,代码的更新不会触发整个组件 DOM 的重绘,因而更加高效和稳定。

prism-react-renderer本身支持的语言是有限的

arduino 复制代码
export const languagesToBundle = <const>[
  "markup",
  "jsx",
  "tsx",
  "swift",
  "kotlin",
  "objectivec",
  "js-extras",
  "reason",
  "rust",
  "graphql",
  "yaml",
  "go",
  "cpp",
  "markdown",
  "python",
  "json",
]

添加语言支持的关键步骤是在组件渲染之前,先加载并注册新的语言定义

1.安装语言模块

首先,你需要通过 npm 或 yarn 安装所需的 Prism.js 语言组件。例如,要添加 Kotlin 支持:

bash 复制代码
npm install prismjs/components/prism-kotlin

2.在组件中引入并注册语言

然后,在你的 React 组件文件中,在导入 Highlight 组件之后 ,立即通过 importrequire 语句引入对应的语言模块。

javascript 复制代码
// 1. 引入 prism-react-renderer 的核心组件
import { Highlight, Prism } from "prism-react-renderer";
​
// 2. 关键步骤:将 Prism 实例设置为全局变量,以便语言插件可以注册自己
// 注意:以下两种方式选择其一即可
// 方式一:适用于现代前端环境(如Webpack 5+可能没有全局的global或window)
import Prism from 'prismjs';
(typeof global !== "undefined" ? global : window).Prism = Prism;
​
// 方式二:或者直接绑定到全局对象
(typeof globalThis !== "undefined" ? globalThis : window).Prism = Prism;
​
// 3. 动态导入或要求你需要的特定语言
// 使用 ES6 动态导入 (异步)
import("prismjs/components/prism-kotlin");
import("prismjs/components/prism-swift");
// ... 导入其他所需语言
​
// 或者使用 require (同步)
require("prismjs/components/prism-kotlin");
require("prismjs/components/prism-swift");
​
// 4. 现在你可以在组件中使用这些语言了
const YourCodeComponent = ({ code }) => {
  return (
    <Highlight Prism={Prism} code={code} language="kotlin">
      {/* ... 你的渲染逻辑 ... */}
    </Highlight>
  );
};
​

!注意:

1.一定注意需要将 prism-react-renderer 中的Prism与 Prismjs 的Prism实例为同一个,并且同步挂载到全局

2.语言模块的加载和注册必须在包含 <Highlight> 的组件渲染之前完成

在工程化环境中

推荐结合工具方法实现 文件名 → 语言识别 → 动态加载

以下是我写的实例

步骤

javascript 复制代码
// 1.引入初始化实例
import { Highlight, themes, Prism  } from 'prism-react-renderer'
(typeof global !== "undefined" ? global : window).Prism = Prism
​
// 2.获取文件包,通过 getLangFromFileName 方法指定语言语法
const detectedLanguage = getLangFromFileName(filename)
​
// 3.通过hook 与 语言语法 动态加载素材包
usePrismLanguageLoader(detectedLanguage)
1. 根据文件名推断语言

获取文件包 getLangFromFileName 方法

dart 复制代码
// 分别建立  LANGUAGE_MAP 、 SPECIAL_FILE_MAP 、 COMPOUND_EXTENSIONS  分别为普通文件后缀名 、 特殊文件名 、 特殊复合文件名
// 其中 PRISM_LANGUAGE_MAP 样式为
export const LANGUAGE_MAP: Record<string, string> = {
  '.js': 'javascript'
	.......
}
​
export function getLangFromFileName(fileName: string): string {
  if (!fileName) return 'text'
  const lowerFileName = fileName.toLowerCase()
  const baseName = lowerFileName.split('/').pop() || ''
​
  if (SPECIAL_FILE_MAP[baseName]) {
    return SPECIAL_FILE_MAP[baseName]
  }
​
  for (const [extension, language] of Object.entries(COMPOUND_EXTENSIONS)) {
    if (lowerFileName.endsWith(extension)) {
      return language
    }
  }
​
  const lastPart = lowerFileName.match(/.[^./\]+$/)
​
  if (lastPart) {
    const extension = lastPart[0]
​
    return LANGUAGE_MAP[extension] ?? 'text'
  }
​
  return 'text'
}
​
2.动态加载语言定义

usePrismLanguageLoader

typescript 复制代码
// 声明缓存位置
const loadedLanguages = new Set<string>()
​
const PRISM_LANGUAGE_MAP: Record<string, () => Promise<any>> = {
  java: () => import('prismjs/components/prism-java'),
}
​
export function usePrismLanguageLoader(language: string) {
  useEffect(() => {
​
    if (language === 'text' || loadedLanguages.has(language)) {
      return
    }
​
    const languageLoader = PRISM_LANGUAGE_MAP[language]
​
    if (!languageLoader) {
      return
    }
​
    languageLoader().then(() => {
      loadedLanguages.add(language)
    }).catch(() => {
      //
    })
  }, [language])
  return
}
3.综合使用
ini 复制代码
import { Highlight, Prism } from "prism-react-renderer";
​
(globalThis as any).Prism = Prism;
​
const CodeBlock = ({ code, filename }: { code: string; filename: string }) => {
  const lang = getLangFromFileName(filename);
  usePrismLanguageLoader(lang);
​
  return (
<Highlight {...defaultProps} code={fileContent} language={detectedLanguage} theme={theme.github}>
    {({ tokens, getLineProps, getTokenProps }) => (
      <List
        height={500}          // 可视区域高度
        itemCount={tokens.length} // 总行数
        itemSize={20}         // 每行高度
        width="100%"          // 列表宽度
      >
        {({ index, style }) => (
          <div style={style} {...getLineProps({ line: tokens[index], key: index })}>
            {tokens[index].map((token, key) => (
              <span key={key} {...getTokenProps({ token, key })} />
            ))}
          </div>
        )}
      </List>
    )}
  </Highlight>
  );
};

参考文章:

关于prism-react-renderer在nextjs中的动态导入prism-react-renderer是基于pr - 掘金

相关推荐
San301 天前
深度解析 React 组件化开发:从 Props 通信到样式管理的进阶指南
前端·javascript·react.js
AAA阿giao1 天前
深度解析 React 项目架构:从文件结构到核心 API 的全面拆解
前端·javascript·react.js
昨晚我输给了一辆AE861 天前
关于 react-hook-form 的 isValid 在有些场景下的值总是 false 问题
前端·react.js
Mintopia1 天前
🏗️ B端架构中的用户归因与埋点最佳实践
前端·react.js·架构
Blossom.1181 天前
大模型AI Agent实战:ReAct框架从零实现与金融研报分析系统
人工智能·学习·react.js·stable diffusion·金融·aigc·知识图谱
南山安1 天前
React 学习:父传子的单项数据流——props
javascript·react.js·前端框架
soda_yo1 天前
React哲学:保持组件纯粹 哈气就要哈得纯粹
前端·react.js·设计
Bigger1 天前
Tauri (22)——让 `Esc` 快捷键一层层退出的分层关闭方案
前端·react.js·app
T___T1 天前
从入门到实践:React Hooks 之 useState 与 useEffect 核心解析
前端·react.js·面试