简介:这份资源是面向微信小程序初学者与传统文化爱好者的唐诗诗词小程序页面源码,帮助开发者在移动端实现诗词阅读与欣赏功能,可作为页面布局、交互逻辑与数据处理的入门参考案例。压缩包共14个文件,约6KB,以5个json配置文件、4个js逻辑脚本、3个wxss样式表和2个wxml页面结构为主,分别承担全局配置、交互响应、视觉样式与页面骨架等职责,另含工具类脚本与本地资源,目录结构清晰便于按模块查阅。目前已有92人学习下载。通过研读源码,读者可掌握首页、列表页、详情页等页面的组织方式,理解小程序如何通过API与服务器交互实现诗词内容的动态加载,并借鉴其简洁美观的界面设计思路,为自身小程序开发积累可复用的实践经验。
1. 唐诗诗词小程序:从一份页面源码里能拆出多少可复用的东西
拿到「唐诗诗词的微信小程序页面源码.zip」这类资源,多数人第一反应是解压、用微信开发者工具打开、点编译,然后看首页能不能跑起来。能跑,就截图发个朋友圈;跑不起来,就丢进回收站。但真正值得花时间的,是把它当成一个「垂直内容型小程序」的最小样本去拆:它怎么组织诗词数据、列表页怎么渲染、详情页怎么传参、搜索怎么做、收藏状态存哪。唐诗诗词这个题材看着简单,实际把内容型小程序该有的页面结构基本都覆盖了——首页推荐、分类列表、详情展示、搜索、收藏。你把这套骨架吃透,换成宋词、成语、菜谱、法律条文,改的是数据源,不是架构。这篇面向的是手里已经拿到或准备找这类源码的开发者,尤其是想用微信小程序做内容类产品、又不想从零搭页面的人。下面按「先看清结构、再动手跑通、最后避坑和进阶」的顺序讲,能直接抄的步骤我都给全。
2. 唐诗诗词小程序的页面骨架:先看清目录再动手
2.1 一份典型诗词小程序的目录长什么样
不同作者打包的源码目录会有差异,但内容型小程序的页面骨架高度趋同。解压后你大概率会看到这样一层结构(名字可能不同,职责基本对应):
tangshi-miniprogram/ ├── app.js # 全局逻辑:启动、全局数据、登录态 ├── app.json # 全局配置:页面路径、窗口、tabBar ├── app.wxss # 全局样式 ├── pages/ │ ├── index/ # 首页:推荐 / 分类入口 │ ├── list/ # 列表页:按分类或朝代展示诗词 │ ├── detail/ # 详情页:正文、注释、译文 │ ├── search/ # 搜索页 │ └── favorite/ # 收藏页 ├── components/ # 自定义组件:诗词卡片、空状态等 ├── utils/ # 工具函数:请求封装、格式化 ├── data/ # 静态诗词数据(json / js) └── images/ # 图标、占位图先别急着改代码,打开app.json看pages数组,那是页面注册表,也是路由的真相来源。数组第一项就是启动页。再看tabBar,它决定了底部有几个入口,通常首页、分类、收藏会占三个。把这两个字段读明白,你就知道用户进来第一眼看到什么、能点到哪去。
2.2 数据从哪来:静态 JSON 还是接口请求
诗词类小程序的数据来源无非两种,源码里通常二选一,或者混用。
第一种是静态数据,诗词直接放在data/下的 json 或 js 文件里,页面require进来渲染。优点是零后端、打开即用、离线可看;缺点是数据量一大,主包体积就顶到 2MB 上限,微信小程序对主包有硬限制,超了直接传不上去。唐诗宋词加起来几万首,全塞主包必炸。
第二种是接口请求,页面onLoad里调后端拿数据。优点是数据可无限扩展、能后台更新;缺点是你得有个服务端,还要处理域名备案和request合法域名配置。
我一般会这么判断:源码里如果data/目录下有大 json,且页面里出现require('../../data/poetry.js'),那就是静态方案;如果看到wx.request或封装过的request.js,那就是接口方案。静态方案适合学习和演示,接口方案才是能上线的形态。下面这段是接口方案里最常见的请求封装写法:
// utils/request.js const BASE_URL = 'https://your-domain.com/api'; // 换成你自己的合法域名 function request(path, params = {}) { return new Promise((resolve, reject) => { wx.request({ url: BASE_URL + path, data: params, method: 'GET', timeout: 8000, // 超时设短一点,避免用户干等 success(res) { if (res.statusCode === 200 && res.data.code === 0) { resolve(res.data.data); } else { reject(res.data); // 业务错误码统一 reject } }, fail(err) { reject(err); // 网络层错误 } }); }); } module.exports = { request };这段代码的关键点有三个。BASE_URL必须是你在小程序后台「开发设置 - 服务器域名」里配过的合法域名,否则真机上直接请求失败,开发者工具里可能因为勾了「不校验合法域名」而看不出来。timeout设 8000 毫秒是经验值,诗词接口数据不大,超过 8 秒基本是服务端挂了,早点失败让用户重试比一直转圈强。code === 0这种业务码约定要跟后端对齐,别一边判 0 一边判 200,联调时最容易在这翻车。
2.3 列表页与详情页的参数传递
列表页点一首诗进详情页,靠的是路由传参。常见写法是列表项绑定>// pages/list/list.js Page({ onTapPoem(e) { const id = e.currentTarget.dataset.id; wx.navigateTo({ url: `/pages/detail/detail?id=${id}` }); } });
详情页在onLoad(options)里拿options.id,再决定是查本地数据还是请求接口。这里有个容易忽略的点:navigateTo的 url 长度有限制,别把整首诗的内容塞进参数里传,只传 id,正文在详情页自己取。我见过有人图省事把标题、作者、正文全拼进 url,结果长诗直接超长截断,详情页显示半截,排查半天才发现是传参方式的问题。
3. 把源码跑起来:微信开发者工具里的最小闭环
3.1 导入项目与 appid 的两种选择
打开微信开发者工具,选「导入项目」,目录指向解压后的根目录。它会让你填 AppID。如果你只是本地学习,点「测试号」就行,测试号能跑绝大多数功能,包括页面渲染、本地存储、大部分 API。但测试号有两个限制要提前知道:一是不能真机预览某些需要正式 appid 的能力,二是云开发相关功能用不了。
如果你要真机调试或者上线,就得用自己注册的小程序 appid。注册本身不复杂,个人主体也能注册,认证费用是另一回事,学习阶段用测试号完全够。导入后工具会自动读app.json生成页面树,如果某个页面路径在pages里注册了但目录不存在,编译会直接报错,按提示补目录或删注册项即可。
3.2 编译报错的三类高频原因
第一次编译就顺利跑起来的概率不高,报错基本集中在三类。
第一类是路径错误。app.json里注册了pages/detail/detail,但实际目录叫pages/details/detail,差一个字母就报「page not found」。解决办法是对着文件树逐个核对pages数组。
第二类是基础库版本不匹配。源码里用了较新的 API,比如某些组件或wx.开头的接口,而你工具里选的基础库版本太低,就会报「xxx is not a function」。在工具右上角「详情 - 本地设置 - 调试基础库」里调高版本,一般选较新的稳定版即可。
第三类是 npm 依赖没构建。如果源码用了miniprogram_npm,你得先在项目根目录执行构建 npm。工具菜单「工具 - 构建 npm」点一下,它会生成miniprogram_npm目录。没构建就引用第三方库,报错是「module not found」。
3.3 静态数据方案的体积红线
前面提到主包 2MB 限制,这里展开说。微信小程序主包上限 2MB,整个小程序(主包 + 所有分包)上限 20MB。诗词数据如果是静态 json,很容易把主包撑爆。判断方法很简单:编译后看工具「详情 - 基本信息」里的代码包大小。
如果超了,两条路。一是分包,把列表、详情、搜索拆到分包里,主包只留首页和 tabBar 页面。分包配置写在app.json的subPackages字段:
{ "pages": ["pages/index/index", "pages/favorite/favorite"], "subPackages": [ { "root": "packagePoetry", "pages": ["pages/list/list", "pages/detail/detail", "pages/search/search"] } ] }注意subPackages里的页面路径是相对root的,别写成完整路径。二是把数据挪到服务端,主包只留请求逻辑,这是更彻底的方案。静态数据适合几百首以内的量,上万首必须走接口。
4. 诗词数据的组织与渲染:列表、详情、搜索怎么落地
4.1 诗词数据该用什么结构存
不管数据放本地还是服务端,结构设计决定了后面所有页面的写法。一首诗至少要有这些字段:唯一 id、标题、作者、朝代、正文(按句或按行拆)、注释、译文、标签。我一般会这么定:
// data/poetry.js 里的一条示例 { id: 'ts-0001', title: '静夜思', author: '李白', dynasty: '唐', content: ['床前明月光,', '疑是地上霜。', '举头望明月,', '低头思故乡。'], notes: ['疑:好像。', '举头:抬头。'], translation: '明亮的月光洒在床前,好像地上泛起了一层白霜……', tags: ['思乡', '五言绝句'] }content用数组而不是一整段字符串,好处是渲染时可以直接wx:for逐句输出,行距和缩进好控制,也方便做「逐句高亮」这类交互。id用带前缀的字符串而不是纯数字,是为了将来多数据源合并时不冲突。tags是数组,方便做标签筛选和搜索命中。
4.2 列表页的渲染与性能
列表页用wx:for渲染诗词卡片,这是最基础也最容易出性能问题的地方。几百条数据一次性渲染,低端机上会明显卡顿。常见做法是分页加载,每次请求 20 条,滚到底部再加载下一页。onReachBottom是列表页的标配钩子:
// pages/list/list.js Page({ data: { list: [], page: 1, hasMore: true }, onLoad() { this.loadPage(1); }, onReachBottom() { if (this.data.hasMore) { this.loadPage(this.data.page + 1); } }, async loadPage(page) { const res = await request('/poetry/list', { page, size: 20 }); this.setData({ list: page === 1 ? res.items : this.data.list.concat(res.items), page, hasMore: res.items.length === 20 // 返回不足 20 条说明到底了 }); } });hasMore的判断逻辑是「本页返回条数是否等于每页大小」,小于就说明没有下一页了。这个判断比让后端返回 total 再算页码更省事,但前提是后端分页稳定。setData里第一页用覆盖、后续页用concat,别每次都覆盖,否则列表永远只有一页。setData本身有性能开销,列表项多的时候尽量只更新变化的部分,别整个 list 重设。
4.3 详情页的正文排版与注释展示
详情页是诗词类小程序的门面,排版好不好直接影响观感。正文逐句渲染,注释和译文折叠或分区展示。一个可用的结构是:标题区(标题 + 作者 + 朝代)、正文区(逐句)、注释区、译文区。正文用wx:for输出content数组,每句一个view,行高给足:
<!-- pages/detail/detail.wxml --> <view class="poem-title">{{poem.title}}</view> <view class="poem-meta">{{poem.dynasty}} · {{poem.author}}</view> <view class="poem-body"> <view class="poem-line" wx:for="{{poem.content}}" wx:key="index">{{item}}</view> </view> <view class="poem-section" wx:if="{{poem.notes.length}}"> <view class="section-title">注释</view> <view class="note-item" wx:for="{{poem.notes}}" wx:key="index">{{item}}</view> </view>wx:key用index在纯静态列表里没问题,但如果列表会增删排序,用 index 会导致渲染错位,那种场景要用唯一 id。注释区用wx:if判断有没有内容再渲染,避免出现空标题。正文行高建议 1.8 到 2.0,诗词阅读需要留白,行高太小会显得挤。
4.4 搜索功能的两种实现
搜索有两种做法。本地搜索直接遍历数据数组,用indexOf或includes匹配标题、作者、正文:
// 本地搜索 function searchPoetry(keyword, allPoems) { const kw = keyword.trim(); if (!kw) return []; return allPoems.filter(p => p.title.includes(kw) || p.author.includes(kw) || p.content.join('').includes(kw) ); }数据量小的时候这样够用,但每次输入都全量遍历,数据上万条就会卡。优化做法是加防抖,用户停止输入 300 毫秒后再搜:
let timer = null; function onInput(e) { clearTimeout(timer); const kw = e.detail.value; timer = setTimeout(() => { this.setData({ result: searchPoetry(kw, this.data.allPoems) }); }, 300); }接口搜索则把关键词发给后端,由后端做匹配和分页。接口搜索的坑在于中文分词和模糊匹配,后端如果用LIKE '%kw%'简单匹配,搜「明月」能中,搜「月光」可能就漏了,因为正文里是「明月光」。这块要跟后端约定好匹配策略,别指望前端兜底。
5. 避坑与排查:诗词小程序最容易翻车的五个地方
5.1 真机上请求全失败,工具里却正常
现象:开发者工具里列表能加载,真机预览一片空白,控制台报request:fail url not in domain list。
原因:工具里默认勾选了「不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书」,真机没有这个豁免,请求的域名必须在小程序后台配置过。
解决:登录小程序后台,「开发 - 开发设置 - 服务器域名」里把接口域名加到 request 合法域名。域名必须 HTTPS,且已备案。测试阶段可以临时在工具里勾选不校验,但上线前必须配好。
5.2 主包超 2MB 传不上去
现象:本地编译正常,点上传报「代码包大小超过限制」。
原因:静态诗词数据全塞在主包,或者图片资源没压缩。
解决:先看代码包分析,找出体积大头。数据走分包或挪到服务端,图片换成压缩过的 webp 或直接用网络图。分包后注意 tabBar 页面必须在主包,不能放进分包。
5.3 详情页返回后列表位置丢失
现象:从列表进详情,返回后列表滚回顶部,用户得重新找位置。
原因:navigateTo会保留页面栈,返回时列表页理论上还在,但如果列表页在onShow里重新请求并setData覆盖了 list,滚动位置就没了。
解决:列表页数据加载放在onLoad而不是onShow,返回时不重新请求。如果确实需要在onShow刷新收藏状态,只更新状态字段,别整个 list 重设。
5.4 收藏状态不同步
现象:详情页点了收藏,返回列表页收藏图标没变。
原因:收藏状态存在详情页的 data 里,列表页不知道。
解决:收藏状态统一存到全局或本地存储,页面onShow时从统一来源读取。用wx.setStorageSync存收藏 id 数组,列表页渲染时判断当前项 id 是否在数组里。别在两个页面各存一份,迟早不一致。
5.5 长诗正文被截断
现象:详情页正文只显示前几句,后面的没了。
原因:要么是传参时把正文塞进 url 被截断,要么是数据里content字段本身就不完整。
解决:路由只传 id,正文在详情页取。检查数据源,确认content数组完整。如果是接口返回被截断,看后端字段长度限制。
6. 从能跑到好用:诗词小程序的进阶技巧
把基础页面跑通只是及格线,真正拉开差距的是细节。分享几个我实际做内容型小程序时验证过的技巧。
第一个是正文的「逐句点击查词」。诗词里生僻字和典故多,用户看到不懂的词想查。做法是给每句正文绑定点击事件,点击后弹出该句的注释,或者跳转到搜索页以该词为关键词。实现上给poem-line加bindtap,>onShareAppMessage() { const p = this.data.poem; return { title: `${p.title} · ${p.author}`, path: `/pages/detail/detail?id=${p.id}`, imageUrl: '/images/share-poem.png' // 建议 5:4 比例 }; }
分享出去的卡片点进来直接落到这首诗的详情页,转化路径最短。imageUrl建议用固定图而不是动态截图,动态截图在部分机型上会失败。
第四个是数据更新策略。诗词数据不是一成不变的,注释和译文可能需要修订。如果走接口,后端改完前端自动生效;如果走静态数据,每次更新都要重新发版,审核周期摆在那。我的习惯是:正文和基础信息可以静态,注释、译文、标签这类会迭代的内容走接口,两者在详情页合并渲染。这样既保证首屏快,又留了更新余地。
最后说个我踩过的坑。早期做这类小程序,我把所有诗词数据打包进主包,想着「反正就几千首」,结果加上图片直接超限,临时改分包又发现 tabBar 页面依赖了分包里的组件,来回折腾了一整天。后来定了个规矩:任何内容型小程序,数据源和渲染层从第一天就分离,本地只放 demo 数据,真实数据一律走接口。这个习惯让我后面做成语、菜谱、法律条文几个小程序时,再没在体积上翻过车。希望帮到你。
本文还有配套的精品资源,点击获取