Spaces:
Sleeping
Sleeping
面试指南:微信公众号 Markdown 编辑器 (WeChat Markdown Editor)
本文档旨在辅助你理解项目核心架构与技术难点,以便在面试中流利作答。
1. 项目概述 (Project Overview)
一句话介绍: 这是一个基于 Web 的 Markdown 编辑器,专注于解决微信公众号排版繁琐、代码展示不友好以及外链限制等痛点。它能够将 Markdown 实时渲染为适配微信公众号样式的 HTML,支持一键复制发布。
核心价值:
- 效率提升:让技术博主专注于写作,而非排版。
- 样式定制:通过 CSS 变量实现多主题切换(如 Notion 风格),满足不同审美。
- 平台适配:针对微信环境的特殊限制(如外链、代码块)做了专门处理。
2. 技术架构 (Technical Architecture)
虽然项目看似简单,但其架构设计体现了轻量化和关注点分离的思想。
- 后端 (Backend): Python Flask
- 角色:主要作为静态资源服务器(Static File Server)。
- 优势:轻量、部署简单(配合 Docker/Gunicorn),易于后续扩展(如增加账号系统或云端存储)。
- 前端 (Frontend): Vue 3 + Tailwind CSS
- Vue 3:利用 Composition API (
setup,ref,computed) 管理编辑器状态(源码、预览 HTML、主题设置)。 - Tailwind CSS:快速构建现代化的 UI 界面,提高开发效率。
- Vue 3:利用 Composition API (
- 核心引擎: Markdown-it + Highlight.js
- 渲染:在浏览器端实时将 Markdown 解析为 HTML AST,再渲染为 DOM。
- 高亮:自动检测代码语言并应用高亮样式。
3. 核心难点与解决方案 (Key Challenges & Solutions)
面试中如果被问到“项目中遇到的最大困难是什么”,可以从以下几点回答:
难点 1:微信公众号的样式兼容性 (Style Adaptation)
问题:微信公众号后台编辑器过滤掉了许多外部 CSS,且对某些 HTML 标签(如 h1, blockquote)的默认样式支持不一致。
解决方案:
- 内联样式模拟:虽然没有使用内联样式库(如 juice),但我通过精心设计的 CSS 选择器(
#preview-content h1等)确保复制到剪贴板的内容带有所需的样式信息。微信编辑器在粘贴富文本时会保留大部分计算后的样式。 - 自定义 CSS 变量:使用
:root和data-theme属性实现主题切换。这样只需修改变量值,无需重写大量 CSS 规则。
难点 2:外链处理 (External Links)
问题:微信公众号文章除白名单外,不支持普通外部超链接,导致文章中的参考链接无法点击。 解决方案:
- 自定义 Renderer:利用
markdown-it的插件机制,重写link_open和link_close规则。 - 脚注转换:在渲染过程中,将所有外链收集到一个
footnotes数组中。渲染结束后,在文章底部自动追加一个“引用链接”列表,并将正文中的链接转换为上标索引(如[1])。
难点 3:富文本一键复制 (Rich Text Copy)
问题:普通的 navigator.clipboard.writeText 只能复制纯文本,无法保留颜色和排版。
解决方案:
- Range API: 创建一个
Range对象选中预览区域的 DOM 节点。 - Selection API: 将
Range添加到当前的Selection中。 - execCommand: 使用
document.execCommand('copy')(虽然标准标记为过时,但在处理“富文本复制”场景下,它仍然是兼容性最好、最可靠的方案)。
难点 4:同步滚动 (Sync Scroll)
问题:左侧 Markdown 源码和右侧渲染后的 HTML 高度不一致,如何实现精准的同步滚动? 解决方案:
- 百分比映射:计算当前滚动条位置占总可滚动高度的百分比 (
scrollTop / (scrollHeight - clientHeight)),并将此百分比应用到另一侧。 - **防抖动 (Debounce/Lock)**:为了防止“循环触发”滚动事件(左滚带动右,右滚又带动左),设置了一个
isScrolling标志位,在滚动触发时锁定,滚动结束后(通过setTimeout)释放。
难点 5:中英文自动空格 (Pangu Spacing)
问题:为了提升排版美感,中英文之间应该有空格(如 "Hello你好" -> "Hello 你好")。 解决方案:
- 正则替换:前端实现了一个轻量级的替换逻辑。
text.replace(/([\u4e00-\u9fa5])([a-zA-Z0-9])/g, '$1 $2'):中文后跟英文/数字。text.replace(/([a-zA-Z0-9])([\u4e00-\u9fa5])/g, '$1 $2'):英文/数字后跟中文。
4. 为什么做这个项目?
- 自我驱动:作为一个技术人员,我经常写技术博客。现有的工具要么收费,要么功能太复杂,我希望有一个轻量、可控、隐私安全(纯本地渲染)的工具。
- 技术实践:我想通过这个项目深入理解前端的文本处理和DOM 操作,同时实践 Vue 3 的 Composition API。