news 2026/10/8 7:42:54

微信小程序云开发菜谱源码:免服务器搭建完整流程与避坑指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
微信小程序云开发菜谱源码:免服务器搭建完整流程与避坑指南

简介:这套菜谱微信小程序源码基于云开发模式构建,适合美食类创业者、小程序开发初学者以及需要快速落地菜谱应用的开发者。无需自备域名和服务器,导入微信开发者工具即可运行并提交审核,大幅降低搭建门槛。压缩包共包含182个文件,以100个PNG图片、23个JS逻辑文件、20个JSON配置、18个WXSS样式和17个WXML结构为主,另有4个JPG封面图,整体体积仅1.79MB,结构紧凑、便于整理与二次开发。目前已有991人学习下载,适合用于学习云开发接口调用、小程序页面布局以及数据分类展示思路。源码仿照京细菜谱的内容组织方式,覆盖八大菜系、特色食品、特殊场合、热门功效、人群细分、烘焙甜品、口味和食材等详细分类,同时提供用户页、列表页、详情页等完整交互流程,开发者可直接沿用其设计语言和模块划分,快速生成专属菜谱小程序。

1. 菜谱微信小程序源码:云开发版直接导入就能跑,省掉服务器那层麻烦

想给餐饮店做一个菜谱小程序,问了一圈服务器最便宜也要几十块一个月,还要备案。这份仿京细菜谱的小程序源码走的是微信云开发路线,不需要域名、不需要服务器,导入微信开发者工具就能跑。我拆了一遍,它的分类做得很细:八大菜系、人群、场合、口味、功效全都有索引,适合做毕业设计、接外包底子,也适合想研究云数据库在小程序里怎么落地的开发。下面我会从文件结构、导入配置、数据流和踩坑几个角度,把它完整摊开,照着操作可以快速复现。

2. 先拆源码:文件职责、云开发原理和集合设计

2.1 从 JS 文件名推断页面职责

拿到压缩包后先别急着导入。我看到的项目正文列出的文件有ascf.jpg、share.jpg、cover_null.jpg和一批 JS:index.js、list.js、recipe.js、user.js、mypage.js、mypage1.js。从这个文件分布看,基本可以确定是微信小程序原生项目,不是 uniapp 或 taro。uniapp 项目通常会有pages.json、App.vue、main.js,而原生小程序是每个页面四个同后缀文件。

按命名习惯推测:

  • index.js对应首页,负责展示分类导航和推荐菜谱;
  • list.js对应分类菜谱列表页,会接收categoryId参数,做分页读取;
  • recipe.js对应菜谱详情页,一般用options.id查询单个菜谱;
  • user.js负责用户信息相关逻辑,比如读取头像昵称、本地缓存;
  • mypage.js和mypage1.js是个人中心,可能是不同版本的页面,实际生效路由看app.json的pages数组。

这里有个判断技巧:不要只看 JS 名字,要打开app.json确认pages数组的顺序。数组第一项是首页,后面依次是其他页面。如果mypage1.js不在pages数组里,说明它只是被mypage.js引用的公共模块,或者已经废弃的旧页面。做二次开发时,删除文件前先看路由,避免删掉还在使用的页面导致编译报错。

share.jpg是转发分享时用的封图,cover_null.jpg应该是菜谱没有封面时的兜底图,ascf.jpg从名字看可能是广告位或头部轮播图。图片资源不多的话,要留意 image 标签的路径,如果引用的是本地相对路径,在分包或发布前别乱移动。

2.2 云开发替代了传统后端的三层结构

很多第一次接触云开发的人把它当成“小程序的数据库”,不够准确。微信云开发其实是一个 BaaS,提供了数据库、云存储、云函数三件套。对这份菜谱源码来说:

  • 菜谱记录和分类信息放在云数据库;
  • 菜谱图片放在云存储;
  • 用户身份和收藏逻辑可以不写云函数,直接用内置的_openid字段区分。

也就是说,常规小程序开发里需要自己买服务器、写接口、处理文件上传下载、申请 HTTPS 域名和备案的那一层,云开发全部接管了。这也是摘要里强调“不需要域名和服务器即可搭建”的原因。

不用云开发、用传统后端的话,流程是这样的:购买轻量服务器、部署接口、申请 HTTPS 证书并备案、小程序后台配置 request 合法域名、联调接口。每一步都可能卡几天,尤其备案要等审核。云开发版本把这些步骤压缩成两步:创建云环境、初始化app.js。对个人开发者来说,省去的不只是钱,还有时间成本。

但要注意,云开发不是完全没有服务器概念,它只是把底层运维隐藏了。你的数据仍然存在云服务商的服务器上,底层资源有免费额度,超过后按量付费。个人菜谱小程序的访问量不大,通常不会超,但上线后要关注控制台的用量指标,避免月底收到超额账单。

2.3 初始化代码与数据库集合字段设计

云开发环境初始化一般在app.js的onLaunch里。下面是一段兼容写法:

// app.js App({ onLaunch() { if (!wx.cloud) { console.error('请使用 2.2.3 以上基础库以支持云开发'); return; } wx.cloud.init({ env: 'cloud1-3g9t0abc', // 替换成你自己的云环境 ID traceUser: true }); } });

代码里env的值决定所有数据库、存储访问落在哪个环境。环境 ID 在云开发控制台首页可以看到,格式通常是cloud1-xxxxxxxx。如果你不填env,默认使用第一个创建的环境,但多人协作时很容易连到别人的环境,所以我建议每次都显式写上。

接下来说集合设计。菜谱类小程序至少需要两张表:dishes和categories。dishes表的核心字段我整理如下:

字段类型说明
_idstring云数据库自动生成,详情页传参用
namestring菜名,用于列表展示和搜索
categoryIdstring分类 ID,关联categories表
imagestring云存储 fileID,也可以是 https 链接
ingredientsarray食材列表,比如["五花肉 500g", "冰糖 30g"]
stepsarray步骤列表,按顺序排列
heatnumber热量或制作难度,可选
createTimedate入库时间,分页排序用

为什么步骤和食材都不用单独建表?因为云开发数据库是文档型,存数组拆取很方便,详情页一次.get()就能拿到所有渲染数据。如果像 MySQL 那样拆成ingredients表、steps表,小程序端还得分两次查询再拼接,完全没必要。这也是一线开发里的常见误区:把后端数据库思维直接搬进小程序会导致性能差、代码啰嗦。

2.4 数据权限:先想清楚谁能读、谁能写

在云开发控制台新建集合时,默认权限是“仅创建者可读写”。对于菜谱这种公共内容,这个权限会让非创建者看到空数据。你需要手动改成“所有用户可读,仅创建者可写”。这样游客打开小程序能读到所有菜谱,但数据库中的文档不会被随意篡改。

这里有一个细节:如果要做收藏功能,收藏表favorites应该继续用“仅创建者可读写”。因为云数据库在写入时会自动给每条记录打上_openid字段,表示创建者身份。用户 A 收藏一条菜,生成一条带 A 的 openid 的文档;用户 B 收藏是他自己的文档,互相不冲突。查询收藏列表时用.where({ _openid: '{openid}' })就能拿到当前用户的数据。这段逻辑不需要自己调用登录云函数,是云开发默认行为。

如果你以后加了管理员修改菜谱的需求,管理员操作通常要走云函数,因为小程序端受权限限制,无法修改其他人的文档。云函数端拿到管理员 openid 后,可以绕过权限校验对任意文档做更新。这个放在后面进阶部分讲。

3. 落地:导入开发者工具、开通云环境并导入数据

3.1 解压后先做三件事

第一件事就是解压,不要用在线压缩工具,因为里面可能有中文文件和嵌套目录,解压不完整会导致导入报错。第二件事检查文件完整性。一个原生小程序项目至少要有的文件是app.js、app.json、project.config.json。缺少project.config.json时,开发者工具无法识别项目类型,会提示“请选择正确的项目目录”。第三件事看project.config.json里的appid。如果里面写的是touristappid或别人的 AppID,导入后云开发功能用不了,必须改成你自己的。

不要直接双击打开文件,用 VS Code 打开整个项目,按 Ctrl+Shift+F 搜索appid,替换成自己的。注意有些模板会把 AppID 也写在app.js里,要一起替换。

3.2 注册小程序并开通云开发

在小程序公众平台注册一个小程序账号,个人主体也可以注册。注册成功后拿到 AppID。然后打开微信开发者工具,点击“导入项目”,选择解压后的目录,AppID 填自己刚申请的。后端服务选择“小程序·云开发”,点确定。

导入成功后,工具栏会出现“云开发”按钮。首次点开会要求创建环境。环境 ID 形如cloud1-xxxx,随便起名,但记住它,后面代码要用。计费模式建议选“按量付费”,个人项目即使有量也很低,按量付费比包月划算。如果暂时没开通,创建免费额度环境也可以,但要注意免费环境有时效和资源限制。

3.3 在云开发控制台创建集合

云环境创建好后,进入“数据库”页面,新建两个集合,集合名必须和代码一致:

  • dishes:菜谱主表
  • categories:分类表

如果源码里用的集合名不是这两个,你在控制台的新建名字要和源码里的.collection('xxx')对应。我拿到一个模板时,会先在代码里搜索collection(,把出现过的集合名列出来,再去控制台建。这是最快的方式。

集合权限设置:dishes和categories都选“所有用户可读,仅创建者可写”。不要选“仅创建者可读写”,否则非管理员用户打开就是空白。这个是菜谱类的公共数据场景,跟收藏表不一样。

3.4 导入菜谱数据:JSON 文件格式注意

数据库支持导入 JSON 或 CSV。最常见的格式是一个 JSON 数组,每个对象是一条记录。下面这个示例可以导入dishes:

[ { "name": "西红柿炒鸡蛋", "categoryId": "home", "image": "cloud://cloud1-xxxx.636c-cloud1-xxxx-1300000000/recipe/tomato.jpg", "ingredients": ["西红柿 2个", "鸡蛋 3个", "盐 5g", "糖 3g"], "steps": ["西红柿切块", "鸡蛋打散炒熟", "下西红柿翻炒", "调味出锅"], "createTime": "2025-01-01T00:00:00+08:00" }, { "name": "红烧肉", "categoryId": "re_cai", "image": "cloud://cloud1-xxxx.636c-cloud1-xxxx-1300000000/recipe/hongshao.jpg", "ingredients": ["五花肉 500g", "冰糖 30g", "生抽 20ml", "姜 3片"], "steps": ["五花肉切块焯水", "炒糖色", "下肉块上色", "加调料炖40分钟"], "createTime": "2025-01-02T00:00:00+08:00" } ]

导入时有几个注意点:

  • 数组最外层别忘了方括号,文件编码保持 UTF-8,不要带 BOM。
  • _id字段不要手动写,导入时会自动生成;导入后再从控制台复制_id去关联分类。
  • createTime建议用 ISO 字符串,方便后面orderBy排序和startAfter分页。
  • 如果图片还没上传云存储,可以先填空字符串,后面用update方法补上去。

导入完成后,在集合里看到这些记录,基本就成功一半了。

3.5 云存储传图并回填 fileID

接下来把菜谱图片上传到云存储。进入云开发控制台“存储”页面,创建recipe目录,批量上传图片。上传完后,在文件列表点“复制文件ID”,会得到类似cloud://cloud1-xxxx.636c-cloud1-xxxx-1300000000/recipe/tomato.jpg的路径,把这个值填到dishes记录里的image字段。

这里有个容易搞混的概念:云存储 fileID 和外链 URL 的区别。fileID 是云开发内部的引用,小程序端<image>标签可以直接用。而外链 URL 需要在微信后台配置 downloadFile 合法域名,如果是http://或非 https 还会被拦截。所以能传云存储就传云存储,不要转外链,省去域名白名单配置。

如果数据较多,不希望手动回填,可以在控制台用导出、修改再导入的方式批量操作。但建议先小批量试通,再全量操作。

3.6 真机预览前要做的检查

打开开发者工具的“编译”按钮,模拟器如果能显示首页但不显示菜谱,先打开 Console 看报错。常见问题:集合名不存在、环境 ID 不对、集合权限太严格。确认无误后,点击“预览”,用微信扫码真机调试。真机和模拟器的差异通常体现在图片和网络请求,多准备一台安卓和一台 iPhone 测试。

到这里,一个可跑的菜谱小程序就搭起来了。下一步,我把列表页和详情页的核心代码逻辑讲透,方便你按自己需求改。

4. 核心逻辑:列表分页、详情跳转与用户页背后的数据边界

4.1 列表页用 skip+limit 加载更多,注意 20 条上限

list.js是菜谱列表页,它的核心是加载对应分类下的一页菜谱。一段常见的基础实现如下:

// list.js const db = wx.cloud.database(); const PAGE_SIZE = 10; let page = 0; let isFetching = false; let hasMore = true; Page({ data: { dishes: [], categoryId: '' }, onLoad(options) { this.setData({ categoryId: options.categoryId || '' }); this.loadList(); }, loadList() { if (isFetching || !hasMore) return; isFetching = true; wx.showLoading({ title: '加载中' }); const query = db.collection('dishes'); if (this.data.categoryId) { query.where({ categoryId: this.data.categoryId }); } query.skip(page * PAGE_SIZE) .limit(PAGE_SIZE) .get() .then(res => { const newList = this.data.dishes.concat(res.data); this.setData({ dishes: newList }); page++; if (res.data.length < PAGE_SIZE) { hasMore = false; } wx.hideLoading(); }) .catch(err => { console.error(err); wx.hideLoading(); }) .finally(() => { isFetching = false; }); }, onReachBottom() { this.loadList(); } });

这段代码里有几个参数说明:

  • PAGE_SIZE是每页数量,这里写 10,实际可以调大到 20,不要超过 20。云开发数据库在普通小程序端单次get()最多返回 20 条,设置 50 也没用,后台会截断。
  • isFetching是并发锁,防止 onReachBottom 在数据还没返回时又触发一次,导致重复请求。
  • hasMore用于判断是否还有下一页,如果返回条数小于 PAGE_SIZE,说明已经到最后一页。
  • where({ categoryId })是精确匹配,菜谱的categoryId必须和分类表_id一致。

这个写法对数据总量不超过 1000 条的菜谱项目够用。如果以后数据量大了,skip会因为扫描偏移量过大而变慢,到时改成orderBy('createTime', 'desc')加startAfter(res.data[res.data.length - 1].createTime)的方式。这个我们放到第 5 章避坑里详细展开。

4.2 触底加载更多:onReachBottom 与页面配置

“微信小程序页面列表加载更多”这个热搜问题,本质上就是两件事:触发条件和翻页逻辑。触发条件除了onReachBottom,还需要在页面的.json里开启:

{ "onReachBottomDistance": 50, "enablePullDownRefresh": true }

onReachBottomDistance是距离底部多少像素时触发,默认 50,不用改也行。enablePullDownRefresh是下拉刷新,如果你不需要下拉刷新可以直接删掉。

然后下拉刷新的处理函数:

onPullDownRefresh() { page = 0; this.setData({ dishes: [], hasMore: true }); this.loadList().finally(() => { wx.stopPullDownRefresh(); }); }

注意:onPullDownRefresh里必须先重置page和dishes,否则你会看到旧数据拼接新数据,列表越来越长。这个细节做外包时经常被我拿来调 bug。

4.3 首页分类点击跳转与参数接收

首页index.js里通常有一段wx.navigateTo:

goList(e) { const categoryId = e.currentTarget.dataset.id; wx.navigateTo({ url: '/pages/list/list?categoryId=' + categoryId }); }

dataset.id是 WXML 里><view class="category-item">// recipe.js const db = wx.cloud.database(); Page({ data: { dish: {} }, onLoad(options) { const id = options.id; if (!id) return; db.collection('dishes').doc(id).get() .then(res => { this.setData({ dish: res.data }); }) .catch(err => console.error(err)); } });

doc(id)是指定记录 ID 直接读取,比where({_id: id})更高效。拿到记录后,dish.steps是一个数组,WXML 里用wx:for渲染步骤:

<view class="steps"> <view wx:for="{{dish.steps}}" wx:key="index" class="step-item"> <text>{{index + 1}}. {{item}}</text> </view> </view>

wx:key="index"用于列表复用时的 key,这里因为 steps 数组的元素可能重复,用index做 key 是可以接受的。注意不要写成wx:key="*item"以免字符串重复导致警告。

4.5 用户页与云开发的 openid 机制

user.js和mypage.js最常做的操作是展示用户登录信息和收藏列表。云开发下识别用户的默认方式不是自己写登录,而是读取云数据库记录里的_openid字段。小程序端调用collection.add时,云平台会自动把当前用户的openid写入_openid。查询时用:

db.collection('favorites') .where({ _openid: '{openid}' }) .get() .then(res => { this.setData({ favorites: res.data }); });

这里'{openid}'是云开发的特殊语法,在服务端或小程序端查询时会被自动替换成当前用户 openid。因此,收藏功能完全不用自己写云函数。但如果需要把收藏与用户的其他信息关联,比如昵称头像,可能需要在用户第一次授权时保存一份 user 文档。此时注意wx.getUserProfile在最新基础库上返回的昵称是“微信用户”默认昵称,头像也是灰色默认头像,真实头像需要用户上传或使用开放数据,这个不是源码问题,是微信平台策略。

这一段主要讲了:云开发数据库的分页限制、页面跳转参数、详情读取、用户 openid 自动注入。理解这四点,修改模板就有方向了。

5. 常见坑与排查:环境、分页、图片和权限

5.1 数据空白类:环境 ID 和集合名不一致

现象:模拟器打开首页,分类能显示,但点进列表加载不出任何数据,console 提示collection not exists或Collection not found,或者干脆没有报错。

原因:一种是app.js里的env没有改成自己的云环境 ID,代码访问的是别人创建的环境,那个环境里没有你的集合。另一种是项目里写的集合名是recipe,但你在控制台建的集合叫dishes。我在处理资源时,见过把.collection('recipe')写成.collection('dishes')的,就是复制时没改名称。

解决:打开开发者工具的 Console,定位到报错信息里的集合名,去云开发控制台创建同名集合;再确认env正确。如果env显示undefined,回到app.js里显式写上环境 ID。改完重启编译。

5.2 分页失效类:skip 超过记录数导致空白

现象:列表第一页正常,下拉加载更多后,第二页偶尔有数据,到第三页或更后直接空白,甚至报错Error: errCode: -502005 database request fail。

原因:skip分页在记录总数超过 1000 条时,数据库会拒绝请求;另外PAGE_SIZE如果设置为 50,而数据库单次最多返回 20 条,第二页的skip就会跳过 40 条,结果只返回后面 10 条,看起来就像缺数据。或者hasMore判断逻辑写错了,导致一直请求。

解决:把PAGE_SIZE改回 20 以内;将分页方式改为基于游标。游标分页示例:

let lastCreateTime = null; function loadMore() { let query = db.collection('dishes').orderBy('createTime', 'desc').limit(20); if (lastCreateTime) { query = query.startAfter(lastCreateTime); } query.get().then(res => { if (res.data.length) { lastCreateTime = res.data[res.data.length - 1].createTime; this.setData({ dishes: this.data.dishes.concat(res.data) }); } else { this.setData({ hasMore: false }); } }); }

这里的startAfter接收排序字段的值,必须是上次返回的最后一条记录的createTime,且排序字段必须已经建索引。索引在控制台数据库中创建,字段选择createTime,排序方式选“降序”。

5.3 图片裂图类:云存储 fileID 被当网络链接处理

现象:模拟器上图片显示正常,真机预览一部分图片显示空白,或控制台出现url not in domain list。

原因:如果image字段填的是https://外链,微信小程序要求下载域名必须加入白名单,且必须是备案过的 HTTPS。如果填的是云存储 fileID,默认是可以直接显示的,不需要白名单,但如果使用了旧环境产生的 fileID,新环境里无法识别,就会裂图。

解决:排除法:先看image值开头是不是cloud://,如果开头是http,去小程序后台配置 downloadFile 合法域名,或者把图传到云存储换成 fileID。如果已经是cloud://还是显示不了,大概率是环境不匹配,重新上传一次图片并替换image字段。还有一个小坑:云存储目录名有中文或空格,复制 fileID 后路径会被编码,建议目录名全英文。

5.4 编译失败类:基础库版本过低和找不到 appid

现象:导入后编译直接报wx.cloud is undefined,或Cannot read property 'init' of undefined,点云开发按钮没反应。

原因:微信开发者工具的基础库版本低于 2.2.3,wx.cloudAPI 不存在;或者 AppID 是测试号,测试号无法开通云开发。

解决:在开发者工具“详情-本地设置”中,把调试基础库切到最新稳定版。AppID 换成正式小程序的,测试号必须替换成自己的。个人主体注册的小程序也可以开通云开发,不需要企业资质。注意基础库设置只影响当前项目,不要全局更改。

5.5 权限太严类:数据导入后其他用户看到空白

现象:自己在控制台导入的菜谱数据,自己用开发者工具能看到,但用其他微信号扫码预览,列表是空的。

原因:集合权限是“仅创建者可读写”,控制台导入的数据创建者是管理员,普通用户没有读权限。云开发数据库权限是集合级别的,不是每条记录单独设置。

解决:把dishes和categories两个集合的权限改成“所有用户可读,仅创建者可写”。这一步必须在控制台手动操作,不是改代码。改完后,让同事或另一个微信号扫码测试。注意,如果之后又用控制台重新导入数据,权限不会重置;但如果新建集合,默认权限又变成“仅创建者可读写”,这是最容易漏的地方。

这五条基本覆盖了这个模板从导入到真机预览的高频问题。按“现象-原因-解决”排查,大多数情况 10 分钟内能定位。

6. 进阶:把菜谱模板改成自己风格的三个小技巧

第一个:搜索功能。菜谱数据量不大时,用模糊查询:

const db = wx.cloud.database(); db.collection('dishes') .where({ name: db.RegExp({ regexp: keyword, options: 'i' }) }) .limit(20) .get()

正则查询无法建索引,几万条以内没问题,再大就要用云函数接入搜索服务。

第二个:默认导航栏够用就别自定义。这个模板用的是系统导航栏,在app.json的window里改navigationBarTitleText、navigationBarBackgroundColor就行。如果你非要自定义,设置"navigationStyle": "custom"后要自己处理wx.getMenuButtonBoundingClientRect算胶囊高度,容易在安卓和 iOS 上出现偏差。我的经验是:不是被产品逼到那份上,别动导航栏。

第三个:分享图已经准备好,share.jpg可以直接用于onShareAppMessage:

onShareAppMessage() { return { title: '这几道家常菜,厨房新手也能做', imageUrl: '/images/share.jpg', path: '/pages/index/index' }; }

注意imageUrl要写本地路径,如果写网络链接需要配域名白名单。

最后说一句自己的教训。我以前接外包用类似模板,就是第 5 章那个分页坑,客户上线后数据量到 200 条,用户在列表页划到一半就空白,我排查了一整天才定位到是limit超过云开发限制和skip超界。从那以后,我每次做云开发列表页都会先看数据量上限再决定用哪种分页,然后强制在真机上把列表翻到底。如果你准备下载这份源码来改成自己的菜谱小程序,建议先在自己账号下按第三章流程跑通,再开始改界面,能少折腾一个下午。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/8 7:42:03

QT/C++开发俄罗斯方块:从核心算法到课设答辩完整指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/8 7:41:27

TPS259483AYWPR+PIC32MX360F512L工业级主动电源保护方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/8 7:41:08

外资企业为何率先采用自动化循环水系统?全成本与落地逻辑解析

站在冷却塔底下往上望&#xff0c;水雾扑面而来&#xff0c;塔底的集水盘里&#xff0c;pH探头、电导率探头、余氯探头一字排开&#xff0c;旁边的小控制柜里PLC指示灯有节奏地闪着。这是我前几年去一家外资化工厂参观时看到的场景。对方负责公用工程的老工程师跟我说了一句话&…

作者头像 李华
网站建设 2026/10/8 7:41:05

OpenCV烟丝图像分割实战:HSV阈值+形态学+轮廓过滤

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/8 7:40:31

TPS259483AYWPR与MKV46F128VLH16工业电源路径协同设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/8 7:38:22

STM32 + eFuse:构建智能电源路径保护方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华