在学习了微信小程序之后,我使用wxml页面可以做出唐诗的内容了。结合wxss,我可以定义好看的布局。在小程序需要备案时,因为唐诗需要资质,而我个人无法满足这些条件,所以我需要开发新的项目。恰好最近在为我的技术笔记发愁。我希望有一个可以展示我笔记内容的工具。
我的最初想法非常简单:
1,可以更新文章内容而不需要升级小程序。
2,需要在小程序端进行展示,并且需要美观。
3,能够根据标题或者关键字进行搜索文章内容。
经过一番研究后,我决定小程序使用原生开发,界面 UI 使用 WeUI,内容渲染使用Markdown。
小程序页面架构是列表+详情。列表作为首页,首页页面非常简单,一个标题和描述,用来解释小程序干什么?一个搜索框用来搜索内容,三个快捷按钮用于快速查找内容。 这个也是为了小程序审核,避免成为资讯类主题。
在完成之后,我发现这套架构可以通用,用来渲染动态内容。像列表页面以及WEUI使用,熟悉前端的非常容易理解实现。下面是一些核心介绍,主要介绍Markdown的渲染实现。
在小程序中,无法直接展示 Markdown 数据,我们需要将 Markdown 转换为 WXML,网上的大神很多,有多款 Markdown 转 WXML 库,我使用的是 TOWXML 库。我使用Markdown主要是因为更新内容不用升级小程序,这是动态渲染的最大优点。当然它还有其它一些优势,比如容易编辑,支持数学公式,布局美观等。
下面是Markdown具体实现,不感兴趣可以跳过。可以看技术下面的架构思考。
1,按照 towxml 库的说明文档,我们将编译后的 towxml 复制到我们的项目中,然后在 app.js 文件中引入这个组件。
App({
towxml: require('./towxml/index'),
2,按照首页的步骤,我们添加文章详情页面。
"pages/article/index"
在 index.wxml 文件中,我们添加页面布局,这个很简单,显示渲染后的内容即可。
<!--pages/article/index.wxml-->
<view class="page">
<!--使用towxml-->
<towxml nodes="{{article}}" />
</view>
3,注意,我们还需要在 index.json 中添加组件引入。
{
"usingComponents": {
"towxml": "../../towxml/towxml"
}
}
4,在 js 文件中,我们需要调用 Markdown 数据。
onLoad(options) {
const _ts = this;
wx.showLoading({
title: '加载中',
})
httpGet('/artd', {
uuid: options.guid,
}).then((res) => {
const result = res.data;
if (result.code == 1) {
let content = result.data;
let obj = app.towxml(content, 'markdown', {
theme: 'light',
events: {
tap: (e) => {
console.log('tap', e);
}
}
});
_ts.setData({
article: obj,
});
wx.hideLoading({
success: (res) => { },
})
} else {
wx.hideLoading()
}
}).catch((err) => {
console.log(err);
wx.hideLoading()
wx.showToast({
title: '网络异常请重试',
})
})
},
我们在 onLoad 中加载函数,这样页面启动就会自动网络请求,然后渲染 Markdown 数据。在请求数据时,我们注意到 guid,这个参数是上一个页面即首页,点击文章列表项带过来的数据。 你也可以使用其它参数,它能唯一代表从服务器拉取哪篇文章的Markdown内容。
首页跳转到详情页方法如下:
jump: function (e) {
const guid = e.currentTarget.dataset.guid
// 调整到文章页面
wx.navigateTo({
url: '../article/index?guid=' + guid,
})
},
现在,关于文章动态展示功能就完成了。这也是最核心的内容。下面的内容主要是服务端架构的改变历史。
数据库存储Markdown内容
第一次,我是使用数据库直接存储Markdown的内容,通过二进制存储。几个月后,服务器到期了。我没有续费,那个承载着API和数据库的“平台”,在物理上画上了句号。
但我不想让它就这样消失。这个从我个人技术笔记成长起来的小程序,我投入了大量的时间和心血,像我的一个数字孩子,我舍不得。我必须在新的约束下——近乎零成本、零维护的约束下——为它找到一条新的活路。
我的第一个决定,是做最彻底的产品减法。我砍掉了所有平台化的幻想。没有创作者上传,没有审核,没有激励系统。它回归了最初的模样:一个只属于我个人的、记录技术知识碎片的数字笔记本。原因很现实:一是我已没有精力维护复杂后台,二是小程序政策对个人开发者的内容上传限制越来越严。它不再是一个“平台”,重新变回一个单纯的“作品”。
产品形态回归简单,但最核心的技术问题来了:内容存哪?怎么更新?
以前,文章存在数据库,有个后台来维护。现在服务器都没了,这条路断了。曾经想过云存储,但是云存储也开始收费了。直到一次偶然的灵感,让我找到了破局点:Git服务器(比如Gitee),不就是个现成的、免费的、极其稳定的文件服务器吗?
Git存储Markdown内容
我立刻动手改造。整个技术栈被彻底重构:
创作回归本质:我不再需要后台。打开任何编辑器,用 Markdown 格式写好文章,这本身就是最舒适的写作。
存储拥抱版本控制:文章就是普通的 .md文件。我像提交代码一样,将它们 push到 Git 仓库的指定目录。这个仓库,成了我免费、永不过期、还带完整版本历史的“云端数据库”和“后台”。当然,为了能够访问,我也要把文章公开,同时也把这种方案分享给了我的公众号读者,并获得了不小的流量,有很多读者与我同感。
小程序变身静态渲染器:小程序端,我删光了所有调用自家后端的代码。新的数据流清晰极了:
a. 先拉目录:我在仓库里放了一个 index.json 文件,它就是全站的文章索引,里面用结构化的数据记录了所有文章的标题、摘要、时间和对应的文件路径。小程序启动后,第一件事就是用 downloadFile下载这个索引。
b. 再找内容:所有文章列表的展示、以及前端搜索,都变成了在本地对这个 index.json文件进行查询和过滤。
c. 最后渲染:用户点击某篇文章,小程序就根据路径,去下载那个具体的 .md文件,然后用我早已写好的 Markdown 解析器,将文本渲染成漂亮的页面。
于是,“豆子碎片”的第三个版本,演化成了一个完全由 Git 驱动的静态小程序。数据在云端(Git仓库),逻辑在本地(小程序)。虽然我为此砍掉了所有动态、交互、平台化的功能,但最核心的东西从未改变:它依然是一个能优雅、可靠地展示我技术文章的地方。
看似是战略撤退,实则是架构新生。它失去了平台的复杂性,却换来了前所未有的健壮性。只要我的 Git 仓库还在,只要小程序的基础框架还能运行,它就能一直活着,安静地待在那里,承载我的思考。
这第三次生命,不再关于扩张的野心,而是关于专注的可持续。它不再试图成为一个生态,而是安心做好一个纯粹的工具——一个完全属于我个人,零成本运行,并且会一直伴随我成长的技术笔记。这次重构,让我更深刻地理解了“少即是多”的力量,以及如何在技术的约束下,找到最本真、最持久的价值。
我的小程序“豆子碎片”,在经历了平台梦的破碎和服务器到期的沉寂后,凭借一个巧妙的构思迎来了第三次生命:完全舍弃后端,将文章以Markdown文件托管在Git仓库,让小程序直接拉取、渲染。它成了一个零成本、静态化的数字笔记本,我以为这便是它最优雅、永恒的形态了。
然而,免费的午餐总有代价。在平稳运行一段时间后,危机不期而至。某一天,用户反馈和后台数据同时告诉我:小程序一整天都无法加载任何文章。排查之后,根源令我心中一沉:我所依赖的第三方Git服务,对非自身域名的高频次外链访问进行了拦截。紧接着,另一个隐患浮出水面:我存放在那里的文章,同样受制于平台的内容审核策略,随时有“被过滤”的风险。
这次宕机像一记警钟。我终于清醒地认识到,我并没有解决核心的依赖问题——我只是将风险从需要付费的云服务器,转移到了一个免费但并不可控的第三方服务上。我的“永续运行”,建立在别人随时可能改变规则的沙地上。
要真正解决问题,我必须为“豆子碎片”建立一个稳定且自主的数据枢纽。于是,我做了一个与“零服务器”理念看似相悖、实则更为务实的决定:重新购买一台轻量级服务器。因为我不再需要做内容平台,所以这台服务器配置可以很小。这一次,它的使命极为纯粹,不做复杂的业务逻辑,不跑庞大的数据库。我只用它做一件事:运行一个由Nginx搭建的、高性能的静态文件代理与缓存服务。
文件存储Markdown内容
架构的升级清晰而有力:
内容源不变:我依然在本地用Markdown写作,用Git进行版本管理,并推送到远程仓库。这保留了我最舒适、最高效的创作工作流。
链路自主化:小程序前端,所有指向第三方Git Raw链接的请求被全部替换。新的请求目标,指向我自己的这台服务器的文件服务。
服务器作为网关:这台Nginx服务器扮演了智能网关的角色。它接收小程序的请求,然后返回对应的 index.json索引文件或 .md文章文件。获取成功后,它会在本地进行缓存,并返回给小程序。
这一改变,一举解决了所有核心痛点:
稳定性:我的服务器不会拦截和过滤文章内容,是合法、常规的访问模式,彻底解决了Git因客户端特征触发的风控拦截。
自主性:文章内容经由我的服务器返回,Git平台不再提供静态文件服务,仅作为仓库,内容审核策略不再能影响我。我对最终交付的内容,拥有了完整的控制权。
性能与成本可控:缓存机制降低了服务的压力,也提升了小程序的加载速度。这台轻量服务器的成本,远低于维护一个完整的应用后端。
核心的东西从未改变——我依然在用Git管理内容,小程序依然在展示Markdown渲染的文章。但一切又都不同了:我用一台极简的Nginx服务器,替换了不可控的第三方依赖,为自己赢得了一片稳定的技术腹地。
夺回了架构的控制权,就像为房子打下了坚实的地基。接下来,我开始考虑如何让住在里面的人更舒适。基于新的稳定架构,我实现了两次关键进化:
- 技术体验:实现静默更新
在以前的纯静态模式下,每次我发布新文章,都必须更新小程序版本才能让用户看到,体验极差。我引入了 “版本号”机制。小程序启动时会向服务器查询一个简单的版本标识,如果发现线上有更新,便自动拉取最新的文章索引。从此,内容更新与小程序发版彻底解耦,用户获得了无缝的静默更新体验。
- 产品形态:建立连接与生态
当技术不再成为掣肘,产品思维便有了空间。我不再仅仅满足于“展示”。
借助服务器,我部署了自己的服务,我加入了 “反馈与联系” 的入口,让这个工具从单向输出,变为能聆听用户声音的窗口。
我尝试在应用中,以不打扰的方式,与我开发的其他工具小程序进行相互引荐。我希望它们能彼此照亮,形成一个微小的、有共同气质的产品矩阵雏形。
“豆子碎片”的第四次生命,始于一次外部依赖导致的崩溃,成于一个务实的架构决策。从完全依赖第三方,到引入自主的Nginx代理层,这一步看似是技术的“后退”(重新引入了服务器),实则是产品主权和稳定性的“大踏步前进”。
它没有变得功能庞杂,反而在核心体验上更加健壮和流畅。它不再是一个脆弱的实验品,而成了一个拥有自主数据通道、可持续更新、并能与用户及其他产品产生连接的、真正意义上的完整产品。
这一次,我守护住的,不仅是那个“展示技术思考”的简单初心,更是让这个初心得以在复杂多变的技术环境中,长期、稳定、自主存在的能力。这,便是第四段生命赋予它的全部意义。
小程序最新状态请扫码浏览: