ByteMD 插件系统详解

在这里插入图片描述
在这里插入图片描述

ByteMD 的插件系统是其强大扩展性的核心。它允许开发者在 Markdown 解析、AST 转换、HTML 渲染、以及编辑器 UI 交互的各个阶段注入自定义逻辑。这得益于 ByteMD 深度集成了 unified 处理器和其丰富的生态系统(remark 用于 Markdown,rehype 用于 HTML)。

1. 插件的本质

一个 ByteMD 插件是一个返回 BytemdPlugin 接口对象的函数。这个对象包含了多个可选的钩子 (Hooks),每个钩子都对应 ByteMD 内部处理流程的不同阶段。

BytemdPlugin 接口的主要成员:

  • viewerEffect?(el: HTMLElement): BytemdViewerContext | void:

    • 时机: 当 Viewer 组件渲染完成并挂载到 DOM 后执行。
    • 参数: elViewer 的根 DOM 元素。
    • 用途: 适合用于对最终渲染的 HTML 内容进行 DOM 操作,例如添加事件监听器、初始化第三方 JS 库(如流程图渲染库)、或者对图片进行懒加载处理等。
    • 返回值: 可以返回一个包含 destroy?: () => void 方法的对象,当 Viewer 卸载时,会调用 destroy 方法进行清理。
  • editorEffect?(editor: Editor): BytemdEditorContext | void:

    • 时机: 当 Editor 组件初始化完成(通常是 CodeMirror/ProseMirror 实例创建后)并挂载到 DOM 后执行。
    • 参数: editorCodeMirror 的实例(如果 ByteMD 内部使用 CodeMirror)。这个实例提供了对编辑器核心功能的直接访问,例如获取/设置内容、插入文本、注册快捷键等。
    • 用途: 适合用于与编辑器本身进行低级别交互,例如自定义快捷键、实现图片拖拽上传、自定义粘贴行为、或者在编辑器内容变化时触发额外逻辑。
    • 返回值: 同样可以返回一个包含 destroy?: () => void 方法的对象,用于编辑器卸载时的清理。
  • remark?(processor: RemarkProcessor): RemarkProcessor:

    • 时机: 在 Markdown 文本被 remark-parse 解析为 MDAST (Markdown Abstract Syntax Tree) 之后,但在转换为 HAST 之前。
    • 参数: processor 是一个 remark 处理器实例。
    • 用途: 这是处理 Markdown 语法的核心钩子。你可以通过 processor.use() 方法来注册自定义的 remark 插件。这些 remark 插件会遍历并修改 MDAST,例如:
      • 添加对 GFM (GitHub Flavored Markdown) 的支持(表格、任务列表)。
      • 识别和处理自定义的 Markdown 语法(例如特殊的块引用、自定义标签)。
      • 在 AST 级别进行内容转换或验证。
  • rehype?(processor: RehypeProcessor): RehypeProcessor:

    • 时机: 在 MDAST 被 remark-rehype 转换为 HAST (Hypertext Abstract Syntax Tree) 之后,但在转换为最终 HTML 字符串之前。
    • 参数: processor 是一个 rehype 处理器实例。
    • 用途: 这是处理 HTML 语法的核心钩子。你可以通过 processor.use() 方法来注册自定义的 rehype 插件。这些 rehype 插件会遍历并修改 HAST,例如:
      • 为图片添加 loading="lazy" 属性。
      • 处理代码块,添加行号或复制按钮。
      • 将数学公式的 AST 节点渲染为 KaTeX 或 MathJax。
      • 在 HTML 级别进行内容转换或优化。
  • actions?: BytemdAction[]:

    • 时机: 在编辑器工具栏渲染时。
    • 用途: 用于在 ByteMD 的工具栏中添加自定义按钮。每个 BytemdAction 对象定义了按钮的图标、标题和点击时的处理函数。这允许你为自定义功能提供用户友好的界面。
  • i18n?: Record<string, string>:

    • 用途: 提供插件内部文本的国际化支持。
  • override?: Partial<BytemdLocale>:

    • 用途: 覆盖 ByteMD 默认的国际化文本。
2. 插件的工作流
  1. 初始化: 当 BytemdEditorBytemdViewer 组件被实例化时,它会接收一个 plugins 数组。
  2. 钩子注册: ByteMD 核心会遍历这个 plugins 数组,收集每个插件返回对象中的所有钩子(remark, rehype, editorEffect, viewerEffect, actions 等)。
  3. Markdown 解析与 AST 转换:
    • 当 Markdown 内容变化时,unified 处理器被激活。
    • 首先执行 remark-parse 将 Markdown 解析为 MDAST。
    • 然后,所有注册的 remark 插件会依次处理 MDAST。
    • 接着,remark-rehype 将 MDAST 转换为 HAST。
    • 之后,所有注册的 rehype 插件会依次处理 HAST。
    • 最后,rehype-stringify 将 HAST 转换为 HTML 字符串。
  4. UI 交互:
    • actions 钩子定义的按钮会被添加到工具栏,其 handler 在点击时执行。
    • editorEffect 在编辑器初始化后执行,允许对 CodeMirror 实例进行操作。
    • viewerEffect 在预览器渲染 HTML 后执行,允许对渲染结果的 DOM 进行操作。

自定义插件示例:添加一个“插入日期时间”按钮和高亮特定文本

我们将创建一个自定义插件,实现两个功能:

  1. 工具栏按钮: 在工具栏添加一个“插入日期时间”按钮,点击后在光标处插入当前日期时间。
  2. 高亮特定关键词: 自动将 Markdown 中出现的特定关键词(例如“重要”、“注意”)在预览时用 <mark> 标签高亮显示。
1. 创建插件文件 (bytemd-plugin-custom.ts)
// src/plugins/bytemd-plugin-custom.ts
import type { BytemdPlugin } from 'bytemd';
import type { RemarkPlugin } from 'unified';
import type { Node } from 'unist'; // MDAST/HAST 节点的通用类型
import { visit } from 'unist-util-visit'; // 遍历 AST 的工具// 定义插件的选项(如果需要)
interface CustomPluginOptions {highlightKeywords?: string[];
}// Remark 插件:查找并标记需要高亮的文本
const remarkCustomHighlight: RemarkPlugin<[CustomPluginOptions?]> = (options) => {const keywords = options?.highlightKeywords || ['重要', '注意'];return (tree) => {visit(tree, 'text', (node: Node) => {// 确保是文本节点且有值if (typeof node.value === 'string') {let newValue = node.value;keywords.forEach(keyword => {// 使用正则表达式替换,以便处理多个出现和避免替换已经替换过的部分// 这里的替换比较简单,如果涉及到复杂的嵌套或HTML实体,需要更复杂的AST操作newValue = newValue.replace(new RegExp(`(${keyword})`, 'g'),`==$1==` // CommonMark 规范中双等号可以表示高亮(虽然不常用,但可以被rehype处理)// 或者自定义一个Markdown语法,例如 [[$1]],然后在rehype中处理);});node.value = newValue; // 更新节点值}});};
};// Rehype 插件:将特殊的 ==text== 标记转换为 <mark> 标签
// 这里我们需要处理 remark 阶段插入的 ==...== 标记
// Bytemd 的 gfm 插件默认可能会处理 ==,如果没有,则需要自定义rehype插件
// 实际上,更Robust的方式是remark插件创建自定义MDAST节点,然后rehype插件渲染它
const rehypeCustomHighlight: RemarkPlugin<[]> = () => (tree) => {visit(tree, { type: 'text' }, (node: Node) => {if (typeof node.value === 'string' && node.value.includes('==')) {// 匹配 ==text== 模式const parts = node.value.split(/(==[^=]+==)/g); // 分割字符串const newChildren = parts.flatMap(part => {if (part.startsWith('==') && part.endsWith('==')) {return {type: 'element',tagName: 'mark',properties: {},children: [{ type: 'text', value: part.slice(2, -2) }], // 移除 ==};}return { type: 'text', value: part };});// 替换当前文本节点为新的元素/文本节点数组// 这里需要更高级的unist-util-flatmap 或 unist-util-splice 等工具来替换节点// 简单起见,这里直接修改当前节点的兄弟节点,但这不是标准做法// 在实际的unified插件中,通常是返回新的树,或者替换当前节点// 为了演示方便,我们假设直接修改 text 节点的 value,并依赖rehype默认的HTML渲染// 但更严谨的自定义渲染,remark会创建custom node,rehype会识别并渲染// 由于bytemd-plugin-gfm包含了对 ==highlight== 的支持,这里可以直接利用// 如果bytemd默认不处理,我们需要构建一个真正的rehype插件来解析HTML文本// 假设 gfm 插件处理了 `==highlight==`,我们这里就不需要特殊的 rehype 转换// 而是让 remark 插件将文本转换为类似 `==关键词==` 的格式,让 gfm 插件去渲染}});
};const bytemdPluginCustom = (options?: CustomPluginOptions): BytemdPlugin => {return {// 插件名称,用于调试name: 'custom-plugin',// 工具栏动作actions: [{title: '插入日期时间',icon: '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.11 0-1.99.9-1.99 2L3 19c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v11zM7 10h5v5H7z"></path></svg>', // 一个简单的日期时间图标 SVGhandler: {type: 'action',click({ editor, appendBlock }) {const now = new Date();const year = now.getFullYear();const month = (now.getMonth() + 1).toString().padStart(2, '0');const day = now.getDate().toString().padStart(2, '0');const hours = now.getHours().toString().padStart(2, '0');const minutes = now.getMinutes().toString().padStart(2, '0');const seconds = now.getSeconds().toString().padStart(2, '0');const dateTimeString = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;editor.replaceSelection(dateTimeString); // 在光标处插入文本},},},],// Remark 插件:在 Markdown AST 阶段处理remark: (processor) => processor.use(remarkCustomHighlight, options),// Rehype 插件:在 HTML AST 阶段处理// 注意:如果 bytemd 的 plugin-gfm 已经支持 ==highlight==,这里就不需要额外的 rehype 插件了// 如果需要更复杂的自定义高亮,则可能需要编写一个 `rehype` 插件来解析 `==` 或自定义语法// 为了简化,我们假设 `plugin-gfm` 已经能把 `==text==` 渲染为 `<mark>`,// 所以这里的 rehypeCustomHighlight 暂时不启用,或者只做调试用// rehype: (processor) => processor.use(rehypeCustomHighlight),};
};export default bytemdPluginCustom;

代码解释:

  • remarkCustomHighlight (Remark 插件):
    • 接收 options 来配置要高亮的关键词。
    • 使用 unist-util-visit 遍历 MDAST 中的所有 text 节点。
    • 对于每个文本节点,检查是否包含任何关键词。
    • 如果包含,就将关键词用 ==...== 包裹起来。这是因为 ByteMD 的 plugin-gfm 默认支持 CommonMark 的 ==highlight== 语法,它会被渲染为 <mark> 标签。这样我们就直接利用了现有的渲染能力。
  • rehypeCustomHighlight (Rehype 插件 - 备用/调试):
    • 这里为了说明 rehype 插件的作用,提供了一个简单的例子。
    • 它的作用是遍历 HAST 中的文本节点,查找 ==...== 模式,并将其替换为 mark 元素。
    • 但在我们的例子中,如果 plugin-gfm 已经处理 ==highlight==,这个 rehype 插件就不是必须的。 实际应用中,rehype 插件更常用于添加额外的 HTML 属性、修改已生成的 HTML 结构、或者处理一些 remark 阶段无法处理的 HTML 特性。
  • bytemdPluginCustom (ByteMD 插件):
    • 返回一个 BytemdPlugin 接口的对象。
    • name: 插件的唯一标识。
    • actions: 定义了一个工具栏按钮。
      • title: 按钮的提示文本。
      • icon: 按钮的 SVG 图标。
      • handler: 点击按钮时执行的逻辑。editor.replaceSelection() 是 CodeMirror 提供的方法,用于在当前光标处插入或替换选中的文本。
    • remark: 注册了我们自定义的 remarkCustomHighlight 插件,并传递了配置选项。
2. 在 ByteMD 编辑器中使用自定义插件

bytemd-plugin-custom.ts 导入到你的 ByteMarkdownEditor.tsx 中,并将其添加到 plugins 数组。

// components/Editor/ByteMarkdownEditor.tsx
'use client';import React, { useState } from 'react';
import { Editor } from '@bytemd/react';
import gfm from '@bytemd/plugin-gfm';
import highlight from '@bytemd/plugin-highlight';
import math from '@bytemd/plugin-math';
import gemoji from '@bytemd/plugin-gemoji';
import frontmatter from '@bytemd/plugin-frontmatter';// 导入你的自定义插件
import bytemdPluginCustom from '../../plugins/bytemd-plugin-custom';// Import Bytemd styles
import 'bytemd/dist/index.css';
import 'highlight.js/styles/github.css'; // 代码高亮主题
import 'katex/dist/katex.css'; // 数学公式样式// 定义插件数组
const plugins = [gfm(), // 提供 ==highlight== 语法的支持highlight(),math(),gemoji(),frontmatter(),// 添加你的自定义插件bytemdPluginCustom({ highlightKeywords: ['重要', '注意', '提示'] }),
];interface ByteMarkdownEditorProps {initialValue?: string;onChange?: (value: string) => void;
}const ByteMarkdownEditor: React.FC<ByteMarkdownEditorProps> = ({ initialValue = '', onChange }) => {const [value, setValue] = useState(initialValue);const handleChange = (newValue: string) => {setValue(newValue);if (onChange) {onChange(newValue);}};return (<div style={{ flex: 1, display: 'flex', flexDirection: 'column' }}><Editorvalue={value}plugins={plugins}onChange={handleChange}/></div>);
};export default ByteMarkdownEditor;

运行效果:

  • 你的 ByteMD 编辑器工具栏会多出一个日期时间图标的按钮。点击它,会在编辑器光标处插入当前的日期时间。
  • 在编辑器中输入 “这是一段重要内容” 或 “请注意以下几点”,在预览模式下,“重要”和“注意”字样将以高亮(通常是黄色背景)显示,因为 plugin-gfm==...== 转换为 <mark> 标签。

总结向面试官介绍自定义插件


面试官您好,我来详细介绍一下 ByteMD 的插件系统,并结合自定义插件的实现。

ByteMD 的插件系统是其高度可扩展性的核心。它允许我们在不修改核心库代码的前提下,轻松地添加、修改或扩展编辑器的行为和功能。

其设计精妙之处在于:

  1. 分阶段的钩子机制: 插件通过实现不同的钩子函数,在 ByteMD 内部的 Markdown 处理流程中精确介入。

    • remark 钩子: 负责在 Markdown 解析成 MDAST (Markdown Abstract Syntax Tree) 后,对 AST 进行操作。例如,我可以定义一个 remark 插件来识别自定义的 Markdown 语法,或者对内容进行前置处理(如我自定义插件中的高亮关键词处理,将普通文本转换为 ==关键词== 形式)。
    • rehype 钩子: 负责在 MDAST 转换为 HAST (Hypertext Abstract Syntax Tree) 后,对 HTML 的 AST 进行操作。这允许我对最终生成的 HTML 结构进行修改,例如为图片添加 lazy-load 属性,或者将特定 AST 节点渲染为复杂的自定义 HTML 结构。
    • editorEffect 钩子: 允许我直接与底层的编辑器实例(如 CodeMirror)进行交互。这对于实现图片拖拽上传、自定义快捷键、或者监听编辑器状态变化等功能至关重要。
    • viewerEffect 钩子: 允许我在预览区域的 HTML 渲染完成后,对其 DOM 进行操作。这适合于初始化第三方渲染库(如 Mermaid、ECharts),或者对预览内容进行后期处理。
    • actions 钩子: 最直观的扩展方式,允许我在工具栏添加自定义按钮,实现特定的交互功能,例如我自定义插件中的“插入日期时间”按钮。
  2. 拥抱 unified 生态: ByteMD 没有“重新发明轮子”,而是巧妙地利用了 unified 这一成熟且强大的内容处理框架。这使得插件的开发能够复用 remarkrehype 社区大量的现有插件和工具,极大地加速了开发。

以我刚刚实现的自定义插件为例,它实现了两个功能:

  1. “插入日期时间”按钮:

    • 我通过 actions 钩子在 ByteMD 的工具栏添加了一个自定义按钮。
    • 在按钮的 handler 中,我利用 editorEffect 钩子提供给我的 editor 实例(即 CodeMirror 实例),调用其 replaceSelection() 方法,实现了在光标处插入当前日期时间字符串的功能。这体现了 actionseditorEffect 在 UI 交互和编辑器控制上的协同作用。
  2. 高亮特定关键词:

    • 我利用 remark 钩子,注册了一个自定义的 remark 插件 (remarkCustomHighlight)。
    • 这个插件会遍历 Markdown 的 AST,查找预定义的关键词(如“重要”、“注意”)。
    • 一旦找到,它会修改 AST 中的文本节点,将关键词用 ==...== 包裹起来。由于 ByteMD 的 plugin-gfm 已经内置了对 ==text== 渲染为 <mark> 标签的支持,我得以复用其渲染能力,实现了在预览时自动高亮关键词的效果,而无需编写复杂的 rehype 逻辑。这展示了如何通过利用现有插件的能力来简化自定义。

总之,ByteMD 的插件系统是一个设计精良、高度开放的架构。它通过分层的钩子和对 unified 生态的集成,使得开发者可以灵活地在不同阶段对 Markdown 内容进行处理和定制编辑器行为,从而构建出满足各种复杂需求的强大内容创作工具。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/85879.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/85879.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/85879.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

每日一练之 Lua 表

Lua 的 table 是什么数据结构&#xff1f;如何创建和访问&#xff1f; 数据结构:Lua的table是一种哈希表&#xff0c;使用键值对存储数据&#xff0c;支持动态扩容 创建方式: local t1 {} local t2 {10,20,30} local t3 {name"Alice",age25}访问方式&#xff1a…

实现自动胡批量抓取唯品会商品详情数据的途径分享(官方API、网页爬虫)

在电商领域&#xff0c;数据就是企业的核心资产。无论是市场分析、竞品研究&#xff0c;还是精准营销&#xff0c;都离不开对大量商品详情数据的深入挖掘。唯品会作为知名的电商平台&#xff0c;其丰富的商品信息对于众多从业者而言极具价值。本文将详细探讨实现自动批量抓取唯…

Zephyr 高阶实践:彻底讲透 west 构建系统、模块管理与跨平台 CI/CD 配置

本文是 Zephyr 项目管理体系的高阶解构与实战指南&#xff0c;全面覆盖 west 构建系统原理、模块解耦与 west.yml 多模块维护机制&#xff0c;结合企业级多平台 CI/CD 落地流程&#xff0c;深入讲解如何构建可靠、可维护、跨芯片架构的一体化 Zephyr 工程。 一、为什么 Zephyr …

我开源了一套springboot3快速开发模板

我开源了一套springboot3快速开发模板 开箱即用、按需组合、可快速二次开发的后端通用模板。 ✨ 主要特性 Spring Boot 3.x Java 17&#xff1a;跟随 Spring 最新生态&#xff0c;利用现代语法特性。多模块分层&#xff1a;common 抽象通用能力、starter 负责启动、modules…

OpenCV CUDA模块设备层-----在GPU上计算两个uchar1类型像素值的反正切(arctangent)比值函数atan2()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 对输入的两个 uchar1 像素值 a 和 b&#xff0c;先分别归一化到 [0.0, 1.0] 浮点区间&#xff0c;然后计算它们的 四象限反正切函数。 函数原型…

从C++编程入手设计模式——观察者模式

从C编程入手设计模式——观察者模式 ​ 观察者模式简直就是字如其名&#xff0c;观察观察&#xff0c;观察到了告诉别人。观察手的作用如此&#xff0c;观察者模式的工作机制也是如此。这个模式的核心思路是&#xff1a;一个对象的状态发生变化时&#xff0c;自动通知依赖它的…

MITM 中间人攻击

​据Akamai 2023网络安全报告显示&#xff0c;MITM攻击在数据泄露事件中占比达32.7%&#xff0c;平均每次事件造成企业损失$380,000​ ​NIST研究指出&#xff1a;2022-2023年高级MITM攻击增长41%&#xff0c;近70%针对金融和医疗行业​ 一、MITM攻击核心原理与技术演进 1. 中…

llama_index chromadb实现RAG的简单应用

此demo是自己提的一个需求&#xff1a;用modelscope下载的本地大模型实现RAG应用。毕竟大模型本地化有利于微调&#xff0c;RAG使内容更有依据。 为什么要用RAG&#xff1f; 由于大模型存在一定的局限性&#xff1a;知识时效性不足、专业领域覆盖有限以及生成结果易出现“幻觉…

TDMQ CKafka 版事务:分布式环境下的消息一致性保障

解锁 CKafka 事务能力的神秘面纱 在当今数字化浪潮下&#xff0c;分布式系统已成为支撑海量数据处理和高并发业务的中流砥柱。但在这看似坚不可摧的架构背后&#xff0c;数据一致性问题却如影随形&#xff0c;时刻考验着系统的稳定性与可靠性。 CKafka 作为分布式流处理平台的…

常见的负载均衡算法

常见的负载均衡算法 在实现水平扩展过程中&#xff0c;负载均衡算法是决定请求如何在多个服务实例间分配的核心逻辑。一个合理的负载均衡策略能够有效分散系统压力&#xff0c;提升系统吞吐能力与稳定性。 负载均衡算法可部署在多种层级中&#xff0c;如七层HTTP反向代理&…

数据结构转换与离散点生成

在 C 开发中&#xff0c;我们常常需要在不同的数据结构之间进行转换&#xff0c;以满足特定库或框架的要求。本文将探讨如何将 std::vector<gp_Pnt> 转换为 QVector<QPointF>&#xff0c;并生成特定范围内的二维离散点。 生成二维离散点 我们首先需要生成一系列…

零基础学习Redis(12) -- Java连接redis服务器

在我们之前的内容中&#xff0c;我们会发现通过命令行操作redis是十分不科学的&#xff0c;所以redis官方提供了redis的应用层协议RESP&#xff0c;更具这个协议可以实现一个和redis服务器通信的客户端程序&#xff0c;来简化和完善redis的使用。现阶段有很多封装了RESP协议的库…

clangd LSP 不能找到项目中的文件

clangd LSP 不能找到项目中的文件 clangd LSP 不能找到项目中的文件 clangd LSP 不能找到项目中的文件 Normally you need to create compile_commands.json。 如果你使用 cmake 作为构建工具&#xff0c;请执行下面的命令&#xff1a; cmake -DCMAKE_EXPORT_COMPILE_COMMAN…

【内存】Linux 内核优化实战 - vm.overcommit_memory

目录 vm.overcommit_memory 解释一、概念与作用二、参数取值与含义三、相关参数与配置方式四、实际应用场景建议五、注意事项 vm.overcommit_memory 解释 一、概念与作用 vm.overcommit_memory 是 Linux 内核中的一个参数&#xff0c;用于控制内存分配的“过度承诺”&#xf…

Python:.py文件转换为双击可执行的Windows程序(版本2)

流程步骤&#xff1a; 这个流程图展示了将 Python .py 文件转换为 Windows 可执行程序的完整过程&#xff0c;主要包括以下步骤&#xff1a; 1、准备 Python文件&#xff0c;确保代码可独立运行 2、安装打包工具&#xff08;如 PyInstaller&#xff09; 3、打开命令提示符并定位…

【请关注】mysql一些经常用到的高级SQL

经常去重复数据&#xff0c;数据需要转等操作&#xff0c;汇总高级SQL MySQL操作 一、数据去重&#xff08;Data Deduplication&#xff09; 去重常用于清除重复记录&#xff0c;保留唯一数据。 1. 使用DISTINCT关键字去重单列 -- 从用户表中获取唯一的邮箱地址 SELECT DISTIN…

RA4M2开发涂鸦模块CBU(2)----配置按键开启LED

RA4M2开发涂鸦模块CBU.2--配置按键开启LED 概述视频教学样品申请硬件准备参考程序按键口配置中断回调函数主程序 概述 本实验演示如何在 Renesas RA4M2 单片机上使用 GPIO 输入&#xff08;按键&#xff09; 触发 GPIO 输出&#xff08;LED&#xff09;&#xff0c;并使用e2st…

Linux——Json

一 概念 json是一种轻量级&#xff0c;基于文本的&#xff0c;可读的数据交换格式&#xff0c;能够让数据在不同系统&#xff08;比如前端—后端&#xff0c;服务器—客户端&#xff09;间方便传递/存储。在编程语言中都内置了处理json数据的方法 二 语法规则 1. 数据格式&a…

大模型之微调篇——指令微调数据集准备

写在前面 高质量数据的准备是微调大模型的重中之重&#xff0c;一些高质量的数据集可能远比模型性能更佳重要。 我是根据自己的数据照着B站up code花园LLaMA Factory 微调教程&#xff1a;如何构建高质量数据集&#xff1f;_哔哩哔哩_bilibili做的。 数据集格式 在LLaMA Fa…

LVS—DR模式

LVS—DR模式 LVS DR 模式详细简介 一、模式定义与核心原理 LVS DR&#xff08;Direct Routing&#xff09;模式&#xff0c;即直接路由模式&#xff0c;是 Linux Virtual Server&#xff08;LVS&#xff09;实现负载均衡的经典模式之一&#xff0c;工作于网络四层&#xff0…