250830微小项目阅读笔记


为了丰富选择微小的架构,我从选取了若干个开源项目进行阅读,快速阅读项目结构与核心逻辑

这个文档/博客的目的是:了解其它项目使用的架构,进而个人项目的技术方案制定提供参考;帮助自己锻炼产品视角。使用豆包和通义千问辅助阅读,内容参考AI回复进行二次梳理。

组件库类

  1. https://github.com/youzan/vant-weapp
    (一)项目类型:组件库

(二)主要功能位置:
项目的目录结构大致如下:

vant-weapp/
├── docs/ # 文档相关
├── example/ # 示例工程
├── lib/ # 编译后的组件库(用于发布)
│ ├── common/ # 公共工具和基础组件
│ ├── definitions/ # 类型定义(.d.ts)
│ └── [组件名]/ # 编译后的组件(如 tab、panel 等)
├── packages/ # 源代码(组件开发目录)
│ ├── common/ # 公共逻辑(组件基类、工具函数等)
│ │ ├── component.ts # VantComponent 核心封装
│ │ └── relation.ts # 组件间关系管理(父子组件通信)
│ ├── [组件名]/ # 组件源码(如 tab、col、panel 等)
│ │ ├── index.ts # 组件逻辑
│ │ ├── index.wxml # 组件模板
│ │ └── demo/ # 组件示例代码
├── package.json # 项目配置(依赖、脚本、版本等)
└── README.md # 项目说明

(三)亮点or没接触到的地方:
若要新增一个组件,流程如下:

  1. 创建组件目录
    新增组件要放在packages目录下。在该目录下创建文件夹my-component与相关核心文件:

    packages/
    my-component/
    index.ts # 组件逻辑(属性、方法、生命周期等)
    index.wxml # 组件模板
    index.wxss # 组件样式
    index.json # 组件配置(声明为自定义组件及依赖)微小
    可能需要.wxs
    demo/ # 组件示例(用于文档和演示)
    index.ts
    index.wxml
    index.json
  2. 配置组件依赖
    在组件的 index.json 中声明组件信息及依赖的其他组件(如van-icon)

  3. 开发环境验证
    Vant Weapp 通过 npm 脚本和构建工具(如 rsbuild、vant-cli)实现打包,相关脚本可以在package.json中的”scirpts”处查看:如npm run lint:进行代码规范校验。
    新增组件后,可先在示例工程中引入组件进行调试:

    • example/pages 中创建示例页面,或在现有页面的 json 中引入组件:
      {
      "usingComponents": {
      "van-my-component": "../../packages/my-component/index"
      }
      }
    • 运行开发命令实时编译:
      npm run dev  # 监听文件变化,实时生成开发版本
  4. 正式构建
    确认组件无误后,执行构建命令生成发布版本:

    # 构建组件库(输出到 dist/ 和 lib/ 目录)
    npm run build:lib
  5. 查看构建成果
    构建后会生成以下目录及文件,用于发布和使用:
    dist/ 目录存放 ES6 版本的组件,保留完整的模块化结构,适用于支持 ES6 模块的环境。
    lib/ 目录 存放 ES5 版本的组件,通过 Babel 转译,兼容更低版本的小程序基础库。
    类型定义文件(TypeScript 类型定义文件.d.ts) 存放于 lib/definitions/ 或组件目录中,它在构建过程中自动生成,存放于 lib/definitions/ 或组件目录中,支持类型提示。

构建成果最终会通过 npm publish 发布到 npm 仓库(包名 @vant/weapp),开发者可通过 npm 安装使用新增组件。

(四)功能性能分析

  1. https://github.com/TooBug/wemark
    (一)项目类型:微小的markdown-to-html组件

(二)主要功能位置:
(三)亮点or没接触到的地方:
(四)功能性能分析

项目demo类

  1. https://github.com/RebeccaHanjw/weapp-wechat-zhihu

(一)项目类型:类似知乎的内容平台

(二)主要功能位置:
列表数据展示: pages/index/ 和 pages/discovery/
下拉刷新与上拉加载更多: 同上
tab 切换:底部——app.json;顶部:

(三)亮点or没接触到的地方:
上拉刷新与下拉刷新,用showToast做好了操作响应;预留了网络请求。

// wxml
<scroll-view scroll-y="true" class="container" bindscrolltoupper="upper" upper-threshold="10" lower-threshold="5" bindscrolltolower="lower" scroll-into-view="{{toView}}" scroll-top="{{scrollTop}}">

// js
refresh: function(){
wx.showToast({
title: '刷新中',
icon: 'loading',
duration: 3000
});
var feed = util.getData2();
console.log("loaddata");
var feed_data = feed.data;
this.setData({
feed:feed_data,
feed_length: feed_data.length
});
setTimeout(function(){
wx.showToast({
title: '刷新成功',
icon: 'success',
duration: 2000
})
},3000)

},

//使用本地 fake 数据实现继续加载效果
nextLoad: function(){
wx.showToast({
title: '加载中',
icon: 'loading',
duration: 4000
})
var next = util.getNext();
console.log("continueload");
var next_data = next.data;
this.setData({
feed: this.data.feed.concat(next_data),
feed_length: this.data.feed_length + next_data.length
});
setTimeout(function(){
wx.showToast({
title: '加载成功',
icon: 'success',
duration: 2000
})
},3000)
}

在“发现”页面中,设置了顶部tab:

// wxml
<view class="top-tab flex-wrp flex-tab " >
<view class="toptab flex-item {{currentNavtab==idx ? 'active' : ''}}" wx:for="{{navTab}}" wx:for-index="idx" wx:for-item="itemName" data-idx="{{idx}}" bindtap="switchTab">
{{itemName}}
</view>
</view>

<scroll-view>
<view class="ctnt0" hidden="{{currentNavtab==0 ? '' : true}}">
...
</view>
/<scroll-view>

// js
switchTab: function(e){
this.setData({
currentNavtab: e.currentTarget.dataset.idx
});
},

(四)功能性能分析
从这个项目,可以了解到小程序设计应该达到的要求:

1. **核心交互符合用户预期**  
- 下拉刷新、上拉加载的操作逻辑与主流内容类APP(如微信公众号、微博)一致,用户无需额外学习成本即可上手。
- 点击问答内容跳转详情页的反馈及时(点击后立即跳转),符合“点击即进入”的直觉式操作预期。

2. **状态反馈清晰**
- 刷新和加载时通过 `wx.showToast` 显示明确的状态提示(“刷新中”“加载中”“刷新成功”),让用户感知操作进度,减少等待焦虑。
- 加载状态使用“loading”图标,成功状态使用“success”图标,视觉反馈直观,符合小程序设计规范。

3. **内容展示优先级合理**
- 问答列表以“问题+回答摘要+互动数据(点赞/评论)”为核心,信息层级清晰:标题(粗体突出)→ 回答内容(摘要展示)→ 互动数据(次要信息),符合用户“先判断内容价值再决定是否点击”的浏览习惯。

4. **轻量化设计减少干扰**
- 页面无冗余装饰元素,专注于内容展示,符合小程序“轻量工具”的定位,也契合知乎“内容优先”的产品调性。
  1. https://github.com/giscafer/wechat-weapp-mapdemo
    地图项目demo;暂不阅读。

  2. https://github.com/CFETeam/weapp-demo-session
    暂不阅读

  3. https://github.com/harveyqing/BearDiary

(一)项目类型:日志

(二)主要功能位置:
pages中,entry查看日志详情;new进行日志编辑、list查看日志列表。

(三)亮点or没接触到的地方:
demo/diaries.js中,mock(模拟)了数据;
定义数据,然后 module.exports

const diaries = {};
module.exports = {
diaries: diaries,
}

(四)功能性能分析

a. 功能不足点:

创作流程断点:“日记可回溯修改” 的核心诉求
反馈缺失:操作后无明确提示(如保存日记、收藏成功等),无法确认操作是否生效
文本输入体验:长文本下频繁滚动
模态框交互:新建日记时的标题输入模态框(mine.wxml)无动画过渡,弹出 / 关闭生硬,影响操作流畅

b. 性能优化方向:

性能问题:

  1. 数据加载与存储效率问题——一次性加载全部数据、本地存储设计缺陷(全部存储在单键上);
  2. 多媒体资源处理缺陷——未限制文件大小与格式:未对选择的图片 / 视频进行压缩或尺寸限制,直接存储原始文件路径
  3. 图片预览模式优化不足——预览swiper一次性渲染,导致初始化卡顿
  4. 页面渲染与生命周期管理问题——数据同步时机错误、无状态清理机制
  5. 网络请求与依赖管理问题——地图 API 请求未做缓存、无请求错误处理(没实现重试机制或错误降级策略,如网络异常时使用缓存数据)

优化方向:
优先级:异步数据依赖(避免白屏/错误)≈图片压缩(减少内存占用)> 分页加载列表数据(提升滚动流畅度)、拆分本地存储(优化读写效率) > 缓存地理位置、请求重试(优化网络体验)

  1. 数据加载与存储优化
  • 实现分页加载

    // 在 app.getDiaryList 中增加分页参数(page、size),列表页(list.js)初始只加载前 10 条,滚动到底部时触发下一页加载(结合 scroll-view 的 bindscrolltolower 事件)
    // 示例:分页获取日记列表
    getDiaryList: function(page = 1, size = 10, cb) {
    // 从本地缓存/服务端按页读取数据
    const start = (page - 1) * size;
    const end = start + size;
    const paginatedList = this.globalData.diaryList.slice(start, end);
    cb(paginatedList);
    }
  • 拆分本地存储结构
    将单键存储改为按日记 ID 拆分存储(如 bearDiary_${id}),更新时仅操作单条数据,减少 IO 开销:

  1. 多媒体资源处理优化
  • 图片/视频压缩与尺寸限制:
    // 在 `new.js` 选择文件后,使用 `wx.compressImage` 压缩图片,限制视频时长和大小: 
    // 示例:压缩图片
    chooseImage: function() {
    wx.chooseImage({
    success: (res) => {
    res.tempFilePaths.forEach(path => {
    wx.compressImage({
    src: path,
    quality: 80, // 压缩质量
    success: (compressed) => {
    // 使用压缩后的路径
    this.writeContent({tempFilePaths: [compressed.tempFilePath]}, 'IMAGE');
    }
    });
    });
    }
    });
    }
  • 预览图片懒加载:
    entry.js 的预览swiper仅初始化当前可见图片,滑动时动态加载前后2张,减少初始渲染压力(可借助小程序 image 组件的 lazy-load 属性)。
  1. 页面渲染与生命周期优化
  • 修复异步数据依赖问题:通过回调后Promise链式调用,控制执行顺序
    //   确保 `getMediaList` 在 `getDiary` 完成后执行,通过回调或Promise链式调用:
    // entry.js 优化
    onLoad: function(params) {
    app.getDiaryList(list => {
    // 获取日记详情
    const diary = params.id ? list[params.id] : list[0];
    this.setData({ diary }, () => {
    // 数据更新后再获取媒体列表
    this.getMediaList();
    });
    });
    }
  • 清理页面状态:
    // 在 `onUnload` 中重置数据,释放内存:
    onUnload: function() {
    this.setData({
    diary: null,
    mediaList: [],
    previewMode: false
    });
    }
  1. 网络与缓存策略优化
  • 缓存地理位置信息:
    `app.js` 中缓存已获取的位置信息,有效期设为1小时,避免重复请求: 
    // 示例:缓存位置信息
    globalData: {
    locationCache: {
    data: null,
    expire: 0 // 过期时间戳
    }
    },
    getLocation: function(cb) {
    const now = Date.now();
    if (this.globalData.locationCache.expire > now) {
    cb(this.globalData.locationCache.data);
    return;
    }
    // 重新获取并缓存
    wx.getLocation({
    success: (res) => {
    this.globalData.locationCache = {
    data: res,
    expire: now + 3600000 // 1小时后过期
    };
    cb(res);
    }
    });
    }
  • 增加请求重试机制:
    //   在 `request.js` 中添加失败重试逻辑(限制重试次数):  
    // 带重试的请求封装
    module.exports = (options, retry = 2) => {
    return new Promise((resolve, reject) => {
    const requestWithRetry = () => {
    wx.request({
    ...options,
    success: (res) => {
    if (res.statusCode === 200) resolve(res.data);
    else if (retry > 0) {
    retry--;
    requestWithRetry(); // 重试
    } else reject(res);
    },
    fail: () => {
    if (retry > 0) {
    retry--;
    requestWithRetry(); // 重试
    } else reject();
    }
    });
    };
    requestWithRetry();
    });
    };

文章作者: Qijia Huang
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Qijia Huang !
  目录