ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Milkdown Image Block 组件详解:从内联图片到可拖拽、可上传的块级图片

Milkdown Image Block 组件详解:从内联图片到可拖拽、可上传的块级图片 Milkdown Image Block 组件详解从内联图片到可拖拽、可上传的块级图片【免费下载链接】milkdown Plugin driven WYSIWYG markdown editor framework.项目地址: https://gitcode.com/GitHub_Trending/mi/milkdownimageBlock是 Milkdown 组件库中负责将图片渲染为块级元素的核心组件。本指南以其官方 API 文档docs/api/component-image-block.md为主线结合 packages/components/src/image-block 的源码实现完整讲解其能力边界、接入方式、全部 10 项配置参数、底层工作原理与自定义样式要点。阅读完本文你将能够在自己的 Milkdown 编辑器中启用带缩放手柄、图注编辑、链接输入、空占位与文件上传能力的块级图片并自由定制其 UI 文案与图片 URL 代理逻辑。组件能做什么在标准 Markdown 中所有图片都被渲染为内联元素。imageBlock组件改变了这一默认行为当一张图片独立成为一个段落时它会被识别并渲染为一个块级节点从而获得一组独有的交互能力图片缩放手柄Image resize handle图片图注Image caption图片链接输入Image link input空图片块占位符Empty image block placeholder图片上传Image upload需要特别强调的是该组件本身不提供任何样式。它只负责渲染 DOM 结构与交互逻辑最终的外观尺寸、边框、悬浮态、布局需要你自己编写 CSS 来完成。这一设计让组件可以无缝适配任意主题。快速接入组件以插件形式提供使用方式与 Milkdown 的其他插件完全一致import { imageBlockComponent, imageBlockConfig, } from milkdown/components/image-block import { defaultValueCtx, Editor } from milkdown/kit/core import { commonmark } from milkdown/kit/preset/commonmark await Editor.make().use(commonmark).use(imageBlockComponent).create()如果你使用milkdown/kit统一入口也可以通过 packages/kit/src/component/image-block.ts 的milkdown/kit/component导出直接引入无需单独安装 components 包。从源码看imageBlockComponent实际上是一组四个插件的组合image-block/index.tsexport const imageBlockComponent: MilkdownPlugin[] [ remarkImageBlockPlugin, // Markdown 解析阶段把独立成段的图片改写为 image-block 节点 imageBlockSchema, // ProseMirror 节点 schema 定义 imageBlockView, // 节点视图Vue 组件渲染 交互 imageBlockConfig, // 配置上下文 ].flat()工作原理一条图片如何变成块理解源码是正确使用组件的前提。一条独立成段的图片从 Markdown 文本到可交互块级元素依次经过三个阶段。1. Remark 解析识别独立成段的图片remark-plugin.ts 中实现了一个 remark 转换器它遍历 AST当且仅当某个paragraph节点的唯一子节点是image时才将该段落整体替换为image-block节点。换句话说alt单独占一个段落 → 升级为块级图片图片与文字混排在同一段落 → 保持内联渲染不受影响。这一唯一子节点判定保证了内联图片的正常使用不受干扰。2. Schema 定义节点属性与序列化image-block/schema.ts 定义了一个atom类型的块级节点包含三个属性属性默认值说明src图片地址caption图注文本ratio1缩放比例由拖拽产生持久化到节点中值得注意的细节parseDOM通过img[data-typeimage-block]选择器识别已有 DOM因此渲染结果可以无损重新解析回编辑器toMarkdown序列化时会把块还原为普通paragraph image其中alt保存的是精确到两位小数的ratio值见 schema.ts。这意味着缩放比例会随着 Markdown 文档一起保存刷新或复制粘贴后依然保留marks: 表示节点不接受内联标记isolating: true使其成为独立的原子节点。3. NodeView 渲染Vue 驱动的交互层view/index.ts 使用 Vue 3 的createApp挂载组件到div.milkdown-image-block容器并实现了完整的 NodeView 生命周期update、selectNode、deselectNode、destroy。其中两个实现细节值得关注XSS 防护setAttr在写入src属性时经过DOMPurify.sanitize清洗view/index.ts编辑保护stopEvent拦截了HTMLInputElement上的事件确保输入框内的键盘操作不会触发编辑器命令同时readonly即!view.editable状态下所有编辑交互上传、缩放、改图注都会被禁用。配置项总览组件通过imageBlockConfig这个 context 进行配置在editor.config中使用ctx.update修改。全部 10 项配置定义于 image-block/config.ts配置项类型默认值说明imageIconstring \| undefined空图片块占位符的图标captionIconstring \| undefined图注切换按钮的图标uploadButtonstring \| undefinedUpload file上传按钮内容confirmButtonstring \| undefinedConfirm ⏎确认按钮内容uploadPlaceholderTextstringor paste the image link ...空图片块占位文本captionPlaceholderTextstringImage caption图注输入框占位文本onUpload(file: File) Promisestring(file) Promise.resolve(URL.createObjectURL(file))上传回调必须返回解析为图片 URL 的 PromiseproxyDomURL(url: string) Promisestring \| stringundefined渲染时对图片 URL 做代理转换的函数onImageLoadError(event: Event) void \| Promisevoidundefined图片加载失败无效 URL、CORS、404 等时的回调maxWidthnumber \| undefinedundefined图片最大显示宽度像素maxHeightnumber \| undefinedundefined图片最大显示高度像素默认配置对象defaultImageBlockConfig中onUpload的默认实现是URL.createObjectURL(file)——即在浏览器内存中为本地文件生成临时 URL适合快速预览场景生产环境建议替换为真实的上传接口。配置详解与完整示例onUpload接入真实上传服务当用户通过文件选择器选中图片时该函数被调用。你需要在其中完成文件上传并返回可访问的 URLimport { imageBlockConfig } from milkdown/components/image-block ctx.update(imageBlockConfig.key, (defaultConfig) ({ ...defaultConfig, onUpload: async (file: File) { const url await YourUploadAPI(file) return url }, }))从 image-input.tsx 的实现可以看到onUpload返回的 Promise 被then消费拿到 URL 后通过setLink写入节点src属性若 Promise reject则会在控制台打印An error occurred while uploading image。字符串类配置任意文本或 emojiimageIcon、captionIcon、uploadButton、confirmButton、uploadPlaceholderText、captionPlaceholderText都是纯字符串你既可以用 emoji 作为图标也可以用任意文案甚至 HTML 结构import { imageBlockConfig } from milkdown/components/image-block ctx.update(imageBlockConfig.key, (defaultConfig) ({ ...defaultConfig, imageIcon: ️, captionIcon: , uploadButton: Upload Image, confirmButton: Confirm, uploadPlaceholderText: or paste an image URL, captionPlaceholderText: Add a caption, }))proxyDomURL渲染时对图片 URL 做代理该函数在渲染阶段把原始图片 URL 转换为另一个 URL。典型场景包括给图片地址附加签名鉴权参数、绕过防盗链、统一走 CDN 代理等。支持同步返回字符串或返回 Promiseimport { imageBlockConfig } from milkdown/components/image-block // 同步返回 ctx.update(imageBlockConfig.key, (defaultConfig) ({ ...defaultConfig, proxyDomURL: (originalURL: string) { return https://example.com/${originalURL} }, })) // 也支持异步 ctx.update(imageBlockConfig.key, (defaultConfig) ({ ...defaultConfig, proxyDomURL: async (originalURL: string) { const response await fetch( https://api.example.com/proxy?url${originalURL} ) const url await response.text() return url }, }))注意proxyDomURL只影响渲染展示并不会改写节点中持久化的src值。在 view/index.ts 中bindAttrs会先调用proxyDomURL再将结果赋给视图的srcref若返回 Promise则通过.then异步更新失败时仅console.error不影响文档数据。onImageLoadError优雅处理加载失败当图片因无效 URL、CORS 限制、404 等原因加载失败时此回调被触发可用于弹出 toast、切换到占位 UI 或上报监控。它同时支持同步与异步Promisevoid两种形式import { imageBlockConfig } from milkdown/components/image-block // 同步 ctx.update(imageBlockConfig.key, (defaultConfig) ({ ...defaultConfig, onImageLoadError: (event: Event) { console.error(Image failed to load, event) // 例如展示提示或替换为占位图 }, })) // 异步 ctx.update(imageBlockConfig.key, (defaultConfig) ({ ...defaultConfig, onImageLoadError: async (event: Event) { await reportToAnalytics(image_load_error, event) }, }))该回调同时在两个位置被挂载空块预览中的图片image-input.tsx和正式渲染的图片image-viewer.tsx两处都会以Promise.resolve(...).catch(() {})的方式调用因此同步抛错也不会中断 UI 渲染。对应行为有专门的单元测试覆盖view/components/__tests__/image-viewer.onImageLoadError.spec.tsx通过 dispatcherror事件断言回调被调用且收到Event参数。maxWidth与maxHeight约束图片显示尺寸这两个参数以像素为单位约束图片的显示上限超出范围的图片会被等比缩小且同样作用于拖拽缩放过程import { imageBlockConfig } from milkdown/components/image-block ctx.update(imageBlockConfig.key, (defaultConfig) ({ ...defaultConfig, maxWidth: 800, maxHeight: 600, }))在 image-viewer.tsx 的onImageLoad中组件会先按宿主容器宽度与maxWidth计算基础高度再套用maxHeight约束最后乘以ratio得到最终显示高度并写入style.height拖拽时onResizeHandlePointerMove同样会以maxHeight封顶。交互细节缩放、图注与输入体验拖拽缩放比例持久化缩放手柄的交互在 image-viewer.tsx 中实现pointerdown时在window上注册pointermove/pointerup监听避免鼠标移出组件后丢失事件拖动过程中实时更新style.height且最小高度被钳制为 100pxif (height 100) height 100pointerup时计算currentHeight / originHeight得到新的ratio通过setAttr(ratio, ratio)持久化到文档节点——这就是前文所述缩放比例随文档保存的实现来源。图注编辑防抖 失焦提交图注切换按钮通过onToggleCaption显示/隐藏输入框readonly时禁用。输入过程采用1000ms 防抖onInputCaption中的setTimeout并在失焦时onBlurCaption立即清除定时器并提交避免编辑过程中高频写文档事务。输入框 id 通过inputId(milkdown-image-caption)生成保证多编辑器共存时唯一。空块占位链接输入与上传二合一当src为空时组件渲染 image-input.tsx 作为占位一个链接输入框、一个隐藏在 placeholder 中的input[typefile][acceptimage/*]由 label 触发以及确认按钮。回车onKeydown中的Enter或点击确认按钮都会触发setLink写入src。自定义样式DOM 结构与关键类名由于组件不带任何样式你需要针对以下 DOM 结构编写 CSS。核心容器类名与渲染结构如下块级根容器div.milkdown-image-block选中时追加selected类见 view/index.ts已渲染图片.image-wrapperimg[data-typeimage-block]操作按钮.operation/.operation-item图注切换缩放手柄.image-resize-handle图注输入框.caption-input空块占位.image-edit、.link-importer聚焦时追加focus类、.link-input-area、.placeholder、.uploaderlabel 触发的上传按钮、.text占位文案、.image-preview、.confirm。示例最小样式示意需按你的设计体系实现.milkdown-image-block { position: relative; margin: 16px 0; } .milkdown-image-block .image-wrapper img { display: block; width: auto; } .milkdown-image-block .image-resize-handle { position: absolute; bottom: -6px; left: 0; right: 0; height: 6px; cursor: ns-resize; background: transparent; } .milkdown-image-block.selected .image-wrapper { outline: 2px solid #4f9cff; } .milkdown-image-block .caption-input { display: block; width: 100%; margin-top: 8px; border: none; text-align: center; } .milkdown-image-block .image-edit { display: flex; align-items: center; gap: 8px; padding: 8px; }测试与质量保障组件行为由测试持续守护。除上文提到的onImageLoadError单测image-viewer.onImageLoadError.spec.tsx外还可以在 e2e 目录找到基于 Playwright 的端到端用例如e2e/src/image-block/main.ts与e2e/tests/crepe/image-block.spec.ts覆盖图片块的插入、渲染与交互流程可作为你集成时的参考基线。注意事项与使用限制样式自负组件不输出任何 CSS未编写样式时交互元素如缩放手柄在视觉上不可见仅独立成段生效与文字同段的内联图片不会被升级为块级节点这是 remark-plugin.ts 中段落唯一子节点判定的刻意设计只读模式view.editable false时上传、缩放、图注编辑全部禁用图片以纯展示形态呈现URL 清洗写入节点src的值会经过DOMPurify.sanitize避免注入风险默认上传仅限本地预览默认onUpload使用URL.createObjectURL页面刷新后 URL 即失效生产环境务必替换为真实上传服务若需以更简短的路径引入可直接使用milkdown/kit/component中重导出的同名 API见 packages/kit/src/component/image-block.ts。至此从插件组合、解析链路、节点属性到全部 10 项配置与底层交互实现imageBlock组件的能力边界与定制方式已经完整呈现。将其与 Milkdown 的 crepe 主题packages/crepe或你自己的设计体系结合即可快速构建出符合产品需求的富图片编辑体验。【免费下载链接】milkdown Plugin driven WYSIWYG markdown editor framework.项目地址: https://gitcode.com/GitHub_Trending/mi/milkdown创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表