最近在技术社区里,和 Typora 相关的搜索词一直是热门:Typora 免费版、Typora 激活、Typora 序列号、Typora 下载……很多人其实不是不想买,而是希望先找到一类真正适合自己的工具。这里我想先给出一个不是套路的判断:如果你只是想在电脑上把 Markdown 写得舒服,Typora 确实很好;但如果你真正的问题是“写完之后能不能自动同步到手机、小程序里阅读”,那就算激活了 Typora,也解决不了跨端分发的问题。
这篇文章要讲的,是一套不折腾破解、不依赖某个编辑器封闭生态的轻量方案:4MB 级别的工具链,实现 Markdown 本地编辑、网盘存储、小程序阅读。它不是一个“新的 Typora 安装包”,而是一个可以自己掌控的内容工作流。读完你会得到 3 样东西:
- 一条从本地 Markdown 文件到网盘再到小程序阅读的完整链路;
- 一个可以直接跑通的 Node 脚本,用于把 Markdown 批量处理成小程序可用的 JSON;
- 一份真实的踩坑清单,尤其是小程序渲染 Markdown 时的那些隐藏限制。
1. 先聊清楚:你真正需要的不是编辑器,而是一套工作流
围绕 Typora,最常见的三个场景是:
- 觉得 Typora 收费后不划算,想找免费替代;
- 用 Typora 写完了文章,但换一台电脑、换手机后看不到内容;
- 想让身边的人不用安装软件,通过小程序或其他网页方式直接阅读你写的东西。
这三个问题,其实只有第一个和“编辑器”有关。第二个问题的关键是存储和同步,第三个问题的关键是内容分发。
如果你还是把注意力放在“找一个 Typora 平替编辑器”上,那么恭喜你,很快你会陷入新的循环:试用一个编辑器,觉得差一点;再去搜另一个编辑器,又发现存储同步不好用。真正值得做的事,是把“编辑、存储、分发”这三个环节拆开,每个环节选最合适的轻量工具。
这也是本文标题里“4MB”想表达的意思:不是某个安装包只有 4MB,而是这套方案的核心逻辑足够轻。本地编辑器承担写作,网盘承担同步,小程序承担阅读;整个链路的依赖和复杂度可以压到非常低,而不是把“Typora 能干的全部功能”都塞进一个包里。
从选型思路来看,这套方案适合:
- 个人博客作者、技术写作者,希望 Markdown 内容自己掌握;
- 小团队想做一个内容发布小程序,但不想一开始就上服务端建站;
- 想摆脱 Typora 激活困扰,又不愿意被某个在线平台的编辑器和存储锁定的人。
如果你需要的是严格的团队协作、多人实时编辑、细粒度权限,那这套方案并不适合,应该考虑更重的 Wiki 或文档平台。
2. 方案总览与架构选型
整套架构可以拆成 4 个环节:
本地 Markdown 编辑器 → WebDAV 网盘 → 转换脚本 → 微信小程序每个环节的职责如下:
| 环节 | 职责 | 可选工具 |
|---|---|---|
| 编辑 | 产生 Markdown 文件 | VS Code、Mark Text、思源笔记等 |
| 存储 | 多端同步、版本保留 | 支持 WebDAV 的网盘服务 |
| 转换 | 把 MD 转成 JSON/HTML | Node.js + marked |
| 阅读 | 在手机上展示内容 | 微信小程序 + rich-text/towxml |
从架构上看,这个组合最大的优势是“内容自控”:
- Markdown 文件是纯文本,放在本地的同时也会同步到网盘;
- 网盘本身有历史版本或文件保留功能,误删可以找回;
- 小程序只是读数据,不锁定你的内容;
- 以后想换别的阅读端,同一份 Markdown 可以再加工。
局限性也要说清楚:
- WebDAV 同步适合个人写作场景,不适合多人同时高频编辑;
- 小程序端如果要动态拉取最新文章,需要有一个合法的 HTTPS 接口域名;
- 如果文章量很大,把所有内容打进小程序包会撑爆包体限制,需要改成远端读取。
从实际项目的发展路径看,这个方案是“先轻后重”的典型:先用纯静态方式把阅读体验跑通,等文章量和更新频率上来了,再增加云托管和搜索能力。这也是我推荐它的原因,它不会让你一开始就陷入服务端开发和域名配置的泥潭。
3. 核心概念:Markdown 渲染、WebDAV 与小程序的限制
在进入操作之前,有三个概念建议先理清楚。
3.1 Markdown 不是格式,是一种编辑语法
Markdown 本质上是一种“用纯文本表达排版意图”的轻量标记语言。真正给人看的不是.md文件本身,而是把它解析之后的 HTML 或排版结果。比如:
# 标题 这是一段**加粗**文本,这是一个[链接](https://example.com)。经过解析会变成:
<h1>标题</h1> <p>这是一段<strong>加粗</strong>文本,这是一个<a href="https://example.com">链接</a>。</p>理解了这一点,你就明白为什么需要“转换脚本”:编辑时写 Markdown,阅读时要渲染成 HTML。编辑器只是减轻了你手写解析过程的负担,但跨端阅读时,我们必须自己处理渲染。
3.2 WebDAV 是什么,为什么它能当同步盘用
WebDAV 是一个基于 HTTP 的文件操作协议,可以对服务器上的文件进行创建、读取、修改、复制等操作。既然是网络协议,就可以被各类客户端挂载成网络磁盘或同步目录。
对普通用户来说,不需要了解协议细节,只需要知道一个结论:
支持 WebDAV 的网盘,不仅能“上传下载文件”,还能被本地工具直接当作一个同步文件夹来使用。
在具体操作时,比如坚果云这类网盘服务,可以生成一个“应用密码”,专门给第三方客户端使用。这样你的 Markdown 文件可以自动同步到云端,也能被 Node 脚本读取处理。
3.3 小程序为什么不能直接渲染 Markdown
微信小程序运行在 JavaScript 引擎上,没有浏览器 DOM 对象,也没有innerHTML。你没法像网页那样随便往页面里塞一段 Markdown 转换后的 HTML 字符串让它自动排版。
目前小程序渲染 Markdown/HTML 有几种常见路线:
| 方案 | 优点 | 缺点 |
|---|---|---|
rich-text组件 | 内置、零依赖 | 对部分标签/样式支持有限,不能通过外部 CSS 控制内部节点 |
| towxml 等解析库 | 支持 Markdown、代码高亮、数学公式 | 引入体积较大,需处理依赖和配置 |
| 服务端/脚本先转成 JSON | 小程序端最简单,包体积小 | 更新内容需要重新生成静态资源 |
本文的核心路线是第三种:在同步阶段就把 Markdown 转成 JSON,小程序端只需要加载 JSON 再交给rich-text渲染。这是最轻量的做法,也是很多静态博客小程序的雏形。
4. 环境准备与前置条件
开始实操前,你需要准备以下环境。版本以官方最新稳定版为准,本文不限定具体版本号,重点是流程通用。
4.1 本地编辑器
不建议继续折腾 Typora 的序列号问题。免费、轻量且适合 Markdown 编辑的工具有很多:
- VS Code:功能全面,配合 Markdown 插件体验不错,适合长期写作;
- Mark Text:界面接近 Typora,对 Markdown 的即时渲染支持好;
- 思源笔记:自带数据仓库和同步机制,适合需要笔记管理的人。
从“写作 + 配合脚本使用”的角度,比较推荐 VS Code,因为它本身就是面向文件的编辑器,终端、文件目录、Markdown 预览一应俱全。如果确实想要更接近 Typora 的“所见即所得”,Mark Text 是更轻的选择。
4.2 WebDAV 网盘
需要一个支持 WebDAV 的网盘服务。国内比较常用的是坚果云,注册后可以在“账号信息”里开启第三方应用密码。本文的示例会以坚果云为例,但原理适用于任何支持 WebDAV 的服务。
4.3 Node.js
转换脚本基于 Node.js 实现。建议安装 LTS 版本,可以通过node -v和npm -v验证是否安装成功。
node -v npm -v4.4 微信开发者工具
从小程序官网下载稳定版微信开发者工具。如果你还没有小程序 AppID,可以先使用测试号,或者在小程序管理后台申请一个个人小程序。注意个人主体小程序对类目有要求,内容阅读类问题不大,但发布上线前需要阅读平台规范。
5. 核心流程一:本地 Markdown 编辑与网盘同步
这一阶段的目标很简单:让 Markdown 文件出现在一个可以被脚本读取的位置,同时自动同步到云端。
5.1 目录结构设计
先建立一个项目目录,建议统一管理:
markdown-notes/ ├── articles/ # 所有 Markdown 文章 │ ├── 2025-06-10-hello.md │ └── 2025-06-12-reader.md ├── images/ # 文章图片 ├── scripts/ # 转换脚本 ├── dist/ # 脚本生成的 JSON 文件 └── miniprogram/ # 小程序代码这样做的原因是:
articles只需要同步到网盘,是源文件;dist是构建产物,可以由脚本生成;images建议单独管理,方便在转换时替换图片地址。
5.2 WebDAV 同步配置
以坚果云为例,操作步骤如下:
- 登录坚果云网页端;
- 进入“账户信息” -> “安全选项” -> “添加应用密码”;
- 创建一个应用密码,例如用于 WebDAV 工具;
- 记录服务器地址,一般是
https://dav.jianguoyun.com/dav/。
然后可以用支持 WebDAV 的同步客户端或编辑器插件,把本地markdown-notes目录同步到云端。这里不做具体客户端的展开,因为不同工具的配置界面不一样,但核心参数是一致的:
WebDAV 地址: https://dav.jianguoyun.com/dav/ 账号: 你的坚果云登录邮箱 应用密码: 步骤3生成的应用密码 同步目录: /markdown-notes如果你是技术型用户,更推荐把articles目录放进 Git 仓库。这样不仅能在本地多台设备间同步,还有完整的版本历史。网盘和 Git 不冲突,可以都用:
- 网盘负责自动同步和镜像备份;
- Git 负责版本管理和回滚。
5.3 验证同步
同步完成后,可以在第二台设备上确认一下,是否能看到刚创建的 Markdown 文件。这一步也能避免后面脚本读取时出现“换台电脑文件不存在”的问题。
从实际经验看,最容易犯的错是把同步目录设置得太浅,比如直接同步整个用户目录,导致大量无关文件上传。建议严格限定为项目目录,避免隐私文件和资源浪费。
6. 核心流程二:用 Node 脚本把 Markdown 转为 JSON
当 Markdown 文件就位后,接下来就是整个方案的“发动机”:把多个 Markdown 文件批量转换成一个docs.json,让小程序直接读取。
6.1 初始化项目
在markdown-notes目录下执行:
npm init -y npm install marked只依赖marked一个包,这也是保持轻量的关键。
6.2 编写转换脚本
在scripts目录下创建sync.js:
// 文件路径:scripts/sync.js const fs = require('fs'); const path = require('path'); const { marked } = require('marked'); const SRC = path.join(__dirname, '../articles'); const DIST = path.join(__dirname, '../dist'); // 读取所有 .md 文件 const files = fs.readdirSync(SRC).filter(file => file.endsWith('.md')); const list = files.map(file => { const filePath = path.join(SRC, file); const raw = fs.readFileSync(filePath, 'utf8'); const html = marked.parse(raw); const title = file.replace(/\.md$/, ''); const stat = fs.statSync(filePath); return { title, html, updatedAt: stat.mtime.toISOString() }; }); // 确保 dist 目录存在 fs.mkdirSync(DIST, { recursive: true }); fs.writeFileSync( path.join(DIST, 'docs.json'), JSON.stringify({ list }, null, 2) ); console.log(`生成完成,共 ${list.length} 篇`);这段脚本做了几件事:
- 读取
articles目录下所有.md文件; - 用
marked.parse把 Markdown 解析成 HTML 字符串; - 把文章标题、HTML 内容和更新时间写入 JSON;
- 把结果保存到
dist/docs.json。
运行脚本:
node scripts/sync.js如果看到:
生成完成,共 2 篇说明转换成功。
6.3 处理图片路径
网盘里的图片在小程序端无法直接访问,需要把相对路径替换成公网可访问的地址。改造脚本,在得到 HTML 后做一次字符串替换:
// 图片地址统一替换,示例域名请替换成你的 CDN 或对象存储域名 const html = marked.parse(raw).replace( /src="\.\.?\/images\//g, 'src="https://cdn.example.com/images/' );这里的cdn.example.com是占位,实际项目中应该换成自己的域名。如果不打算上 CDN,也可以把图片放到小程序项目里,但一般只适合少量文章,因为小程序主包有体积限制。
6.4 生成小程序可复制的静态数据
在文章量少、更新不频繁的阶段,可以直接把docs.json的内容复制到小程序的utils/docs.js中:
// 文件路径:miniprogram/utils/docs.js module.exports = { list: [ /* 把 docs.json 中的 list 数组粘贴到这里 */ ] };这样做的好处是零请求、零配置,发布小程序时内容已经在包里。缺点是更新文章后要重新生成并发版小程序。适合个人创作者初期验证流程。
如果你想实现“文章更新后小程序不重新发版也能看到新内容”,就需要把docs.json上传到支持 HTTPS 的对象存储或云托管服务,在小程序端用wx.request拉取。注意小程序后台需要配置服务器域名白名单,这是很多人忽略的一步。
7. 核心流程三:小程序端列表页与阅读页实现
接下来是阅读端。这里用一个最简小程序示例,包含两个页面:首页展示文章列表,详情页展示文章内容。
7.1 小程序目录结构
miniprogram/ ├── app.json ├── app.js ├── app.wxss ├── utils/ │ └── docs.js # 由 dist/docs.json 生成 └── pages/ ├── index/ │ ├── index.js │ ├── index.wxml │ └── index.wxss └── detail/ ├── detail.js ├── detail.wxml └── detail.wxss7.2 首页列表页
在pages/index/index.js中:
// 文件路径:pages/index/index.js const docs = require('../../utils/docs.js'); Page({ data: { list: [] }, onLoad() { this.setData({ list: docs.list }); }, onTapArticle(e) { const index = e.currentTarget.dataset.index; wx.navigateTo({ url: `/pages/detail/detail?index=${index}` }); } });在pages/index/index.wxml中:
<!-- 文件路径:pages/index/index.wxml --> <view class="list"> <view class="list-item" wx:for="{{list}}" wx:key="index" >// 文件路径:pages/detail/detail.js const docs = require('../../utils/docs.js'); Page({ data: { title: '', html: '' }, onLoad(options) { const index = Number(options.index || 0); const doc = docs.list[index]; if (doc) { this.setData({ title: doc.title, html: doc.html }); } } });在pages/detail/detail.wxml中:
<!-- 文件路径:pages/detail/detail.wxml --> <view class="detail"> <view class="detail-title">{{title}}</view> <rich-text nodes="{{html}}"></rich-text> </view>这里有个非常关键的坑:rich-text组件内部节点的样式,无法通过detail.wxss里的类选择器覆盖。也就是说,如果你在 Markdown 转换后的 HTML 里写<h1>、<code>,必须在转换脚本阶段为这些标签补上style属性,或者使用支持完整样式解析的组件方案。
一个常见的处理方式是在脚本里统一给标题加内联样式:
let html = marked.parse(raw); // 为标题补充基础样式,保证小程序 rich-text 内排版正常 html = html .replace(/<h1>/g, '<h1 style="font-size:22px;line-height:1.5;">') .replace(/<h2>/g, '<h2 style="font-size:20px;line-height:1.5;">') .replace(/<p>/g, '<p style="font-size:16px;line-height:1.8;margin-bottom:12px;">') .replace(/<code>/g, '<code style="background:#f5f5f5;padding:2px 4px;border-radius:4px;">');如果你的文章里有大量表格、代码块、引用块,个人经验是rich-text的样式控制能力会让排版的头发掉不少。这时候可以换成 towxml 之类的组件,它对 Markdown 渲染的支持更完整,缺点是引入体积变大。权衡标准很简单:
- 文章以文字为主,
rich-text够用; - 文章以技术文档为主、包含大量代码块和表格,用 towxml 会更省心。
8. 运行结果与验证方法
搭建完之后,按下面顺序验证:
- 在
markdown-notes目录下运行node scripts/sync.js,确认dist/docs.json生成; - 打开
dist/docs.json,确认 HTML 片段不是空的; - 用微信开发者工具导入
miniprogram目录,查看首页是否出现文章列表; - 点击文章,查看详情页是否能正常渲染;
- 如果使用了
docs.js静态数据,注意查看utils/docs.js里的list是否是有效数组。
预期输出类似:
{ "list": [ { "title": "2025-06-10-hello", "html": "<h1>Hello</h1><p>...</p>", "updatedAt": "2025-06-10T08:00:00.000Z" } ] }如果失败,优先按这个顺序排查:
1. 脚本是否报错 -> 查看终端完整错误信息; 2. JSON 是否生成 -> 检查 dist 目录; 3. 小程序是否报错 -> 查看开发者工具 Console; 4. 页面空白 -> 确认 require 路径,确认 docs.list 是否存在; 5. 样式错乱 -> 检查 rich-text 的限制,确认转换脚本是否加了内联样式。9. 常见问题与排查思路
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 图片不显示 | 图片还是本地相对路径 | 查看 HTML 中的 img src 值 | 在转换脚本中替换为公网可访问地址 |
| rich-text 不渲染表格 | 表格标签兼容性问题 | 在开发者工具中查看 nodes 内容 | 使用 towxml 或在转换时做表格拆解 |
| 小程序包体积超限 | 文章量多且未外部化 | 查看详情里的代码包大小 | 把 JSON 上传到对象存储,改为运行时请求 |
| WebDAV 同步冲突 | 多台设备同时编辑 | 查看网盘同步状态 | 同一时间只在一台设备编辑,或用 Git 管理版本 |
| 更新文章后小程序没变 | 数据仍在本地打包 | 检查是否有请求远端 JSON | 更新 dist 并重新发布/同步远端文件 |
| 代码块样式错乱 | rich-text 对 pre/code 支持有限 | 检查渲染后的 HTML 节点 | 使用专用 Markdown 渲染组件,如 towxml |
10. 最佳实践与工程建议
从一个小项目跑通到稳定使用,中间还有不少可以沉淀的经验。这里整理几条比较实际的做法。
10.1 用日期做文件名前缀
articles/2025-06-10-hello.md比hello.md更好排序,也方便脚本按时间排序生成列表。如果以后接入搜索功能,文件名和时间信息可以直接复用。
10.2 图片统一外链化
不要依赖网盘的临时分享链接,推荐在前期就把图片上传到对象存储或 CDN,然后在脚本里统一替换前缀。这样才能保证小程序的rich-text能顺利加载图片,也方便以后迁移。
10.3 脚本只处理增量文件
当文章数量变多时,每次全量转换会浪费时间和磁盘 IO。可以增加一个updatedAt对比逻辑,只处理修改时间大于上次构建时间的文件。这个优化在小体量项目中收益不大,但思路值得保留。
10.4 小程序端加缓存
如果以后改为运行时请求 JSON,建议在本地缓存一份。比如用wx.setStorageSync保存最近一次拉取的数据,下次进入先展示缓存,再请求更新。这样即使网络较差,用户也能看到历史内容。
10.5 发布前检查合规
小程序发布上线需要注意平台的内容规范。不要在你的小程序里展示未获授权的内容,尤其是一些整理转载的文章。自用可以,公开上线前,建议只放自己原创或明确授权的内容。
10.6 保留网盘版本作为回滚手段
WebDAV 网盘通常支持版本历史。在脚本生成新的docs.json前,可以先复制一份旧文件到backups目录。这样即使转换脚本出现问题,也能快速回滚到上一版内容。
11. 需要提醒你的几个底层判断
这套方案真正的价值,不是“替代 Typora”这个动作,而是把 Markdown 的编辑、存储、分发三个环节从单一产品中解放出来。你可以继续用 Typora,也可以换 Mark Text,这都不影响网盘里的源文件;小程序只是内容的阅读终端,不是内容的仓库。只要源文件是纯文本 Markdown,未来你的选择空间就是开放的。
不过它也有明显的适用边界:
- 如果你需要多人同时在同一个文档上编辑,WebDAV 和脚本方案不够用;
- 如果你的目标是做一个内容社区,需要评论、用户系统,那这套静态渲染方案只能作为 MVP,后面要补服务端;
- 如果你对 Markdown 渲染的视觉要求特别高,比如要精确控制代码高亮、数学公式、脚注等,
rich-text方案会有不少妥协,改用组件库是更理性的选择。
从实际项目的成长路径看,这个方案最合理的定位是:个人知识库或小团队内容站的轻量起步方案。先用它验证“写作-存储-阅读”闭环是否真的需要那么多功能,再根据真实使用频率决定要不要引入更重的架构。
下一步建议这样实践:先创建 3 篇文章,从一篇 Markdown 开始,跑通脚本,再把数据导进小程序。遇到一个坑就解决一个,等你真正用它写了三五十篇文章之后,你对“内容管理”这件事的理解会比单纯找一个 Typora 平替要深得多。