Markdown 语法教程

我们使用 Markdown 作为我们内容的格式,所以学习 Markdown 就显得很必要了。 Markdown 是一种轻量级标记语言,后缀为.md。因我常用 Markdown 来写一些内容,记录下来常用语法格式。方便自己查找使用,也增加自己的一些流量。Markdown 可以用来撰写电子书,文章,博客等。介绍标题、段落的使用。 标题 # 一级标题 ## 二级标题 ### 三级标题 #### 四级标题 ##### 五级标题 ###### 六级标题 段落 Markd 段落没有特殊的格式,直接编写文件就好,段落的换行使用两个以上空格加上回车。也可以使用一个空行来表示重新开始一个段落。 字体 使用如下格式分别表示斜体,粗体,粗斜体。 *斜体文本* _斜体文本_ **粗体文本** __粗体文本__ ***粗斜体文本*** ___粗斜体文本___ 分割线 你可以在一行中用三个以上的星号、减号、底线来建立一个分割线,行内不能有其它东西。你也可以在星号或减号中间插入空格。 *** * * * ***** - - - --------- 删除线 如果段落上的文字要添加删除线,只需要在文字的两端加上两个波浪线~~即可。 ~~删除线~~ 下划线 下划线可以通过 HTML 的标签来实现: <u>下划线文本</u> 脚注 脚注是对文本的补充说明。 [^要注明的文本] 列表 支持有序列表和无序列表。 无序列表使用星号、加号、减号作为列表标记,这些标记后面要添加一个空格,然后再填写内容。 * 第一项 * 第二项 * 第三项 + 第一项 + 第二项 + 第三项 - 第一项 - 第二项 - 第三项 有序列表使用数字并加上.号来表示。 ...

2026-01-05 · 2 min · Eagle

从单页网站开启数字创造

所有的宏大工程都始于最微小的表达。这一节,我们聊聊那个只有 index.html 的纯真时代。本文将带你了解数字世界背后的故事。 1. 域名的“翻译”艺术 服务器在网络中是以 IP 地址(如 123.123.123.123)存在的。为了让人类能够记忆,我们引入了域名系统(DNS)。 解析逻辑: 通过配置一条 A 记录,我们将 91demo.top 映射到特定的数字 IP 上。从此,枯燥的数字拥有了可读的身份。 2. 安全的阶梯:从 HTTP 到 HTTPS 一个现代化的网站不应“裸奔”。 使用 Let’s Encrypt 提供的免费证书,配合 Nginx 的反向代理配置,我们为网站穿上了 TLS 加密的铠甲。 成就达成: 浏览器地址栏那个小锁头的出现,标志着你已初步掌握了网络协议的基础。 3. 守门人:Nginx 的静默力量 在本项目中,没有繁琐的数据库和 API 逻辑。Nginx 扮演了一个纯粹的“守门人”角色。 它安静地运行在后台,监听着 80 与 443 端口,随时准备将那一个笨拙却真诚的 index.html 展示给世界。 “虽然 CSS 是 AI 生成的,但我终于学会了如何让代码在服务器上跳舞。” 4. 归于简单:单页的本质 现在的 Web 世界如迷宫般复杂,但回看那个单页网站,那不仅是 HTML,更是开发者第一次触碰数字创造的本质:用基础的材料,构建被看见的天地。

2025-03-02 · 1 min · Eagle

八年了,从一个随机数按钮到我的技术底座:豆子工具复盘

有些事,不做笔记真的意识不到已经过去了这么久。 最近在整理博客内容时,我翻到了**“豆子工具”小程序**的最早版本记录。那是 2018 年,最初的它简陋得甚至有点滑稽:整个页面只有一个按钮,点一下,生成一个随机数。 谁能想到,这颗“小豆子”一跑就是八年。 八年,它长成了我最趁手的“瑞士军刀” 这八年里,我在这款小程序上倾注了太多的时间和精力。它更像是我个人开发者生涯的一个“活化石”,记录了我每一个阶段遇到的问题和想出的方案。 我始终坚持一个原则:只做真正有用的。 现在的“豆子工具”已经从当年的随机数生成器,进化成了一个覆盖网络、多媒体、开发调试的全能工具箱: 网络与运维必备:获取 WIFI 公网地址、云厂商安全组管理、查服务端口、域名证书检测。 多媒体处理:音频格式转换(m4a 转 mp3)、图片格式转换(png 转 webp)、二维码识别与生成。 开发调试利器:局域网调试(TCP/UDP/WebSocket)、ESP 网络配置、时间戳转换、Base64 与 URL 编解码。 安全与提醒:私密密码本、事件邮件通知、微信服务通知、语音验证码。 这中间还有很多功能,比如查看上报信息、特定的算法实现等。当然,也有不少功能因为受众太小或者微信审核的边界问题,遗憾地消失在了版本更迭中。 它是工具,也是我的技术底座 很多人问过我,市面上工具软件那么多,为什么要自己写? 答案很简单:自由。 我可以为了适配一个特殊的局域网协议去写一套调试代码,也可以为了保护自己的隐私数据写一个加密密码本。这八年积累下来的不只是功能,更是对 Go、Rust、前端交互以及各种底层协议的深度理解。 关于未来的“深度复盘” 八年的积淀太厚,小程序那方寸之间已经装不下太多的原理说明。 所以,我决定在我的新博客中,为这些工具开辟专门的专栏。我会挑选出其中含金量最高、逻辑最有趣的工具,拆解它们的实现原理。比如: 多媒体黑盒:音频和图片格式转换在小程序前端是如何高效完成的? 网络深水区:局域网内的协议调试有哪些不为人知的坑? 消息推送系统:我是如何设计那套多端触发的事件通知系统的? 这些内容我会以“实战笔记”的形式,在这里逐一连载。 这颗“豆子”还会继续长下去,而关于它的故事,我们去博客细聊。

2018-01-03 · 1 min · Eagle

实战笔记:网站使用二维码登录

扫码基石:构建视觉化的连接钥匙 二维码(QR Code)是连接物理世界与数字世界的“虫洞”。在登录系统中,它承载着一个临时的身份信标。 1. 生成有效二维码 登录二维码通常包含一个加密的 URL 或一个唯一的 UUID(通用唯一识别码)。 唯一性: 每一对扫描动作都必须对应一个独一无二的 ID。 时效性: 二维码必须配合 Redis 设置过期时间(如 2 分钟),逾期自动失效。 Redis 最核心的用法是 Key-Value (键值对)。 在本项目中,我们将用户的微信 OpenID 作为 Key,生成的验证码作为 Value: // 存储验证码,并设置 5 分钟过期 err := rdb.Set(ctx, "user:123:code", "8888", 5*time.Minute).Err() // 读取验证码 val, err := rdb.Get(ctx, "user:123:code").Result() 在 Go 生态中,我们使用 skip2/go-qrcode 等库来完成像素的绘制: // 生成二维码字节数组 var png []byte png, _ = qrcode.Encode("91demo.top"+sessionID, qrcode.Medium, 256) 为了防止用户伪造扫码请求,二维码里的内容通常是加密的或者是不可预测的长随机数(UUID)。只有真实存在的 ID 才能通过后端的 Redis 校验。 2. 传输二维码 我们不希望在用户硬盘上产生大量的临时 .png 文件。 最佳实践是将二进制图片转换为 Base64 字符串,通过结构体返回给前端: // 转换为前端可直接识别的 Data URL base64Img := "data:image/png;base64," + base64.StdEncoding.EncodeToString(png) 为了让前端不至于崩溃,后端必须返回统一的格式。 func HandleLogin(c *gin.Context) { // 逻辑处理... c.JSON(200, gin.H{ "code": 1, "content": base64Img, }) } 在前端,我们不再需要引入沉重的第三方库来做简单的请求。浏览器原生提供的 Fetch API 简洁且基于 Promise。 ...

2025-03-15 · 3 min · Eagle

Wander 项目功能列表状态--列表形式

记录曾经存在的功能,列表形式展示。 音频转 MP3 可以将 IOS 的录音文件 m4a 格式转换为 mp3 格式,在老设备播放,或者其它特殊应用场景。 使用方法:搜索豆子工具小程序,点击音频格式转换。 推荐理由:方便用户在手机上操作,可以转换音频,还可以试听效果。 获取 AST 账户 AST 账户用于连接 Asterisk,是自助语音验证码重要的一个环节。 你可以搜索【豆子工具】小程序,获取自己的 AST 账户。 注意:AST 账户获取后并不是马上生效,它需要到次日方可使用。这是因为它在凌晨会进行一次同步,会同步到 Asterisk 数据库。 后续我们会讲下如何实现自助语音验证码。 获取识别码 识别码用于开放接口认证。专属 ID 码为自定义识别码,方便用户记忆。 目前开放接口为: 获取本机公网 IP 获取 IP 地址的归属地 你可以搜索【豆子工具】小程序,获取自己的识别码。 然后使用你喜爱的编程语言调用开放接口。 开放接口 API 请查看【豆子笔记】网站。 获取本机局域网 IP 状态:上架 上架原因:有适用场景,可用于获取手机的局域网 IP,排查问题时使用。 实现原理:调用小程序的本地获取 IP API。 获取网络打印机地址 状态:上架 上架原因:有适用场景,可用于获取局域网的网络打印机 IP 地址。 实现原理:调用小程序的 mdns API。 获取验证码 验证码用于访问豆子笔记网站认证使用。它是一次性码,用完即作废,5 分钟内有效。 豆子笔记网址:www.91demo.top 除了在豆子工具小程序获取验证码外,还有一个自助语音验证码,使用 Asterisk 实现。我们会在后续的章节进行讲解。 获取豆子点数 观看激励广告可获得豆子点数。 豆子点数用于使用小工具。添加豆子点数机制,是为了更好的促进豆子工具良性的发展。 除了观看激励广告获取豆子点数外,还可以使用九宫格大转盘抽取豆子点数, 报 IP 地址归属地也可以获取豆子点数奖励。 ...

2018-02-25 · 1 min · Eagle

实战笔记:网站使用验证码登录

我们都知道手机号验证码可以登录网站。这里我也做了类似的功能,方便在手机端不能使用扫码也能登录。 我在小程序端制作了一个获取验证码界面,它可以生成模拟编号和验证码。当用户点击获取验证码时,会返回编号和验证码,它同时绑定用户的小程序身份Openid。 当用户在网站端输入编号和验证码时,后端会校验是否存在这对编号和验证码,如果校验正确,将取出openid并绑定到sessionID上,然后返回给前端,存入cookie中。 除了在小程序生成验证码外,我还添加了在公众号发送消息获取验证码。 1. 公众号获取验证码 公众号不仅是内容分发平台,更是一个强大的身份认证中间件。 1. 握手与回调 (Webhook) 要在 Go 中接收微信消息,你必须先在微信公众平台配置一个 服务器地址 (URL)。 校验: 微信会向你的 URL 发送一个 GET 请求,包含签名、随机数等。你必须按照规定的算法计算并返回正确的 echostr,这被称为“服务器验证”。 消息推送: 验证通过后,每当用户发送消息,微信服务器就会以 POST 方式将消息体推送给你。 2. 解析 XML 数据 与现代 API 不同,微信公众号的推送采用的是 XML 格式。Go 的标准库 encoding/xml 提供了强大的解析能力: type WxMsg struct { ToUserName string `xml:"ToUserName"` FromUserName string `xml:"FromUserName"` // 这就是用户的 OpenID Content string `xml:"Content"` // 用户发来的文字 } 3. 验证码生存逻辑 (Redis 联动) 当用户发送“验证码”关键字时,我们的 Go 后端会: 生成一个 4-6 位的随机数。 将 OpenID 与 随机数 存入 Redis,并设置 TTL(如 5 分钟)。 通过 XML 响应将验证码发回给用户。 4. 身份对撞 用户在Web端输入这个验证码。后端从 Redis 中根据验证码反查 OpenID,若存在且有效,则代表身份验证成功。执行登录流程。 ...

2025-03-11 · 1 min · Eagle

git开发常用命令

如果你要开始项目开发,看看如下 Git 常用命令对你绝对有帮助。 在每次开发新功能时,拉出一个新分支,分支名称为开发的功能名称,格式为 func-xxxx。当开发的功能完成并测试没有问题后,合并到主分支。 当在开发新功能时,如果出现 bug,能够马上解决的,直接在主分支上修改,如果需要时间长,则新创建一个分支,格式为 bug-xxxx。当开发的功能完成并测试没有问题后,合并到主分支。 1,开发分支。 切换到 dev 分支,进行代码开发。开发完毕后,合并到主分支。 git checkout dev 2,合并到主分支。 git checkout master git merge dev git push origin master 3,主分支添加 tag,并发布到线上。 git checkout master git tag -a v1.0.0 -m "V1.0版本" git push origin v1.0.0 git push --tags 4,删除分支 git branch -d 分支名称 5,设置用户名和邮箱 git config --global user.name 你的用户名 git config --global user.email 你的邮箱 打标签 # 列出标签 git tag # 列出特定版本标签 git tag -l "v1.8.5*" # 创建标签 git tag -a v1.4 -m "my version 1.4" # 显示标签 git show v1.4 # 根据提交打标签 git tag -a v1.2 9fceb02 # 推送标签 git push origin v1.5 # 删除标签 git tag -d v1.4 # 检出标签,浏览使用 git checkout v1.4

2026-01-05 · 1 min · Eagle

豆子碎片的故事

豆子碎片小程序很早就注册了,刚开始做的功能就是浏览 Go Web 框架 Beego 的教程。当时,正在自学 Beego,而 Beego 的网站时常打不开,就想做一个小程序,可以浏览 Beego 内容。当时看到了 towxml 组件,正好可以实现 Markdown 转 Wxml,于是就将 Beego 的 内容文档写成 Markdown 格式,做成了小程序。小程序的项目名叫 visit,就是浏览的意思。 当时小程序还是使用的云环境,云环境免费,并提供 dev 和 pro 两个环境。将 Beego 的 Markdown 文件存在云存储中。使用云函数调用数据。在小程序端使用 towxml 解析 Markdown 数据。当时发布小程序后,还是很受欢迎的。 中间歇了一段时间,没有更新文章,也没有维护小程序,小程序日浏览人数就下降了。云环境收费后,就彻底不管了。 后来,买了一台云服务器,为了练习自己所学的 Golang 语言,就将小程序重新拾起来。新做的项目内容是浏览唐诗,使用 Gin 框架进行后端开发,小程序端的布局和现在的豆子碎片布局差不多,只有一个首页,首页包含标题、一个搜索框、几个快捷按钮。项目很快完成,将小学和初中的唐诗、宋词、古诗经都录入了数据库,维护了很长一段时间。小程序上线后,每天也有几个人访问。在录入高中的唐诗内容时,就无法坚持下去了,高中的唐诗等内容都比较长,页面排版非常不美,所以高中的内容就没有录入。小程序在展示唐诗内容时,刚开始使用 Markdown 展示。后来想添加拼音,就将 Markdown 去掉,手撸 wxml,最后,拼音也添加了,但是费了很大劲,自己不擅长前端,美工。本还计划添加录音,但实在没有素材,真正做起来,费时费力,没有精力和动力就搁置了。 2024 年微信平台要求小程序都要备案。我有三个小程序,本想都放弃了,但后来,在工作中要用到音频格式转换,在小程序端实现操作起来是真的很方便,省时省力,不需要开电脑,也可以在任何时间操作。所以计划留下一两个吧。在备案的时候,注销了一个,剩下了两个,目前还在使用和维护,一个是豆子工具,用来制作工具,平时我也在用,一个是豆子碎片,记录技术经验和代码片段。在备案豆子碎片小程序时,小程序原名称叫找唐诗,提交备案后,微信备案客服人员说唐诗这两字不让用,让换个名称。在换名称的时候,我就在想这个小程序要不要放弃?不放弃做什么内容?已经有一个豆子工具小程序做工具了,没有必要再做一个工具类小程序吧。一直没有想好,等微信备案人员二次打电话时(真的很负责),才确定下来,做笔记类应用,记录日常开发经验和技巧以及代码片段,本想叫代码碎片,为了和豆子工具保持一致,小程序名称就叫豆子碎片,寓意像玩积木一样,学了本小程序内容,你也可以制作一款你想要的小程序。 豆子碎片项目已经开源,项目地址:https://github.com/littletow/visit 豆子碎片目前没有实名认证,如果你喜欢就收藏一下。豆子碎片面对的人群是有技术门槛的,后续看情况再进行实名认证。 豆子碎片的文章也已经在我公众号发布。有兴趣可以查看一下,一个程序员之小程序的成长与蜕变 想要查看豆子碎片,可以扫下面的小程序码:

2026-01-05 · 1 min · Eagle

豆子碎片小程序开发实践:从核心功能到技术实现

前面介绍了如何学习豆子碎片小程序,今天本文将介绍包括核心功能实现、技术方案对比和业务逻辑落地等方面。豆子碎片小程序是一款小程序技术和经验等内容展示类的小程序,主要用于展示 Markdown 文件解析后的内容。 核心功能实现 豆子碎片小程序的核心功能主要包括以下几个方面: 数据下载与本地缓存:每次小程序启动时,从服务器下载最新的 Markdown 文件,并缓存到本地,以便后续使用。 Markdown 解析与渲染:使用 towxml 组件解析下载的 Markdown 文件,并渲染成 wxml 文件,以便在小程序中展示。 首页布局与搜索:首页采用上中下布局,上方介绍小程序的用途,中间是搜索框,下方是快捷按钮。当用户在搜索框输入内容或点击快捷按钮时,查询出相关的文章列表。 文章详情展示:点击文章列表中的文章,跳转到文章详情页,展示文章的详细内容。 技术方案对比 在实现豆子碎片小程序的过程中,我们对多种技术方案进行了对比,最终选择了以下技术方案: 数据下载与本地缓存 方案一:每次启动时都从服务器下载数据:这种方案实现简单,但会增加网络请求次数,影响小程序的启动速度和用户体验。 方案二:使用本地缓存:在小程序启动时,先检查本地缓存是否有最新数据,如果有则直接使用,否则从服务器下载数据并缓存到本地。最终我们选择了这种方案,以提高小程序的启动速度和用户体验。 Markdown 解析与渲染 方案一:自定义解析器:自行开发 Markdown 解析器,解析 Markdown 文件并生成 wxml 文件。这种方案实现复杂,维护成本高。 方案二:使用现有组件:我们对比了 wxParse 组件和 towxml 组件,最终选择使用开源的 towxml 组件进行 Markdown 解析和渲染,它组件成熟,易于集成和使用。最终我们选择了这种方案,以简化开发流程并提高开发效率。 首页布局与搜索 方案一:手动实现布局和搜索功能:自行编写代码实现首页布局和搜索功能,灵活性高,但开发成本较高。 方案二:使用微信小程序提供的组件:使用微信小程序提供的 UI 组件,如<view>、<input>、<button>等,实现首页布局和搜索功能,并使用 weui 样式。最终我们选择了这种方案,以简化开发工作并提高开发效率。 业务逻辑落地 下方只展示了代码片段,详情请查看 visit 项目。 数据下载与本地缓存 在小程序的 app.js 文件中,我们实现了数据下载和本地缓存的逻辑: // 登入 async login() { const that = this; const tzms = utils.getTodayZeroMsTime(); // 获取今日零时毫秒时间戳 // 检查设备信息? that.logDevInfo(); // 检查Git服务是否正常? const isAlive = await that.chkServerAlive(); console.log('url,', that.globalData.url, isAlive); // 检查版本更新? that.uptVer(tzms); // 加载用户信息 that.onLogin(); }, // 小程序每次启动都会调用 onLaunch: function () { const startTime = Date.now(); this.globalData.startTime = startTime; this.login(); const loginTime = Date.now(); const launchTime = loginTime - startTime; console.log(`app onLaunch: ${launchTime} ms`); }, 首页布局与搜索 在首页的 index.wxml 文件中,我们实现了上中下布局和搜索框: ...

2026-01-05 · 3 min · Eagle

将数组按照时间重新降序排序

在写笔记列表页面时,发现从服务器获取的笔记列表包含最新更新时间 lastts,当我们想将最新更新的条目展示在最上端时,我们需要排序数组,将数组按照时间重新排序,排序规则为最新的时间条目放在最前面。 这样我们就可以在笔记列表中让用户看到最新更新的内容了。 我们需要将其封装为函数,方便调用。 函数代码如下: // 按照更新时间排序的函数 sortNotesByLastTs: function(notes) { return notes.slice().sort((a, b) => { // 将时间字符串转为时间戳比较 const timeA = new Date(a.lastts).getTime(); const timeB = new Date(b.lastts).getTime(); // 降序排列(最新的在前) return timeB - timeA; }); }, 当运行之后,发现时间戳格式和服务器返回格式不一致,我们需要重新调整函数,服务器返回格式 YYYY-MM-DD HH:mm:ss,本地函数使用的时间格式 ISO 8601 格式。 调整后的代码如下: // 按照更新时间排序的函数(适配"YYYY-MM-DD HH:mm:ss"格式) sortNotesByLastTs: function(notes) { return notes.slice().sort((a, b) => { // 解析自定义时间格式 const parseTime = (timeStr) => { if (!timeStr) return 0; // 替换空格为T,使其符合ISO格式 const isoFormat = timeStr.replace(' ', 'T'); return new Date(isoFormat).getTime() || 0; }; const timeA = parseTime(a.lastts); const timeB = parseTime(b.lastts); // 降序排列(最新的在前) return timeB - timeA; }); }, 推荐还是调整服务器端,让返回的时间格式转为 ISO 8601 格式。

2026-01-05 · 1 min · Eagle