个人案例
小微智能体
扣子Coze智能体转微信小程序工具
小微智能体:AI智能体秒变小程序解决方案扫码体验
- 微信小程序的 5 种分享方式,你知道几种?
前言在微信生态中,微信小程序的裂变传播效果尤为显著,而实现裂变的关键在于分享方式,需要在不同的场景选择合适的分享方式。微信小程序内提供很多分享方式,我将为大家详细介绍微信小程序的 5 种分享方式。 方法转发卡片场景:私聊/微信群 操作:点击卡片可以直达具体页面 好处:最常用的方式,简单,直接 [图片] 触发形式: 用户主动通过分享按钮触发用户通过点击小程序右上角菜单的「转发给朋友」触发[图片] [图片] 技术实现: 在页面加入 button 组件设置 open-type 属性为 share自定义分享卡片内容需要重写Page的onShareAppMessage 方法[图片] 相关文档: https://developers.weixin.qq.com/miniprogram/dev/component/button.htmlhttps://developers.weixin.qq.com/miniprogram/dev/reference/api/Page.html#onShareAppMessage-Object-object转发朋友圈场景:朋友圈分享 操作:需要用户点击然后再点击下方「前往小程序」才能达到目标页 好处:朋友圈展示形式较友好,类似公众号文章分享,具备更高的打开率 触发:用户通过点击小程序右上角菜单的「转发到朋友圈」触发 [图片] [图片] [图片] [图片] 技术实现: 自定义分享卡片内容需要重写Page的onShareTimeline方法[图片] 相关文档: https://developers.weixin.qq.com/miniprogram/dev/reference/api/Page.html#onShareTimeline小程序码场景:私聊/微信群/朋友圈/线下操作:长按/扫码即可达到目标页好处:适用性非常广泛,覆盖了线下场景[图片] 技术实现: 生成小程序二维码API[图片] 相关文档: https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/qrcode-link/qr-code/getQRCode.html 生成链接场景:私聊/微信群/朋友圈/外部浏览器操作:需要两步到达目标页,打开链接再打开小程序好处:外部浏览器支持打开会自动拉起微信进入小程序触发:通过 API 生成获取[图片] [图片] 技术实现: 生成小程序链接 API [图片] 相关文档: https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/qrcode-link/short-link/generateShortLink.html 生成短链场景:私聊/微信群/朋友圈好处:微信内一步可打开目标页[图片] 注意:目前只开放给电商类目(具体包含以下一级类目:电商平台、商家自营、跨境电商)类目调用接口生成。触发: 通过 API 生成获取用户通过点击小程序右上角菜单的「复制链接」获取[图片] [图片] 技术实现: 生成小程序短链API[图片] 相关文档: https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/qrcode-link/short-link/generateShortLink.html最后欢迎大家补充更多分享方式
2024-12-30 - AI智能体+小程序解决方案,扣子Coze智能体秒变小程序!
前言在《1000%增长!我仅用一个小时搞定!AI智能体+AI小程序=MVP王炸组合!》文中我提到了我们团队开发了一套扣子Coze智能体通过API发布到小程序的解决方案,有多位小伙伴留言想要体验。 [图片] 于是我们团队耗时半个月(需求梳理-界面设计-产品研发-账号备案-版本发布)把这套AI智能体+AI小程序的解决方案单独做成了一个小程序,它叫做「小微智能体」我们希望它能帮助个体、微型的企业或组织能快速试错,让你们的AI产品找到PMF(产品与市场契合点)。 [图片] 它与扣子Coze官方导出小程序有什么区别呢? 第一:无需企业资质,在微信上线AI类型的小程序必须要企业主体才能开通第二:可发多个智能体,官方智能体导出到小程序只能导出单个智能体第三:更多的分享形式,为了让你更好的触达用户我们内置了4种分享形式接下来我来详细和大家说说具体操作步骤。 步骤首先进入「小微智能体」小程序,选择发布扣子智能体。 如果没有扣子智能体可以去扣子AI应用平台创建。扣子官网:https://www.coze.cn/官方教程:https://www.coze.cn/docs/guides/welcome[图片] 在这里需要输入扣子账号的个人令牌和智能体ID [图片] 如何获得个人令牌呢? 请看《如何获取扣子个人令牌?》 个人令牌可以理解为你的一种特殊的“电子钥匙”。就像家门钥匙能打开你的家门一样,个人令牌是一种安全工具,它可以让你在互联网上“打开”特定的服务或资源。这把“电子钥匙”通常是一个密码,它能够证明你是你所说的那个人。 如何获得智能体ID及发布API呢? 请看《如何获取扣子智能体ID及发布API?》 智能体ID可以理解成智能体的“身份证号码”。就像每个人都有一个唯一的身份证号码来标识自己一样,智能体ID是每个智能体的唯一标识码。这个“身份证号码”可以区分不同的智能体,确保它们在执行任务时的身份是唯一的。 [图片] 当配置完成后,可点击「查询Bot」查到对应的智能体,你可以点击「预览」进行对话调试,没有问题即可「发布」。 [图片] 发布成功后会显示在我的智能体列表中,可以点击进入详情对话以及「分享」给用户。 [图片] 分享形式支持4种 发送给朋友:以小程序卡片形式分享到好友聊天/微信群中复制链接:以链接形式分享,当用户点击链接可以跳转到智能体详情页公众号传播:复制路径支持嵌入到公众号文章以及公众号菜单中复制VID:适用在微信生态外分享,在首页输入VID即可跳转智能体详情当你分享后,可以在「我的」菜单页的「数据统计」中看到具体数据。 [图片] 默认是你发布的所有智能体数据,右上角可以选择某个智能体查看独立的数据。 最后这只是小微智能体v1.0版本,肯定有很多不完善的地方,欢迎一起讨论,我们会持续迭代。 [图片] 希望与你在这个AI时代交个朋友。
2024-12-27 - Anthropic:Agents 最佳实践指南
[图片] 本章基于 Anthropic 最新发表的《Building effective agents》总结,这篇文章主要记录 Anthropic 与在最近一年中与多个行业团队的合作经验,其中包含了 Agent 实施的最佳实践和实用建议。以下为文章核心内容: agents 的定义和分类agents(智能体) 的定义完全自主的系统:在扩展时间段内独立操作,使用各种工具完成复杂任务。预定义工作流程的实现:遵循预定义的工作流程。agents(智能体) 与 workflows(工作流) 的区别Workflows(工作流):大模型和工具通过预定义的代码路径进行编排。Agents(智能体):大模型动态指导其自身的过程和工具使用,保持对任务完成的控制。如何使用 agents ?使用场景复杂任务:当需要灵活性和规模化的模型驱动决策时,Agents(智能体)是更好的选择。不使用场景简单任务:当任务可以清晰分解为固定子任务时,工作流程提供了可预测性和一致性。框架的使用常用框架LangGraph:用于简化LLM调用、定义和解析工具以及链接调用的框架。Amazon Bedrock的AI Agent框架:简化代理系统实现的框架。Rivet:拖放GUI LLM工作流程构建器。Vellum:用于构建和测试复杂工作流程的GUI工具。使用框架的建议初学者使用LLM API:许多模式可以用几行代码实现。理解底层代码:确保理解框架的底层代码,避免错误的假设。构建blocks、workflows 和 agentsaugmented LLM(增强型大模型)基本构建块:增强型LLM具有检索、工具和内存等增强功能。关键方面:定制这些功能以适应特定用例,并确保为LLM提供易于使用且有良好文档的接口。[图片] The augmented LLM Workflow(工作流)Prompt chaining(提示链):将任务分解为一系列步骤,每个LLM调用处理前一个调用的输出。[图片] 案例: 生成营销文案,然后翻译成不同的语言。编写文档的大纲,检查大纲是否满足特定条件,然后根据大纲编写文档。Routing(路由):对输入进行分类并将其引导至专门的后续任务。[图片] 案例: 将不同类型的客户服务查询(一般问题、退款请求、技术支持)引导到不同的下游流程、Prompt 和工具中。将简单/常见问题路由到较小的模型,将困难/不寻常的问题路由到功能更强大的模型,以优化成本和速度。Parallelization(并行化):LLMs可以同时处理任务,并聚合其输出。[图片] 案例: 任务拆分一个模型实例处理用户查询,而另一个模型实例筛选用户查询是否存在不适当的内容或请求。这往往比使用相同的 LLM 调用同时处理响应的性能要好。评估 LLM 性能,其中每个 LLM 调用都会评估模型在给定 Prompt 下性能的不同方面。投票检查一段代码是否存在漏洞,如果发现问题,则几个不同的 Prompt 会检查并标记代码。评估给定的内容是否不合适,多个 Prompt 评估不同的方面或要求不同的投票阈值来平衡误报和漏报。Orchestrator-worker(协调器-工作者):中央 LLM 动态分解任务并将其委托给 worker LLMs ,然后综合结果。[图片] 案例: 每次对多个文件进行复杂更改的产品。涉及从多个来源收集和分析信息的搜索任务。Evaluator-optimizer(评估器-优化器):一个LLM调用生成响应,另一个提供评估和反馈。[图片] 案例: 文学翻译,译者 LLM 最初可能无法捕捉到细微差别,但评估者 LLM 可以提供有效建议。复杂的搜索任务,需要多轮搜索和分析以收集全面的信息,其中评估者 LLM 决定是否需要进一步搜索。Agents用于开放式问题,难以预测所需的步骤数量,无法硬编码固定路径。 [图片] 案例用于解决 SWE 工作台任务 的编程 Agent ,该任务涉及根据任务描述对许多文件进行编辑;SWE-bench(https://www.anthropic.com/research/swe-bench-sonnet) 是一个用于评估大型语言模型(LLM)在软件工程任务中的表现的基准测试平台。它通常涉及编写、修改和分析代码的任务。 “computer use” 参考实现,其中 Claude 使用计算机完成任务。Anthropic Computer Use Demo(https://github.com/anthropics/anthropic-quickstarts/tree/main/computer-use-demo)是一个帮助开发者快速开始使用 Anthropic 的 Claude AI模型进行操作电脑的例子。[图片] 总结核心原则保持 agents 设计的简洁性:避免不必要的复杂性。透明性:展示 agents 的计划步骤。精心设计 agent-computer interface(ACI):通过彻底的文档和测试设计工具。框架的使用建议快速启动:框架可以帮助快速启动,但不要害怕减少抽象层并使用基本组件进行生产。实际应用客户支持:结合聊天机器人界面和工具集成,适用于更开放的 agents。编程辅助:特别是在软件开发领域,agents 特别有效,因为代码解决方案可以通过自动化测试进行验证。 原文地址 https://www.anthropic.com/research/building-effective-agents 相关文章: 更多相关文章 《全面解读Agent,斯坦福顶尖学者综述》 《2024 Agent AI综述,14位顶尖学者(来自微软、斯坦福等)联合撰写!》 《为什么 AI 能一句话操作手机和电脑?(GUI Agent 最新综述)》
2024-12-27 - 文心AI智能体搭建比赛,我拿了个第一!
[图片] 最近一直都在搭建智能体,看到百度文心智能体平台发布了 1024 的智能体比赛,我抱着试一试的心态搭建了个智能体参加了比赛,灵感主要来源于之前有个新闻说AI程序员出来了,我就想那么哪能少了程序员的好搭档产品经理?所以就做了一个AI产品经理,他叫傲娇产品经理杰森。 专注解决“我有一个好的想法!”,你可以说出来让他听听?他可以为你理清思路,然后输出详细的需求文档以及绘制流程图和思维导图。 效果[图片] 我给这个产品经理设置了一个有趣的人设,这样沟通起来不会那么多AI味。当你说出自己的想法时候,他会帮你挖掘你的理清思路,追问需求细节。 [图片] 当你把问题回答完成后他会调用文档生成工作流为你生成详细的文档 [图片] [图片] [图片] 在有些时候,我们并不是一整个新的想法,而是某个流程不太明白,所以你告诉他,他还能为你绘制流程图,比如AI写作工具的流程图: [图片] [图片] 只需要我们会生成的流程图代码放到 https://mermaid.live/ 就可以生成完整的流程图了。 [图片] 除此之外还可以帮我们发散思维,如果你对某个方向感兴趣他还会帮你梳理成思维导图。 [图片] [图片] [图片] [图片] 思路该智能体采用的是多工作流的设计结构,在提示词中通过不同的情况去掉用不同的工作流完成具体的事情。 这样有以下两个好处: 从功能上更独立,复杂逻辑都可以在工作流上具体做处理 从扩展上更灵活,添加技能修改提示词和新增工作流即可 [图片] 最后我觉得大家可以多了解一些智能体搭建平台,比如字节的扣子,腾讯的元器,百度的文心,很多方式不一定需要代码解决,如果要用代码开发这些功能可能都需要几天时间才能完成,而在平台智能体搭建1个小时就搞定了,然后再暴露API出来就可以很快的完成这些功能了。
2024-10-27 - AI智能体应用发布篇(公众号/小程序)
前言 上一篇《教你 3 分钟搭建 AI 助手(无需编码)》让大家快速搭建了微信云开发的AI智能体Web版和H5版。 如果想在微信生态中快速获取用户,那么公众号和小程序是必须要做的载体,所以这篇主要分享以下 3 点: AI智能体发布到公众号 小程序中集成AI智能体 AI深度合成类目如何申请 步骤 AI智能体发布到公众号 进入云模板 首先我们先进入云模板控制台,在这里再教大家一种快速进入云模版的方式,除了在《教你 3 分钟搭建 AI 助手(无需编码)》中提到的云开发控制台进入的方式之外。 还可以直接在微信开发者工具的代码目录区域右键呼出菜单然后选择「通过云模板或AI配置页面」菜单项。 注:非正式AppId,小游戏,游客态、代开发小程序,不是小程序开发者,看不到该菜单项。 [图片] 授权公众号 从「我的应用」列表进入「AI智能体应用」详情页点击「添加至多个平台」 [图片] 可以选择一个AI智能体进行「配置」支持 3 个平台 微信小程序客服 微信公众号(服务号) 微信公众号(订阅号) [图片] 配置方式非常方便只需要填写AppId即可 [图片] 前往微信公众平台“设置与开发” - “基本配置” - “公众号开发信息”,复制”开发者ID(AppID)”信息 [图片] 获取到AppID填写后点击「下一步」扫码授权即可,授权成功后 未授权 会变为 已授权 状态 [图片] 接下来来看下效果: [图片] 没有认证的公众号需要回复“继续”,已认证公众号无需回复“继续”可直接输出文案 小程序中集成AI智能体 集成应用 回到「添加至多个平台」面板选择「添加至小程序」 [图片] 根据操作指南下载好代码包解压 [图片] 复制到 miniprogram/ 目录下 [图片] 再将下载的 project.config.json 进行替换 [图片] 当以上两步都完成之后可以以下两种方式进行跳转: JS跳转代码 [代码]wx.navigateTo({url: "/$weda_root/packages/mIOXHS1t/pages/chat/index"}); [代码] WXML布局代码 [代码]<navigator url="/$weda_root/packages/mIOXHS1t/pages/chat/index">跳转至智能体</navigator> [代码] 在这里就相当于把整个AI智能体应用集成到小程序中了 [图片] 在这里需要注意,如果要发布小程序上线还需要在小程序管理后台更新域名配置 [图片] 集成API 如果想自定义界面,可以直接集成API即可 回到「AI智能体应用」详情页面切换到「接口展示」 [图片] 可直接复制代码在小程序端进行调用,以查看AI智能体列表接口为例 [代码] wx.cloud.callFunction({ name: 'cloudbase_module', data: { name: 'ai_bot_get_bot_list', data: { filter: { where: { }, }, select: { $master: true, // 常见的配置,返回主表字段 }, }, }, success: (res) => { console.log(res) }, }); [代码] [图片] 每个接口除了有示例代码,还有详细的参数说明: [图片] [图片] AI深度合成类目如何申请 想要上线AI相关的小程序,必须申请深度合成类目,所以这一步至关重要,回到「AI智能体应用」详情页切换到「AI算法备案资料」- 「获取AI算法合作协议」输入小程序主题即可 [图片] 截图证明,在这里需要注意截图一定要露出云模板字样,这样便于类目审核人员区分截图证明来源 [图片] 然后到小程序管理后台添加类目选择【深度合成 - AI问答】选择 2.2 使用第三方技术,上传截图证明即可 [图片] 通过以上方式类目已审核通过 [图片] 总结 本篇主要讲解了AI智能体应用的多平台发布,整体而言从创建到发布非常方便,不管你是公众号运营者还是小程序开发都可以拥有自己AI智能体应用,赶紧去试试吧~
2024-06-28 - 教你 3 分钟搭建 AI 助手(无需编码)
前言 今天给大家带来的是云开发AI智能体,上篇《云开发之云模版CMS体验》文章提到了期待云模版有更多实用模板,这周云模版就更新了AI智能体应用,无需代码一句话搞定一个 AI 助手。 步骤 进入「微信开发者工具」-「云开发」控制台 [图片] 选择「云后台」-「去使用」 [图片] 进入云模板控制台选择「模板中心」 [图片] 进入「AI智能体应用」点右上角「安装」 [图片] 安装成功后切换到「关联资源」菜单「访问」管理后台 [图片] 默认会内置一些AI助手 [图片] 选择「分享」可以直接对话体验 [图片] 如果没有满意的AI助手可以通过「从模版创建」 [图片] 模板中还有 10 多AI助手供你选择 [图片] 选择后可以进入AI助手编辑详情页面 左边区域可以设置人设提示词 右边区域可以直接对话调试效果 [图片] 确认没问题点击「提交」就创建成功了 接下来再来一个自定义一个AI助手,选择「创建智能体」 [图片] 输入AI助手名称点击「确认」 [图片] 进入AI助手详情页,不会写人设提示词可以点击「获取AI建议」 [图片] AI会根据名称来生成一套专业的人设提示词,点击「使用AI建议」即可 [图片] 设定好AI助手人设后,可以在右边区域对话调试 [图片] 除此之外还可以设置AI助手的基础信息: 对外展示信息 头像 名称 简介 背景 开场白 欢迎语 推荐问题 [图片] [图片] 确认无误后点击「提交」就创建好了,可以「分享」给你的好友使用了 [图片] 电脑端效果: [图片] 手机端效果: [图片] 还支持在「对话」菜单中查看所有智能体的对话记录 [图片] 总结 整个体验下来,创建流程非常简单,只需要输入你要创建的AI助手名称即可生成,全程无需代码,赶紧去试试吧~ 发布到小程序/公众号已经在来的路上了,后续开放后我会更新相关体验文章。
2024-05-17 - 用了 18 个月时间,做 AI 小程序从 0 到 200 万用户,从亏损到盈利(4000 字全面复盘)
前言 距离上次《离职一年,收入10倍增长》总结到现在已经过去了 22 个月。在这段时间里我经历了从高峰跌到谷底又慢慢回弹。组建团队后经历了 10 个月的连续亏损,目前已经连续 12 个月盈利,专注于 AI 应用小程序方向,已累计 200 多万用户。 接下来分享一下我这段时间的经历与思考。 胸有成竹 2022 年 6 月份那段时间是我的群工具矩阵用户增长趋势最高点。我当时的规划是将过往在群工具产品的经验持续延续,并从中开拓更为深入场景的群工具的解决方案。 通过群工具的后台数据分析了解到用户的需求,主要分为:收集行程码、健康码图片信息,其次是老师收集作业。于是我决定做一个班级收作业的群工具助手。当时胸有成竹地定了个目标,这款产品预计 6 个月内实现盈利。 确定这个项目后,我开始成立公司组建团队,我叫来了两位技术开发的老伙计,之前是同学并且还在一个团队工作过。我们讨论完需求就开始编码了起来,连续加班加点开发了 4 个月,我的新产品发布了。 新产品上线之后,我们休息了一天,去爬了个岳麓山。当我爬到山顶的时候拿出手机看了一眼小程序数据,群工具小程序数据直接腰斩掉了一倍的流量。我心想大事不妙,新的小程序还没有靠上这一波用户,原来的小程序就要不行了,这可如何是好。我寄希望于明天数据有所好转,但是现实总是残酷的。 [图片] (拍摄于岳麓山) 当我已经做好迎接新产品的高光时刻,没想到遇到的是原有业务数据的断崖式下滑。说实话我当时有点不知所措了。后来随着疫情逐步开放,我的群工具的收集需求也越来越少了,从最高日活 20 万掉到了现在日活 5 千直接下降 40 倍,这直接导致了新产品的数据不理想,最终新产品不得以停服下架。 调整方向 我看到数据趋势不对,于是果断地放弃群工具方向,转头奔向了AI效率工具。虽然都是效率工具,但是不一样的是群工具有两个角色,一个是活动发起者,一个是活动参与者。群工具产品的核心用户是活动发起者,如果收费只能收发起者的费用,但是同行大多数都是免费的,而参与者占比整个小程序的用户数量较多,所以只能靠广告收入。 那么如何让每个用户都是核心用户?当时想到的就是做AI创作工具。短视频内容创作者对AI配音是有需求的,我的第一款 AI 工具做的是 AI 配音小程序,主要也是考虑到短视频的整体增长趋势。我们结合多家厂商的 AI 配音的音色,给用户提供更多的音色选择,做会员订阅的盈利模式。 盈利模式也从之前的群工具靠广告收入转向了AI 工具靠会员订阅模式。这种模式的好处就是不需要靠用户量赚钱,只要用户人群足够精准,就可以有稳定的收入来源。 产品上线 1 个月后,慢慢有了些收入,但是距离收支平衡还有很长一段距离。于是我开始围绕整个视频创作过程去寻找更多机会。如:获取配音之前的文案处理,以及配音后的视频字幕处理,我们都做了很多的尝试。 团队在不断地开发新产品,这个时候我的定位也从原来的开发角色变成了产品与推广。在转变的过程中我通过请教之前的运营同事以及在网上学习了很多推广方法,加上不断地实践尝试不同推广方式在不同推广渠道做推广,但是整体效果并不理想。 时间飞逝,又过去了 3 个月。由于收支一直没有平衡,团队的资金慢慢枯竭,面临解散。那段时间,我在下班回家的路上以及到家后的漫漫长夜中无数次的自我拷问,是不是我的能力不行?这个方向真的对吗?增长方向到底在哪里? [图片] (从公司回家的路上) 在此期间有过一个朋友想要收掉我的团队,说实话当时我是有一些动摇的,陷入了自我怀疑,而且确实是没钱了。我也坦诚地向团队伙伴们说明了情况,让我没想到的是他们不同意被收掉。有位团队成员和我说:“大不了我们解散后,兼职搞这个项目,等项目赚钱了,我们再一起做!”。这段话触动了我,这让我体会到了那句“一个人走得更快,一群人走得更远”的含义。最后我决定坚持下去,走自己的路,即便是在曲折中摸索前行,也要去享受这个过程。 下定决心后,我尝试过去找钱,但是由于自身人脉与资源受限,再加上团队还是在亏损状态,找过身边的一些朋友都没有拿到钱,与此同时我还投了不少投资机构也被一一拒绝了。 最后冲刺 时间来到了 2023 年的 1 月份,此时 ChatGPT 已经火爆全球。在此之前我也规划了要做AI写作产品,对于创作者来说输出文案属于在AI配音的前置步骤。我打听了几家国内做AI写作的团队API费用,效果一般,价格偏高。当我得知 OpenAI 开放了ChatGPT 的API接口的时候,第一时间让团队开发了一款AI写作小程序,仅用一天时间就上线了,界面非常简单,输入写作需求,写作字数,选择写作风格,即可开始写作,结果支持复制,根据写作字数付费。 果不其然 AI 写作在第一天就收到了会员付费,同时也因为 ChatGPT 的热度,快速的获得了第一批用户以及一些自媒体达人的推荐。紧接着我们开始快速迭代版本,更新了常用的写作模板,降低用户写作的门槛。随着用户的持续增长以及产品的迭代,付费转化率也在不断地提升,眼看马上就要实现收支平衡了。 此时不好的消息来了,我的AI写作小程序被下架了,原因是 ChatGPT 的 API 存在安全合规问题。我记得非常清晰,那天是 2023 年 4 月 1 日,是愚人节。我当时感慨,这不会是老天给我开的一个玩笑吧!让我看到团队生的希望,同时又要让我希望破碎。 当时国内的AI大模型开放接口并不多,我找了好几天终于找到了国内的智谱AI,我们赶紧接入了智谱 AI 的接口,重新上架了新的 AI 写作小程序。 然而在这几天里 ,因为 AI 写作小程序被下架,很多用户买了会员又找不到新的小程序,微信支付账号被投诉过多,导致了支付账号被限制支付,我只好紧急申请了新的支付主体,并用了一个多月的时间处理用户投诉退款等问题。紧接着又是微信小程序对上架 AI 生成类小程序的资质要求,解决的过程也是非常曲折,在此就不一一赘述了。 这一波接着一波的事情,把我搞得焦头烂额,但是最终问题还是被解决了。也正因为问题被及时解决了,我的团队这个月收支平衡了。 持续增长 当时我很看好抖音生态,因为抖音有小程序,并且作品可以挂载小程序,所以我开始找做运营的朋友推荐做抖音账号方面的运营人员。好不容易招到一个有经验并且对 AI 工具感兴趣的小伙伴,他的入职让我看到了一些增长的希望,但是他做了半个月看到数据不理想,提出了辞职。 但我坚信这个方向是正确的,于是开始自己做内容。连续做了 3 个月发布了 100 多个作品,这时作品数据渐渐有了一些效果,开始每天有 5 单付费了,当我做到第 6 个月的时候发布了 200 多个作品,抖音小程序从每天的 5 单增长到每天 200 单,直接增长了 40 倍。这让我体会到了从量变到质变,整个过程“坚持”是最重要的策略。 [图片] (抖音小程序的订单曲线) 从最开始是个门外汉,对推广一知半解,到做了 6 个月初见成效,通过这段经历我开始对运营推广 AI 效率工具,有了自己的理解,开始提高自己的更新频率以及开启了外部兼职机制。将这种有效的推广模式持续放大,然后再根据产品数据不断迭代整体产品交互体验。 在这个期间我收到了微信团队的邀请,在微信公开课 PRO 上作为特邀讲师,分享了我做 AI 小程序的思路以及与云开发和 Donut 平台的最佳实践。 [图片] (微信公开课pro分享现场) 从最初用私人账户转账到公司账号以支付工资,到现在团队已经盈利了 12 个月,公司账上还有半年以上的发展资金储备,也顺利度过了生死阶段,进入了发展阶段,转向AI效率工具到现在已经整整 18 个月了,我们的 AI 效率工具矩阵用户量达到 200 万,同时公众号关注用户达到了 30 万。 这个数据在如今的 AI 时代,对比那些炸裂的消息,算不上什么成绩,但是对于我来说非常有意义。经历了无数次绝望,到现在才算站稳脚跟,过程可以说是一步一个坎,但是也正是因为这些坎坷,才让我更加坚定发展方向。 总结 这次创业我感觉真是起起落落落落落落落落落起,从一开始的最高点掉到了谷底,然后经历煎熬又慢慢回升。在这里非常感谢团队中每位成员的贡献,尤其是我两位程序员哥们,还有一边带孩子一边默默支持我的媳妇。 整个创业经历对于我来说有三大挑战: 从个体到团队。之前一个人的时候,顶多就是少赚一些,但是有了团队,不赚钱就是亏钱。人多意味着能做的事情更多了,同时也意味着成本更高,决策更需谨慎,做任何决策之前要有更全面深度的思考。我决定要不要做新产品前都会研究行业数据、搜索数据、推广数据,再去做决定。 从免费到付费。之前一直做的都是免费的工具,用户对产品的要求不会很高,能用就行,但是变成付费产品,用户有一点不爽可能就导致投诉退款,这使我对产品的要求从能不能用变成了好不好用。我会不断地去体验优秀的产品,记录好的体验感受,再去体验自己的产品记录糟糕的感受,从而改进自己的产品。 从开发到推广。之前一个人的时候我精力分配是 20% 产品,80% 的开发,一个人沉浸在自己的代码世界里。而现在我的分配是 40% 产品, 60% 推广,在同质化产品泛滥的时代,把产品推出去是活下来的唯一出路。我会通过各种渠道(请教高手、听播客、看书、看文章、看视频)去学习,然后再实践各种推广方法,最后形成一套自己的推广方法论,在推广的过程中不断持续迭代方法与对产品推广的理解。 以上是这段时间的思考与成长。 最后,祝大家都能在AI时代找到属于自己的机会,让我们一起用有限的时间,去探索无限的可能。 更新下自我介绍: 陈宇明,长沙,创业中,专注 AI 效率工具。 经历: 11年互联网产品/研发/管理经验 Github 开源库 BRVAH 24k+ stars 群工具小程序矩阵 1100 万用户 AI效率小程序矩阵 200 万用户 关于我的更多文章可查看:我的主页
2024-04-19 - 推荐 7 款小程序产品运营必备的官方小程序
前言 接触小程序从2018年到现在已经5年多了,从最开始进入公司管理小程序团队,到后来全职做一名小程序独立开发,再到现在创业组建一个小团队,大大小小的小程序累计加起来差不多100多个,在这个过程中这 7 个官方小程序一直在用。 小程序 以下均为微信官方小程序,直接在微信搜一搜名字即可查到。 微信指数 [图片] 微信指数小程序对我来说使用频率最高了,无论是小程序方向的决策还是公众号文章内容方向的决策我都会先查找一下微信指数,如果内容有热度才能去做,否则做了也白做。 [图片] 使用非常简单,只需要输入关键词即可。能够查看这个词的指数趋势以及数据来源。 [图片] 小程序助手 [图片] 可以看作简化版的小程序管理后台,我用的最多的是审核管理。很多时候代码提交审核了,版本通过了,我人在外面,不方便用电脑,这个时候我就直接用这个小程序进行版本发布,非常方便。 [图片] We分析 [图片] 当小程序上线后,数据分析是必不可少的。从最开始的小程序数据助手到现在的We分析,我几乎每天都会查看数据分析数据来源,这样可以及时的去调整我的运用策略和发现程序问题。 [图片] 微信开放社区 [图片] 只要涉及到微信相关的问题,大部分都能在这个社区得到解决,在这里有很多常见问题的解决方案也有官方最新动态以及提出问题后能得到官方人员和贡献者的解答。 [图片] 我也是贡献者之一,截止到现在回答了3000多个问题和分享了100多篇文章。 [图片] 公众平台助手 [图片] 我主要是用它来查看公众号的数据统计。 [图片] 如果你有发布公众号的需求可以下载官方的【订阅号助手】App。 流量主服务助手 [图片] 小程序盈利模式无法就是广告模式或者付费模式,如果是广告那就用这个小程序。 [图片] 微信支付商家助手 [图片] 付费模式就用这个,在这里还可以直接解决客户投诉问题。 [图片] 最后 你在小程序开发过程中还有什么实用的小程序?欢迎留言一起讨论
2023-09-24 - 小程序独立开发者必备的 3 个思维
前言 我接触小程序到现在已经有 5 年了,经历过在公司管理小程序团队到全职小程序独立开发者再到现在成立小工作室做小程序产品实现盈利,我认为以下 3 个思维是独立开发者必不可少的。 产品思维 作为一名开发者,很容易想事情第一时间用开发者思维,如:如何实现这个功能?用什么技术框架?可能会遇到什么技术难点?需要多少时间?等等之内的。 如果做一名独立开发者那就需要从用户场景的角度去思考问题,如:要开发这个产品,为什么要做这个产品?解决了用户的什么问题?用户使用场景是什么?用户使用中会遇到什么问题?如何降低用户使用成本?如何更快的解决用户的问题? 开发者思维转化产品思维,把“怎么做”转换成“做什么”。 MVP思维 思考完怎么做,那么接下来就是怎么做了,很多时候开发者容易一开始设计的很复杂,思考到后续的迭代,用户量,各种什么缓存策略,架构,技术选型等等,最后花了大量时间做出来之后发现用户没多少,时间白费了。 MVP 是“Minimum Viable Product”的缩写,意思是“最小可行性产品”。 在产品开发的早期阶段,应该专注于最小化开发成本和最大化产品的功能表达,以便尽快验证产品构思并获得反馈。 这种反馈可以是用户对产品的功能和性能的反馈,也可以是用户对产品的需求和市场环境变化的反馈。通过不断迭代优化产品,可以更好地满足用户需求和适应市场环境,从而实现可持续的商业增长。 在不断变化的市场环境中保持灵活性,并不断优化产品以满足用户需求。 不要一开始追求完美,最小成本的做到能用,降低试错成本,因为大部分的产品都是失败的。 这些年来我做过的小程序超过100个,真正做起来的只有10个不到。 运营思维 当产品做完发布之后,那么怎么让用户知道呢? 如:在什么地方?用什么形式?把你的产品推广出去? 一开始就要思考清楚,多学习下同行的经验,不断尝试新的方式,新的推广渠道,形成可持续的推广计划。 在推广的过程中不断的分析数据,持续的优化推广内容。 建议先学习SEO知识,然后再学习制作推广内容,最后就是持续的输出推广内容。 当用户进来后,还要思考如何让用户留下来,以及如何让用户分享和付费。 最后 当你准备做一名独立开发者的时候,需要补充以上 3 个思维,先从这 3 个方面的去思考,具体执行落地的方法有很多,先有意识然后再去行动就很容易了。 更多小程序内容看这篇《【集合】我花了 3 年,写了 60 篇小程序文章》
2023-10-06 - 【集合】花了 3 年,写了 60 篇小程序文章
前言 记得上次写集合文章《【集合】花了 3 个月,写了 40 篇小程序文章》是2020年,现在已经是2023年了,在这3年的时间我写了60篇小程序相关的文章涉及小程序产品、小程序运营、小程序技术等。 经历 我成长最快的那五年 我是如何从网瘾少年变成程序员的? 全职小程序创业一年,尝试 3 个方向,收入提升 10 倍 技术 2020年 快速上手微信小程序云开发的3个小建议 实战:如何降低云开发服务器成本? 实战:图片处理服务之快速压缩模版 云开发:一键部署 Wordpress 应用 云开发比赛|我是如何从初赛走到决赛 小程序云开发:data exceed max size 解决方案 实战:CloudBase CMS 「关联」操作 2021年 云开发实战:实现短信跳小程序 推荐 4 款值得学习的小程序源码 云开发实战:小程序导出Ecxcl表格功能 如何引导用户添加到我的小程序? 我为什么选择云开发? 小程序getUserInfo更新为getUserProfile实战案例 【小技巧】不装插件的图片取色方法 开源推荐|小程序数字滚动动效组件 微信小程序如何设置背景图片? 微信小程序代码优化3个小技巧 开源小程序-高仿微软待办To-Do源码分析 第三方API和云开发的结合使用 2022年 30分钟实现小程序打卡签到送积分功能,提升用户留存利器! 微信小程序如何实现页面传参? 微信小程序开发快速入门-小程序之开发前准备(1/5) 微信小程序开发快速入门-第一个页面编写(2/5) 云函数中实现耗时操作解决方案 2023年 微信小程序开发快速入门-列表页编写(3/5) 微信小程序开发快速入门-添加备忘录页面(4/5) 微信小程序开发快速入门-备忘录代码优化(5/5) 微信小程序开发快速入门(视频版) 微信云开发管理工具入门教程 Dount 多端框架入门体验 微信小程序云开发快速入门(1/4) 微信小程序云开发快速入门(2/4) 微信小程序云开发快速入门(3/4) 微信小程序云开发快速入门(4/4) 微信小程序前后端开发快速入门(完结篇) 产品与运营 2021 公众号提取小程序跳转路径及注意事项 聊聊我对做什么小程序的思考 云开发低码平台初体验 小程序账号企业主体注销后流量主如何结算? 如何查看别人的微信小程序服务类目? 2022年 低代码平台微搭入门教程 如何搭建用户成长体系? 如何快速验证产品想法? 如何做增长用户? 如何做产品变现? 如何策划一场运营活动? 如何做出最优的产品方案? 2023年 申请开通【商家转账到零钱】超时后如何反馈? 微信小程序如何推广? 如何在微信生态推广小程序? 如何做微信公众号SEO? 做微信小程序产品,需要注意这 3 点! 用做App的思路去做微信小程序可行吗? 如何在微信开放社区提问? 无需一行代码,轻松分析微信小程序产品体验 申请开通「商家转账到零钱」需要什么材料?【案例】 微信小程序深度合成类目资质如何准备? 微信小程序深度合成类目如何通过?
2023-09-15 - 微信小程序云开发快速入门(1/4)
前言 小程序快速入门开发可看:《微信小程序开发快速入门之码仔备忘录系列教程》 从上次完成了码仔备忘录本地版本后,码仔就养成了每天记录备忘录的好习惯,每周早上会记录下自己要做的任务,然后晚上在复盘一下今天的计划是否完成。 [图片] 有一天,码仔看到它最喜欢的码妞在一旁愁眉苦脸。 [图片] 码仔:“怎么了?” 码妞:“工作事物太多了,总是忘记工作上的一些事情” 码仔心里暗喜,这不是和我之前一样吗?让她用用我的码仔备忘录,她一定会觉得我很厉害,hahahaha。 [图片] 码仔:“我教你一个方法” 码妞:“什么方法?” 码仔:“用我的码仔备忘录” 于是码妞用上了码仔备忘录进行工作日志记录,突然有一天码妞非常生气的跑到码仔身边。 [图片] 码妞大声骂道:“死猴子,我就知道你写的东西不靠谱,我今天换了个手机,然后再次打卡你的备忘录小程序,结果数据都没有了!” 码仔解释道:“目前只支持单机版,换手机小程序本地缓存就没了,所以的记录就没有了。” 码仔为了解决码妞数据问题,当天码仔熬夜查资料。 [图片] 终于找到了小程序云开发!码仔打算将把备忘录的本地数据存储在云端。 在使用云开发之前,让我们先来了解一下云开发的一些基本信息,以便我们后续更好的学习云开发。 [图片] 什么是云开发? 云开发是可以帮助我们快速成为全栈的一种后端云服务,采用的是Serverless的架构。开发者无须搭建服务器,可直接使用其中的云数据库、云存储、云函数等云服务基础功能。 那么这个时候你可能会想,这和现在的传统开发模式有什么区别呢?下面通过现实生活中盖房子的过程来了解传统开发模式与云开发模式的区别。 如果传统方式来盖房子需要6步: 1)将房子的地基打好,设计整理结构。(后端云服务基础架构) 2)打地梁,地梁也是决定房子是否稳固的关键。(搭建云数据库) 3)主体的砌筑,将房子的基本结构盖好。(搭建云存储) 4)在步屋内填上土,砸夯,将地面砸实。(搭建云函数) 5)在屋顶铺上水泥,封实。(提供静态托管和扩展能力) 6)装修成自己喜欢的风格。(编写业务需求逻辑代码) 而云开发模式只有一步,即装修成自己喜欢的风格。可以看到图1-1展示的是传统开发需要管理的基础资源与云开发需要管理的基础资源的对比,可以帮助我们更清晰的了解两者之间的差距。 [图片] 图1-1 传统开发需要管理的基础资源VS云开发需要管理的基础资源 就这一步即可。云开发提供完整的后端云服务,提供数据库、存储、函数、静态托管等基础能力,以及扩展能力,无须管理基础架构。相比较传统的开发模式,云开发至少可节省50%的人力成本、交付效率提升70%。 实战云开发改造 码仔为了获取码妞肯定,码仔下定决心,决定通宵也要搞定小程序云开发! [图片] 云开发模式调整 在此之前我们需要把原来码仔备忘录项目改成云开发项目,所以需要先把项目从项目管理中先删除。(如果是直接新建的云开发项目可以忽略这一步) 首先,先进入小程序项目管理页面: [图片] 然后点击右上角「管理」按钮进入该页面的管理状态。 [图片] 然后选中你之前的单机版本勾选进行删除即可。 [图片] 接下来我们在导入项目界面对云开发进行勾选即可。 [图片] 这个时候进入我们会发现小程序开发工具自动给我们生成了一套云开发Demo,我们先直接处理掉。 [图片] 首先进入 project.config.json 文件,把 “miniprogramRoot”: “miniprogram/” 修改成 “miniprogramRoot”: “/”,这样小程序的页面目录就是从根目录读取。然后我们会看到日志区域会出现错误日志。 [图片] 意思就是找不到小程序云开Demo代码的里面的一些模块,这个不用理会直接先把Demo代码全部删除。 [图片] 删除后我们就可以看到正常的页面渲染了。 [图片] 回到正常状态下,我们接下来要改造的就是数据操作这块的业务。 数据缓存改造云开发 在改造之前我们先要梳理思路。 首先我们先通过之前学习过抽象方法。 把所有和数据操作相关的方法都抽取到一个js类里面,这样便于维护。 将所有的方法替换成云数据库的操作函数。 抽象数据操作API 我们在根目录下面新增 api 的文件夹,然后在其文件夹下面新增一个 memo.js 用于存放所有备忘录的操作API。 [图片] 梳理下有哪些方法: 获取备忘录列表 获取备忘录详情 新增备忘录信息 删除备忘录信息 修改备忘录信息 先给相关的方法取好函数名称,然后进行导出声明。 memo.js 完整代码 [代码]// 获取备忘录列表 function getMemoList() { } // 获取备忘录详情 function getMemoInfo() { } // 新增备忘录信息 function addMemoInfo() { } // 删除备忘录信息 function deleteMemoInfo() { } // 修改备忘录信息 function updateMemoInfo() { } // 导出声明 export { getMemoList, getMemoInfo, addMemoInfo, deleteMemoInfo, updateMemoInfo } [代码] 首先我们来实现获取备忘录列表,在列表页面 onShow 函数中会被调用。 [代码]// 初始化数据 onShow() { let list = wx.getStorageSync('list') || [] this.udpateList(list) }, // 更新列表数据 udpateList(list){ this.setData({ list: list, isEmpty:!list.length>0 }) }, [代码] 在这个时候setData是页面操作函数,所以我们要抽象的部分是: [代码]wx.getStorageSync('list') || [] [代码] 接下来我们就把这段代码放在memo.js里面去。 memo.js 关于 getMemoList 的代码片段 [代码]// 获取备忘录列表 function getMemoList() { return wx.getStorageSync('list') || [] } [代码] 然后替换成列表页面的调用,先引入memo的获取 getMemoList 函数。 list.js 头部引入代码片段 [代码]import { getMemoList } from '../../api/memo.js' [代码] 引入后在 onShow 函数中使用 在 onShow 所有代码片段 [代码] onShow() { let list = getMemoList() this.udpateList(list) } [代码] 采取同样的方式,我们可以把之前的获取详情、删除、修改都抽象到 memo.js 中: memo.js 所有代码 [代码]// 获取备忘录列表 function getMemoList() { return wx.getStorageSync('list') || [] } // 获取备忘录详情 function getMemoInfo(index) { const list = getMemoList() return list[index] } // 新增备忘录信息 function addMemoInfo(data) { const list = getMemoList() list.unshift(data) wx.setStorageSync('list', list) } // 删除备忘录信息 function deleteMemoInfo(index) { const list = getMemoList() list.splice(index, 1) wx.setStorageSync('list', list) return list } // 修改备忘录信息 function updateMemoInfo(index,data) { const list = getMemoList() list.splice(index, 1) wx.setStorageSync('list', list) addMemoInfo(data) } // 导出声明 export { getMemoList, getMemoInfo, addMemoInfo, deleteMemoInfo, updateMemoInfo } [代码] 在相应业务场景进行调用,调用之前都要先引入方法才行。由于引入代码相对重复,下面就不贴引入代码了,只贴使用代码。 删除代码方法调用: list.js 中 del 函数所有代码 [代码]// 删除 del(event) { let that = this let index = event.currentTarget.dataset.index wx.showModal({ title: '提示', content: '你确定删除?', success(res) { if (res.confirm) { // 修改部分 const list = deleteMemoInfo(index) that.udpateList(list) } } }) } [代码] 查看备忘录详情调用: edit.js 中 onLoad 函数所有代码 [代码]onLoad: function (options) { if (options.index) { // 修改部分 let item = getMemoInfo(options.index) this.setData({ title: item.title, content: item.content, nowDate: item.date, nowTime: item.time, index: options.index }) } }, [代码] 新增和删除方法调用: edit.js 中 save 函数中的关于保存和修改的代码片段 [代码] // 修改部分 if (this.data.index) { // 修改逻辑 updateMemoInfo(this.data.index,data) }else{ // 新增逻辑 addMemoInfo(data) } [代码] 到这里我们就完成了第一步,先抽取数据操作对象,然后我们要进行云数据库到操作了。 操作云数据库 初始化数据库 首先我们先进入云开发控制台。 [图片] 然后选中「数据库」。 云开发提供了一个 JSON 数据库,顾名思义,数据库中的每条记录都是一个 JSON 格式的对象。一个数据库可以有多个集合(相当于关系型数据中的表),集合可看做一个 JSON 数组,数组中的每个对象就是一条记录,记录的格式是 JSON 对象。 [图片] 我们会发现没有可用的集合,再次新建一个集合 memo,用于存放备忘录数据。 [图片] 新增一条数据 我们先手动新增一条测试数据看看,点击「添加记录」。 [图片] [代码]{ "title": "我是标题", "content": "我是内容", "date": "2021-07-22", "time": "03:56" } [代码] 我们用以上测试数据添加进去 [图片] 除此之外我们可以看到可以添加多种不同类型的数据,云开发数据库提供以下几种数据类型: String:字符串 Number:数字 Object:对象 Array:数组 Bool:布尔值 Date:时间 Geo:多种地理位置类型 Null 我们先默认都用 string 类型的数据,新增完成之后就可以做memo集合中看到一条数据了。 [图片] 数据表已经有数据了,那么我们怎么获取? 获取数据库数据 我们回到小程序编辑台,对我们的获取数据方法进行下改造。 [代码]// 获取备忘录列表 function getMemoList() { return wx.getStorageSync('list') || [] } [代码] 这个是获取本地的数据方法,下面是获取数据库方法 [代码]// 获取备忘录列表 function getMemoList() { // 1. 获取数据库引用 const db = wx.cloud.database() // 2. 找到集合获取数据 db.collection('memo').get({ success: function (res) { // 3. 使用数据 console.log(res) } }) } [代码] 当我们去列表页面进行调用的时候发现报错: [图片] 原来是在此之前还需要进行云API的初始化。 那么现在问题来了,我们后续会遇到大量的调用云API,那么都需要一开始初始化,并且这个初始化只需要一次,这个时候初始化我们应该在哪里进行调用呢? 当一个函数应用一开始就需要调用的时候并且只需要调用一次,我们可以在app.js的 onLaunch 生命周期中调用。 app.js 的 onLaunch 函数代码片段 [代码]// app.js App({ onLaunch() { wx.cloud.init() } }) [代码] 接下来在看调用看看,在这里告诉大家一个调试技巧,使用调试器中的Network面板,然后选中Cloud标签进行过滤,在这里我们就可以刚才调用API请求。 [图片] 先看看请求状态,从状态来看是成功的请求。 [图片] 然后我们再来看看返回的数据: [图片] 明明有一条数据,为什么获取不到呢? 因为云开发数据库集合读取权限问题。默认我们新建的集合读取权限是仅创建则可以读写,第一条测试数据是我们手动录入的,所以没有创建者,默认权限于是就读取不到。 我们可以来到云开发控制面板,然后找到 数据库=> 数据权限。 [图片] 我们把权限从「仅创建者可读写」修改为「所有用户可读,仅创建者可读写」试试看。修改完成之后再次调用获取方法,看到返回结果中出现了这条测试数据。 [图片] 虽然数据出来了,但是我们会发现还有一个错误日志。 [图片] 具体代码块: [图片] 原因:在没有使用云API之前我们使用的缓存操作API是同步操作返回了具体数据,而修改后的获取数据方法是异步函数,没有给到数据到列表页面进行使用。最终导致list数据对象为空,list的length自然就出现了undefined的错误提示。 从数据库获取数据除了可以用以上的实现 success 方法,同样还支持可以用 Promise 风格调用: 可以简单理解成将包裹的回调函数调用方式改成了链式调用的回调函数 [代码]db.collection('todos').doc('todo-identifiant-aleatoire').get().then(res => { // res.data 包含该记录的数据 console.log(res.data) }) [代码] 基于 Promise 风格调用,我们可以将函数拆解成两部分。接下来再改造一下,传递数据的部分代码: 将查询代码在 getMemoList 完成 [代码]// 获取备忘录列表 function getMemoList() { // 1. 获取数据库引用 const db = wx.cloud.database() // 2. 找到集合获取数据 return db.collection('memo').get() } [代码] 将查询结果在列表页面进行列表渲染 [代码] onShow() { getMemoList().then(res => { this.udpateList(res.data) }) } [代码] 这样我们就成功的将数据库的数据显示出来了 [图片] 接下来,我们就来把所有方法进行云API的实现。 查看数据详情 在这里我们就不能用index下标来,通常与后端交互我们都会采用ID来进行查询,每条数据都会生成一个ID字段,字段名称为_id。 [图片] 那么我们就需要需改之前组件传递参数以及获取的字段和页面之前的参数字段: list.wxml 代码片段,其中 data-id="{{item._id}}" 为修改部分 [代码]<view bindtap="toEdit" bindlongtap="del" data-id="{{item._id}}" class="list" wx:for="{{list}}"> <view> <text class="list-title">{{item.title}}</text> <text class="list-date">{{item.date}} \n {{item.time}}</text> </view> <view class="list-content">{{item.content}}</view> <view class="line"></view> </view> [代码] 获取id进行页面传递 [代码]// 去编辑 toEdit(event) { let id = event.currentTarget.dataset.id if (id) { // 从列表点击 wx.navigateTo({ url: '/pages/edit/edit?id=' + id, }) } else { // 从新建按钮点击,省略相关代码 } } [代码] 查询方法改造,由于通过id查询是非常常用的方法,所以云API直接有个doc方法传入id即可查询: memo.js 中 getMemoInfo 函数代码 [代码]// 获取备忘录详情 function getMemoInfo(id) { // 1. 获取数据库引用 const db = wx.cloud.database() // 2. 找到集合获取数据 return db.collection('memo').doc(id).get() } [代码] 查询方法调用: edit.js 中 onLoad 函数代码 [代码]onLoad: function (options) { if (options.id) { // 显示已有数据 getMemoInfo(options.id).then(res=>{ console.log(res) const item = res.data this.setData({ title: item.title, content: item.content, nowDate: item.date, nowTime: item.time, id: options.id }) }) } } [代码] 新增备忘录数据 新增方法改造: memo.js 中 addMemoInfo 函数 [代码]// 新增备忘录信息 function addMemoInfo(data) { // 1. 获取数据库引用 const db = wx.cloud.database() // 2. 找到集合获取数据 return db.collection('memo').add({data}) } [代码] 调用方法: edit.js 中 save 函数新增逻辑代码片段 [代码]// 新增逻辑 addMemoInfo(data).then(res=>{ console.log(res) wx.navigateBack() }) [代码] 新增成功后会返回新增数据的id信息。 [图片] 然后在数据库当中就可以看到新增的数据了。 [图片] 我们会发现手动新增的数据和使用云API新增的函数新增的数据不一样,这条数据多了一个“_openid”的字段。这个 _openid 是代表当前用户的唯一识别,每个用户在一个小程序中 _openid 就是代表这个用户,在不同小程序中 _openid 是不一样的。 小知识:小程序也是通过这个_openid字段来区别这条数据是不是当前用户的,我们可以做个实验。我们将memo集合的数据库权限调整成「仅创建者可以读写」 。 [图片] 然后在看下列表返回的数据,只有自己新增的那条数据了。 [图片] 修改备忘录数据 修改方法改造: memo.js 中 updateMemoInfo 函数。 [代码]// 修改备忘录信息 function updateMemoInfo(id, data) { // 1. 获取数据库引用 const db = wx.cloud.database() // 2. 找到集合获取数据 return db.collection('memo').doc(id).update({data}) } [代码] 之前修改的逻辑是采用的删除之后再新增做的“假”修改,这次我们直接采用修改方法。 edit.js 中 save 函数修改逻辑代码片段 [代码]// 修改逻辑 updateMemoInfo(this.data.id,data).then(res=>{ console.log(res) wx.navigateBack() }) [代码] 返回结果中的 updated 是代表修改成功数据的数量 [图片] 删除备忘录数据 删除方法改造: memo.js 中 deleteMemoInfo 函数 [代码]// 删除备忘录信息 function deleteMemoInfo(id) { // 1. 获取数据库引用 const db = wx.cloud.database() // 2. 找到集合获取数据 return db.collection('memo').doc(id).remove() } [代码] 调用方法: list.js 中 del 函数 [代码]// 删除 del(event) { let that = this let id = event.currentTarget.dataset.id wx.showModal({ title: '提示', content: '你确定删除?', success(res) { if (res.confirm) { const list = deleteMemoInfo(id).then(res=>{ console.log(res) getMemoList().then(res => { that.udpateList(res.data) }) }) } } }) } [代码] 在这里要注意的是删除之后还需要调用查询所有数据方法对列表更新。 [图片] 返回值 removed 为删除数量。 总结 在本小节我们使用云API在小程序端操作了云数据库,学习了以下函数: 使用 get() 进行了数据库的查询 使用 add() 进行了数据添加 使用 update() 进行了数据修改 使用 remove() 进行了数据删除 在这里我们就已经完成了使用云数据库来存储备忘录数据,码仔以后再也不用数据丢失问题了。于是码仔拿着这个云数据版本的备忘录给码妞使用去了,得到了码妞的赞许。 [图片]
2023-07-26 - 如何在微信开放社区提问?
搜索 在提问前,先搜索一下是否已经有类似的问题被问过,如果有,可以选择查看已有的解决方案。 [图片] 当你搜索到一个问题的时候,如果在这个问题下没有找到答案,可以查看下相关的问题是否能够找到答案。 [图片] 如果没有,再进行提问。 提问 使用简洁明了的语言,避免使用过于专业的术语和缩略语,这样可以让回答者更容易理解你的问题。 避免使用模糊词语,如“大概”、“可能”、“有时候”等,这些词语会让回答者难以理解你的问题。 详细说明重现步骤及可能存在的问题,附上问题截图,已经尝试过的解决方法等,这样可以让回答者更好地理解你的问题并给出更准确的解决方案。 如果调用接口报错,请直接把接口的官方文档地址贴出来,标明调用接口地址,附上请求的参数以及返回的数据。 必要时提供能复现问题的简单代码片段(https://developers.weixin.qq.com/miniprogram/dev/devtools/minicode.html) 回复 在得到回答时,要尊重他人的回答,认真阅读和理解回答给予反馈。 对你有用的回答可以点击回复下方的【有用】表示感谢认可。 [图片]
2023-05-29 - 做微信小程序产品,需要注意这 3 点!
前言 微信小程序已经成为了越来越多企业和个人的选择,因为它可以为用户提供便捷、快速的服务和体验。然而,要想在微信小程序市场上脱颖而出,需要考虑以下 3 点。 1.明确目标受众 在开发微信小程序产品之前,需要确定你的目标受众是谁,并了解他们的需求和行为模式。这有助于在设计和开发产品时提供更好的用户体验和满足他们的需求。 在明确受众的同时需要还需要明确市场需求大小,可以通过【微信指数】小程序来查询需求量。 例:在微信指数小程序输入「微信小程序」查看指数趋势。 [图片] 2.界面与交互 微信小程序的界面设计非常重要,因为它是用户与你的产品进行交互的第一印象。一个好的界面设计应该清晰、简洁、易于导航,,同时提供有用的功能和信息。可以学习微信设计指南,以及参考官方小程序的界面设计,如:微信指数,微信支付,腾讯系列小程序。 微信小程序产品的用户体验至关重要。在开发产品时,要考虑到用户使用的便捷性以及整体交互,尽量减少用户在使用过程中遇到的障碍和问题。 3.最小可行性产品 做微信小程序我不推荐做的像App那样功能全面,一个小程序最好只解决一个问题。第一个版本不需要很完善,做个最小可行性产品(mvp)就可以上线快速验证。 [图片] 在此需要注意安全性和稳定性,微信小程序产品的安全性和稳定性是至关重要的。在开发产品时,要考虑到数据的安全性和系统的稳定性,并采取措施来确保用户的数据不会被恶意攻击或泄露,这个是最基本的保障。 通过上线后用户数据反馈产品需要不断的更新优化,持续提高其性能和用户体验。 推荐使用PDCA循环:Plan(计划)、Do(执行)、Check(检查)和Act(处理) P(Plan)计划,包括方针和目标的确定,以及活动规划的制定。 D(Do)执行,根据已知的信息,设计具体的方法、方案和计划布局;再根据设计和布局,进行具体运作,实现计划中的内容。 C(Check)检查,总结执行计划的结果,分清哪些对了,哪些错了,明确效果,找出问题。 A(Act)处理,对总结检查的结果进行处理,对成功的经验加以肯定,并予以标准化;对于失败的教训也要总结,引起重视。对于没有解决的问题,应提交给下一个PDCA循环中去解决。 以上四个过程不是运行一次就结束,而是周而复始的进行,一个循环完了,解决一些问题,未解决的问题进入下一个循环,这样阶梯式上升的。
2023-05-03 - 如何做微信公众号SEO?
前言 随着越来越多的人使用微信,微信公众号SEO也变得越来越重要。在这篇文章中,我们将探讨如何通过一些简单的技巧来提高你的微信公众号在微信搜索引擎中的排名,从而吸引更多阅读和关注。 技巧 公众号名称和头像:公众号名称要包含业务关键词,内容与公众号定位相符;头像要清晰美观,最好是公众号运营者的真实头像。 账号权重:微信公众号的权重主要由以下几个因素决定:粉丝数量、文章质量、原创度、互动率、文章打开率、读完率等。 关键词优化:在文章中合理使用关键词,并将其放在标题、正文等位置,以便搜索引擎更好地理解文章主题。 内容质量:提供高质量、有价值的内容,吸引读者阅读和分享,从而提高公众号的权威性和可信度。 标题优化:制作有吸引力的标题,让读者一眼就能看出文章的主题和亮点,同时避免使用过于夸张或误导性的标题。 发布时间和排版:公众号文章的发布时间要选择在用户活跃的时间段,例如早上8点、中午12点和晚上6点左右。同时,文章的排版要美观、清晰,没有错别字和语法错误。 定期更新:保持一定的更新频率,让读者能够持续关注公众号,同时也有利于搜索引擎爬取和索引。 互动交流:积极与读者互动交流,回复评论和私信等,建立良好的用户关系,提高公众号的活跃度和口碑。可以通过在文章中添加互动引导、互动抽奖等方式来提高读者互动率。 公众号认证:微信公众号认证可以增加公众号的可信度和权威性,从而有助于提高搜索引擎排名。 内容标签:将文章按照主题分类做好标签,有利于搜索引擎更好地理解公众号的主题和内容。同时,也可以提高读者的阅读体验,让用户更容易找到自己感兴趣的内容。 最后 需要注意的是,微信公众号SEO并非一蹴而就的过程,需要长期坚持和不断优化。同时,也需要遵守微信公众平台的规定和要求,避免使用违规手段进行SEO优化。
2023-04-27 - Skyline|在小程序实现原生相册的效果
相册在日常生活中经常使用到,如手机自带相册、朋友圈、商品展示图、评论贴图等等,都经常用到相册的能力。 👇下面演示 iOS 原生相册、朋友圈等相册使用效果,我们可以看到图片切换非常顺滑,视觉焦点不变。 [图片] 😭 但是在小程序中,页面切换会有明显的切换感。用户焦点会丢失,缺少视觉关联性。 [图片] 共享元素🔥 为了丰富用户交互效果、提升用户体验、增强视觉关联性,小程序支持了页面间的共享元素 下图展示有无共享元素的页面切换效果,可以看出使用共享元素之后,转场动画更灵活 [图片] 共享元素 经常作用在图片上,例如上面示例中的相册效果,是那么共享元素动画要怎么实现呢? 在页面跳转时,两个页面 key 相同的 share-element 组件则会产生飞跃的过渡效果 [图片] 在上一篇文章中,我们学习了 页面转场动画,共享元素动画跟页面转场动画是类似的,同样是在页面切换间的动画。 动画进度、时间 与 路由进度、时间保持一致(非自定义路由也支持共享元素动画) 在共享元素飞跃的过程中,前后页面图片的裁剪方式(mode) 可能不一致 这种情况下容易导致图片突然跳变,所以我们需要在飞跃的过程中改变图片的大小来保证平滑飞跃 [图片] 在共享元素动画进行的过程中,share-element 可以收到 onFrame 表示动画帧回调 我们可以在帧回调中处理内部元素的显示 例如:我们这里通过在帧回调中改变图片宽高来达到平滑飞跃的效果 // .wxml // .js // 初始化 attached() { this.aspectRatio = shared(0) this.curRect = shared(undefined) // 绑定 worklet 动画 this.applyAnimatedStyle('.img', () => { 'worklet' const curRect = this.curRect.value return { left: `${curRect.left}px`, top: `${curRect.top}px`, width: `${curRect.width}px`, height: `${curRect.height}px` } }) }, // 获取图片初始宽高比 onImageLoad(e) { const { width, height } = e.detail this.aspectRatio.value = width / height }, // 动画帧回调,调整图片大小 onFrame(data) { 'worklet' // 当前帧容器的宽高、进度等信息 const { begin, end, progress, direction } = data ... // 根据图片初始宽高比、共享元素容器、动画进度等计算出变化过程中的值 this.curRect.value = { left = lerp(begin.left, end.left, t), top = lerp(begin.top, end.top, t), width = lerp(begin.width, end.width, t), height = lerp(begin.height, end.height, t), } } 更多共享元素动画原理请查看 官方文档 手势搭配打开图片之后,我们经常需要用到手势来操作图片,如缩放、移动、双击等等 [图片] 我们上次学过的 手势系统 又派上用场啦 通过监听手势事件配合 worklet 函数即可在小程序实现图片预览效果 👇 下面演示缩放手势的处理,除了缩放之外,相册在手势处理上还有很多复杂的逻辑,包括惯性、边界逻辑判断等 点击查看更多相册相关的手势操作 // .wxml // 绑定缩放手势 let sharedValues = this.sharedValues ?? [] // .js // 绑定缩放 this.applyAnimatedStyle('#image', () => { 'worklet' // worklet 函数,sharedValues 变化时,函数会立即执行 return { transform: `scale(${sharedValues[SCALE].value})` } }) // 监听缩放 onScale(evt) { 'worklet' // 连续的手势状态 && 双指放缩 if (evt.state === GestureState.ACTIVE && evt.pointerCount === 2) { // 计算出当前真正的缩放值 sharedValues[SCALE].value = evt.scale / sharedValues[TEMP_LAST_SCALE].value sharedValues[TEMP_LAST_SCALE].value = evt.scale } } 最后,我们来看下小程序实现出来的相册跟原生相册的使用对比,在小程序也可以顺滑的实现类原生的效果啦~ [图片] 目前,同程旅行 已经上线了共享元素结合手势的相册效果,mark这个 相册源码 直接接入到你的小程序吧~ [视频]
2023-08-03 - 2023-02-08
- 微信小程序开发快速入门-添加备忘录页面(4/5)
系列教程目录 [图片] 《微信小程序开发快速入门-小程序之开发前准备(1/5)》 《微信小程序开发快速入门-第一个页面编写(2/5)》 《微信小程序开发快速入门-列表页编写(3/5)》 在上一章节中我们实现了列表页面,那么列表页面的数据是从哪里来的呢?接下来这一章节我们就要接着完成备忘录,需求如下: 支持备忘录数据添加 支持备忘录数据修改 支持备忘录数据删除 当大家看到需求之后第一步是什么? 这个时候有同学肯定会说了:“开始编码!时间不等人!” 当然不是这样的,我们做一个事情时间很重要,但是不要站在单点去看时间。站在整个项目周期来看,先分解需求,然后才开始动手,思路清晰之后写起代码来其实是非常快速的。 先编写编辑页面布局(添加/修改复用页面) 编写数据添加逻辑 编写显示列表逻辑 编写编辑数据逻辑 编写删除数据逻辑 分解完任务就可以做起来。 1.添加/编辑页面 前期准备 首先我们先来新增一个页面。 新增文件夹取名 [图片] 新增Page [图片] 然后为了提高效率,新增编译模式。 添加编译模式 [图片] 选择好页面路径、名称 [图片] 大家要记住以上这两步是做任何页面都需要操作的步骤。上个章节也提过,我是怕大家忘记所以再次重新提一次。 配置标题 开始正式编写页面,先看下效果图 [图片] 从上往下看,相对我们的列表页面布局简单多了。我们先看下头部有个“编辑备忘录”,然后这个头部标题只有当前页面需要用到,所以这个配置需要在页面设置(json)里面配置。从页面配置文档里面我们可以找到“navigationBarTitleText”属性。 [图片] 当我们配置完标题之后,我们会发现还没没有达到我们预期的效果。 edit.json [代码]{ "navigationBarTitleText": "编辑备忘录" } [代码] [图片] 因为我们在app.json里面配置了导航栏背景颜色,所以还需要在调整下导航栏背景颜色以及文字样式,根据文档我们再做下调整。 [代码]{ "navigationBarTitleText": "编辑备忘录", "navigationBarBackgroundColor": "#ffffff", "navigationBarTextStyle": "black" } [代码] [图片] 三个配置轻松搞定!除此之外其实还有方法也可以达到同样的效果,那就是在js里面通过调用方法来设置。 代码设置的优先级要高于配置的优先级,从这可以看出来小程序渲染机制是先读json然后再去调用js的代码,所以js的方法覆盖的json的配置。 [图片] 看些关于设置导航的方法,有设置标题、样式、显示/隐藏进度条、隐藏返回首页按钮。当然标题和样式通常来说还是在json里面配置,只不过我们要知道有这种方式。 [图片] 页面样式 解决完导航栏之后,我们可以正式进入开始布局。 [图片] 整体布局就4个组件,前面4个组件分别用text(时间)、view(分界线)、input(标题,单行输入框)、textarea(内容,多行输入框)组件上个从上往下排列,然后保存按钮可以用个view组件来做,做个对底部的定位。 view、text组件在我们做列表页面就接触过了它们分别属于容器组件和基础内容组件,而input、textarea属于表单组件用于收集信息的,更多表单组件可以查看表单组件文档。 先输出布局结构: [代码]<view> <!-- 时间 --> <text>2018-07-28 00:00</text> <!-- 标题 --> <input>请输入标题</input> <!-- 分界线 --> <view></view> <!-- 内容 --> <textarea>请输入内容</textarea> </view> <!-- 保存 --> <view>保存</view> [代码] [图片] 大家仔细看下,这里面是不是有个问题。我们会发现input标签中间输入的文字并没有显示,并且我们其实不是单纯的显示文字,而是需要默认提示文字,当用户输入文字后默认提示文字自动隐藏。在input里面显示默认文字不能用text和view这种标签包裹内容的方式先实现,而是它有自己特有的属性。 虽然textarea组件中间可以输入文字,但是也无法达到输入隐藏默认文字的效果,并且输入文字还会叠在中间的文本上,所以textarea不能使用这种方式。 [图片] 鼠标悬浮到组件上,打开input文档我们来找找看。通过文档我们发现“placeholder”属性。同理textarea也一样。 [图片] 通过设置完成后就达到了我们想要的效果: [代码]<view> <!-- 时间 --> <text>2018-07-28 00:00</text> <!-- 标题 --> <input placeholder="请输入标题" ></input> <!-- 分界线 --> <view></view> <!-- 内容 --> <textarea placeholder="请输入内容"></textarea> </view> <!-- 保存 --> <view>保存</view> [代码] [图片] 完成基础结构布局后,我们再来写写样式。 [图片] [代码]<view class="container"> <!-- 时间 --> <text class="date">2018-07-28 00:00</text> <!-- 标题 --> <input class="title-font-size" placeholder-class="input-placeholder title-font-size" placeholder="请输入标题" maxlength="20"></input> <!-- 分界线 --> <view class="line"></view> <!-- 内容 --> <textarea class="content-font-size" placeholder-class="input-placeholder content-font-size" placeholder="请输入内容" maxlength="1000"></textarea> </view> <!-- 保存 --> <view class="save">保存</view> [代码] 从布局结构上来看,我们没有变化,但是我们会发现和之前不一样的地方。 看到输入框组件,有个“placeholder-class”的属性,这个是影响placeholder(占位符)的样式。因为对于css来说它无法区分控制在什么状态下,但是又有对同一个组件不同状态下的需求,所以通过不同属性的设置来分别设置class,正常输入状态下我们使用的是class,占位符样式下是设置placeholder-class。 除此之外有有个“maxlength”属性,它是控制最大长度,默认为140。我们就需要根据具体业务需求要定制这个限制,如:标题最多20,内容区域最多1000。当然还可以设置无限制,设置“-1”即可。 再仔细观察下,我在“placeholder-class”属性中用到了多个class使用空格分开,这种用法同样适用于class,当你一个组件需要用到多个样式的时候就可以这样写,这种写法也是常用写法。通常来说有些样式是通用的就会抽取出来,然后个性化的样式就单独写,这样提高了样式的复用性。 然后我们再看看css部分,可以看到input-placeholder这个样式就是两个输入框通用的部分,title-font-size和content-font-size就是不两个输入框不一样的地方。 [代码]/* 容器 */ .container { margin: 30rpx 30rpx; } /* 时间 */ .date { font-size: 24rpx; font-weight: 400; color: #999999; } /* 分割线 */ .line { width: 690rpx; height: 1rpx; background: #E9E9E9; margin: 20rpx 0rpx; } /* 占位符通用样式 */ .input-placeholder { font-weight: 400; color: #CCCCCC; } /* 标题字体大小 */ .title-font-size { font-size: 32rpx; } /* 内容字体大小 */ .content-font-size { font-size: 28rpx; } /* 保存按钮 */ .save { width: 510rpx; height: 110rpx; background: #F8D300; border-radius: 20rpx; position: absolute; left: 120rpx; bottom: 115rpx; color: #FFFFFF; text-align: center; line-height: 110rpx; } [代码] 搞定!虽然看着简单但是涉及到了很多知识点。 小结 导航栏配置,两种方法 json配置 API设置 表单组件输入框 CSS 使用小技巧 数据添加 先来分析下如何实现: 获取当前时间 获取输入框内容 添加时做数据校验 保存数据到本地缓存 1. 获取当前时间 我们需要在用户进入这个页面的时候就把当前时间现在出来,这里就需要了解生命周期的概念了。我们会发现每次我们新建一个Page的时候都会有一些默认的函数,里面就包含生命周期函数。 [代码]// pages/t/t.js Page({ /** * 页面的初始数据 */ data: { }, /** * 生命周期函数--监听页面加载 */ onLoad: function (options) { console.log('onLoad','监听页面加载') }, /** * 生命周期函数--监听页面初次渲染完成 */ onReady: function () { console.log('onReady','监听页面初次渲染完成') }, /** * 生命周期函数--监听页面显示 */ onShow: function () { console.log('onShow','监听页面显示') }, /** * 生命周期函数--监听页面隐藏 */ onHide: function () { console.log('onHide','监听页面隐藏') }, /** * 生命周期函数--监听页面卸载 */ onUnload: function () { console.log('onUnload','监听页面卸载') }, }) [代码] 我们使用console.log打印一下,分别在不同的生命周期打印一下。 当用户进入一个页面的时候,小程序会调用用下面的顺序调用三个生命周期函数: [图片] 当这个页面被覆盖的时候会调用 onHide(监听页面隐藏)如:从A页面跳转到B页面,A页面的onHide就会被调用。 如果用户再从B页面退回到A页面的时候就会触发页面的onShow(监听页面显示)。 当页面被关闭的时候就会调用onUnload(监听页面卸载)。 那么什么时候会被关闭什么时候又不会被关闭呢? 还记得我们之前学习的路由API吗? 这个取决于你的路由API的模式: 使用wx.redirectTo会关闭当前页,这种情况下A页面到B页面,A跳转到时候就会调用onUnload(监听页面卸载)。 使用wx.navigateTo会保留当前页,这种情况下A页面到B页面,A跳转到B时候就会onHide(监听页面隐藏)并且返回会触发A页面的onShow(监听页面显示)。这里要注意保留小程序中页面栈最多十层。 PS:页面栈里面就是存放所有未关闭的页面。 以上内容很重要,要理解并且吸收在后续很多场景会用到这个知识点。 知道了生命周期之后,所以我们需要在onLoad函数里面编写获取时间的代码。将时间获取完成后setData更新页面,在这里使用Date对象的自带方法。 [代码]// pages/edit/edit.js Page({ /** * 页面的初始数据 */ data: { }, /** * 生命周期函数--监听页面加载 */ onLoad: function (options) { this.setData({ nowDate: this.getNowDate(), nowTime: this.getNowTime() }) }, // 获取当前日期 getNowDate: function () { let dateTime let YYYY = new Date().getFullYear() let MM = new Date().getMonth() + 1 let DD = new Date().getDate() dateTime = YYYY + '-' + MM + '-' + DD return dateTime }, // 获取当前时间 getNowTime: function () { let dateTime let HH = new Date().getHours() let mm = new Date().getMinutes() < 10 ? '0' + new Date().getMinutes() : new Date().getMinutes() dateTime = HH + ':' + mm return dateTime } }) [代码] 然后在页面上进行显示。这里要注意如果data里面没有先定义变量名也可以进行赋值,系统会自动创建一个变量然后再进行赋值,只是在data里面看不见,可以在调试器中的AppData里面看到。虽然可以这样做,但是建议最好在data里面先定义,这样代码可读性会更好。 [代码]view class="container"> <!-- 时间 --> <text class="date">{{nowDate}} {{nowTime}}</text> <!-- 省略无关内容 --> </view> [代码] [图片] 时间显示搞定了,接下来获取下输入框的内容 2. 获取输入框内容 想要获取输入框内容需要用到bindinput事件。 [图片] [代码]<view class="container"> <!-- 省略无关布局 --> <!-- 标题 --> <input bindinput="bindTitle" class="title-font-size" placeholder-class="input-placeholder title-font-size" placeholder="请输入标题" maxlength="20"></input> <!-- 分界线 --> <view class="line"></view> <!-- 内容 --> <textarea class="content-font-size" placeholder-class="input-placeholder content-font-size" placeholder="请输入内容" maxlength="1000"></textarea> </view> [代码] 监听了键盘触发时间,触发对应js文件中的bindTitle方法 [代码]bindTitle(event){ console.log(event) } [代码] 我们在js里面用console.log输出下内容。这里讲一下console,这个类特别常用的,用于开发过程中打印调试日志。console支持打印不同级别的日志,这个对应我们调试器中的console面板,面板还可以过滤不同级别的日志。我想你一定对这里不陌生,因为同样系统报错信息也会输出到Console日志区。 [图片] 不同级别的console面板显示的样式也不一样。如:普通、警告、错误日志这三种类型。 [代码]bindTitle(event){ console.log(event) console.warn(event) console.error(event) } [代码] [图片] 通常我们调试只需要用console.log就行了,还支持输出多个参数用“,”隔开。 接着上面的继续,我们输入input里面的内容。当我输入“123”的时候触发了3次,输出这个回调参数,找到我们想要的value,打印出来。这样就获取到我们的标题内容了,内容输入框一样。获得到之后我们还要存储在data里面,便于保存的时候使用。 [图片] 代码如下: 标题和内容分别绑定监听,然后在js能够获取到用户输入的内容,并且存储在data里面。 [代码]<view class="container"> <!-- 省略无关布局 --> <!-- 标题 --> <input bindinput="bindTitle" class="title-font-size" placeholder-class="input-placeholder title-font-size" placeholder="请输入标题" maxlength="20"></input> <!-- 分界线 --> <view class="line"></view> <!-- 内容 --> <textarea bindinput="bindContent" class="content-font-size" placeholder-class="input-placeholder content-font-size" placeholder="请输入内容" maxlength="1000"></textarea> </view> [代码] [代码]// pages/edit/edit.js Page({ /** * 页面的初始数据 */ data: { title: null, // 标题 content: null,// 内容 }, // 监听标题 bindTitle(event) { this.data.title = event.detail.value }, // 监听内容 bindContent(event) { this.data.content = event.detail.value } }) [代码] 给data里面的数据赋值需要用this.data.变量名。 3. 添加时做数据校验 首先要给按钮绑定点击事件。 [代码]<!-- 省略无关代码 --> <!-- 保存 绑定保存事件--> <view bindtap="save" class="save">保存</view> [代码] 当用户点击保存的时候去判断是否为空。在下面的代码中我抽取出来了一个判空函数,对不用户不输入任何参数与空格做个判断。在这里注意写代码的时候如果一段代码在多处使用就需要单独抽取出来,减少重复代码。 从上往下看如果标题为空我就使用return这样代码就不会往下走了,只有标题和内容都不为空的时候这样才能提交数据。 [代码]// 保存数据 save() { let title = this.data.title let content = this.data.content if (this.isEmpty(title)) { console.log('标题不能为空!') return } if (this.isEmpty(content)) { console.log('内容不能为空!') return } console.log('提交数据') }, // 判断字符串是否为空 isEmpty(str) { if (str != null && str.trim().length > 0) { return false; } return true; } [代码] 以上代码从逻辑上来看数据校验没有什么问题,但是实际上对于用户的交互就很问题。因为我在需要提示用户的时候使用的是console.log,这个只适合给开发人员调试看而实际用户是看不到的。在这里需要给用户一个提示,这个时候就需要用到showToast方法,给用户一个提示。 然后我们替换掉log部分代码 [代码]// 保存数据 save() { let title = this.data.title let content = this.data.content if (this.isEmpty(title)) { wx.showToast({ title: '标题不能为空', icon: 'error', duration: 2000 }) return } if (this.isEmpty(content)) { wx.showToast({ title: '内容不能为空', icon: 'error', duration: 2000 }) return } wx.showToast({ title: '提交成功', icon: 'success', duration: 2000 }) }, // 判断字符串是否为空 isEmpty(str) { if (str != null && str.trim().length > 0) { return false; } return true; } [代码] [图片] 现在数据有了,接下来就把它存起来。 4. 存放数据缓存 小程序数据缓存管理需要用到Storage操作。 首先先检查本地是否有缓存数据,如果有就拿出来已有数据,否则新建数组,新增数据。由于是多条数据所以要用到js中的数组,在维护数组的过程中会用到很多数组方法。 [代码]// 保存数据 save() { let title = this.data.title let content = this.data.content // 省略校验代码 // 获取缓存数据,如果没有回返回[],如果有返回已有数据 let list = wx.getStorageSync('list')||[] // 组装数据对象 let data = { title: title, content: content, date: this.getNowDate(), time: this.getNowTime() } // 在开头插入到数组中 list.unshift(data) // 设置到本地缓存 wx.setStorageSync('list', list) // 省略提示代码 // 回到上一页面 wx.navigateBack() }, [代码] 存储成功后,我们可以在调试器中的Storage面板看到数据,保存成功后回到列表页面。 [图片] 如果想清空本地缓存可以用「清楚缓存」中的清除数据缓存。 [图片] 保存成功后回到列表页面。 在此之前先把列表页面跳转路由设置下。 [代码]<view> <!-- 省略无关代码 --> <view bindtap="toEdit" class="content-btn">+ 新建</view> <!-- 省略无关代码 --> <!-- 添加icon --> <image wx:if="{{!isEmpty}}" bindtap="toEdit" class="write" src="../../images/write.png"></image> </view> [代码] [代码]toEdit(){ wx.navigateTo({ url: '/pages/edit/edit', }) } [代码] 在这里补充只要是路径参数就可以用绝对路径和相对路径。 绝对路径就是文件的真正存在的路径,进行一级级目录指向文件。 相对路径就是以当前文件为基准进行一级级目录指向被引用的资源文件。 …/ 表示当前文件所在的目录的上一级目录 以这个项目结构为例,列表页跳转到编辑页 [图片] 绝对路径:/pages/edit/edit 相对路径:…/edit/edit PS:不仅仅是页面路径,图片路径同理 数据显示 1. 列表显示 接下来看下用户操作路径,从列表页到编辑页面添加,然后添加完成之后就会刷新列表。这个时候就会调用页面生命周期的onShow方法,所以我们需要在在onShow方法里面获取缓存数据并且更新isEmpty。在这里要注意一定要更新isEmpty,否则就算列表有值也显示不出来,isEmpty控制了是否显示列表。 [代码]onShow(){ let list = wx.getStorageSync('list')||[] this.setData({ list:list, isEmpty:!list.length>0 }) }, [代码] 2. 查看单条数据 首先给列表子项绑定点击事件,然后通过设置data-*进行点击传值。设置了一个data-index将用户点击到当前列表下标传到toEdit方法中。 [代码]<view bindtap="toEdit" data-index="{{index}}" class="list" wx:for="{{list}}"> <view> <text class="list-title">{{item.title}}</text> <text class="list-date">{{item.date}} \n {{item.time}}</text> </view> <view class="list-content">{{item.content}}</view> <view class="line"></view> </view> [代码] 获取参数方法 [代码]toEdit(event){ console.log(event) let index = event.currentTarget.dataset.index console.log(index) } [代码] [图片] 在这里要注意由于从新建按钮点击跳转到编辑页面和点击列表中数据是不一样的数据逻辑,所以要找个方式区分开来。 经过日志调试输出,发现如果是从新建按钮点击进来index就获取不到,列表点击就能获取到下标通常是等于0或者大于0,这样我们就可以做个判断。如果是列表进入的进入编辑页面就填充好点击当前的数据。 [代码]toEdit(event) { let index = event.currentTarget.dataset.index let isClickItem = index >= 0 if (isClickItem) { // 从列表点击 wx.navigateTo({ url: '/pages/edit/edit?index=' + index, }) } else { // 从新建按钮点击 wx.navigateTo({ url: '/pages/edit/edit', }) } } [代码] 如果点击了列表项就将下标数据传递到下个页面。补充页面跳转传参:参数与路径之间使用 ? 分隔,参数键与参数值用 = 相连,不同参数用 & 分隔;如 ‘path?key=value&key2=value2’,更多传参数方式可以看《微信小程序如何实现页面传参?》 在跳转到下个页面onLoad函数中只要获取到index参数就确认是修改并取出数据。取值补充:在onLoad函数options参数中获取对应的变量名数据。 [代码]onLoad: function (options) { if (options.index) { // 显示已有数据 let list = wx.getStorageSync('list') let item = list[options.index] this.setData({ title: item.title, content: item.content, nowDate: item.date, nowTime: item.time, index: options.index }) } else { // 显示最新数据 this.setData({ nowDate: this.getNowDate(), nowTime: this.getNowTime() }) } }, [代码] [代码]<view class="container"> <!-- 时间 --> <text class="date">{{nowDate}} {{nowTime}}</text> <!-- 标题 --> <input value="{{title}}" bindinput="bindTitle" class="title-font-size" placeholder-class="input-placeholder title-font-size" placeholder="请输入标题" maxlength="20"></input> <!-- 分界线 --> <view class="line"></view> <!-- 内容 --> <textarea value="{{content}}" bindinput="bindContent" class="content-font-size" placeholder-class="input-placeholder content-font-size" placeholder="请输入内容" maxlength="1000"></textarea> </view> <!-- 保存 --> <view bindtap="save" class="save">{{index?'修改':'保存'}}</view> [代码] 双括号中支持js表达式 {{index?‘修改’:‘保存’}} 就是js中的三元表达式,类似于if else效果。 查看数据详情就已经完成了,接下来就开始进行数据修改了 数据修改 当用户点击保存的时候去判断是否存在item这个对象,如果存在就是修改否则就是新增。修改本身是两个操作:删除当前数据,然后插入一条新数据。因为插入数据新增也需要做这个操作,所以实际上只需要在新增之前加一个判断,如果是修改就删除掉当前数据即可。 [代码]// 保存数据 save() { // 省略无关代码 // 如果有下标就是修改 if (this.data.index) { // 删除 list.splice(this.data.index, 1) } // 在开头插入到数组中 list.unshift(data) // 设置到本地缓存 wx.setStorageSync('list', list) // 省略提示代码 // 回到上一页 wx.navigateBack() }, [代码] 数据删除 列表项目绑定长按事件(bindlongtap) [代码]<view bindtap="toEdit" bindlongtap="del" data-index="{{index}}" class="list" wx:for="{{list}}"> <view> <text class="list-title">{{item.title}}</text> <text class="list-date">{{item.date}} \n {{item.time}}</text> </view> <view class="list-content">{{item.content}}</view> <view class="line"></view> </view> [代码] [代码]// 删除 del(event) { let that = this let index = event.currentTarget.dataset.index wx.showModal({ title: '提示', content: '你确定删除?', success(res) { if (res.confirm) { that.data.list.splice(index, 1) wx.setStorageSync('list', that.data.list) that.setData({ list: that.data.list, isEmpty:!that.data.list.length>0 }) } } }) }, [代码] 长按删除之前还需要弹出对话框让用户进行二次确认,对话框就需要用到showModal方法。 [图片] 确认后需要更新下页面数据以及状态。我们会发现更新数据出现过两次一样的代码,那这个时候其实我们可以抽象出一个函数,叫做udpateList。 [代码]onShow() { // 添加后更新数据 let list = wx.getStorageSync('list') || [] this.udpateList(list) }, // 删除 del(event) { let that = this let index = event.currentTarget.dataset.index wx.showModal({ title: '提示', content: '你确定删除?', success(res) { if (res.confirm) { that.data.list.splice(index, 1) wx.setStorageSync('list', that.data.list) that.udpateList(that.data.list) } } }) }, // 更新列表数据 udpateList(list){ this.setData({ list: list, isEmpty:!list.length>0 }) }, [代码] 搞定! 总结 这一章节学完我们基本就完成了本地版备忘录的所有功能了,从单个页面到多个页面交互以及数据的增删改查都完成了。 下一章节就是优化之前的代码,要知道没有最好只有更好,我们要持续成长,才能成为更好的自己。 如果觉得不错,可以给本文点赞,收藏,分享,在学习过程中有任何问题可以留言给我,包教包会。
2023-02-07 - 云开发短信跳小程序(自定义开发版)教程
写在前面如果你想要自主开发,但没有云开发相关经验,可以采用演示视频来学习本教程: [视频] 一、能力介绍境内非个人主体的认证的小程序,开通静态网站后,可以免鉴权下发支持跳转到相应小程序的短信。短信中会包含支持在微信内或微信外打开的静态网站链接,用户打开页面后可一键跳转至你的小程序。 这个链接的网页在外部浏览器是通过 URL Scheme 的方式来拉起微信打开主体小程序的。 总之,短信跳转能力的实现分为两个步骤,「配置拉起网页」和「发送短信」。本教程将介绍如何执行操作完成短信跳转小程序的能力。 如果你想要无需写代码就能完成短信跳转小程序的能力,可以参照无代码版教程进行逐步实现。 二、操作指引1、网页创建首先我们需要构建一个基础的网页应用,在任何代码编辑器创建一个 html 文件,在教程这里命名为 index.html 在这个 html 文件中输入如下代码,并根据注释提示更换自己的信息: window.onload = function(){ window.web2weapp.init({ appId: 'wx999999', //替换为自己小程序的AppID gh_ID: 'gh_999999',//替换为自己小程序的原始ID env_ID: 'tcb-env',//替换小程序底下云开发环境ID function: { name:'openMini',//提供UrlScheme服务的云函数名称 data:{} //向这个云函数中传入的自定义参数 }, path: 'pages/index/index.html' //打开小程序时的路径 }) } 以上引入的 web2weapp.js 文件是教程封装的有关拉起微信小程序的极简应用,我们直接引用即可轻松使用。 如果你想进一步学习和修改其中的一些WEB展示信息,可以前往 github 获取源码并做修改。 有关于网页拉起小程序的更多信息可以访问官方文档 如果你只想体验短信跳转功能,在执行完上述文件创建操作后,继续以下步骤。 2、创建服务云函数在上面创建网页的过程中,需要填写一个UrlScheme服务云函数。这个云函数主要用来调用微信服务端能力,获取对应的Scheme信息返回给调用前端。 我们在示例中填写的是 openMini 这个命名的云函数。 我们前往微信开发者工具,定位对应的云开发环境,创建一个云函数,名称叫做 openMini 。 在云函数目录中 index.js 文件替换输入以下代码: const cloud = require('wx-server-sdk') cloud.init() exports.main = async (event, context) => { return cloud.openapi.urlscheme.generate({ jumpWxa: { path: '', // 打开小程序时访问路径,为空则会进入主页 query: '',// 可以使用 event 传入的数据制作特定参数,无需求则为空 }, isExpire: true, //是否到期失效,如果为true需要填写到期时间,默认false expire_time: Math.round(new Date().getTime()/1000) + 3600 //我们设置为当前时间3600秒后,也就是1小时后失效 //无需求可以去掉这两个参数(isExpire,expire_time) }) } 保存代码后,在 index.js 右键,选择增量更新文件即可更新成功。 接下来,我们需要开启云函数的未登录访问权限。进入小程序云开发控制台,转到设置-权限设置,找到下方未登录,选择上几步我们统一操作的那个云开发环境(注意:第一步配置的云开发环境和云函数所在的环境,还有此步操作的环境要一致),勾选打开未登录 [图片] 接下来,前往云函数控制台,点击云函数权限,安全规则最后的修改,在弹出框中按如下配置: [图片] 3、本地测试我们在本地浏览器打开第一步创建的 index.html ;唤出控制台,如果效果如下图则证明成功! 需要注意,此处本地打开需要时HTTP协议,建议使用live server等扩展打开。不要直接在资源管理器打开到浏览器,会有跨域的问题! [图片] 4、上传本地创建好的 index.html 至静态网站托管将本地创建好的 index.html 上传至静态网站托管,在这里静态托管需要是小程序本身的云开发环境里的静态托管。 如果你上传至其他静态托管或者是服务器,你仍然可以使用外部浏览器拉起小程序的能力,但会丧失在微信浏览器用开放标签拉起小程序的功能,也不会享受到云开发短信发送跳转链接的能力。 如果你的目标小程序底下有多个云开发环境,则不需要保证云函数和静态托管在一个环境中,无所谓。 比如你有A、B两个环境,A部署了上述的云函数,但是把 index.html 部署到B的环境静态托管中了,这个是没问题的,符合各项能力要求。只需要保证第一步 index.html 网页中的云开发环境配置是云函数所在环境即可。 部署成功后,你便可以访问静态托管的所在地址了,可以通过手机外部浏览器以及微信内部浏览器测试打开小程序的能力了。 5、短信发送云函数的配置在上面创建 openMini 云函数的环境中再来一个云函数,名字叫 sendsms 。 在此云函数 index.js 中配置如下代码: const cloud = require('wx-server-sdk') cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV, }) exports.main = async (event, context) => { try { const config = { env: event.env, content: event.content ? event.content : '发布了短信跳转小程序的新能力', path: event.path, phoneNumberList: event.number } const result = await cloud.openapi.cloudbase.sendSms(config) return result } catch (err) { return err } } 保存代码后,在 index.js 右键,选择增量更新文件即可更新成功。 6、测试短信发送能力在小程序代码中,在 app.js 初始化云开发后,调用云函数,示例代码如下: App({ onLaunch: function () { wx.cloud.init({ env:"tcb-env", //短信云函数所在环境ID traceUser: true }) wx.cloud.callFunction({ name:'sendsms', data:{ "env": "tcb-env",//网页上传的静态托管的环境ID "path":"/index.html",//上传的网页相对根目录的地址,如果是根目录则为/index.html "number":[ "+8616599997777" //你要发送短信的目标手机,前面需要添加「+86」 ] },success(res){ console.log(res) } }) } }) 重新编译运行后,在控制台中看到如下输出,即为测试成功: [图片] 你会在发送的目标手机中收到短信,因为短信中包含「退订回复T」字段,可能会触发手机的自动拦截机制,需要手动在拦截短信中查看。 需要注意:你可以把短信云函数和URLScheme云函数分别放置在不同云开发环境中,但必须保证所放置的云开发环境属于你操作的小程序 另外,出于防止滥用考虑,短信发送的云调用能力需要真实小程序用户访问才可以生效,你不能使用云端测试、云开发JS-SDK以及其他非wx.cloud调用方式(微信侧WEB-SDK除外),会提示如下错误: [图片] 如果你想在其他处使用此能力,可以使用服务端API来做正常HTTP调用,具体访问官方文档 7、查看短信监控图表进入 云开发控制台 > 运营分析 > 监控图表 > 短信监控,即可查看短信监控曲线图、短信发送记录。 [图片] 三、总结短信跳转小程序核心是静态网站中配置的可跳转网页,外部浏览器通过URL Scheme 来实现的,这个方式不适用于微信浏览器,需要使用开放标签才可以URL Scheme的生成是云调用能力,需要是目标小程序的云开发环境的云函数中使用才可以。并且生成的URL Scheme只能是自己小程序的打开链接,不能是任意小程序(和开放标签的任意不一致)短信发送能力的体验是每个有免费配额的环境首月100条,如有超过额度的需求可前往开发者工具-云开发控制台-对应按量付费环境-资源包-短信资源包,进行购买。如当前资源包无法满足需求也可通过云开发 工单 提交申请[图片]短信发送也是云调用能力,需要真实小程序用户调用才可以正常触发,其他方式均报错返回参数错误,出于防止滥用考虑云函数和网页的放置可以不在同一个环境中,只需要保证所属小程序一致即可。(需要保证对应环境ID都能接通)如果你不需要短信能力,可以忽略最后两个步骤CMS配置渠道投放、数据统计可参考官方文档
2021-04-07 - 微信开发者工具 1.02.1909051 RC 更新说明
下载地址 Windows 64 、 Windows 32 、 macOS已知问题 本地预览正常,真机预览空白;尝试关闭 项目详情 - 本地设置 - 代码保护;将在最近的 nightly 和 RC 版本修复 1. 本地编译时进行合并编译本地编译时使用合并编译可以加快小程序加载的速度, 通过 [代码]项目详情 - 本地设置 - 本地编译时进行合并编译[代码] 可以打开此功能 [图片] 使用合并编译后,主包的 js 文件会被打包成一个 [代码]__APP__/appservice.js[代码] 文件 [图片] 合并编译对于错误调试暂时还是不友好的,异步报错从控制台中点击会先跳转到合并后的文件,而如果合并后的文件过大会导致调试器卡顿 2. PC 微信开发版小程序自动预览PC 微信内测版 支持小程序 开发者工具 [代码]设置 - 通用设置 - 启用 PC 端自动预览[代码],可以将开发版小程序通过自动预览推送到同登录态的 PC 微信上 [图片] 3. 自动真机调试增加自动真机调试功能,减少真机调试扫码的交互 [图片] 4. 使用测试号进行多帐号调试在 [代码]菜单 - 工具 - 多帐号调试[代码],可以打开多帐号调试帐号管理面板 本次为所有 appid 添加了 4 个测试帐号,可以使用测试号进行多帐号调试 [图片] 5. 支持多线程 [代码]worker[代码] 的单步调试本次更新优化了多线程 [代码]worker[代码] 的编译和代码加载的方式,从而支持了单步调试 [图片] 6. 公众号网页调试增加 url 收藏功能之前只有通过地址栏历史记录的匹配交互来快速调试常用页面,本次新增了收藏功能,更加便利 [图片] 7. 云开发套餐支持代金券支付在云开发控制台支付套餐时,可以选择使用代金券进行支付 [图片] 8. 新增周期性更新调试拉取周期性数据由于微信客户端每隔 12 个小时才会发起一次请求,调试周期性更新功能会显得不太方便。 目前新增能够在开发者工具上调试整个流程,操作路径为点击菜单 [代码]工具 -> 拉取周期性缓存数据[代码], 点击后开发者工具会立即向配置的数据下载地址请求数据,如下图所示: [图片] 清除周期性数据如果需要清除工具缓存的周期性数据,可以通过点击工具栏的 [代码]清除数据缓存[代码] 或者 [代码]全部清除[代码] 来进行清除。 9. 支持小游戏关系链互动数据开发在小游戏项目 [代码]project.config.json[代码] 中指定 [代码]jsserverRoot[代码] 为项目内目录,可以在该目录下进行小游戏关系链互动数据开发,右键支持直接上传到开发环境或正式环境,或者对比文件 [图片] 10. 小游戏节点审查插件通过 [代码]菜单 - 工具 - 插件[代码] 打开插件面板,可以添加小游戏节点审查调试器插件 [图片] 添加插件后重启工具,选择 [代码]Game Inspector[代码] 调试器面板,支持使用主流游戏引擎的小游戏获取当前游戏场景下的节点树信息 [图片]
2019-09-09