- 【流量主求助贴】接近200W用户的小程序,广告收益差点不够服务器费用,真就这么离大谱?
我是一个人开发者,业余时间开发了小程序,还不容易积攒了200多万的用户。 但是现在就很为难了。先请教官方或者运营大神,如何提高收入?! 以前,开发云还是免费,那是一天不到1k用户,小程序广告也就加了两个,每月也能收到接近1K块钱的广告费,那时候,就幻想要有1w用户每天,是不是就有1W的收益。没想到现在都已经4W用户每天,比之前都翻了40倍!但是,收入还是1k不到。真的蓝瘦香菇!! 小程序是个人的,只有流量主的广告收入,开发云服务器费用居然快要超过流量主的广告收益,现在,都不知道要怎么样做商业化? 广告收益: 广告单价一直降,有最高的14块钱降到不到1块钱,直接降到14倍。 19年 eCPM:6.04 元 20年 eCPM:14.82元 21年 eCPM:6.95 元 22年 eCPM:1.78元 23年 eCPM:0.98 元 [图片] [图片] [图片] [图片] [图片] 小程序状况: [图片] 上个月云开发费用 (主要是调用量超太多了,在开发云收费后,已经特意做了代码优化,在之前的基础上已经减了50%,现在,只剩必要的调用量) [图片] 顶配套餐(团队版) 999 元每月 资源包 :400 + 400 + 1500 (购买了三次调用量) 10月份总费用 3299 ,广告收益 4300,收入还是1K多。 这两年,明明发布了很多版本,付出了不少努力,用户量也翻了好几倍,但是,实际结果是收入还不如两年前一样。心生无力!!!
2023-11-06 - 从源码看微信小程序启动过程
一、写作背景接触小程序一年多,真实体验就是小程序开发门槛相对而言确实比较低。不过小程序的开发方式,一直是开发者吐槽的,如习惯了 Vue,React 开发的开发者经常会吐槽小程序一个 Page 必须由多个文件组成,组件化支持不完善或者说不能非常愉快的开发组件。在以前小项目中没太大感觉,从加入有赞,参与有赞微商城小程序的开发,是真切的体会到对于大型小程序项目开发的复杂性。 有赞从微信小程序内测就开始开发小程序,在不支持自定义组件的时代,只能通过 import 的形式拆分模块或实现组件。在业务复杂的页面,可能会 import 非常多的模块,而相应的 wxss 也需要 import 样式,除了操作繁琐,有时候也难免遗漏。 作为开发者,我们当然希望可以让工作更简单,更愉快,也希望改善我们的开发方式。所以希望能够更了解微信小程序框架,减少不必要的试错,于是有了一次对小程序框架的 debug 之旅。(基础库 1.9.93) 通过三周空余时间的 debug,也算对小程序框架有了一些浅显的认识,达到了最初的目的;对小程序启动,实例,运行等有了真切的体会。这篇文章记录了小程序框架的基本代码结构,启动流程,以及程序实例化过程。 本文的目的是希望把我看到的分享给对小程序感兴趣或者正在开发小程序的读者,主要解答“框架对传入的对象等到底做了什么”。 二、从启动流程一窥小程序框架细节在开发者工具中使用 help() 方法,可以查看一些指令和方法。使用其中的 openVendor 方法可以打开微信开发者工具在小程序框架所在目录。其中以包括以基础库命名的目录和其他帮助文件,如其中有两个工具 wcc,wcsc。wcc 可把 wxml 转换为对应的 JS 函数 —— $gwx(path, global),wcsc 可将 wxss 转换为 css。而基础库目录包括 WAService.js 和 WAWebview.js 文件。小程序框架在开发者工具中以 WAService.js 命名(WAWebview.js 不知其作用,听说在真机环境使用该文件)。 在开发中工具命令行使用 document.head 可以查看到小程序的启动流程大致如下: [图片] 以小节的方式分别介绍这些流程,小程序是如何处理的(小节编号与图中编号相同)。 1、初始化全局变量下图是小程序启动是初始化的一些全局的变量: [图片] 那些使用“__”开头,未在文档中提及可使用变量是不建议使用的,__wxAppCode__ 在开发者工具中分为两类值,json 类型和 wxml 类型。以 .json 结尾的,其 key 值为开发者代码中对应的 json 文件的内容,.wxml 结尾的,其 key 值为通过调用 $gwx('./pages/example/index.wxml') 将得到一个可执行函数,通过调用这个函数可得到一个标识节点关系的 JSON 树。 [图片] 2、加载框架(WAService.js)使用工具对 WAService.js 进行格式化后进行 debug。可以发现小程序框架大致由: [代码]WeixinJSBridge[代码]、 [代码]NativeBuffer[代码]、 [代码]wxConsole[代码]、 [代码]WeixinWorker[代码]、 [代码]JavaScript兼容[代码](这部分为猜测)、 [代码]Reporter[代码]、 [代码]wx[代码]、 [代码]exparser[代码]、 [代码]__virtualDOM__[代码]、 [代码]__appServiceEngine__[代码] 几部分组成。 其中除了 [代码]wx[代码] 和 [代码]WeixinJSBridge[代码] 这两个基础 API 集合, [代码]exparser[代码], [代码]__virtualDOM__[代码], [代码]__appServiceEngine__[代码] 这三部分作为框架的核心, [代码]__appServiceEngine__[代码] 提供了框架最基本的接口如 App,Page,Component; [代码]exparser[代码] 提供了框架底层的能力,如实例化组件,数据变化监听,view 层与逻辑层的交互等;而 [代码]__virtualDOM__[代码] 则起着链接 [代码]__appServiceEngine__[代码] 和 [代码]exparser[代码] 的作用,如对开发者传入 Page 方法的对象进行格式化再传入 exparser 的对应方法处理。 框架对外暴露了以下API:Behavior,App,Page,Component,getApp,getCurrentPages,definePlugin,requirePlugin,wx。 3、业务代码的加载在小程序中,开发者的 JavaScript 代码会被打包为 define( 'xxx.js' , function ( require , module , exports, window, document, frames, self , location, navigator, localStorage, history, Caches , screen, alert, confirm, prompt, fetch, XMLHttpRequest , WebSocket , webkit, WeixinJSCore , Reporter , print , WeixinJSBridge ) { 'use strict' ; // your code }) 这里的 define 是在框架中定义的方法,在框架中提供了两个方法:require 和 define 用来定义和使用业务代码。其方式有些像 AMD 规范接口,通过 define 定义一个模块,使用 require 来应用一个模块。但是也有很大区别,首先 define 限制了模块可使用的其他模块,如 window,document;其次 require 在使用模块时只会传入 require 和 module,也就是说参数中的其他模块在定义的模块中都是 undefined,这也是不能在开发者工具中获取一些浏览器环境对象的原因。 在小程序中,JavaScript 代码的加载方式和在浏览器中也有些不同,其加载顺序是首先加载项目中其他 js 文件(非注册程序和注册页面的 js 文件),其次是注册程序的 app.js,然后是自定义组件 js 文件,最后才是注册页面的 js 代码。而且小程序对于在 app.js 以及注册页面的 js 代码都会加载完成后立即使用 require 方法执行模块中的程序。其他的代码则需要在程序中使用 require 方法才会被执行。 下面详细介绍了 app.js,自定义组件,页面 js 代码的处理流程。 4、加载 app.js 与注册程序在 app.js 加载完成后,小程序会使用 require('app.js') 注册程序,即对 App 方法进行调用,App 方法是对 __appServiceEngine__.App 方法的引用。 下图是框架对于 App 方法调用时的处理流程: [图片] App 方法根据传入的对象实例化一个 app 实例,其生命周期函数 onLaunch 和 onShow 因为使用不同的方式获取 options的参数。在有些需要根据场景值来实现需求的,或许使用 onShow 中的场景值更合适。 在实际开发过程中发现,在微信顶部唤起小程序和在小程序列表唤起的 options 也是不一样的。在该案例中通过点击分享的小程序进入后,关闭小程序,再通过不同方式进入小程序,通过顶部唤起的还是 options 的 path 属性还是分享出来的 path,但是通过列表中打开直接回到了首页,这里 App 中的 onShow 就会获取到不同的 options。 5、加载自定义组件代码以及注册自定义组件自定义组件在 app.js 之后被加载,小程序会在这个过程中加载完所有的自定义组件(分包中自定义组件没有有测试过),并且是加载完成后自动注册,只有注册完成后才会加载下一个自定义组件的代码。 下图是框架对于 Component 方法处理流程: [图片] 图中介绍了框架如何对传入 Component 方法的对象的处理,其后面还有很多深入的对于组件实例化的步骤没有在图中表示出来,具体可以在文章最后的附件中查看。 自定义组件在小程序中越来越完善,其拥有的能力也比 Page 更强大,而后面会提到在使用自定义组件的 Page 中,Page 实例也会使用和自定义组件一样的实例化方式,也就是说,他拥有和自定义组件一样的能力。 6、加载页面代码和注册页面加载页面代码的处理流程和加载自定义组件一样,都是加载完成后先注册页面,然后才会加载下一个页面。 下图是注册一个页面时框架对于 Page 方法的处理流程: [图片] Page 方法会根据是否使用自定义组件做不同的处理。使用自定义组件的 page 对象会被处理为和自定义组件的结构,并在页面实例化时使用不同的处理流程进行实例化。当然对于开发而言没任何不同。 从图中可以发现 Page 传入的(生命周期)代码并不会在这里被执行,可以通过下面小节了解 Page 实例化的详细过程。 7、等待页面 Ready 和 Page 实例化还记得上面介绍的启动流程中最后一步等待页面 Ready?严格来讲是等待浏览器 Ready,小程序虽然有部分原生的组件,不过本质上还是一个 web 程序。 在小程序中切换页面或打开页面时会触发 onAppRoute 事件,小程序框架通过 wx.onAppRoute 注册页面切换的处理程序,在所有程序就绪后,以 entryPagePath 作为入口使用 appLaunch 的方式进入页面。 下图是处理导航的程序流程: [图片] 从图中可以看出页面的实例化是在进入页面时进行,下图是具体的实例化过程: [图片] 下图是最终可得到 Page 实例: [图片] 可以发现其中多了 onRouteEnd API,实际该接口不会被调用。其中以 component 标记的表示只有在使用了自定义组件时才会有的方法和属性。在前面第 5 小节提到了对于使用自定义组件的页面会按照自定义组件方式解析,这些属性和方法与自定义组件表现一致。 8、关于 setData小程序框架是一个以数据驱动的框架,当然不能少了对他如何实现数据绑定的探索,下图是 Page 实例的 setData 执行流程: [图片] 其中 component:setData 表示使用自定义组件的 Page 实例的 setData 方法。 三、写在最后这是一次不完全的小程序框架探索,是在微信开发工具中 debug 的结果。虽然对于实际开发没有什么太大的帮助,但是对框架如何对开发的 js 代码进行处理有了一个很明确的认识,在使用一些 js 特性时可以有明确的感知。如果你还疑惑“小程序框架对传入的对象等到底做了什么”那一定是我表达能力太差,说声对不起。 通过这一次 debug ,也给我引入了新的问题,还希望能够有更多的讨论: 自定义组件太多启动时会耗时处理自定义组件文件太多会耗时读文件合理的设计分包很重要一份在调试过程中的笔记 小程序框架不完全分析.xmind,如果看官有兴趣可以下载看看。当然最后对于框架中已有的能力,还是非常希望微信可以开放更多稳定的接口,并在文档中告知开发者,让开发变得简单一些。
2020-10-22 - 省钱有道之 云开发环境共享小结
#前言 最近为了节省一点小程序的运营成本,一些没啥流量的小程序如果每个月也要19块略微有些肉疼(主要还是穷),研究了一下云环境共享,在这里简单做一下总结。 [图片] 这里有官方的小程序环境共享文档需提前了解一下,具体共享步骤按官方文档操作即可。 https://developers.weixin.qq.com/miniprogram/dev/wxcloud/guide/resource-sharing/introduce.html #注意点 共享环境有几个注意点大致如下: 1、必须是相同主体 2、开通了云开发环境的小程序可以共享给同主体的小程序、公众号,被共享方无需开通云开发环境 3、一个云开发环境最多可以共享给10个小程序/公众号 4、共享后双发均可主动解除 5、按官方文档要求,资源方需有云函数cloudbase_auth,测试时发现没有这个云函数其实也能正常运行,可能我验证的场景还不够多 6、云能力初始化的方式不同,资源方按传统的云环境初始化方式即可,也就是 wx.cloud.init({ env: env.activeEnv, traceUser: true }); 而调用方的初始化方式有所不同 const cloud = new wx.cloud.Cloud({ //资源方AppID resourceAppid, //资源方环境ID resourceEnv, }) // 跨账号调用,必须等待 init 完成 // init 过程中,资源方小程序对应环境下的 cloudbase_auth 函数会被调用,并需返回协议字段(见下)来确认允许访问、并可自定义安全规则 const initRes = await cloud.init(); 后续调用资源方的云函数就用这个cloud就行了:cloud.callFunction({...}); 7、调用方有操作到云存储文件的api也需要用6步骤中的cloud 8、云存储fileId需要用cloud.getTempFileURL转换成临时/永久链接,否则在调用方无法展示 9、一些api的云调用方式也有变化,需指明具体的appid。比如A小程序授权给了B小程序,想给B小程序推送客服消息需要写成 await cloud.openapi({appid:B小程序appid}).customerServiceMessage.send({...}); 10、获取调用方的appid/openid/unionid也有所不同 // 跨账号调用时,由此拿到来源方小程序/公众号 AppID console.log(wxContext.FROM_APPID) // 跨账号调用时,由此拿到来源方小程序/公众号的用户 OpenID console.log(wxContext.FROM_OPENID) // 跨账号调用、且满足 unionid 获取条件时,由此拿到同主体下的用户 UnionID console.log(wxContext.FROM_UNIONID) #适配 基于以上注意点,开始进行适配,由于我是一套代码部署N个小程序,然后一个云环境共享给其他小程序,希望通过配置决定哪个小程序作为资源方,哪些作为调用方 首先是云开发环境的初始化: 1、env.js 环境配置: //云开发环境 const cloudBase = { //使用共享云环境资源,资源方=false,调用方=true useShareResource: false, //资源方AppID resourceAppid: "wx9d2xxxxxxxx0088", //资源方环境ID resourceEnv: "prod-9gxqvi3qb3c257ef", //云环境ID prod: "prod-9gxqvi3qb3c257ef" } 2、api.js 操作模块 const env = require('../env.js'); let cloud; /** * 初始化云能力 * @returns {Promise} */ const wxCloudInit = async function () { const {cloudBase} = env; if (!wx.cloud) { console.error('请使用 2.2.3 或以上的基础库以使用云能力') } else if (cloudBase.useShareResource) { const {resourceAppid, resourceEnv} = cloudBase; // 声明新的 cloud 实例 cloud = new wx.cloud.Cloud({ //资源方AppID resourceAppid, //资源方环境ID resourceEnv, }) // 跨账号调用,必须等待 init 完成 // init 过程中,资源方小程序对应环境下的 cloudbase_auth 函数会被调用,并需返回协议字段(见下)来确认允许访问、并可自定义安全规则 const initRes = await cloud.init(); console.log("初始化云能力完毕:", initRes, "资源方appid:", resourceAppid, "资源方环境ID:", resourceEnv); } else { wx.cloud.init({ env: env.activeEnv, traceUser: true }); console.log("初始化云能力完毕,当前环境:", env.activeEnv); cloud = wx.cloud; } this.cloud = cloud; } /** * 云函数调用 * @param name * @param data * @param success * @param fail * @param complete */ const callCloudFunction = function (name, data, success, fail, complete) { //执行云函数 cloud.callFunction({ // 云函数名称 name: name, // 传给云函数的参数 data: Object.assign({}, data, {env: env.activeEnv}) }).then(res => { typeof success == 'function' && success(res); }).catch(res => { typeof fail == 'function' && fail(res); }).then(res => { typeof complete == 'function' && complete(res); }); }; 3、在app.js中初始化云环境,后续有用到wx.cloud的都需要改成api.cloud const api = require('utils/api.js'); App({ onLaunch: async function (options) { await api.wxCloudInit(); } }); 其次是资源方的获取用户信息调整 每次都要判断wxContext.FROM_OPENID是否为空,不为空则是调用方的用户信息,为空则是资源方的用户信息,略微繁琐,干脆封装了一个npm包wx-server-inherit-sdk,改造了一下getWxContext函数,源码如下,引入这个包后也就可以不用引入官方的wx-server-sdk const cloud = require('wx-server-sdk'); // 保存原始getWXContext方法到另一个变量 const originalGetWXContext = cloud.getWXContext; cloud.getWXContext = function () { //调用原始getWXContext方法 const wxContext = originalGetWXContext.call(this); const {FROM_APPID, FROM_OPENID} = wxContext; //云开发环境共享时获取到的APPID会替换成源方APPID if (FROM_APPID) { Object.assign(wxContext, {APPID: FROM_APPID}); } //云开发环境共享时获取到的OPENID会替换成源方OPENID if (FROM_OPENID) { Object.assign(wxContext, {OPENID: FROM_OPENID}); } return wxContext; } module.exports = cloud; 到此也就大功告成。为了省钱也是够折腾的[哭笑]
2023-08-28 - 小程序涉及iOS虚拟支付修改指引
近期,开发者社区几乎隔三差五就被“如何整改虚拟支付”霸占。社区君在大家的强烈诉求下,特地整理了大家审核过程中经常遇到的问题。 问题1 虚拟支付到底是什么? 虚拟支付是指购买非实物商品。比如:VIP会员、充值、课程、音视频等虚拟产品。 问题2 为什么关闭了虚拟支付功能,还是不能过审? 部分开发者在提审时,仅在后台关闭了支付接口,但在前端仍保留购买/支付/订阅等功能或按钮,在iOS端的界面也都会有类似“订阅课程、开通VIP”等标识,这类呈现货架价格、提示购买的行为将被禁止。 比如: [图片] 这类展示货架(价格标签)的,将被拒绝 问题3 为什么已经去掉了引导关注公众号,还是不能过审? 请勿试图引导用户到微信公众号、H5、App或是任何外链进行付费。任何为用户提供前往支付流程的路径/文案,都将被拒绝。 比如: [图片] 这类通过【客服消息】-【个人微信号】,完成购买行为,将会被拒绝 [图片] 这类引导到H5,完成购买的行为,将会被拒绝 [图片] 这类引导到公众号,完成购买的行为,将会被拒绝 以下提供3种修改示例,仅供参考。 1、关闭iOS付费通道。 以小程序【腾讯视频】为例,iOS端关闭会员卡开通渠道,原【开通VIP】按钮,现仅显示【你还不是会员】,不再提供会员开通服务。比如: [图片] 2、所有付费内容均更改为免费。 以小程序【有道精品课】为例,iOS端将所有付费内容更改为免费,原【付费解锁】,现已完全免费使用。比如: [图片] 3、前端直接拦截提示不可服务。 以小程序【网易公开课精品】、【腾讯视频VIP】为例,iOS端点击订阅、开通VIP服务,前端直接拦截提示不可服务。但,如果页面呈现货架价格,提示购买行为的(如问题2),将会被拒绝。比如: [图片]
2018-08-17 - 云数据库中,每条记录的一个字段是数组,数组的每个元素是对象,怎么可以检索到数组中的某个对象?
比如其中一条记录的一个字段是info:[{gh:001,name:'张三',age:24},{gh:002,name:'李四',age:24},{gh:003,name:'小明',age:24}] ,怎么能以 李四的gh,检索到002呢。望大神指点
2020-07-07 - 小程序页面通信、数据刷新、event bus 解决方案之 iny-bus 2.0 来了
背景介绍 在很久之前,我在写小程序的时候需要遇到一些问题,为了解决这些问题,便有了 [代码]iny-bus[代码] 这个库,当时主要解决了那些问题呢,让我们回顾一下并介绍一下 2.0的新功能 存在问题 [图片] 在各种小程序中,我们经常会遇到这种情况 有一个 列表,点击列表中的一项进入详情,详情有个按钮,删除了这一项,这个时候当用户返回到列表页时, 发现列表中的这一项依然存在,这种情况,就是一个 [代码]bug[代码],也就是数据不同步问题,这个时候测试小姐姐 肯定会找你,让你解决,这个时候,你也许会很快速的解决,但过一会儿,测试小姐姐又来找你说,我打开了 四五个页面更改了用户状态,但我一层一层返回到首页,发现有好几个页面数据没有刷新,也是一个 [代码]bug[代码], 这个时候你就犯愁了,怎么解决,常规方法有下面几种 解决方法 将所有请求放到 生命周期 [代码]onShow[代码] 中,只要我们页面重新显示,就会重新请求,数据也会刷新 通过用 [代码]getCurrentPages[代码] 获取页面栈,然后找到对应的 页面实例,调用实例方法,去刷新数据 通过设置一个全局变量,例如 [代码]App.globalData.xxx[代码],通过改变这个变量的值,然后在对应 [代码]onShow[代码] 中检查,如果值已改变,刷新数据 在打开详情页时,使用 [代码]redirectTo[代码] 而不是 [代码]navigateTo[代码],这样在打开新的页面时,会销毁当前页面, 返回时就不会回到这个里面,自然也不会有数据不同步问题 存在的问题 假如我们将 所有 请求放到 [代码]onShow[代码] 生命周期中,自然能解决所有数据刷新问题,但是 [代码]onShow[代码] 这个生命周期,有两个问题 第一: 它其实是在 [代码]onLoad[代码] 后面执行的,也就是说,假如请求耗时相同,从它发起请求到页面渲染, 会比 [代码]onLoad[代码] 慢 第二:那就是页面隐藏、调用微信分享、锁频等等都会触发执行,请求放置于 [代码]onShow[代码] 中就会造成大量不需要的请求,造成服务器压力,多余的资源浪费、也会造成用户体验不好的问题 通过 [代码]getCurrentPages[代码] 获取页面栈,然后找到对应的页面实例,调用实例方法,去刷新数据,这也不失为一个办法,但是还是有限制 第一: [代码]getCurrentPages[代码] 无法获取 [代码]tabBar[代码] 页面,而[代码]tabBar[代码] 页面是需要刷新和通信的重灾区 第二: 当需要通信的页面有两个、三个、多个呢,这里去使用 [代码]getCurrentPages[代码] 就会比较困难、繁琐 通过设置全局变量的方法,当需要使用的地方比较少时,可以接受,当使用的地方多的时候,维护起来就会很困难,代码过于臃肿,也会有很多问题 使用 redirectTo 而不是navigateTo,从用来体验来说,很糟糕,并且只存在一个页面,对于tab 页面,它也无能为力,不推荐使用 最佳实践 在 Vue 中, 可以通过 new Vue() 来实现一个 event bus作为事件总线,来达到事件通知的功能,在各大 框架中,也有自身的事件机制实现,那么我们完全可以通过同样的方法,实现一个事件中心,来管理我们的事件, 同时,解决我们的问题。iny-bus 就是这样一个及其轻量的事件库,使用 typescript 编写,100% 测试覆 盖率,能运行 js 的环境,就能使用 安装 方式一. 通过 npm 安装 小程序已经支持使用 npm 安装第三方包,详见 npm 支持 [代码] # npm npm i iny-bus -save # yarn yarn add iny-bus --production [代码] 方式二. 下载代码 直接通过 git 下载 iny-bus 源代码,并将[代码]dist[代码]目录 中的 index.js 拷贝到自己的项目中 [代码] git clone https://github.com/landluck/iny-bus.git [代码] 使用 使用内置方法(2.0 推荐) [代码] // App、Page、Component 使用方法一致,完全不需要手动去添加监听和移除监听了 import bus from 'iny-bus' // bus.app bus.page bus.component const page = bus.page({ busEvents: { // 简单使用 postMessage(msg) { this.setData({ msg }) }, // 一次性事件 postMessageOnce: { handler (msg) { this.setData({ msg }) }, once: true } }, onLoad() { bus.emit('postMessage', 'hello bus') bus.emit('postMessageOnce', 'hello bus once') } }) Page(page) [代码] 在生命周期中使用(1.0使用 和 2.0完全兼容) [代码] // 小程序 import bus from 'iny-bus' // 添加事件监听 // 在 onLoad 中注册, 避免在 onShow 中使用 onLoad () { this.eventId = bus.on('事件名', (a, b, c, d) => { // 支持多参数 console.log(a, b, c, d) this.setData({ a, b, c } }) } // 移除事件监听,该函数有两个参数,第二个事件id不传,会移除整个事件监听,传入ID,会移除该页面的事件监听,避免多余资源浪费, 在添加事件监听后,页面卸载(onUnload)时建议移除 onUnload () { bus.remove('事件名', this.eventId) } // 派发事件,触发事件监听处更新视图 // 支持多参传递 onClick () { bus.emit('事件名', a, b, c) } // 清空所有事件监听 onClear () { bus.clear() } [代码] 借助 iny-bus 小程序webview和原生通信 小程序 [代码]webview[代码] 和原生页面怎么通信,我相信这是一部分同学遇到的一个很不好处理的问题,那么如何借助 [代码]iny-bus[代码] 来实现 小程序 [代码]webview[代码] 和原生页面通信呢 webview 和原生通信要借助 web-view 组件的 bindmessage 方法 [图片] 要提前在小程序中内置好 [代码]iny-bus[代码] 的事件监听 要在 [代码]H5[代码] 中使用 [代码]wx.miniProgram.postMessage[代码] [图片] 4. 参考以下代码 [代码] // 小程序 webview // web-view 组件的 bindmessage 方法 onMessage ({ detail: { data } }) { for (let i = 0; i < data.length; i++) { const event = data[i] // 分享事件 if (event.type === 'share') { } // 刷新事件 if (event.type === 'refresh' && event.name) { // 派发事件,传递参数 bus.emit(event.name, event.data) } // 其它事件 } } // H5 wx.miniProgram.postMessage( { type: 'refresh', name: 'refreshData', data: { a: 1 } } ) [代码] 最后 iny-bus 的核心代码,非常少,但是能解决我们在小程序中遇到的大量 通信 和 数据刷新问题,是采用 各大平台小程序 原生开发时,页面通信的不二之选,同时,100% 的测试覆盖率,确保了 iny-bus 在使用中的稳定性和安全性,当然,每个库都是从简单走向复杂,功能慢慢完善,如果 大家在使用或者源码中发现了bug或者可以优化的点,欢迎大家提 pr 或者直接联系我 最后,如果 iny-bus 给你提供了帮助或者让你有任何收获,请给 作者 点个赞,感谢大家 点赞 代码地址
2020-03-07 - 小程序app.onLaunch与page.onLoad异步问题的最佳实践
场景: 在小程序中大家应该都有这样的场景,在onLaunch里用wx.login静默登录拿到code,再用code去发送请求获取token、用户信息等,整个过程都是异步的,然后我们在业务页面里onLoad去用的时候异步请求还没回来,导致没拿到想要的数据,以往要么监听是否拿到,要么自己封装一套回调,总之都挺麻烦,每个页面都要写一堆无关当前页面的逻辑。 直接上终极解决方案,公司内部已接入两年很稳定: 1.可完美解决异步问题 2.不污染原生生命周期,与onLoad等钩子共存 3.使用方便 4.可灵活定制异步钩子 5.采用监听模式实现,接入无需修改以前相关逻辑 6.支持各种小程序和vue架构 。。。 //为了简洁明了的展示使用场景,以下有部分是伪代码,请勿直接粘贴使用,具体使用代码看Github文档 //app.js //globalData提出来声明 let globalData = { // 是否已拿到token token: '', // 用户信息 userInfo: { userId: '', head: '' } } //注册自定义钩子 import CustomHook from 'spa-custom-hooks'; CustomHook.install({ 'Login':{ name:'Login', watchKey: 'token', onUpdate(token){ //有token则触发此钩子 return !!token; } }, 'User':{ name:'User', watchKey: 'userInfo', onUpdate(user){ //获取到userinfo里的userId则触发此钩子 return !!user.userId; } } }, globalData) // 正常走初始化逻辑 App({ globalData, onLaunch() { //发起异步登录拿token login((token)=>{ this.globalData.token = token //使用token拿用户信息 getUser((user)=>{ this.globalData.user = user }) }) } }) //关键点来了 //Page.js,业务页面使用 Page({ onLoadLogin() { //拿到token啦,可以使用token发起请求了 const token = getApp().globalData.token }, onLoadUser() { //拿到用户信息啦 const userInfo = getApp().globalData.userInfo }, onReadyUser() { //页面初次渲染完毕 && 拿到用户信息,可以把头像渲染在canvas上面啦 const userInfo = getApp().globalData.userInfo // 获取canvas上下文 const ctx = getCanvasContext2d() ctx.drawImage(userInfo.head,0,0,100,100) }, onShowUser() { //页面每次显示 && 拿到用户信息,我要在页面每次显示的时候根据userInfo走不同的逻辑 const userInfo = getApp().globalData.userInfo switch(userInfo.sex){ case 0: // 走女生逻辑 break case 1: // 走男生逻辑 break } } }) 具体文档和Demo见↓ Github:https://github.com/1977474741/spa-custom-hooks 祝大家用的愉快,记得star哦
2023-04-23 - taiwindcss在小程序中的应用
css 工程化思想有很多(sass,less,css-in-js…),基于原子类书写思想的 taiwindcss(以下用 tw 代替)风头很盛,去年底 tw3 上线,搭配新的引擎让[代码]just-in-time[代码]模式平稳落地。 tw 的思想和好处这里不在赘述,官网介绍的很清楚(tw2 中文官网)。 这里主要描述下如何在小程序中使用tw书写样式。 题外话:很难想象前端最佳实践到来的那一天,但 css 最佳实现或许已初见苗头… tw书写小程序 tw3 是即时(实时)生成类样式。tw 会根据您的配置文件,匹配您指定文件中包含的关键字,自动生成对应配置的样式。 在小程序中,只需要在 app.wxss 中引入 tw 生成的样式文件(记得组件中配置 addGlobalClass:true),就不必在书写组件时,关心 wxss 文件了。 base类 base类好比预设一些样式 (由于官方默认配置基于web端,小程序中只能自己定义了) [代码] /* wxss */ page { height: 100%; line-height: 1.2; -webkit-text-size-adjust: 100%; font-family: "fontFamily.sans", ui-sans-serif, system-ui" } [class], view, image, ::before, ::after{ box-sizing: border-box; border-width: 0; border-style: solid; border-color: currentColor; } /* variables */ page{ --color-primary:#22c55e; --color-primary-dark: #16a34a; /* ... */ } @media (prefers-color-scheme: dark) { page { --color-primary:#ef4444; --color-primary-dark: #dc2626; /* ... */ } } [代码] utilities(工具类) tw 中最核心的思想(工具类 css,也有叫原子化 css),大多数内部模块可以基于官方默认配置,有些还需要自定义配置。 Demo1 [代码] <view> <text class="text-red text-40 ml-10 px-20 "> 我的字体颜色是红色,大小为40rpx,左外边距10rpx,内边距x轴20rpx</text > </view> <!-- wxss新增 .text-red{ color:red }, .text-40{ font-size: 40rpx }, .ml-10{ margin-left:10rpx } .px-20{ padding: 0 20rpx } --> [代码] components(组件类) 有时几个工具类样式会经常出现,我们可以把他们自定义为组件类(比如下面 Demo3 中的[代码]ellipsis[代码]类) Demo2 [代码] <view> <button class="bg-primary hover_bg-primary-dark inline-block w-100 h-60 whitespace-nowrap overflow-hidden overflow-ellipsis" > 工具类写法--按钮1 </button> </view> <view> <button class="btn ellipsis">组件类写法--按钮2</button> </view> <!-- wxss新增 .bg-primary{ color:var(--color-primary) } .hover_bg-primary-dark:hover{ color:var(--color-primary-dark) } .inline-block { display: inline-block; } .w-100 { width: 100rpx; } .h-60 { height: 60rpx; } .whitespace-nowrap{ white-space: nowrap; } .overflow-hidden{ overflow: hidden; } .overflow-ellipsis{ text-overflow: ellipsis; } .btn{ // 组件类 color:var(--color-primary); display: inline-block; width: 100rpx; height: 60rpx; } .btn:hover{ // 组件类 color:var(--color-primary-dark); } .ellipsis { // 组件类 overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } --> [代码] 不必纠结这么多工具类样式很难记,因为这些类都基于原生样式且可以自定义的,搭配官方插件 Tailwind CSS IntelliSense还可以得到代码提示(小程序编辑器也支持)。 先写这么多,不知道是否有人关注tw在小程序中的应用,下篇预期发具体核心类编译配置,如果您感兴趣请点赞支持。
2022-01-29 - 在小程序中愉快的使用 Tailwind CSS 吧!
[图片] 在小程序中愉快的使用 Tailwind CSS 吧! 把 [代码]tailwindcss JIT[代码] 思想带入小程序开发吧! 在小程序中愉快的使用 Tailwind CSS 吧! Usage uni-app (vue2/3) uni-app for vite (vue3) Taro v3 (React/vue2/3) remax (react) rax (react) 原生小程序(webpack5 mina) Options 配置项 使用 arbitrary values Q&A 1. 我在 [代码]js[代码] 里写了 [代码]tailwindcss[代码] 的任意值,为什么没有生效? 2. 一些像 [代码]disabled:opacity-50[代码] 这类的 [代码]tailwindcss[代码] 前缀不生效? 3. 和原生组件一起使用注意事项 4. 编译到 h5 注意事项 Related projects 模板 template 预设 tailwindcss preset Bugs & Issues 笔者之前写了一个 tailwindcss-miniprogram-preset,可是那个方案不能兼容最广泛的 [代码]Just in time[代码] 引擎,在写法上也有些变体。 于是笔者又写了一个 [代码]weapp-tailwindcss-webpack-plugin[代码],这是一个 [代码]plugin[代码] 合集,包含 [代码]webpack/vite plugin[代码],它会同时处理类 [代码]wxml[代码] 和 [代码]wxss[代码] 文件,从而我们开发者,不需要更改任何代码,就能让 [代码]jit[代码] 引擎兼容微信小程序。 此方案可兼容 [代码]tailwindcss v2/v3[代码],[代码]webpack v4/v5[代码],[代码]postcss v7/v8[代码]。 随着 [代码]@vue/cli-service[代码] v5 版本的发布,uni-app 到时候也会转为 [代码]webpack5[代码] + [代码]postcss8[代码] 的组合,到时候,我会升级一下 [代码]uni-app[代码] 的示例,让它从 [代码]tailwindcss v2 jit[代码] 升级到 [代码]tailwindcss v3 jit[代码] Usage uni-app (vue2/3) 使用方式 | Demo 项目 uni-app for vite (vue3) 使用方式 | Demo 项目 Taro v3 (React/vue2/3) 使用方式 | React Demo 项目 | vue2 Demo 项目 | vue3 Demo 项目 remax (react) 使用方式 | Demo 项目 rax (react) 使用方式 | Demo 项目 原生小程序(webpack5 mina) 使用方式 | Demo 项目 Options 配置项 配置项 类型 描述 [代码]htmlMatcher[代码] [代码](assetPath:string)=>boolean[代码] 匹配 [代码]wxml[代码]等等模板进行处理的方法 [代码]cssMatcher[代码] [代码](assetPath:string)=>boolean[代码] 匹配 [代码]wxss[代码]等等样式文件的方法 [代码]jsMatcher[代码] [代码](assetPath:string)=>boolean[代码] 匹配 [代码]js[代码]文件进行处理的方法,用于 [代码]react[代码] [代码]mainCssChunkMatcher[代码] [代码](assetPath:string)=>boolean[代码] 匹配 [代码]tailwindcss jit[代码] 生成的 [代码]css chunk[代码] 的方法 [代码]framework[代码] ([代码]Taro[代码] 特有) [代码]react[代码]|[代码]vue2[代码]|[代码]vue3[代码] 由于 [代码]Taro[代码] 不同框架的编译结果有所不同,需要显式声明框架类型 默认[代码]react[代码] [代码]customRuleCallback[代码] [代码](node: Postcss.Rule, options: Readonly<RequiredStyleHandlerOptions>) => void[代码] 可根据 Postcss walk 自由定制处理方案的 callback 方法 [代码]cssPreflight[代码] [代码]Record<string,string>[代码]| [代码]false[代码] 在所有 [代码]view[代码]节点添加的 [代码]css[代码] 预设,可根据情况自由的禁用原先的规则,或者添加新的规则。 详细用法如下: [代码]// default 默认: cssPreflight: { 'box-sizing': 'border-box', 'border-width': '0', 'border-style': 'solid', 'border-color': 'currentColor' } // result // box-sizing: border-box; // border-width: 0; // border-style: solid; // border-color: currentColor // case 禁用所有 cssPreflight: false // result // none // case 禁用单个属性 cssPreflight: { 'box-sizing': false } // border-width: 0; // border-style: solid; // border-color: currentColor // case 更改和添加单个属性 cssPreflight: { 'box-sizing': 'content-box', 'background': 'black' } // result // box-sizing: content-box; // border-width: 0; // border-style: solid; // border-color: currentColor; // background: black [代码] 使用 arbitrary values 详见 tailwindcss/using-arbitrary-values 章节 | Sample Q&A 1. 我在 [代码]js[代码] 里写了 [代码]tailwindcss[代码] 的任意值,为什么没有生效? 详见 issue#28 A: 因为这个插件,主要是针对, [代码]wxss[代码],[代码]wxml[代码] 和 [代码]jsx[代码] 进行转义的,[代码]js[代码] 里编写的 [代码]string[代码] 是不转义的。如果你有这样的需求可以这么写: [代码]import { replaceJs } from 'weapp-tailwindcss-webpack-plugin/replace' const cardsColor = reactive([ replaceJs('bg-[#4268EA] shadow-indigo-100'), replaceJs('bg-[#123456] shadow-blue-100') ]) [代码] 你不用担心把代码都打进来导致体积过大,我在 ‘weapp-tailwindcss-webpack-plugin/replace’ 中,只暴露了2个方法,代码体积 1k左右,esm格式。 2. 一些像 [代码]disabled:opacity-50[代码] 这类的 [代码]tailwindcss[代码] 前缀不生效? 详见 issue#33,小程序选择器的限制。 3. 和原生组件一起使用注意事项 假如出现原生组件引入报错的情况,可以参考 issue#35 ,忽略指定目录下的文件,跳过插件处理,比如 [代码]uni-app[代码] 中的 [代码]wxcomponents[代码]。 如何更改?在传入的配置项 [代码]cssMatcher[代码],[代码]htmlMatcher[代码] 这类中过滤指定目录或文件。 4. 编译到 h5 注意事项 有些用户通过 [代码]uni-app[代码] 等跨端框架,不止开发成各种小程序,也开发为 [代码]H5[代码],然而 [代码]tailwindcss[代码] 本身就兼容 [代码]H5[代码] 了。此时你需要更改配置,我们以 [代码]uni-app[代码] 为例: [代码]const isH5 = process.env.UNI_PLATFORM === 'h5'; // 然后在 h5 环境下把 webpack plugin 和 postcss for weapp 给禁用掉 // 我们以 uni-app-vue3-vite 这个 demo为例 // vite.config.ts import { defineConfig } from 'vite'; import uni from '@dcloudio/vite-plugin-uni'; import { ViteWeappTailwindcssPlugin as vwt } from 'weapp-tailwindcss-webpack-plugin'; // vite 插件配置 const vitePlugins = [uni()]; !isH5 && vitePlugins.push(vwt()); export default defineConfig({ plugins: vitePlugins }); // postcss 配置 // 假如不起作用,请使用内联postcss const isH5 = process.env.UNI_PLATFORM === 'h5'; const plugins = [require('autoprefixer')(), require('tailwindcss')()]; if (!isH5) { plugins.push( require('postcss-rem-to-responsive-pixel')({ rootValue: 32, propList: ['*'], transformUnit: 'rpx' }) ); plugins.push(require('weapp-tailwindcss-webpack-plugin/postcss')()); } module.exports = { plugins }; [代码] Related projects 模板 template uni-app-vite-vue3-tailwind-vscode-template uni-app-vue3-tailwind-vscode-template uni-app-vue2-tailwind-vscode-template weapp-native-mina-tailwindcss-template 预设 tailwindcss preset tailwindcss-miniprogram-preset Bugs & Issues 目前这个插件正在快速的开发中,如果遇到 [代码]Bug[代码] 或者想提出 [代码]Issue[代码] 欢迎提交到此处 <!-- ## 关于其他小程序 处理了其他小程序的: [代码]/.+\.(?:wx|ac|jx|tt|q|c)ss$/[代码] 样式文件和 [代码]/.+\.(?:(?:(?:wx|ax|jx|ks|tt|q)ml)|swan)$/[代码] 各种 [代码]xxml[代码] 和特殊的 [代码]swan[代码] -->
2022-05-16 - docker快速入门
1. 介绍docker是什么Docker使用go基于linux lxc(linux containers)技术实现的开源容器,诞生于2013年年初,最开始叫dotcloud公司,13年年底改名为docker inc。 2017年下载次数达到了百亿次,估值达13亿美元,通过对应用封装(Packaging)、分发(Distribution)、部署(Deployment)、运行(Runtime)全生命周期管理,达到“一次封装,到处运行” [图片] 为何使用docker?Docker直译码头工人,将各种大小和形状的物品装进船里。这对从事软件行业的人来说,听起来很熟悉,花了大量时间和精力把一个应用放在另一个应用里 [图片] docker出现之前,对不同环境的安装、配置、维护工作量很多,如部署,配置文件,crontab,依赖等等。 使用docker,无需关心环境,只需要一些配置就能构建镜像,而部署则用一条run命令 [图片] 虚拟机 vs 容器 虚拟机需要有额外的虚拟机管理应用和虚拟机操作系统层,操作系统层不仅占用空间而且运行速度也相对慢 docker容器是在本机操作系统层面上实现虚拟化,因此很轻量,速度接近原生系统速度 [图片] 虚拟机启动速度是分钟级别,性能较弱、内存和硬盘占用大,一个物理机最多跑几十个虚拟机,但它的隔离性比较好。 docker启停都是秒级实现,内存和硬盘占用非常小,单机支持上千个容器,在ibm服务器上可运行上万个容器 容器跟虚机相比,有着巨大的优势 [图片] docker优点 只关心应用:以往我们需要关心操作系统、软件、项目,有了docker我们可以只关心应用而不是操作系统,docker发展迅速,基于docker的paas平台也层出不穷,使得我们能更方便的使用docker 快速交付:docker可在秒级提供沙箱环境,开发,测试,运维使用完全相同的环境来部署代码 微服务:docker有助于将一个复杂系统分解,让用户用更离散的方式思考服务 离线开发:将服务编排在笔记本中移动办公,使用docker可在本机秒级别启动一个本地开发环境 降低调试成本:在测试和上线时产生无效的类、有问题的依赖、缺少的配置等问题,docker可让一个问题调试和环境重现变得更简单 CD:docker让持续交付实现变得更容易,特别是对于蓝绿部署就更简单。 第一版上线时,需要上第二版新功能,两个版本功能会有冲突,这时用docker实现蓝绿部署就非常方便了 如:可以部署两个版本同时在线,新版本测试没问题了把老版本流量切到新版本就可以了 迁移:可以很快的迁移到其他云或服务器 与传统虚拟机方式相比,容器化方式在很多场景下都是存在极为明显的优势。无论是开发、测试、运维都应该尽快掌握docker,尽早享受其带来的巨大便利 [图片] 容器化方式在很多场景下都有极大的优势。无论是开发、测试、运维都应该尽快掌握docker,尽早享受其带来的巨大便利 [图片] 概念再来了解docker非常关键的概念,这样才能理解docker容器整个生命周期 [图片] 概念—镜像 镜像(类)=文件系统+数据,我常常用开发语言中的类比作镜像,对象比作容器 镜像由多个层加上一些docker元数据组成,容器运行着由镜像定义的系统 [图片] 概念—容器容器(对象)=镜像运行实例 容器是镜像的运行实例,可以使用同一个镜像运行多个实例。如图所示,一个ubuntu docker镜像产生了三个ubuntu容器,docker利用容器运行和隔离应用 [图片] 从读写角度来说,镜像是只读的,容器是在镜像上添加了一层可读写的文件系统 [图片] [图片] 概念—层层=文件变更集合 像传统虚机应用,每个应用都需要拷贝一份文件副本,运行成百上千上磁盘空间会迅速耗光,而docker采用写时复制来减少磁盘空间,当一个运行中的容器要写入一个文件时,它会把该文件复制到新区域来记录这次的修改,在执行docker提交时将这次修改记录下并产生一个新的层。docker分层解决大规模使用容器时碰到的磁盘和效率问题 [图片] 概念—仓库docker借鉴了大量git优秀的经验。docker仓库分公有库和私有库,最大的公开仓库是docker hub,国内也有很多仓库源 [图片] 2. 创建第一个docker应用通过创建一个docker应用来看看docker是怎么方便使用的 创建docker镜像方式 创建docker有四种方式 [图片] 但最常用的docker命令+手工提交和Dockerfile的方式 [图片] 对于我们来说Dockerfile是最常用也是最有用的 “dockerfile” [图片] 那创建一个docker应用只需要三步:编写dockerfile、构建镜像、运行容器 编写dockerfile那我们就开始用dockerfile来创建一个应用 Dockerfile是包含一系列命令的文本文件,这个文件包含6条命令 1、FROM是使用php官方镜像,左边是镜像名字,右边是标签名字,标签名字不写默认是latest 2、声明维护人员 3、RUN运行一条linux命令,我们把php代码重定向到/tmp/index.php 4、EXPOSE声明要开放的端口 5、WORKDIR启动容器后默认目录 6、CMD容器启动后,默认执行的命令,相当于应用的入口,用php自带的webserver监听8000 [图片] 构建镜像使用docker build命令生成镜像,—tag指定镜像的名字,左边是名字,右边是标签,最后有个.表示在当前目录查找Dockerfile 可以看到,每个命令都会有个输入输出,输入是命令,输出是给到层的id,所以,基本上每个命令都会产生一个层 最后提示镜像构建成功,并打上镜像标签 [图片] 运行容器第三,使用docker run命令运行镜像,-p将容器的8000端口映射到本机8000端口,—name给容器起个名字 用curl对本机8000端口请求,服务器返回当前时间,说明我们构建的容器运行成功了 [图片] 请求本地8000端口,服务器返回当前时间 [图片] dockerfile常用命令其实Dockerfile常用命令就5个:from、add、run、workdir、cmd 创建docker应用步骤编写dockerfile 构建镜像 运行容器 使用docker应用步骤拉取镜像 运行容器 dockerfile最佳实践精简镜像用途 尽量让每个镜像的用途单一 选择合适基础镜像 选择以alpine、busybox等基础的镜像 busybox:号称操作系统里的瑞士军刀,只有……这么大,但却有一百多常用命令 如果你的目标是小而精,busybox是首选,因为它已经精简到没有bash,使用的是ash,一个兼容posix的shell [图片] Alpine:你的目标是小但是又有一些工具的话,可以选择alpine,它是一个面向安全的轻量linux发行版,它关注安全、性能和资源效能,比busybox功能更完善,还提供apk查询和安装软件包,大小只有2-3兆 [图片] 很多官方的镜像都有alpine的镜像,像刚刚使用的php镜像 [图片] 提供维护者信息 正确使用版本 使用明确的版本号,而非依赖于默认的latest,避免环境不一致导致的问题 [图片] 删除临时文件 如安装软件后的安装包,如上图2、3步骤 提高生成速度 如内容不变的指令尽量放在前面,这样可以复用 减少镜像层数 多条命令写在一起,使生成的镜像层数少,如上图2、3步骤 恰当使用multi-stage 保证最终生成镜像最小化 3. 常用命令search想使用一个镜像,用这个命令就可以了,默认按评分排序 official如果是ok表示是官方镜像 Auto标示它是否用dickerfile进行自动化镜像构建 [图片] pull一旦确定一个镜像,通过对其名称执行docker pull来下载 标签默认是latest,严格来讲,镜像的仓库名还应该添加仓库地址的,默认是registry.hub.docker.com Docker images命令查找下载的镜像 [图片] run使用docker run运行一个容器,it表示用交互式方式运行,最后表示要执行的命令 [图片] 其实更常用的方式是以后台方式来执行,这时用d参数在后台运行 运行后用exec命令进去到容器 [图片] tagDocker tag给镜像一个新tag名字 Docker images查看centos镜像,把centos:latest打上centos:yeedomliu,这时再看会有3个centos,latest和yeedomliu的镜像id是相同的 把centos:yeedomliu删除,再查看latest还会存在,最后用rmi命令删除latest就会真正把latest镜像删除掉 如果相同镜像存在多个标签,只有最后一次的rmi命令会真正删除镜像 [图片] psPs可以查看运行中的容器 [图片] rmi删除一个镜像,同一个镜像id的不同标签的镜像,使用rmi删除最后一个镜像才会真正删除这个镜像 [图片] rm删除docker容器,如果运行中的容器需要加-f [图片] diff容器启动后文件变化情况 [图片] logs查看容器运行后的日志 [图片] cp我们想从容器里面拷贝文件到宿主机,或相反的过程就可以用到cp命令 [图片] container prune随着使用docker时间越长,停止状态下的容器会越来越多,这些都会占据磁盘空间 [图片] image prune未被打标签的镜像可以用image prune命令清理 [图片] system prune/df如果你觉得刚刚两条命令执行起来麻烦,可以用docker system prune一条命令搞定 另外用system df查看docker磁盘空间 [图片] 4. 实战了解了docker基础知识后,可进入相对实战的环节 本地开发 常见问题 架构 优化 本地开发 我们的项目使用了很多服务,如redis/mysql/mongodb等等,如果一个个运行起来,还加上配置,容易出手,也比较麻烦 kitematic:与使用命令行管理本地容器相比,你更想使用图形工具对容器管理,官方推出的容器管理工具,通过它可以查找镜像、创建容器、配置、启停容器等管理 [图片] [图片] 这是配置容器端口和宿主机端口,目录,网络等映射界面 [图片] docker-composecompose定位是“定义和运行多个docker容器的应用”,前身fig,目前仍然兼容fig格式的模板文件。 一条命令可以把一个复杂的应用启动起来 日常工作中,经常碰到多个容器相互完成某项任务 [图片] docker-compose示例1 默认模板文件名叫docker-compose.yml,结构很简单,每个顶级元素为服务名称,次级信息为配置信息 这里使用了redis/mongodb/mysql/nginx镜像,分别给它们映射了本地目录、端口、密码等信息,nginx镜像需要使用redis/mysql等服务,用links命令连接进来 [图片] docker-compose示例2 如果在本地开发,每个项目都可以像之前说的那样配置,这里提供了另外一种做法 我把公共的资源在一开始就启动,每个项目里只启动nginx镜像并关联其它的服务即可 公共服务compose [图片] 项目compose [图片] 常见问题 主进程:docker启动第一个进程称主进程,就是id为1的进程,这个进程退出就意味着容器退出,所以想要使docker作为服务使用,这个进程是不能退出的 expose命令是声明暴露的端口,运行时用-P才会生效。一般ports命令是做真正的端口映射,比较常用 架构 安装了docker的主机,一般在一个私有网络上 1、调用docker客户端可以从守护进程获取信息或发送指令 2、docker守护进程使用http协议接收来自docker客户端的请求 3、私有docker注册中心存储docker镜像 4、docker hub是由docker公司运营的最大的公共注册中心 互联网上也存在其他公共的注册中心 调用 Docker客户端可以从守护进程获取信息或给它发送指令。守护进程是一个服务器,它使用 HTTP协议接收来自客户端的请求并返回响应。相应地,它会向其他服务发起请求来发送和接收镜像,使用的同样是 HTTP协议。该服务器将接收来自命令行客户端或被授权连接的任何人的请求。守护进程还负责在幕后处理用户的镜像和容器,而客户端充当的是用户与 REST风格 API之间的媒介。 理解这张图的关键在于,当用户在自己的机器上运行 Docker时,与其进行交互的可能是自己机器上的另一个进程,或者甚至是运行在内部网络或互联网上的服务。 [图片] 优化 使用小镜像:一般来说,使用小的镜像都相对比较优秀,如官方的镜像基本上都有基于alpine的镜像 事后清理:删除镜像里软件包或一些临时文件,减小镜像大小 命令写一行:多个命令尽量写在一起有助于减少层数,也会减少镜像的大小 脚本安装:使用脚本进行初始化时,可以有效减少dockerfile的命令,同时带来另外的问题,可读性不好并且构建镜像时缓存不了 扁平化镜像:构建镜像过程中,可能会涉及到一些敏感信息,或者用了上面的办法镜像依然很大,可以试试这个办法 docker export 容器名或容器id | docker import - 镜像标签 multi-stage:从docker 17.05版本开始,docker支持multi-stage(多阶段构建),特别适合编译型语言,如我在一个镜像下编译,在另外一个很小的系统运行,如下图,go项目在golang环境下编译,在alpine环境下运行 [图片]
2019-03-27 - WeUI官方组件库:助力小程序高效设计与开发
原文来自「微信开发者」公众号。 本文主要介绍了WeUI官方组件库有什么,怎么用。 提起 WeUI,相信大家都不陌生,WeUI 是一套同微信原生视觉体验一致的基础样式库,由微信官方设计团队为微信内网页和微信小程序量身设计,令用户的使用感知更加统一。 不过,对于 WeUI 样式库,开发者就有疑问了。 [图片] 1 有什么 我们来看看 WeUI 组件库到底有什么可用的 UI 组件呢?WeUI 样式库有的各个元素,WeUI 组件库是基于 WeUI 样式库做了组件化处理,开发者可以便捷的使用,无需考虑组件层面的逻辑问题。 2 怎么用 有了心动的组件之后,大家肯定想知道 WeUI 组件库是怎么使用的。 第一步:引用 要使用 WeUI,首先要把 WeUI 引入我们的小程序项目,引入 WeUI 的方式有以下两种,使用其中一种即可~ 方法一:通过 useExtendedLib 扩展库 的方式引入,这种方式引入的组件将不会计入代码包大小。(推荐👍) 方法二:可以通过 npm 方式下载构建,npm 包名为 weui-miniprogram。 与方法一不同,npm 引入的方式需要多操作一步,在 app.wxss 中引用 weui.wxss。 // app.wxss @import '/miniprogram_npm/weui-miniprogram/weui-wxss/dist/style/weui.wxss'; 第二步:使用 引入之后,我们就要开始来使用了,WeUI 组件库是基于小程序自定义组件构建的,所以使用是以自定义组件的形式来使用。 下面通过几个例子来感受下 WeUI 组件库的使用。 由于是自定义组件的形式,所以使用组件都需要在页面配置中引入,像这样: // page.json { "usingComponents": { "mp-half-screen-dialog": "weui-miniprogram/half-screen-dialog/half-screen-dialog", "mp-searchbar": "weui-miniprogram/searchbar/searchbar" } } 引入组件之后,就可以直接在 wxml 中使用了,当然,为了让开发者接入更加简便,我们也加入了做了一些常见的实用性功能。 半屏弹窗 小程序提供了 wx.showModal、wx.showToast 供开发者进行页面交互,在开发过程中,可能需要自定义按钮相关的内容,所以 WeUI 提供了半屏弹窗让开发者可以有更多的自定义空间。 我们来看下代码,使用很简单,直接使用 mp-half-screen-dialog,配置相关属性即可。 // page.wxml // page.js data配置 buttons: [ { type: 'default', className: '', text: '辅助操作', value: 0 }, { type: 'primary', className: '', text: '主操作', value: 1 } ] 来看下半屏弹窗的效果~ u1s1,这交互体验真的爱了😍 [图片] Form 表单校验 Form 表单这里,除了基础的的功能之外,WeUI 组件库还提供了表单校验的能力,通过 rules 规则的配置(支持长度、手机号码、电子邮件、url 链接地址等),轻松解决表单校验问题。 [图片] 左滑删除 相比 Web 端,手机端的操作按钮更多的是通过⬅️左滑等来实现,考虑到左滑删除的普遍性,WeUI 组件库也是支持的。 在 mp-slideview 组件中设置 buttons属性即可。 [图片] 搜索组件 同样是基本功能的搜索,WeUI 组件库也封装了搜索组件,开发者只需配置搜索结果即可拥有搜索功能~ [图片] 除了这些组件之外,WeUI 组件库还提供了很多实用的组件,包括基础的 icon、loading,表单的 uploader、cell 等等。 第三步:适配DarkMode 伴随客户端、小程序对 DarkMode 的支持,WeUI 组件库也同步适配 DarkMode 的模式,让 WeUI 组件库的使用同学可以快速适配 DarkMode。 在根结点(或组件的外层结点)增加属性 data-weui-theme="dark" ,即可把 WeUI 组件切换到 DarkMode 的表现,如: ... 3 体验WeUI 最后,如果想体验 WeUI 组件库的效果,欢迎扫码下方二维码进行小程序示例体验👏及接入使用,使用过程中如有建议或者疑问,欢迎到微信开放社区与我们交流。 [图片]
2022-03-24 - 知识竞赛答题小程序
前几天恰逢五四青年节,帮我们党支部开发了一套知识竞赛类答题小程序,文章末尾有小程序码可以体验 该小程序目前已完成 用户授权,授权后答题、答题完成展示排名,完整支持知识竞赛答题活动的需求, 答题目前已支持单选、多选、判断三种题型 不详细介绍了,具体先上截图吧 [图片] 1 [图片] 2 [图片] 3 [图片] 4 [图片] 5 [图片] [图片] 6 大家不妨扫码体验下,有问题的可以反馈给我 [图片]
2020-11-24 - 知识竞赛答题小程序
通过长期的答题小程序开发,不断完善答题小程序的各种功能,整理出知识竞赛答题活小程序活动玩法。 今天我讲解一下知识竞赛答题小程序的规则 一、知识竞赛答题小程序规则一 个人排行榜 根据分数排名,分数相同则答题时间短的排前面。共有三种计分规则,详见下面描述 活动周期内最高得分 例如:活动周期内可以多次答题,取最好的一次成绩参与排名。 累计每天最高得分 例如:每天可以多次答题。第一天最好成绩为90分,第二天最好成绩为80分,两天答题最高得分为90分。 累计每次答题得分 例如:每天可以多次答题,累计每次答题成绩。每人每天最多可以答5次。 单位排行榜 可以根据参赛人数、平均分、总分进行排名。 学习模块 例如:每天不限次数练习答题,不断学习 答题记录 例如:答题的记录都可以随时查看,查看错题,查看答案,还可以生成海报等。 [图片] [图片] 二、知识竞赛答题小程序规则二 本知识竞赛答题小程序活动主要有以下规则: 1.答题活动开始后才可以答题 2.答题活动结束后不能答题 3.每答对一题得10分,答题以每次累计最高分数为主要分数。类似于微信小游戏跳一跳计分规则 4.每天不限制答题的次数,可多次答题,以最高分数为主,破纪录后将统计最高分数 5.每次答题将有3张错题卡,答错3次后需重新开始答题 6.每次答题记录统计答题分数或次数,通过累计分数显示答题段位 7.答题可设置答题段位,答题次数越多积分越高,段位越高 8.答题排行榜分为积分榜和段位榜 9.结束答题后根据积分榜选出一等奖,二等奖,三等奖。或根据段位榜选出一等奖,二等奖,三等奖或参与奖 [图片] [图片] 如果搭建过程中遇到问题可到程序员锤哥公众号提问。
2022-04-26 - 云函数获取用户IP归属地
云函数获取当前用户IP归属地的极简代码: const cloud = require('wx-server-sdk') cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV }) exports.main = async () => { const wxContext = cloud.getWXContext() let opt = { uri: 'https://apis.map.qq.com/ws/location/v1/ip', qs: { ip: wxContext.CLIENTIP, key: 'HCDBZ-OHMA3-IMQ3R-*****-*****-YNBVU'//在这里获取这个key:https://lbs.qq.com }, json: true } return await rp(opt) } 10行代码,简单地实现了该功能。还不需要申请wx.getLocation接口。
2022-05-26 - 小程序云开发获取并保存用户IP属地
现在各大平台发表文章、评论等内容都显示出了用户的IP属地,现在来探讨一下小程序使用云开发怎么获取并保存用户IP属地。 1、获取到用户ip,这里演示使用云函数获取。 2、使用腾讯位置服务的WebService API的IP定位接口,获取归属地。 响应示例: { "status": 0, "message": "Success", "result": { "ip": "111.206.145.41", "location": { "lat": 39.90469, "lng": 116.40717 }, "ad_info": { "nation": "中国", "province": "北京市", "city": "北京市", "district": "", "adcode": 110000 } } } 演示代码: // 云函数入口文件 const cloud = require('wx-server-sdk') const axios = require('axios') cloud.init() // 云函数入口函数 exports.main = async (event, context) => { const wxContext = cloud.getWXContext(); var ip = wxContext.CLIENTIP ? wxContext.CLIENTIP : wxContext.CLIENTIPV6; if (ip) { const res = await axios.get("https://apis.map.qq.com/ws/location/v1/ip", { params: { ip: ip, key: "xxx" // 使用腾讯WebService API:https://lbs.qq.com/service/webService/webServiceGuide/webServiceIp } }); return res; } return null; }
2022-05-11 - 云数据库数据导入要点
导入时,有时会报[代码]invalid character ']' looking for beginning of value[代码]等错误,应该是最后一个元素后面有逗号。要点有下面几条: 数据记录与记录之间,不使用逗号分隔,不使用[]汇总全部记录; 每条记录中数组[],对象{}最后一个元素后面不能加逗号; 所有属性名称,以及字符串的值,全部使用双引号 系统:win7 工具版本:1.05.2107090
2022-02-25 - 「微信云托管」首篇实战|极简DEMO入门
一、项目介绍 此项目基于微信云托管能力范围编写,构建了一个简单的WEB后端服务,统计WEB网页的访问日志,并保存至serverless形态的mysql数据库中,然后在小程序中调用服务接口,获得访问次数。 项目包含微信云托管在单体运行中的基础能力使用,包含基础容器、mysql、微信小程序调用等。 项目技术栈:后端服务(nodejs + express)、WEB网页(原生js)、微信小程序(原生) 如果你已经体验过微信云托管基础能力,可以继续深入体验微信云托管在腾讯云其他产品的关联使用,日志监控等能力。 二、部署流程 (1) 访问github仓库,将项目下载到本地,打开IDE准备对项目进行改造 (2) 访问微信云托管官网,使用微信扫码,选择自己拟用到的小程序,进入开始创建环节。 [图片] (3) 这里环境名称根据自己用途填写即可,一般小程序项目创建一个环境即可,一个环境里可以包含多个服务,可满足绝大部分企业级需求。 (4) 如果你之前小程序中有使用过云开发,并登录过腾讯云官网配置过腾讯云其他云产品,为了方便使用,可在创建框中「网络类型」选择私人网络,将云托管环境配置到与之前相同的网络环境内(如果你不想这么做或者说你什么也没有过,直接系统默认创建即可) (5) 明确知悉云托管环境的费用,并同意服务条款,开始创建。 (6) 创建成功后,会显示如下效果: [图片] (7) 开始创建mysql数据库,如下图所示: [图片] [图片] (8) 填写密码后,mysql正在进行初始化,请注意这里的费用信息,初始化完成后如下图所示: [图片] (9) 点击右上角「数据库管理」按钮,进入数据库管理后台,开始创建一个库,如下图所示,这里库名叫 online (名称随意,若更改的话注意之后操作相应改变) [图片] [图片] (10) 将项目目录中 database.sql 文件导入到 online 库中,效果如下: [图片] (11) 返回微信云托管控制台,mysql栏中添加一个账户,专门用于编程调用,如下图所示: [图片] (12) 在目录werunserver/db.json中,填写mysql数据库信息,其中host和port信息在控制台中 [图片] { "host": "10.0.224.13",//上图中的冒号之前ip地址 "user": "code", //上一步设置的账户名称 "password": "*****", //上一步设置的密码 "port": "3306", //上图中的冒号之后数字 "database": "online" //之前创建的数据库 online } (13) 将项目保存,将项目目录werunserver作为根目录,上传私人git仓库,在这里已github为例。【注意一定是以werunserver为根目录 (14) 在微信云托管控制台,服务栏中,选择新建服务,如下图所示,记得勾选开启公网访问,名字随意设置(这里示例为aaa) [图片] (15) 创建完成后,进入服务管理,如下图所示,选择新建流水线 [图片] (16) 流水线新建配置如下,代码源第一次使用时需要授权: [图片] (17) 新建完成后,如下图所示,点击开始第一次构建: [图片] (18) 构建完成后,如下状态,此时我们就可以开始发布环节了 [图片] (19) 前往「部署发布」开始全量发布流程 [图片] [图片] (20) 发布后效果如下: [图片] (21) 返回服务列表,在服务详情中找到公网地址,复制到浏览器打开: [图片] (22) 由于配置的是低成本模式,所以首次访问存在容器加载过程,耐心等待即可(不断刷新过程中会出现大概几十秒的请求失败框,正常配置网络中,无伤大雅)访问成功后效果如下,每次刷新都会有记录: [图片] (23) 开始微信小程序的测试,将项目中 miniprogram 做为小程序根目录用微信开发者工具导入打开,填写第一步自己创建服务相同的小程序APPID (24) 在小程序开发者工具中打开 pages/index/index.js 文件,填写如下几处信息: [图片] (25) 重新运行,大功告成! [图片] 三、项目总结 本次部署实践后,你可以继续在此项目基础上进行修改,重新推送更新代码到仓库中,流水线会自动触发构建。 在新版本发布前,可以进行充分测试,云托管提供各种测试白名单配置;测试验证后可以进行灰度发布,最终到100%新版本接单,完成新版本的全量更替;要是嫌麻烦,相信自己的本事,可以全量发布,给个痛快! 后续在控制台中会支持自定义域名等更多功能,还在路途中,记得常看看更新。
2022-06-02 - 云托管征文活动火热进行中,参与即送200元代金券!更有Switch、Kindle等精美好礼
[图片] 很久很久以前,编程江湖就有一个流传至今的未解之谜:什么是世界上最好的编程语言?公元贰零贰贰年,后端技术江湖愈发风起云涌,一时间各路编程豪杰并起,用一身技术本领,演绎一出出拿手好戏,为自己心目中的最佳语言疯狂打Call。在不断地摸爬滚打后,人们不禁:是否有一招从天而降的功法,集各家编程语言之大成,融会贯通,让各路豪杰从眼花缭乱的技术选型中解放出来,在云上闯荡江湖更加轻松?少侠,请留步!欢迎体验腾讯云和微信团队联合推出的后端上云新姿势——微信云托管!汇集丰富开源模板一键部署,带你无需服务器,1分钟部署小程序/公众号/网站服务端。目前,已有数万个业务拥抱云托管,获得更加降本增效的开发生产模式。 [图片] 现在体验云托管,即可享受新用户三个月免费额度! 输出技术实践文章,还可额外获得价值 200 元的云托管代金券,人人有份! 征文主题微信云托管知识分享季 投稿时间长期有效 活动规则文章需要发布在微信开放社区;文章标题:文章的标题后需加后缀“ | 云托管征文”;文章发布后,需将文章链接附在本文的评论区下方;添加小助手微信号boqun1116,领取代金券:示例:一图读懂微信云托管丨云托管征文:https://cloud.tencent.com/developer/article/1842101奖项设置1、200元云托管代金券;优秀文章可任选腾讯视频VIP、QQ音乐VIP、QB、公仔等奖励;云开发公众号、技术社区等渠道署名宣传机会;微信云托管新功能优先体验;专家团队技术交流指导。彩蛋:优秀作者将受邀成为云托管布道师,赠送 Switch、Kindle、京东卡等精美礼品(具体奖品视贡献度而定)。 内容要求分享主题:围绕云托管功能,或针对项目的实践教程、功能介绍、使用心得等;分享形式:技术文章;文章必须为本人原创,不得有广告引流/洗稿/凑字数等行为。一经发现侵权,取消活动参与资格。了解微信云托管微信云托管是微信团队推出的 Serverless 后端项目托管服务,代替服务器,1分钟部署小程序/公众号后端服务。近期推出的「一键部署」功能,还支持通过 Node.js、PHP、Python、Java 和 Golang 等 5 种语言的示例模板,鼠标点一点,即可快速部署一个完整的后端服务,新用户也能轻松上手。 微信云托管官网(请在PC端访问以下地址) https://cloud.weixin.qq.com/ 微信云托管官方文档 https://developers.weixin.qq.com/miniprogram/dev/wxcloudrun/src/basic/intro.html 微信云托管系列教程 https://developers.weixin.qq.com/community/business/course/00068c2c0106c0667f5b01d015b80d 微信云托管专家1V1服务 https://cloud.tencent.com/act/pro/cloudrun *本活动最终解释权归云托管团队所有
2022-02-23 - 开通企业付款到零钱的整个过程
看到很多人在为开通企业付款到零钱而烦恼,以下说明一下我们开通的整个过程,仅供参考。 先说一下开通企业付款到零钱的必要条件: 1、入驻90天。(我的理解是上线至少90天) 2、连续正常支付30天。 3、保持健康交易。(无法解释,自己理解吧,别到处问了,不可能有官方来解释哪种是健康交易,哪种不是。) 说在前面: 1、我们开通的时候是在19年,当时的内部判断规则和现在的是不是一样,不得而知,大家自己考虑。 2、我们小程序是付费内容的业务,需要给博主支付到零钱,当时还做不到每天一定有正常流水,所以只能手动开通该功能。 以下是开通的过程: 1、每天早晚两个闹钟,提醒我们打开小程序支付;(第10天的时候忘了,前功尽弃,重新开始) 2、每次都是支付1分钱。(现在建议随机费用,每次不要相同) 3、在体验版上支付。(现在建议在线上版上支付,留个暗口,总能做到的) 4、硬写的支付代码,直接统一下单,拉起支付,没有与正常业务流程有关系。(现在建议最好与小程序正常的业务流程一致) 5、整个支付流程闭环,即从统一下单到支付回调,并应答,每次流程完整。(现在依然建议如此) 6、从头到尾,只有一个人负责此事,支付的也是同一个人。(现在建议如果允许,经常换人支付,看上去更正常嘛) 以上差不多就是当时过程的全部,如果现在再按此操作,还是不能开通,要不只能怪人品,要不就是现在后台规则变了,需要你自己去摸索了,官方是不可能正面回答这类问题的。
2021-03-19 - 微信小程序UI组件库合集
UI组件库合集,大家有遇到好的组件库,欢迎留言评论然后加入到文档里。 第一款: 官方WeUI组件库,地址 https://developers.weixin.qq.com/miniprogram/dev/extended/weui/ 预览码: [图片] 第二款: ColorUI:地址 https://github.com/weilanwl/ColorUI 预览码: [图片] 第三款: vantUI(又名:ZanUI):地址 https://youzan.github.io/vant-weapp/#/intro 预览码: [图片] 第四款: MinUI: 地址 https://meili.github.io/min/docs/minui/index.html 预览码: [图片] 第五款: iview-weapp:地址 https://weapp.iviewui.com/docs/guide/start 预览码: [图片] 第六款: WXRUI:暂无地址 预览码: [图片] 第七款: WuxUI:地址https://www.wuxui.com/#/introduce 预览码: [图片] 第八款: WussUI:地址 https://phonycode.github.io/wuss-weapp/quickstart.html 预览码: [图片] 第九款: TouchUI:地址 https://github.com/uileader/touchwx 预览码: [图片] 第十款: Hello UniApp: 地址 https://m3w.cn/uniapp 预览码: [图片] 第十一款: TaroUI:地址 https://taro-ui.jd.com/#/docs/introduction 预览码: [图片] 第十二款: Thor UI: 地址 https://thorui.cn/doc/ 预览码: [图片] 第十三款: GUI:https://github.com/Gensp/GUI 预览码: [图片] 第十四款: QyUI:暂无地址 预览码: [图片] 第十五款: WxaUI:暂无地址 预览码: [图片] 第十六款: kaiUI: github地址 https://github.com/Chaunjie/kai-ui 组件库文档:https://chaunjie.github.io/kui/dist/#/start 预览码: [图片] 第十七款: YsUI:暂无地址 预览码: [图片] 第十八款: BeeUI:git地址 http://ued.local.17173.com/gitlab/wxc/beeui.git 预览码: [图片] 第十九款: AntUI: 暂无地址 预览码: [图片] 第二十款: BleuUI:暂无地址 预览码: [图片] 第二十一款: uniydUI:暂无地址 预览码: [图片] 第二十二款: RovingUI:暂无地址 预览码: [图片] 第二十三款: DojayUI:暂无地址 预览码: [图片] 第二十四款: SkyUI:暂无地址 预览码: [图片] 第二十五款: YuUI:暂无地址 预览码: [图片] 第二十六款: wePyUI:暂无地址 预览码: [图片] 第二十七款: WXDUI:暂无地址 预览码: [图片] 第二十八款: XviewUI:暂无地址 预览码: [图片] 第二十九款: MinaUI:暂无地址 预览码: [图片] 第三十款: InyUI:暂无地址 预览码: [图片] 第三十一款: easyUI:地址 https://github.com/qq865738120/easyUI 预览码: [图片] 第三十二款 Kbone-UI: 地址 https://wechat-miniprogram.github.io/kboneui/ui/#/ 暂无预览码 第三十三款 VtuUi: 地址 https://github.com/jisida/VtuWeapp 预览码: [图片] 第三十四款 Lin-UI 地址:http://doc.mini.talelin.com/ 预览码: [图片] 第三十五款 GraceUI 地址: http://grace.hcoder.net/ 这个是收费的哦~ 预览码: [图片] 第三十六款 anna-remax-ui npm:https://www.npmjs.com/package/anna-remax-ui/v/1.0.12 anna-remax-ui 地址: https://annasearl.github.io/anna-remax-ui/components/general/button 预览码 [图片] 第三十七款 Olympus UI 地址:暂无 网易严选出品。 预览码 [图片] 第三十八款 AiYunXiaoUI 地址暂无 预览码 [图片] 第三十九款 visionUI npm:https://www.npmjs.com/package/vision-ui 预览码: [图片] 第四十款 AnimaUI(灵动UI) 地址:https://github.com/AnimaUI/wechat-miniprogram 预览码: [图片] 第四十一款 uView 地址:http://uviewui.com/components/quickstart.html 预览码: [图片] 第四十二款 firstUI 地址:https://www.firstui.cn/ 预览码: [图片]
2023-01-10 - ColorUI3.x 组件库 原生小程序版 MP CU
[图片] 介绍本项目为 [代码]colorUI3.x[代码] 微信小程序原生版。 [代码]colorUI3.x[代码] 默认只支持 [代码]uni-app[代码],本项目中 [代码]colorUI[代码] 框架为移植修改版。 [代码]colorUI3.x[代码] 地址: https://github.com/weilanwl/coloruiBeta 在线文档:https://mp.color-ui.com/ (主地址,挂在Github上的) 备用在线文档:https://mp-cu.izaizai.cn/ (防止Github抽风,挂在vercel.com上的,建议收藏下) 内置的vuex 渲染引擎参考了 wxMiniStore 项目。 准备配置需要先升级小程序开发工具到 [代码]2021-10-11[代码] 之后的版本, 然后,检查根目录下,[代码]project.config.json[代码] 配置文件内的 [代码]"setting"[代码] 节点下,是否配置了: "useCompilerPlugins": [ "sass" ] 如果没有配置,需要手动配置一下 由于小程序默认开启了 [代码]v2[代码] 的样式,在v2模式下,[代码]colorUI[代码] 部分样式会失效。 完整 [代码]colorUI[代码] 样式,需要在 [代码]app.json[代码] 文件内,删除 [代码]"style": "v2"[代码] 即可 "style": "v2" 框架配置您可单独设置一个 [代码]config.js[代码] 里面配置相关信息,然后暴露方法给 [代码]app.js[代码] 在全局引用 import { ColorUi } from './config' App({ ColorUi, //挂载到app上,此步骤必须要有! onLaunch() { } }) 然后在根目录的 [代码]app.scss[代码] 文件里引入相关框架的css文件。 @import './mp-cu/colorUI/scss/ui'; /* 实际项目中,可删除下面的相关文件和引用,因为图标太多,体积较大,可能你项目里并不需要这么多图标,建议自行添加需要的扩展icon图标引用。*/ /* @import './mp-sdk/icon/doc'; */ 更多相关内容,可访问:mp.color-ui.com/ 鸣谢感谢 【文晓港(ColorUI作者 / @weilanwl)】, 【胖虎(@bypanghu)】
2021-12-27 - 小程序的第六年,我们还能怎么玩?
2022 年是微信小程序上线以来的第六年。在今年的微信公开课 Pro 公开数据得知,微信小程序数量 300 万+(占全网小程序数量 43%),DAU(日均活跃用户数量)4.5 亿,同比增长 32%。活跃小程序稳步增长 41%,有交易的小程序数持续增长 28%。 [图片] 去年,微信小程序的生态趋于稳步发展趋势,生态能力也在不断扩建,甚至延伸到微信外,补足更多营销和服务场景,助力商家实现交易增长。 支持短信、邮件、App、网页外部链接跳转到微信小程序 上线微信客服,提供微信内(小程序、网页、公众号、搜一搜、支付)和微信外(企业官网、APP )多个接入口,无需添加微信好友也能实时处理客户消息 微信小程序可识别二维码,直接跳转到个人微信、微信群,缩短私域流量运营路径 视频号打通小程序,商家可在后台申请将小程序绑定视频号,并进行直播带货;支持小程序直接跳转视频号直播间,缩短用户的决策和交易路径,提高交易转化率 小程序与公众号、视频号、企业微信、朋友圈的互联互通,形成服务闭环,为企业提供更强的全链路营销能力。 但对于中长尾小商家和新入局的开发者来说,增长慢、转化差、收益低依旧是当下面临的困境。 用户从哪里来 如何更精准的触达用户 辛苦吸引来的用户,流失率高,转化率低 消费者对大促活动失去吸引力,企业该如何破局 App、网站、多个平台的小程序同时运营,如何做到统一的用户管理 …… 小程序还有哪些可突破的点,是否值得创业者或者传统企业入局? 小程序与外部生态,全渠道获客 结合近期微信生态能力升级内容,小程序正在不断拓展互联网边界,流量也不仅限于微信生态内,这或许可以给到小程序商家更多的运营空间和可能性。 2021 年初,微信支持短信、邮件、App、网页外部链接跳转到微信小程序,基于该开放能力,商家可全渠道向目标用户推送活动消息,用户点击链接直达小程序平台,了解详情。整个过程降低了用户的使用门槛,最大化提高营销内容的转化效果。 [图片] 2021 年中,有媒体发现在京东 App、小红书 App 里搜索知名品牌(如 GUCCI、CELINE),在品牌企业号里可直接进入该品牌微信小程序,无需切换 App。目前该功能已停止使用,倘若微信与外部生态的联合发展,或许将探索出更强大的互联网营销模式,赋予商家更大的发展空间。 [图片] 小程序与微信消息结合带来的品牌营销价值 今年春节期间,微信用户在对话框发送品牌文案,例如「康师傅、百事可乐」等,就可以看到专属表情雨,好友可以通过点击从天而降的福袋来获得现金红包、红包封面、商家福利等。品牌借助微信庞大的用户群体和社交属性,以与品牌和节日相关的元素,充分调用全民的参与积极性,有效进行品牌的曝光与传播。 更值得关注的是,表情雨还与小程序相通,品牌可以引导微信用户跳转到小程序领取福利,体验服务。小程序与微信消息相结合,品牌宣传与转化环环相扣,助力商家实现营销闭环。 [图片] 小程序与视频号、直播,优质内容带来粘性更强、路径更短的用户转化 微信视频号在近两年一直保持着小步快跑的姿态前进,更新内容多,新入驻的创造者也越来越多。取得这些成绩的原因在于视频号已经成为微信中的基础内容组件,它变成了一种原子化的产品工具,在微信生态内进行流转。 在近期的微信生态能力升级中: 公众号可跳转到视频号、同步直播状态,为视频号、直播间导流 直播间、视频号可挂载小程序,用户一键购买商品,提高内容变现能力 小程序内可直接跳转到视频号直播间,提高用户交易决策效率 [图片] 公众号为视频号、直播导流 [图片] 直播间&视频号挂载小程序,一键购买商品 小程序与视频号打通,为企业提供了更多连接用户的场景和方式。相较于传统的文字内容,短视频更加轻松、有趣、易懂,而直播更能拉近企业品牌与用户之间的距离,促使企业借助视频与直播新载体,通过传递品牌价值、产品种草、直播带货等,将内容和服务结合,形成更强的品牌营销效果。 接下来的一年,微信也将重点关注消费增长、能力释放、鼓励内容、作者激励。这对于既能通过视频号输出优质内容,也能借助小程序提供闭环服务的企业来说,将在流量获取和收益转化上有更大的突破。 [图片] 小程序与私域,助力企业连接用户,创造收益 「私域」这个词相信大家不陌生。构建私域流量池的价值在于,企业可以在营销活动中直接触达用户、反复触达用户,缩短产品营销的路径,强化品牌效果,从而达到更快更高的转化效果。这两年,从喜茶、百果园、完美日记等大品牌,再到家门口的早餐店,都在探索自己的私域流量运营之路。 作为微信生态的一员,企业微信是助力企业连接用户,创造价值的重要载体,也是构建私域流量池,提高用户服务效率和质量的重要工具。 企业微信与小程序互通,小程序可快速识别二维码(个人微信、微信群),让企业可以在私域流量的搭建和运营中获得更好的效果。但在人人皆可构建私域体系的时代里,如何更好地了解用户的需求、解决他们的实际问题、提供更加周到的服务,更是每一个企业应进一步思考和落实的关键点。 [图片] 小程序与超级 App,打造全平台统一用户身份 超级 App,即允许在一个应用里动态加载多个小程序,覆盖多个场景和功能服务。它可以根据用户请求按需加载,同时具有用户统一身份的特点。 超级 App 对于企业有什么机会呢? 企业的业务逐步线上化,可能需要企业在原有的 App 上实现多项服务(行业资讯、社区交流、产品交易、营销活动等)。如果能让 App 与小程序的结合,App 将不会因为功能过多影响性能,用户也不需要安装无数个应用才能体验各种各样的服务。而企业还能通过不断运营小程序,将流量沉淀在自己的 App 中,或许超级 App 将激发企业更大的商业潜能。 企业数字化转型,是顺应时代发展的客观要求,更是企业加强以客户为中心的产品升级、业务优化和服务提升的重要措施。小程序作为企业数字转型的底层基础设施之一,它正在以一种低成本、高效率的形式验证企业数字化转型的成效。与此同时,也为企业带来更多的可能性。对于中长尾小商家和新入局的开发者,是挑战,也是机会。 但企业在解决如何突破用户和收益增长瓶颈等问题的同时,也需要不断提高平台服务质量,提供更有保障的产品和服务,才能真正满足客户的需求,获得更持久的发展。 知晓云是爱范儿旗下的数字化服务平台, 提供云端一体化服务,以「数据」+「营销」双引擎,驱动产业数字化升级。 2017 年 8 月,知晓云正式上线,是国内首家专注于小程序开发的后端云服务,为开发者提供最低门槛的 Serverless 无服务架构接入体验。 知晓云作为数字化转型浪潮中的支撑者,通过运用云计算、大数据、物联网等前沿技术,为政府单位、企业、SaaS 服务商、 独立开发者提供拿来即用的开发工具、运营工具、营销工具、数据分析引擎、品牌创意营销等产品和服务。全行业全场景的数字化解决方案,助力政企解锁数据价值, 实现降本增效,驱动业务持续增长。
2022-03-07 - 【笔记】WXSS中定位属性position的使用总结
前言 WXSS (WeiXin Style Sheets)是微信开发的样式语言,用于描述 WXML 的组件样式,它具有CSS 的大部分特性。关于在定位时常用到的属性position,文档中没有对应的描述,所以我自己总结了position的一些基础知识点。 定位 定位的目的是为了更好控制和摆放box,从而实现需要的网页布局。与flex布局的容器、坐标轴等概念不同,定位布局可以轻松任意指定box的摆放位置,定位的属性分为两种,一是边的偏移,二是定位的方式。 边偏移 边偏移有四个属性,分别是top,left,right和bottom,对应于基于顶部,左边,右边和底部的偏移。注意,如果position是默认值,也就是静态定位static时,指定边偏移是无效的行为。例如下面的代码,对于static-view类选择器,我们没有指定position的值,因此position设为默认值static,此时指定属性left是无效的行为: [代码]/* index.wxss */ .static-view{ height: 100px; width: 100px; background-color:red; left:50px; /* position: static; */ } [代码] 显示效果如下,红色正方形仍然贴着屏幕左端: [图片] 定位方式 定位的方式即position的值,常用的有五种,分别是静态定位static(默认值), 相对定位relative, 绝对定位absolute, 固定定位fixed和粘性定位sticky。接下来分别介绍一下各种定位方式的区别。 静态定位 static 静态定位是所有元素的默认定位方式,当你没有为元素指定position的值时,默认值就是static,举个例子,在.wxml中渲染三个不同颜色的view,它们的position都为默认的static: [代码]<!-- index.wxml --> <view class='view-red'></view> <view class='view-blue'></view> <view class='view-green'></view> /* index.wxss */ .view-red{ height: 100px; width: 100px; background-color:red; /* position: static; */ } [代码] 显示效果如下,三个view按照标准流的方式排列: [图片] 注意,在静态定位状态下,无法通过边偏移属性(top、bottom、left或right)来改变元素的位置。 相对定位 relative 相对定位是将元素相对于它的正常位置进行定位的,所谓的正常位置就是position为static时元素在标准文档流中的位置。相对定位的position值为relative,我们修改刚刚红色正方形的position为relative,并设置left为50px: [代码]/* index.wxss */ .view-red{ height: 100px; width: 100px; background-color:red; left: 50px; position: relative; } [代码] 显示效果如下,可以看到红色正方形相对于刚刚的位置向右偏移了50px: [图片] 绝对定位 absolute 绝对定位比较特殊,因为它脱离了标准文档流,以其父容器或父容器中最近的非static(其他四种定位方式)元素的位置为参考。如果没有找到这样的非static元素,就以page作为参考。绝对定位的position值为absolute,我们修改蓝色正方形的position为absolute,并设置left为100px,top为100px;: [代码]/* index.wxss */ .view-blue{ height: 100px; width: 100px; background-color:blue; left:100px; top:100px; position: absolute; } [代码] 显示效果如下,由于没有非static的父容器,view-blue以page为参考位置,left和top指定了它的偏移。还可以看到,绿色正方形在原来的位置向上移动了,原因是蓝色正方形使用了绝对定位,脱离了标准文档流,就为绿色正方形“腾出”了它原来的位置。 [图片] 我们换个例子,让红色正方形作为蓝色正方形的父容器,代码如下: [代码]<!-- index.wxml --> <view class='view-red'> <view class='view-blue'></view> </view> /* index.wxss */ .view-red{ height: 300px; width: 300px; background-color:red; left: 50px; position: relative; } .view-blue{ height: 100px; width: 100px; background-color:blue; left:200px; top:100px; position: absolute; } [代码] 显示效果如下,可以看出此时蓝色正方形以红色正方形的位置作为参考: [图片] 固定定位 fixed 固定定位也脱离了标准文档流,可以把它理解为以page为参考的绝对定位,固定定位的position值为fixed,我们常常会在顶部或底部导航栏中使用固定定位。以自定义tabBar组件为例: [代码].tab-bar { position: fixed; bottom: 0; left: 0; right: 0; height: 48px; background: white; display: flex; padding-bottom: env(safe-area-inset-bottom); } [代码] 显示效果如下,无论如何滚动,tabBar都会固定在屏幕底部。 [图片] 关于自定义tabbar组件,可以查看该代码片段:自定义tabBar组件 粘性定位 sticky 粘性定位的元素依赖于用户的滚动,在相对定位和固定定位之间切换。一般情况下,sticky等同于relative,当页面滚动超出目标区域时,它便切换为fixed,固定在目标位置。目标位置通过指定top, left, right和bottom来设置。修改代码如下: [代码]<!-- index.wxml --> <view style="height:100px"></view> <view class='view-blue'></view> <view class='view-green'></view> <view style="height: 3000px;"></view> /* index.wxss */ .view-blue{ height: 100px; width: 100px; background-color:blue; position: relative; } .view-green{ height: 100px; width: 100px; background-color:green; position: sticky; top:0px; } [代码] 显示如下,此时绿色正方形是相对定位,在标准文档流中: [图片] 下拉滚动条100px,可以看出绿色正方形仍为相对定位: [图片] 继续下拉滚动条,可以发现蓝色正方形消失,但绿色正方形一直处于屏幕顶端,此时已经切换为固定定位: [图片] 覆盖问题 在开发中,我们常常使用子元素绝对定位,父元素相对定位的方法来控制布局,但是仍会遇到不同元素的覆盖现象,可以通过设置属性z-index来解决。z-index的默认值是0,取值越大,定位元素在层叠次序中就越优先;如果取值相同,则以后渲染的优先。 总结 定位布局是各自布局方法中较为简单的一种,它的属性一是边偏移,二是定位方式,两者配合使用才可以构造出美观的布局。
2021-11-10 - 在线答题小程序关于完形填空题的设计
~ 在线答题小程序关于完形填空题的设计 ~ [图片] 上图为一个普普通通的完形填空题,但是要反馈到小程序里面是如何设计呢? 其实这个问题,从我开始做答题小程序就开始困扰着我,直到现在尚未解决 ~ 我其实也做过调研,如下图所示 [图片] ~ [图片] ~ 这是我在调研过程中,极少有的支持这种场景的小程序, 希望吧,每次总结都有一定的收获,今天算是把这个问题抛出来了 ~ 完形填空题其实存在的一个问题就是,是把整个题做为一个题,还是完形填空作为几个题,几个题的话,通过一种什么方式关联起来 可以很肯定,单独创建一个题型类别,代表完形填空 比如typecode= 05 typename = 完形填空 该题的数据结构,就是title,list,其中list里面包含了每个题的细节信息。 本次先整理这么多,未完待续
2021-07-24 - 答题小程序中完形填空的设计
离寒假结束还有一段时间,起 作为华南十八线小县城的一个高三英语老师,不管对学生还是老师,他们的寒假已经没有了, 作为普通社畜大年初七已经奔走在岗位了,我们也一样,初七学校已经安排在家远程授课,在科目校外培训严打的今天,学校老师的远程授课最近逐渐常规化了 除了正常授课之外,如何给学生布置一定的课外(课后)作业,这个问题其实,我在新冠爆发的20年就开始着手准备了 刚开始做 了一个很基本的能满足学生答题的小程序到现在持续打磨了二年了,其实也不算持续,仅仅是在每年的寒暑假重拾起来,每次都会有一定迭代 这个寒假也不例外,就是增加对了完形填空的支持 正常的题目都是 一个题目,一个活多个空 完形填空是, 一段信息在前,后面多个空在后,结构是不一样的,我对这块也迟迟没有落地,实现难度确实存在,但是主要的问题是我从内心觉得在手机上做完形填空那就是很反人类的一件事情 所以我的主动性很难调动起来 要不是学科主任的不断催我,我实在是没有这个机会去实现。 其实在之前我也调研过很多,基本上所有的答题小程序都不会支持对这种题型,但是我前几天看到了一个完美解决了 而且解决的 很有创新,他没有把完形填空作为一个全新题型,而是融合到了普通的填空题之中 就是将前面的一段话跟第一个空的标题结合在一起 [图片] 8 [图片] 体验了这个小程序,我的思路也就有了 1 [图片] 2 [图片] 3 [图片] 4 [图片] 5 [图片] 6 [图片] 7 答题小程序中完形填空的设计 除此之外,一般的论述题或者简答题都是同样的设计可以参考的
2022-02-11 - 单词天天斗 (毕业设计/实战小程序学习/微信小程序完整项目)
单词天天斗 (毕业设计/实战小程序学习/微信小程序完整项目) 介绍 小程序我们都很熟悉,它是一种不用下载安装就能使用的、基于微信容器的轻应用。并且微信小程序提供了云开发能力,即无需搭建服务器就可实现后端服务,提供了数据库、云存储、云函数等能力。 该项目基于「微信小程序」原生框架和「微信小程序云开发」实现单词对战类小程序,支持好友对战、随机匹配、人机对战三种不同模式的「对战模式」;另外提供「每日词汇」、「生词本」、「排行榜」、「设置」等功能,实现完整的业务闭环。 单词库包含从小学、初中、高中、四六级、考研、雅思等常需掌握的词汇,支持自定义词库,支持自定义拓展无限本单词书。 技术栈主要为微信小程序、云开发、TypeScript 等,从头搭建项目,基于 git 管理代码版本,使用 eslint 作为代码格式校验,并且组件化拆分页面,前端和云函数均采用 TypeScript。其中将大量实践小程序能力,比如用户信息获取、用户登录、全局状态管理、路由、wxs、npm 包、音频播放、震动、转发分享、动画、云数据库等。 项目提供完整设计稿,项目演示可查看微信小程序「单词天天斗」,扫码体验 ↓ [图片] 适宜人群 毕设参考:项目文档齐全、难度合适、技术广度大、业务闭环,含项目解析文档和教程,这是一个超级适合作为毕设学习的小程序项目。 私有化部署运营:无论你是想通过小程序变现,还是想给自己的「英语课程班级」增加一个支持词汇和单词书自定义的学习渠道,这个都是一个「企业级」的项目选择。 有小程序的前端开发经验,想尝试全栈开发,快速搭建全栈项目的同学。 掌握一定 JavaScript 语法基础,想通过一个精炼的实战案例,来整体地学习前端开发的同学。 想了解小程序开发知识的互联网从业人员。 对小程序开发感兴趣的 IT 技术爱好者。 想要开发一个「对战」类目的小程序,比如单词 PK,公务员考试题目 PK,诗词 PK,驾照考试 PK 等都可以参考该项目实现。 需求介绍 首页 [图片] 首页主要由用户信息展示、提示卡和单词书工具栏、核心功能入口区、底部栏几个模块组成,详细介绍如下: 用户信息展示:当用户已经授权过用户信息的情况下,展示用户授权的头像和昵称信息,如果用户还未授权,则展示昵称为「神秘学霸 (点击登录)」和默认头像,点击后可进行用户信息授权。另外需要展示用户的词力值,对战模式的对站局数和胜利局数。 工具栏:用户当前的「提示卡数目」和当前选择的「单词书的缩写」。点击提示卡后,弹窗提示提示卡的作用;点击单词书后,弹出单词书选择的浮窗列表,可以上滑无限加载,单词书顺序支持配置,单词书列表每项需要展示单词书的完整名称和缩写、词汇数量、选择人数。 核心功能入口:随机匹配、好友对战、每日词汇、生词本入口,当点击除了「生词本」外的其他项,若用户还未进行过用户信息授权,则拉起授权,授权后跳转至对应功能页,「好友对战」入口循环放大缩小动画。 底部栏:排行榜入口、关于&公告入口、建议反馈入口,点击后跳转至不同的功能页,建议反馈跳转页使用微信提供的feedback实现。 其他:整页背景图及设置入口,「设置」增加无限旋转动画,点击后跳转至「设置页」。新用户打开小程序后,增加「添加小程序」引导,点击弹窗后打开操作指南,当点击「我知道了」后,打开小程序不再弹窗引导。 对战模式 [图片] 单词对战模式主要由好友对战、随机匹配、人机对战三种不同的模式组成,详细介绍如下: 对战通用介绍 入口:在小程序首页可通过点击「好友对战」、「随机匹配」分别进入两种不同的模式,在「随机匹配」模式中可开始「人机对战」。 答题模式:对战模式使用根据单词选择单词释义的形式进行答题。 对局词汇数目:每一局对战的词汇数目可在「设置页」中进行设置,支持每局 8、10、12、15、20、30 个词汇进行对战,对战词汇数由对战创建者决定。 对局词汇分类:对战的单词书可通过首页「单词书」选择进行更换,对战的单词分类由对战创建者决定。 对战过程分数机制:对战过程中,当有任意一方选择后,显示该题答题者的得分情况,每道题满分 100 分,选择越快分数越高,选择正确最多获得 100 分,最少获得 1 分,选择错误扣除 10 分。 对战过程答题机制:每道题最长答题时间为 10 秒,当倒计时结束,如若用户还未答题,判定为该题答题错误。当双方都答题结束,切换下一题。 提示卡机制:答题过程中,用户可使用提示卡进行答题,当提示卡数目大于 0 时,点击提示卡则进行选择,得分机制和普通答题一致,但使用提示卡答题的单词需要自动加入生词本。 对战结束:每局对战结束后,需要显示该局的对战结果,包含:本局所有词汇选择结果、用户的选择得分、对战输赢情况。对战结束后根据本局分数计算增加相应的词力值,对战结束页可选择「分享战绩」、「再来一局」。 分享战绩:对战结束后,用户可分享本局对战结果给任意用户或群,用户点击分享结果可进入查看本局的对战结果,但不显示「分享战绩」和「再来一局」,换成显示「创建好友对战」和「返回首页」。 再来一局:对战结束后,对战创建者显示「再来一局」,对战加入者显示「等待创建对战房间」,当房主点击再来一局创建同上一局对战词汇分类和数目相同的对战,另外一个用户由「等待创建对战房间」变为「再来一局」,点击后可加入房间。 对战检测:当对战过程中,有任一用户退出对战则结束对战,提示「对方逃离」后立即进行结算。对战过程中,如果倒计时结束5秒了还没切换至下一题,则认为有用户连接中断,也同样显示「对方逃离」后进行对战结算。 对战过程其他功能:答题错误或者使用提示卡的单词自动加入用户生词本,题目切换时自动播放单词发音,对战过程中播放对战背景音乐,用户可随时开启和关闭。 好友对战 可通过首页「好友对战」创建好友对战房间,可将房间发送到个人或群聊,其他用户点击分享结果后,可点击「加入房间」进行对战加入,房间创建者「好友邀请」按钮变为「开始对战」,点击后双方开始对局。 随机匹配 随机匹配不强制每局的单词对战数目选择一样,只需单词书选择一致的用户就可以匹配到一起进行对战。 如果点击「随机匹配」后,没有已经创建好的随机匹配房间,则创建一个对战房间,等待匹配其他后面点击进入的用户。 如果 2 分钟后,还是没匹配到用户,则开始一局人机对战模式。 人机对战 可在随机匹配页面进入人机对战模式。 人机的用户数据随机抽取使用数据库中预设的用户数据作为头像和昵称展示。 人机选择的正确率为 75% ,人机的选择时间随机在 2 ~ 3 秒之间,在用户选择后 200 毫秒,如果人机还没选择,则人机立即进行答题,减少用户等待时间。 每日词汇 [图片] 答题模式:每日词汇可根据用户选择的单词书进行答题,根据单词选择单词释义。 生命值:每局答题拥有三条生命值,每答错一次则扣除一条生命值,但生命值没有后,可通过分享小程序获取一次复活机会,当机会完全没有后,显示再来一局。 答题分数排名机制:每答题正确一题,可获得一分,一局对战的分数进行叠加,对战结束时使用当前得分和自己的历史最高得分进行比较,如果高于历史最高得分,则记录分数和当前选择的单词书,用于「每日词汇」排行。 倒计时:每道答题时间限制为30秒,如果没有答题则判定为错误。 其他:答题过程中可以使用答题卡进行答题,可以点击「播放发音」选项进行该题单词发音。 其他 (生词本 - 排行榜 - 设置) [图片] 生词本 列表:显示用户在「对战模式」和「每日词汇」中答题错误和使用提示卡进行答题的单词,列表按照词汇加入时间进行排序,先显示最新加入的生词,列表可无限上拉加载。 列表中默认不显示生词的释义,可通过点击「单词」查看释义。 长按生词可切换「删除」生词和「播放单词发音」选项。 排行榜 排行榜支持按照「词力值」和「每日词汇最高分数」进行排序,均最多显示前20名的用户头像、昵称信息。 排行榜底部可根据当前排行榜类型显示自己的排名信息。 设置 对战词汇数目:可设置「对战模式」的每局对战词汇数量。 可设置「对战模式」和「每日词汇」答题过程中是否默认播放背景音乐、是否默认播放单词发音、错题时是否震动。 生词本:一键清空单词本所有数据。 用户信息:可在设置中更新用户的昵称和头像信息,支持自定义昵称和头像。 关于入口 && 微信联系方式。 技术栈 前端:微信小程序原生框架。 服务:微信小程序云开发。 编程语言:typeScript、JavaScript、CSS。 全局状态管理:一个基于微信小程序的mini全局状态管理库 wxMiniStore。 前端路由管理:The router for Wechat Miniprogram - wxapp-router 全局事件管理:Tiny 200 byte functional event emitter / pubsub - mitt 服务端路由、控制器等:轻量级原生实现,支持路由、控制器、Model 数据操作。 动画实现:小程序原生动画,css 动画。 文档 微信小程序云开发:手摸手开发单词对战小程序 (编写中) 基础知识:前端开发快速上手 基础知识:微信小程序开发快速上手 基础知识:微信小程序云开发快速上手 项目新建:小程序TypeScript云开发项目创建及项目工程化 首页样式:首页需求介绍及布局样式开发 首页开发:全局状态管理及登录的实现 首页交互:数据导入及首页数据渲染 首页功能:单词书的分页加载及选择 单词对战:对战需求介绍及全局路由管理 对战创建:用户信息获取及对战房间创建 对战加入:好友邀请及加入好友房间的实现 对战开始:对战过程的实现及题目切换 对战结算:对战结果展示及再来一局的实现 对战匹配:随机匹配的实现及匹配高并发处理 人机模式:人机对战的实现及全局事件管理 对战总结:对战模式其他优化及对战模式实现总结 每日词汇:需求介绍及布局样式开发 词汇选择:答题交互实现及题目切换 词汇结算:任务复活及再来一局的实现 生词本:生词本实现及页面级上拉分页加载实现 排行榜:词力值及每日词汇排行实现 其他页面:设置页及基于富文本的关于页实现 总结:独立开发之旅及项目复盘 附录:小程序调试技巧 附录:数据库设计 附录:小程序自动化脚本部署及源码下载 部署教程 在线文档 部署文档:http://words-pk-deploy.i7xy.cn/ 部署视频教程 部署视频教程 其他 联系我 微信:34805850 后台 [图片] 项目开源 github 单词天天斗:https://github.com/arleyGuoLei/wechat-app-words-pk gitee:https://gitee.com/arley66/wechat-app-words-pk
2022-04-05 - 虚拟业务指南请收好。
在小程序生态中,基于苹果运营规范,小程序内暂不支持iOS端虚拟支付业务。为此小编为大家整理了一份虚拟支付业务指南,希望大家在做虚拟业务时有所帮助: [视频] 那么,到底什么是虚拟支付业务呢? 虚拟支付业务是指购买非实物商品。比如:VIP会员、充值、录制课程、录制音频视频等虚拟产品。目前iOS端暂不支持虚拟支付业务。 我们常见iOS虚拟支付的不合规示例有哪些呢? 示例一 :小程序内存在付费购买虚拟内容或道具。商品多体现为提前编辑好的、录制好的虚拟商品。如录制视频课程、游戏道具。 整改建议 :建议去除小程序内所有付费购买虚拟服务,并根据提示修改相关内容及文案,文案可参照“由于相关规范,iOS功能暂不可用”。 [图片] 示例二 :付费解锁优质服务。多体现为提供虚拟商品的小程序可通过支付购买、开通虚拟会员等形式,体验小程序付费服务。比如:支付阅读章节小说、同城生活服务平台付费发帖/付费置顶等。 整改建议 :建议可以关闭iOS端虚拟支付通道,并将【马上充值】更改为【由于相关规范,iOS功能暂不可用】,并不再提供iOS端会员服务。 [图片] 示例三 :关闭iOS端虚拟支付功能后,虚拟商品页面仍然保留货架价格标签展示、购买/付费/订阅等功能或按钮。 整改建议 :建议去除小程序中的虚拟商品的价格展示,并更改为【免费】;并将【订阅 ¥128】更改为【由于相关规范,iOS功能暂不可用】,并不再提供iOS端虚拟商品购买服务。 [图片] 示例四 :关闭iOS端虚拟支付功能后,提供引导用户前往其他支付的路径/文案,完成虚拟支付闭环。 整 改建议 :建议去除iOS端小程序内引导用户前往其他支付路径/文案,并不再提供iOS端虚拟商品购买服务。 [图片] 示例五 :小程序含需要付费的虚拟商品,并设置限时免费的服务,限时免费结束后需付费才能继续提供服务。 整改建议 :建议将iOS端小程序中所有虚拟付费内容更改为免费,并不再提供iOS端虚拟商品购买服务。 [图片] 示例六 :关闭iOS端虚拟支付功能后,小程序中虚拟产品页面不可以含有付费性质的关键字(如:购买、已购、付费、支付等),包括但不限于功能按钮、功能页面、支付提示及任何商品介绍等。 整改建议 :建议将小程序iOS端虚拟产品页面中的文案/按钮/功能tab含有限制的关键字更改为【免费】或删除。并不再提供iOS端虚拟商品购买服务。 [图片] 如小程序内存在以上不合规的虚拟支付内容,请开发者重视并及时整改。对于首次违规的小程序,平台将下发站内信整改通知,并给予三天整改时间,请开发者按照提示在限期内完成整改。平台将会对到期未完成整改的小程序进行搜索策略调整,并在小程序功能使用上进行一定的限制,直到小程序完成内容整改。
2020-04-23 - 小程序云开发如何根据“某个字段中的值是否包含某个字”来查询出所有符合条件的数据?
比如{ "word":"爱吃苹果", "name":"aaa" }, { "word":"爱吃香蕉", "name":"bbb" } 我想查询出所有 word字段的值包含“吃”这个字 的记录
2021-02-21 - 浅谈javascript的原型和原型链(新手懵懂想学会原型链?看这篇文章就足够啦!!!)
由于小程序前端页面涉及到一些数组及对象的方法,当然也可以在原型链上新增自己所需的一些方法,因此本篇文章对于原型链做个大概介绍。 本篇文章我将从概念和对应题目知识点讲起,希望大家能有所收获 一、原型 ①所有引用类型都有一个_proto_(隐式原型)属性(类似链表中的next指针), 链表可以通过.next访问下个元素,原型中可通过._proto_访问上一级元素。 [图片] ②所有类都有一个prototype(原型)属性,例如:Object,Function,Array ③所有引用类型的_proto_属性指向它构造函数的prototype 例如:arr是一个数组实例,那么arr._proto_=Array.prototype <br/><br/> 二、原型链 当访问一个对象的某个属性时,会先在这个对象本身上查找,如果没有找到,则会去它的_proto_上查找,即它的构造函数的prototype,如果还没有找到就会继续在构造函数prototype的_proto_中查找,这样一层一层向上查找就会形成一个链式结构,我们称为原型链。 下面例子有助于你对原型链的理解: arr为Array数组的实例 arr._proto_=Array.prototype Array.prototype._proto_=Object.prototype 分析:arr这个数组实例,沿着原型链找,找到数组的原型对象,数组这个类沿着原型链找,找到对象的原型对象(最高级),因此也可以用arr._proto_._proto_来找到Object.prototype,类似链表中的next指针,只不过_proto_是往上找。 <br/><br/> 面试真题: 题目一: instanceof的原理,并用代码实现 <br/> 分析:如果A沿着原型链能找到B.prototype,那么A instanceof B 为true(用_proto_来找) 解法:遍历A的原型链,如果找到B.prototype,返回true,否则返回false [代码]const instanceof =(A,B)=>{ let p = A; while(p){ if(p === B.prototype){ return true; } p = p._proto_; } return false; } [代码] <br/> 题目二: var foo = {}, F = function(){}; Object.prototype.a = ‘value a’; Function.prototype.b = ‘value b’; console.log(foo.a); console.log(foo.b); console.log(F.a); console.log(F.b); <br/> 分析:如果在A对象上没找到x属性,那么会沿着原型链找x属性。(如果A为函数实例,那么A上面找不到,就去找Function这个类上有没有挂载x属性,如果没有就继续往上找到Object原型对象上有没有x属性) 解法:明确foo和F变量的原型链,沿着原型链找a属性和b属性 因此答案为: [代码]'value a' 'undefined' 'value a' 'value b' [代码] foo这个对象实例上没有b属性,是因为原型链不能往下找,只能一层一层往上找,即对象实例不能腆着脸问他的下级Function有没有挂载b这个属性 <br></br><br></br> 觉得本篇文章对你有帮助的请不要忘记一键三连加关注~~ 你的支持就是对我最大的动力!! 会继续努力码更多的精品文章!!! 欢迎大家关注我的公众号:Smooth前端成长记录 公众号同步更新CSDN博客内容,想方便阅读博客的C友可以来关注我的公众号以便获得更优良的阅读体验~ [图片]
2021-11-07 - 基于小程序云开的会务通小程序设计方案
功能介绍包括大会资讯,参会流程,特邀嘉宾,会场信息,专题论坛,交流沙龙等功能,采用腾讯提供的小程序云开发解决方案,无须服务器和域名 技术运用本项目使用微信小程序平台进行开发。 使用腾讯专门的小程序云开发技术,云资源包含云函数,数据库,带宽,存储空间,定时器等,资源配额价格低廉,无需域名和服务器即可搭建。 小程序本身的即用即走,适合小工具的使用场景,也适合快速开发迭代。 云开发技术采用腾讯内部链路,没有被黑客攻击的风险,安全性高且免维护。 资源承载力可根据业务发展需要随时弹性扩展。 预约管理:开始/截止时间/人数均可灵活设置,可以自定义客户预约填写的数据项 预约凭证:支持线下到场后校验签到/核销/二维码自助签到等多种方式详尽的 预约数据:支持预约名单数据导出Excel,打印 [图片] [图片] [图片] [图片] [图片]
2022-02-28 - Painter 一款轻量级的小程序海报生成组件
生成海报相信大家有的人都做过,但是canvas绘图的坑太多。大家可以试试这个组件。然后附上楼下大哥做的可视化拖拽生成painter代码的工具:链接地址https://developers.weixin.qq.com/community/develop/article/doc/000e222d9bcc305c5739c718d56813
2019-09-27 - 表单组件使用小练习——制作一套微信风格的动态表单
表单组件使用小练习笔记——制作一套微信风格的动态表单 写在前面 使用git仓库的quick start进行快速体验一番: WeuiDynamicForm (gitee.com) 一、 效果 一次网络维修的工单提交流程是这样的: 用户选择服务->小程序获取该服务所指定的表单设置数据->小程序展示表单-> 用户按照格式进行填写->小程序根据表单设置进行数据校验->小程序提交表单 [图片] 二、需求 通过json或js数据对表单进行渲染 表单组件可以输入和输出数据 三、思路 [图片] 传入一个组件数据对象list,通过[代码]wx:for[代码]和[代码]wx:if[代码]去判断“组件类型、组件设置”; 表单组件能够正常将值传递到表单待校验的数据中; 伪代码: [图片] 四、开干 1、通过表单设置对表单进行渲染 (一) 新建项目,通过NPM的方式引入同微信原生视觉体验一致的UI组件库weui (1)通过useExtendedLib方式引入weui组件库 通过npm或useExtendedLib方式引入组件库 | 微信开放社区 (qq.com) 顺便看看官方的组件展示,可以归纳为以下这些 选择列表:选项会以列表的方式展示 [图片] 输入框包含标题:一种样式; [图片] 输入框包含标题及按钮; [图片] 日期输入框; [图片] 图片选择框 [图片] (2)先放下一个大表单 [图片] (3) 再搭建一个单一表单组件的样子 ,通过[代码]wx:for[代码]进行遍历, [图片] (二)将单个组件的设置传入对应的表单组件中 [图片] 其实就是把每个元素的设置都传进去就好了,输入框同理 (三)将部分特殊的组件独立出一个组件 可以不分开,但考虑到代码的整洁,决定将一些复杂的组件独立出来 这里以图片选择组件为例 (1)图片选择器 创建一个新的组件,组件中引入weui的uploader,然后将官方文档的[代码]wxml[代码] [代码]js[代码]代码粘贴进来Uploader | 微信开放文档 (qq.com) [图片] [代码]{ "component": true, "usingComponents": { "mp-uploader": "weui-miniprogram/uploader/uploader" } } [代码] 在大表单中引入自定义组件,包括两步,在json中配置[代码]usingComponents[代码],然后放入组件的标签,传入单个表单组件的数据 [图片] 再在自定义组件中,将部分官方放的静态数据替换成动态的 [图片] 从官方文档中,我们可以看出这个图片上传组件的工作流程是这样的: [图片] 那我们的工作就简单了,我们只需要实现[代码]uploadFile[代码]里面的上传函数即可,最后在[代码]uploadSuccess[代码]方法里面将事件传递出去 组件间通讯,仅传递事件 如图,我们需要在[代码]my-uploader.js[代码]里的[代码]uploadSuccess[代码]里面使用[代码]this.triggerEvent[代码]将事件传递上去,注意,这只是说明操作逻辑的图,不代表实际传递过程。 [图片] 操作过程 在最外层的[代码]<my-uploader/>[代码]里绑定事件 在[代码]my-uploader.js[代码]的图片上传成功回调里面使用[代码]this.triggerEvent()[代码]触发事件 [图片] [图片] (2)图形验证码组件 [图片] 我们需要实现验证码的生成,将组件输入框及验证码加密后的数据传递到表单页中 图像生成:使用云函数配合[代码]captchapng[代码]库生成base64编码的验证码图片字符串并返回; [图片] 验证码校验:将用户输入结果使用配置数据库的盐值进行解密 [代码]npm install --save captchapng[代码] [代码]var captchapng = require('captchapng') [代码] [代码] var vcodeInt = parseInt(Math.random() * 9000 + 1000) var vcodeRaw = new captchapng(80, 30, vcodeInt) // width,height,numeric captcha vcodeRaw.color(0, 0, 0, 0) // First color: background (red, green, blue, alpha) vcodeRaw.color(80, 80, 80, 255) // Second color: paint (red, green, blue, alpha) var vcodeRaw64 = vcodeRaw.getBase64() var vcodeBase64 = new Buffer(vcodeRaw64, 'base64') //选择性进行MD5加盐 //设置加密字符串 var salt = '' //在原来的字符串的基础上加上一些特殊文本 var vcodeEncrypt = md5(salt + vcodeInt) return { openid: OPENID, vcodeImg: vcodeBase64.toString('base64', 0), vcodeEncrypt: vcodeEncrypt } [代码] 拿到图片的base64字符串后,在<image/>里进行回显 [图片] 当然,图片的点击会再次召唤新的验证码 2、表单数据校验 Form | 微信开放文档 (qq.com) 从文档得知,表单数据校验需要设置“规则”和“需校验数据”,重点:了解“规则”和“数据”是如何联系的 (一)传入规则及数据 规则设置及传入 将包含一整个表单规则的数组丢进去即可 [图片] 需校验的数据 将整个表单的数据传入即可 [图片] (二)将“规则”和“数据”联系起来 (1)阅读官方的规则,发现是这样连起来的 规则名称对应models对象的一个结点名称 [代码] <mp-form id="form" rules="{{formConfig.rules}}" models="{{formData}}"> [代码] [代码]{ name: 'ticketClass', rules: {required: true, message: '分类多少选一个吧'}, } [代码] 如上述规则,他的名字是[代码]ticketClass[代码],那如果需要对他进行校验,那他在大的动态表单[代码]form[代码]里面需要是[代码]formData.ticketClass[代码] [图片] (2)这样的目标就很明确了,我们需要对[代码]formData[代码]进行赋值,名称是这个字段的名称,值是输入值 普通表单组件 官方demo里面,每个表单组件都有一个属性[代码]data-feild[代码] [图片] 在每次对组件进行事件监听时,在返回的事件对象中,我们可以在 [代码]event.currentTarget.dataset[代码]中找到数据名称 [代码]event.detail.value[代码]中找到数据值 [图片] 然后,我们将“名称”和“值”放到[代码]formData[代码]上,使用[代码]this.setData({})[代码]方法进行赋值,其中,值的名称是动态的,采用一个小点,不是单引号!(因为在markdown的缘故,无法在这单独打这样一个点出来,自行体会一下,常规的87 104键键盘会在左上角数字1的左边,[代码]点${能返回值的函数或者值}点[代码]是ES6新增的字符串方法) [代码] const { field } = event.currentTarget.dataset this.setData({ [`formData.${field}`]: event.detail.value }) [代码] [图片] 五、小提问 可否通过设置表单id的方式,一次性生成多张表单呢? 如何使用<slot/>的方式优化渲染性能呢?
2021-11-15 - 使用开发工具云开发存储下载次数为何一直在自动增加,无任何访问?
最开始只是看到这个现象,所以特意跟了一下,希望其他人也一起观察下,不然下次次数会消耗很快。 使用了云开发,当然也使用了存储,免费版的试用了下。 最开始存储只是上传了很少的几张图片,没感觉下载次数增加很快,有一天上传了大概10多张,不到20张图片,而且还是一样差不多的,不同的图片就三张而已,是通过云开发上传的。 这家伙,感觉下次次数那是蹭蹭网上涨,很快要到1000了。免费的才一月2000,不到一天就接近1000,几乎用了一半。 就打开开发工具,啥都不动,就一会看下那个下载次数变化,发现过一会就还是在涨,次数虽然不多,还是在上涨。 后面,索性把所有下载图片都换成同样的链接,也就是请求一张图片,缓存配置也都是几天的,之前是2小时,并非很短时间,继续观察。下载次数,依然会涨。 [图片] 好家伙,啥都不动,次数也在变化。真的是啥都没动,而且是开发版,只能自己访问。就过一会打开云开发控制台看一下而已,其他的任何操作都没有,就观察下载次数是否有变化,反正每次看,基本上都在变化,有时候少,只有1,2,4这样的增加,依然在上涨变化。 [图片] 还有下载配额不是2000么,还没到1000,就有提示:存储读请求次数余量不足 20%。 [图片] 之前是700多,然后800多的时候,调整了图片,使用了同一张图片,并且连接也是同一个。然后慢慢涨到,现在949了。 [图片] 又增加了4次。 [图片] 也就不到5分钟,又增加了5次。 [图片] [图片] 基本上14:20调整图片为一张图片,连接为同一个连接之后,就没通过小程序访问资源,就只观察云开发控制台,那个数量变化次数跟图上截图变化还对得上。 [图片] 感觉,每过5分钟就有变化。 [图片] [图片] 补充记录: ============= 已删除页面,只保留两个空页面,甚至都没有image标签,而且把开发工具缓存也清空然后重启,依然有下载次数增加。 [图片] 我存储数量目前总共才21个,其实文件只有15个的样子,其他的是目录6个。当然,之前有过文件上传,删除过的。 什么都没有操作,有一次增加30的,这非常诡异,感觉有偷偷跑次数的嫌疑。 [图片] [图片] 感觉,真就是随机的,如果是打开的页面,里面有东西在跑,页面啥都没动,即使内部有处理,按理应该也是相同的次数,不可能是这样随机的变化。 而且,依稀次数变化已经超过我实际的图片的个数。 [图片] [图片] 这个是不是要找官方解释下,不然用着咋放心^_^ 不知其他有仔细观察过没,有没有遇到这个情况。 20220105,继续补充 ================ 特意停了两天,今天有时间再跟了一下。次数依然在增加,所以不是偶然因素。 [图片] [图片] [图片] [图片] [图片] [图片] 今天特意用Fildder抓包看了下,确实小程序内没有产生任何请求。只是在每次进入云开发管理平台,也就是查看存储下载次数的时候,会看到有一个跟云存储相关的请求。 [图片] 完整的请求地址大概是这样的:https://[云开发环境ID相关].cos.ap-shanghai.myqcloud.com/?prefix=&max-keys=1000 唯一这个跟云存储相关,看了下返回的内容,是获取存储的目录文件列表相关的信息。 是否跟该请求有关,按理说即使有关会计算次数,也不会那么多次。这个请求只有一次,次数增加也是随机的。只是发现这个请求次数越多,增加的随机值越大,应该是有一些关联。 这个希望官方确认排查下。
2022-01-05 - 怎样查找表中一数据中的数组字段是否存在符合条件的数据?
[图片] 我想通过ID查询到指定的表中的数据,然后查询那个数组字段中是否存在符合条件的数据
2021-08-13 - “网赚”小程序,你只了解1%
大部分微信开发者对“网赚”的初步认识仅仅局限于网上刷单赚佣金、或阅读文章赚佣金等业务模式。 除以上模式外,还包括自行或协助他人以拟人程序、利诱其他用户参与、转发、下载或委托刷单平台等方式等网赚行为。 今天小编通过实际案例给大家详细剖析相关网赚违规行为: 1. 小程序内纯粹做分享文章/内容后可立即得到奖励的内容(奖励包括但不限于现金、积分、礼品等) 违规示例:如下图违规小程序通过做阅读/转发文章即可获得金币奖励的模式贯穿业务,金币支持兑换现金并提现到账,属于网赚行为。[图片] 2. 小程序内含网赚刷单业务 违规示例:如下图违规小程序为APP提供刷单业务,完成刷单任务后,下载APP即可获取收益,属于网赚行为。[图片] 3. 小程序内存在通过体验APP/小程序/小游戏等产品赚取奖励的行为 违规示例:如下图违规小程序通过体验/转发小程序获取奖励,奖励包括但不限于现金、礼品、积分等,属于网赚行为。 [图片] 4. 小程序内存在通过体验自身业务获取奖励的行为 违规示例:如下图违规小程序通过体验自身业务15秒,即可获得5-10g水滴,水滴可兑换实物或现金等,属于网赚行为。 [图片] 5. 小程序昵称/简介/头像含明显网赚信息 违规示例:如下图违规小程序的头像/简介/昵称含明显网赚信息,诱导进入后获取用户信息,达到网赚推广目的,属于网赚行为。 [图片] 6. 小程序涉及以体验赚奖励、分享赚奖励等业务模式贯穿整个业务 违规示例:如下图违规小程序表面包装成打卡瓜分奖金的业务形态,实际必须通过跳转体验其他小程序、公众号后完成体验任务,才能完成每日打卡任务,属于网赚行为。 [图片] 7、小程序内无实质内容,存在通过批量观看激励视频的形式进行网赚的行为 违规示例①:如下图违规小程序内无实质内容,通过批量观看视频广告获得刮刮卡解锁机会。 [图片] 违规示例②:如下图违规小程序的每一份测试题结果,均需通过观看视频广告才能获得。[图片] 通过以上网赚违规类型及示例的介绍,希望开发者们能对小程序网赚违规有更进一步的了解。如若小程序存在网赚内容,平台将下发警告限期整改,视违规情节严重程度对小程序功能进行限制,或封号处理。
2020-03-18 - 在线答题小程序技术方案征集?
寻求一个解决方案: 目前答题活动有10 0000 人参与,满分100分,每个微信用户只参加一次答题,也就是目前集合有10 0000条记录,那么当一个用户进来的时候怎么获取他的排名? 排名规则:分数优先,分数相同的,按答题用时从短到长升序排列。 数据库选择:目前使用云开发,答题记录放在集合historys里面,也就是historys集合目前躺着10 0000条答题记录数据。 ~~~ 已知 1)当前用户_openid 2)10000 条答题记录 historys 求解 该用户的排名? ~~~ 只说下思路即可,不需要具体实现代码? 方案一经录用,打赏100, 方案可不唯一,也就是说最终方案可不止一个,视参与情况,最多会打赏最优的五个方案,如果有效方案多于10个,最少打赏二个方案,最终会在该贴公布具体打赏情况。 具体方案可评论区留言,同一技术方案,以提出时间最早有效。 具体数据结构如下所示 [图片] ~~~~ 该方案征集有效期为3天,截止时间为本周日晚上9点,即12月20号晚上9点 ~~ 由于社区私信不能发收款码图片,请评论第一,第二两位热心用户,在下面文章评论区留下收款码截图,或者直接微信联系我,我好安排后续的转账工作,非常感谢各位的参与 学习贯彻十九届五中全会知识竞赛答题活动小程序? - 微信开放社区 https://developers.weixin.qq.com/community/develop/article/doc/00068ecced0278a1dd6b58f2051413 相关赏金已打赏两位同学 [图片] [图片] 再次感谢
2020-12-22 - 微信小程序发红包的两种方法
本篇主要介绍微信小程序给用户发现金红包的两种方式,大家可根据自己的实际情况酌情使用。1,小程序开通了小程序红包的接口,但是目前该接口的的使用场景有限,仅限于场景值1011,1025,1047,1124时,基本为用户扫码打开小程序领取红包,这种方式的优势是有红包封面,有点击拆红包的按钮体验比较好,比较适合线下推广二维码时使用。开发时先后端调用发放红包接口,然后再传参给小程序端,调用领取红包接口wx.sendBizRedPacket方法即可。 打开微信支付官方文档中的小程序红包,公众号appid和用户openid参数描述模糊不清,说得就是微信公众号的appid和用户openid,压根没有提到小程序。 [图片] 经过多次调试,终于测试通过,小程序红包开通条件和开发过程中需要注意以下几点: 小程序需要绑定微信支付,且绑定的微信支付商户号需开通现金红包,如果小程序是用公众号认证的,可以很方便的绑定认证微信公众号时开通的微信支付商户号。文档里公众号appid即小程序的appid,用户openid为用户对小程序的openid,该用户openid需在小程序里调用 wx.login() 获取临时登录凭证code,并回传到开发者服务器。调用auth.code2Session接口,换取用户唯一标识OpenID和会话密钥session_key。调用发放红包接口,https://api.mch.weixin.qq.com/mmpaymkttransfers/sendminiprogramhb 如果不填写'notify_way' => 'MINI_PROGRAM_JSAPI',不返回package,可以直接发现金红包,通知形式为服务通知,用户打开即可领取成功;如果填写notify_way则返回有package,只是package里的参数没有官方给的appid,但是有spid,但是不影响小程序正常领红包,用户依然可能通过领取红包接口成功领取现金红包。下面为带notify_way时请求参数和接口返回结果。$redpackData = [ 'mch_billno' => $mch_billno, 'send_name' => '今日头彩查询', 're_openid' => $miniopenid, 'total_num' => 1, //固定为1,可不传 'total_amount' => $total_amount, //单位为分,不小于100 'wishing' => '天天中头彩', 'scene_id' => 'PRODUCT_1', 'notify_way' => 'MINI_PROGRAM_JSAPI', 'act_name' => '打开有礼', 'remark' => '今日头彩,天天好彩头']; $result = [ "return_code" => "SUCCESS", "return_msg" => "发放成功", "result_code" => "SUCCESS", "err_code" => "SUCCESS", "err_code_des" => "发放成功", "mch_billno" => "1508784931202012171608188656", "mch_id" => "150878XXX", "wxappid" => "wxdccaff246b9f9b5c", "re_openid" => "ogn1H45HCRxVRiEMLbLLuABbXXX", "total_amount" => "100", "send_listid" => "1000041701202012173014299105358", "package" => "sendid=41469f3063dfc421bf3377897711d6f040e096a74ec6cb2a6c138178f67d681f&ver=8&sign=b3ff1X1fa2ba0aeeb6b9006dec2bc75b872ef7ee11d5e8bbb22e8836eaa2e76f090c307ba12452010f57dcaebbdec45a3196bff2d850eb9e9a49eedf483aa061&mchid=100846xxX&spid=150878xxx"]; 调用小程序领取红包接口成功领取红包,传给前端的参数中特别注意 'timeStamp' => time() . "",//时间戳,必须为字符串,增加.""转为字符串。小程序红包目前仅支持场景值1011,1025,1047,1124,包括扫描二维码,扫描小程序码和扫描一物一码等,具体可参与小程序红包文档。 2,调用微信支付的企业付款到零钱接口,微信支付的文档里是没有专门提到这种方式的,相信很多的开发者都已经在生产环境中大量使用了,这里不再赘述,实际是用小程序的appid和用户之于小程序的openid来调用企业付款到零钱接口,这种方式的优势是现金直接划到用户零钱帐户,很多小程序做红包相关的活动时都是采用此种变通方式实现的。 【今日头彩查询】是一款供彩民订阅和查询福利彩票和体育彩票开奖信息的小程序,里面已经集成了以上两种的发送红包的方式,用户扫码(注意是扫码不是长按识别)打开程序即可体验第一种方式;第二种方式隐藏在转发里,打开小程序后,点击右上角转发按钮转发到微信群或者微信好友,如果你的微信号是经过实名认证并且近期没有违规操作的,会在微信支付收到商家转帐入帐通知。 [图片] [图片] [图片]
2021-01-25 - 如何使用小程序云开发轻松支撑一场超过50000人的投票活动
记录一次投票活动小程序的运营复盘 ~ 还有不到1小时,今天就结束了,历时10天的投票活动也来到了尾声,从最初的忐忑,到过程中的胸有成竹,到如今的些许遗憾,不过怎么样,这个活动算是完成了 活动官方链接为 https://mp.weixin.qq.com/s/kY6hZTei5vl1nX-Fw 具体的数据和费用我跟大家汇总下 本次活动大概消费了330元,单日日活最高为4.5W,累计投票人次超 8W,累计投票次数超30W 费用这块由于我个人的疏忽造成了很多不必要的消费,这个在前面的文章中已经多次提到了。 ~ 费用这块 [图片] ~ [图片] ~ [图片] ~ [图片] ~ ~ 数据最终定格在 [图片] ~ 其实后面几天扣费一直为0,买的几个套餐都没有消耗,看上去虽然数据库读操作,单日最高突破了150W,但是其实读操作是最便宜的,最贵的还是CDN的流量 第一天没有处理好这一块,第一天就跑了将近一个T,而我买的套餐只是100G,所以整体的支出是在这块,如下图所示 [图片] ~ 今天最后一天我盯了下数据,发现其实还是存在很严重的刷票现象的,比如下面这张截图 [图片] ~ 由于前期对于刷票这个行为没有考虑到,就导致很多用户在后台抱怨数据的不真实性,但是作为投票活动,要真正杜绝刷票真的还是太难了,这个问题也为我日后在小程序优化指明了方向。 今天趁尾声上线了几个小功能 1)增加了用户手机号的收集; 2)在用户反馈模块增加了用户联系方式的收集; 希望通过这二个优化,可以在用户抱怨的时候,做一些安抚的工作,除此之外还真得没有其他好的方式。 后面还有一件事情就是如上面公众号所描述的,随机抽取500位幸运投票用户奖励100元购物礼金,由于在昨天上线了地址收集功能,计划是在本市区随机选500位了 通过这种方式也可以最大程度保证购物礼金的实际兑付。 总结 刷票行为的存在让本来一次很完美的活动出现了一些不好的声音,也给带来了一点遗憾,但是作为一个新的尝试,我也只能安慰自己,其实想想之前第一次做答题活动的时候漏洞百出,甚至导致活动不得延期举办,这本身已是很大的进步了 参考 如何使用小程序云开发轻松支撑一场超过50000人的答题活动? - 微信开放社区 https://developers.weixin.qq.com/community/develop/article/doc/0004e484c14ba0587e5b018b656413 看到这个标题,我临时把刚开始的标题改成了跟这个类似的,也许是为了一种延续,目前答题、投票、抽奖、问卷,我已有其三,希望后面的路越走越宽
2022-01-20 - [开盖即食]九宫格抽奖component组件分享
[图片] 这次继续分享第二个抽奖组件,参考了网上多个版本,本人根据实际工作中进行了一些优化,并将其做成component组件方便大家食用~ [图片] 1、现在上吃的,呸,上代码 页面引用部分: [代码]<!-- 数据是根据外部配置的,同时也修改组件自定义callback返回内容 --> <LuckComponent lucks-data="{{lucksData}}" bind:callBack="luckCb"></LuckComponent> <view class="roll">当前抽奖结果index:{{luck_num}}</view> [代码] [代码]Page({ data: {}, onLoad() { //在这里配置显示数据,未来还能添加图片等等 let lucksData = [{ //这里修改后,可以通过后台请求配置 "key": "baofu", "name": "暴富", "indexli": 1 }...]; this.setData({ lucksData }) }, /** * 结果回调函数 * @param {*} e */ luckCb(e){ console.log(e); if(e.detail){ this.setData({ luck_num:e.detail }) } } }) [代码] Component组件部分 [代码]<view class="luck_box"> <view class="luck"> <view class='li {{amplification_index===item.indexli?"indexli":""}}' wx:key="item" wx:for="{{lucksData}}"> <!-- 开始 --> <view bindtap="startrolling" class="startrolling" wx:if="{{item.indexli === -1}}"> <view class="st1">抽奖</view> </view> <block wx:if="{{item.indexli !== -1}}"> <view class="setup_title"> <view class="txt">{{item.name}}</view> <view class="index">当前index:{{item.indexli}}</view> <view wx:if="{{item.parentsClass}}" class="^parentsClass">{{item.parentsClass}}</view> </view> <view class="indexli_view"></view> </block> </view> </view> </view> [代码] [代码]Component({ /** * 组件的属性列表 */ properties: { lucksData: { type: Array, value: [] }, }, /** * 组件的初始数据 */ data: { amplification_index: 0, //轮盘的当前滚动位置 roll_flag: true, //是否允许滚动 max_number: 8, //轮盘的全部数量 speed: 300, //速度,速度值越大,则越慢 初始化为300 myInterval: "", //定时器 max_speed: 40, //滚盘的最大速度 minturns: 8, //最小的圈数为2 runs_now: 0, //当前已跑步数 luck_num: 0, // 中奖位置!!!!!!!!!!!!!!!!!!!!!!!!! end_amp: 0, //上一次滚动的位置 start_flag: true, lucksData: [], //这里是渲染数据 }, /** * 组件的方法列表 */ methods: { //开始滚动 startrolling: function () { let _this = this; //roll点 let random = parseInt(Math.random() * 8 + 1); if (this.data.start_flag == true) { _this.setData({ luck_num: random, start_flag: false }) //初始化步数 _this.data.runs_now = 0; //当前可以点击的状态下 if (_this.data.roll_flag) { _this.data.roll_flag = false; //启动滚盘, _this.rolling(); } }; //回调行数,把结果传出去 this.triggerEvent('callBack', random); }, //滚动轮盘的动画效果 rolling: function (amplification_index) { let _this = this; this.data.myInterval = setTimeout(function () { _this.rolling(); }, this.data.speed); this.data.runs_now++; //已经跑步数加一 this.data.amplification_index++; //当前的加一 //获取总步数,接口延迟问题,所以最后还是设置成1s以上 let count_num = this.data.minturns * this.data.max_number + this.data.luck_num - this.data.end_amp; //上升期间 if (this.data.runs_now <= (count_num / 3) * 2) { this.data.speed -= 30; //加速 if (this.data.speed <= this.data.max_speed) { this.data.speed = this.data.max_speed; //最高速度为40; } } //抽奖结束 else if (this.data.runs_now >= count_num) { clearInterval(this.data.myInterval); this.data.roll_flag = true; this.setData({ end_amp: _this.data.amplification_index, start_flag: true }) if (_this.data.is_selected == 0) { wx.showModal({ title: '很遗憾', content: _this.data.prize_name, showCancel: false, success(res) { } }) } else if (_this.data.is_selected == 1) { wx.showModal({ title: '恭喜您', content: _this.data.prize_name, showCancel: false, success(res) { } }) } } //下降期间 else if (count_num - this.data.runs_now <= 10) { this.data.speed += 20; } //缓冲区间 else { this.data.speed += 10; if (this.data.speed >= 100) { this.data.speed = 100; //最低速度为100; } } if (this.data.amplification_index > this.data.max_number) { //判定!是否大于最大数 this.data.amplification_index = 1; } this.setData(this.data); }, } }) [代码] 2、食用指南 可以通过 [代码]<slot>[代码] 、 [代码]^class[代码] 和 [代码]~class[代码] 等方法外部配置组件的样式,使其能在多个地方复用 如果还想配置如起始点,速度等,可以统一通过option传参的方式,二次开发下这个组件。 可以通过修改组件让callback返回更多参数 [图片] 3、具体代码片段 地址: https://developers.weixin.qq.com/s/a5NiCwms7gpI 建议将IDE工具升级到 1.03.24以上,避免一些BUG [图片] 如有疑问请留言~ 觉得有用,请点个赞哦,让我继续分享更有动力~
2021-04-13 - 非常简单的长列表(无限上拉触底加载 onReachBottom)实现方案
我们知道小程序针对长列表有两个硬杠杠,一旦越界直接给白屏: 1、setData的数组数据不能超过1M。 2、DOM数不能太多,具体数据未知。 官方这样处理也不无道理,太长了本身性能确实也有问题。所以长列表一定要人为干预处理,不处理一直上拉加载肯定是不行的。 目前主流的处理方法有三种: 1、二维数组,就是把数据改为二维的,每一个分页数据作为一个一维数组的元素。这样处理,只解决了setData的问题,DOM的问题并未解决。并且把本来是一维的数据强行二维化,在很多逻辑处理上变得复杂。 2、官方提供了一个扩展组件recycle-view,但它要求item等高,存在局限性。https://developers.weixin.qq.com/miniprogram/dev/extended/component-plus/recycle-view.html 3、自行搭建骨架屏,类似于方案2的自研版本,根据自己的实际需要编写代码。实现成本非常高。 实现方案如下(太简单了,不提供代码示例): 1、思路:只保留最新的n页数据进行setData,每新加载一页数据,就舍弃最前面一页的数据。同时把第1页的数据保存起来,监听onPageScroll,如果发现用户拉回到了页面顶部,则舍弃所有数据,把第1页的数据setData回来。 2、举个例子:假设n=5,那么当加载了第6页数据时,第6页数据合并到数组尾部,把数组头部的第1页数据去掉,让setData的数据始终保持5页。这里有个细节要处理好,就是去除数据的时候先setData一次,新数据加载合并后,再setData一次,这样可以保证用户的scrollTop不会走位,停留在最新一条数据那个位置。 3、这个方案也存在一些弊端,看你实际项目中能否接受,主要有两个问题:a、用户如果倒着往回逐条浏览,体验是不连续的,因为中间一段我们已经舍弃掉了,如果拉到页面顶部时,将会出现直接回到第一条数据。b、去除数据的setData操作时,存在一定程度的闪屏现象。(针对问题a,应该可以解决,无非就是把数据再逐页塞回来,而不是像我的方案简单粗暴的回到第一页数据,如果项目有需要可以自行尝试。) 4、适用范围:比较适合信息处理类应用,比如后台管理系统。这类应用,往往头几屏内容就能找到信息,或者借助搜索,比较少会拉很多屏。而且往往处理完毕时是直接回到顶部的,不会逐条翻回去。所以这类应用只要保证不出白屏,一些小概率场景下存在一些几乎可以忽略的体验小瑕疵可以接受。信息浏览类应用,比如新闻应用,往往都是长列表浏览,小瑕疵就不一定能接受。
2020-10-28 - 如何彻底解决小程序滚动穿透问题
背景 俗话说,产品有三宝:弹窗、浮层加引导,足以见弹窗在产品同学心目中的地位。对任意一个刚入门的前端同学来说,实现一个模态框基本都可以达到信手拈来的地步,但是,当模态框里边的内容滚动起来以后,就会出现各种各样的让人摸不着头脑的问题,其中,最出名的想必就是滚动穿透。 什么是滚动穿透? 滚动穿透的定义:指我们滑动顶层的弹窗,但效果上却滑动了底层的内容。 具体解决方案分析如下: 改变顶层:从穿透的思路考虑,如果顶层不会穿透过去,那么问题就解决了,所以我们尝试给蒙层加catchtouchmove,但是发现部分场景无效果,那么就不再赘述了。 改变底层:既然是顶层影响了底层,要是底层不会滚动,那就没这个问题了。 如何改变底层解决该问题呢? 不成熟方案: 底部页面最外层view设置position: fixed;页面不可滚动,但是这个时候会导致页面回到顶部。 滚动时监听滚动距离,弹窗时记录滚动位置,关闭弹窗后使用wx.pageScrollTo回滚到记录的位置。 成熟方案 使用page-meta组件,通过该组件我们可以操作Page的style样式,类似于h5里body设置overflow: hidden; 控制页面不可滚动。文档地址:https://developers.weixin.qq.com/miniprogram/dev/component/page-meta.html 使用wx.setPageStyle设置overflow: hidden, 也可以实现给Page组件设置样式。) page-meta组件: 通过该组件我们可以直接操作[代码]Page[代码]组件 ,我们给它的wxss样式overflow动态设置[代码]hidden[代码]or[代码]visible[代码]or[代码]auto[代码] 就可以控制整个页面是否可以滚动。 [图片] wx.setPageStyle方法: 调用这个api,动态设置它为hidden/auto,用于控制页面是否可滚动,主要用于页面组件内使用,比如封装好的弹窗组件,就不用单独写page-meta组件了。。 [代码]wx.setPageStyle({ style: { overflow: 'hidden' // ‘auto’ } }) [代码] 老规矩,结尾放代码片段: https://developers.weixin.qq.com/s/U6ItgQmP7upQ 拓展 支付宝小程序虽然存在page-meta组件,但是由于内核为69版本,给page设置overflow: hidden 也无法控制底部元素不可滚动,目前已联系支付宝的底层开发同学提供API控制页面disableScroll,目前正在封装Appx,近期开放。
08-06 - 小程序实现高性能虚拟列表优化+节流+分页请求(固定高度)
场景引入 为什么需要用到高性能虚拟列表+节流+分页请求的优化? 当有场景需求为需要将大量数据(10000条)呈现在一页上,我们不断下拉访问,页面中有大量的数据列表的时候,用户会不会有不好的体验?会不会出现滚动不流畅而卡顿的情况?会不会因卡顿而出现短暂的白屏现象(数据渲染不成功)? 通过微信开发者工具自带的调试器->Network页面,我们可以观察到当有长列表时如果不使用高性能虚拟列表+节流+分页请求的优化,会出现以下问题: FPS:每秒帧数,图表上的红色块表示长时间帧,很可能会出现卡顿。 CPU:CPU消耗占用,实体图越多消耗越高。 NET:网络请求效率低,一次性请求10000条的渲染效率远远低于分1000次,每次请求10条数据 内存:滑动该列表时明显能看到内存消耗大。 总结:如果需要将大量数据(10000条)呈现在一页上,可以通过高性能虚拟列表+节流+按需请求分页数据并追加显示。 优化的具体实现可拆分为以下需求(将一个大问题拆分为一个个小问题并逐个去解决): 不把长列表数据一次性全部直接显示在页面上。 截取长列表一部分数据用来填充屏幕容器区域。 长列表数据不可视部分使用使用空白占位填充。 监听滚动事件根据滚动位置动态改变可视列表。 监听滚动事件根据滚动位置动态改变空白填充。 分页从服务器请求数据,将一次性请求所有数据变为滚动到底部才再次向服务器发送获取数据的请求 开始实战 此次实例我设定每个元素的固定高度为210rpx [图片] (1)首先计算屏幕内的容积最大容量(即屏幕一次性可以容纳多少个高度为210rpx的元素) 调用wx.createSelectorQuery()的api [图片] 上图是id为scrollContainer的组件,滚动时触发函数handleScroll() [代码]// 计算容器的最大容积,onReady中触发,即初次渲染时触发 getContainSize() { wx.createSelectorQuery().select('#scrollContainer').boundingClientRect(function (rect) { rect.id // 节点的ID rect.dataset // 节点的dataset rect.left // 节点的左边界坐标 rect.right // 节点的右边界坐标 rect.top // 节点的上边界坐标 rect.bottom // 节点的下边界坐标 rect.width // 节点的宽度 rect.height // 节点的高度 }).exec((option) => { // console.log(~~(option[0].height / this.data.oneHeight) + 2); this.data.containSize = ~~(option[0].height / this.data.oneHeight) + 2; }) }, [代码] 调用api后返回的option中的height就是小程序页面的视口高度,除以oneHeight(210rpx)就是能容纳的个数,用[代码]~~[代码]来对结果进行向下取整(实际能容纳的应+1),由于在滚动时会出现上一个元素在上边界还没完全消失,第四个元素就从下边界进入视口了,因此最大容纳量应再+1。即实际容纳量应该如图最后一行代码所示+2. [图片] (2)监听滚动事件动态截取数据 监听用户滚动、滑动事件,根据滚动位置,动态计算当前可视区域起始数据的索引位置 startIndex,再根据containsize,计算结束数据的索引位置 endIndex,最后根据 startIndex与endIndex截取长列表所有数据a11Datalist 中需显示的数据列表 showDatalist。 PS:下列代码中函数handleScroll()最下面的[代码]this.setDataStartIndex(data);[代码]才是滚动时真正进行的滚动事件动态截取数据,上面那些代码用途在文章后面部分再详细介绍(节流)。 [代码]// 定义滚动行为事件方法 handleScroll(data) { if (this.data.isScrollStatus) { this.data.isScrollStatus = false; // 节流,设置一个定时器,1秒以后,才允许进行下一次scroll滚动行为 let mytimer = setTimeout(() => { this.data.isScrollStatus = true; clearTimeout(mytimer); }, 17) this.setDataStartIndex(data); } }, [代码] [代码]// 执行数据设置的相关任务, 滚动事件的具体行为 setDataStartIndex(data) { // console.log("scroll active") this.data.startIndex = ~~(data.detail.scrollTop / this.data.oneHeight); // 通过scrollTop滑动后距离顶部的高度除以每个元素的高度,即可知道目前到第几个元素了 this.setData({ showDataList: this.data.allDatalist.slice(this.data.startIndex, this.data.endIndex) }) // 动态截取实际拥有10000条数据的数组中下标为startIndex到endIndex的数组出来呈现在前端页面上 // 容器最后一个元素的索引 if (this.data.startIndex + this.data.containSize <= this.data.allDatalist.length - 1) { this.setData({ endIndex: (!this.data.allDatalist[this.data.endIndex]) ? this.data.allDatalist.length - 1 : this.data.startIndex + this.data.containSize // 滚动到底部了吗,是的话那就将endIndex设置为9999,不然的话设置为startIndex+视口最大容量 }) } else { console.log("滚动到了底部"); this.data.pageNumNow++; // 例如一次性从数据库拿10条数据赋值到allDataList,如果滚动到底部(即allDataList所有数据都已经呈现了),那就再次向服务器发送请求获取数据库中的下10条数据 this.addMes(); // 该函数内就写你实际向数据库请求时的代码,请求成功后拼接到allDataList即可 console.log(this.data.allDatalist.length) } }, [代码] (3)使用计算属性动态设置上下空白占位 我们设置了根据容器滚动位移动态截取ShowDatalist 数据,现在我们滚动一下发现滚动2条列表数据后,就无法滚动了,这个原因是什么呢? 在容器滚动过程中,因为动态移除、添加数据节点丢失,进而强制清除了顶部列表元素DOM节点,导致滚动条定位向上移位一个列表元素高度,进而出现了死循环根据 startIndex和endIndex的位置,使用计算属性,动态的计算并设置,上下空白填充的高度样式blankFi11Sty1e,使用padding或者margin 进行空白占位都是可以的 PS:由于小程序没有computed,所以为了使用计算属性,得另外引入封装好computed的包,引入computed组件的教程我放在最后,我使用的是官方推荐的computed,且注意使用该插件时,不要加[代码]this -> this.data[代码],直接data即可 [代码]computed: { // 定义上空白高度 topBlankFill(data) { // console.log("change") return data.startIndex * data.oneHeight; }, // 定义下空白高度 BottomBlankFill(data) { return (data.allDatalist.length - data.endIndex) * data.oneHeight; }, // 定义一个 待显示的数组列表元素 showDataList(data) { // console.log(data.allDatalist.slice(data.startIndex, data.endIndex)) return data.allDatalist.slice(data.startIndex, data.endIndex) }, }, [代码] (4)下拉置底自动请求加载数据 下方代码中[代码]setDataStartIndex()[代码]函数末尾的if-else便是判断是否已经滚动到现有全部数据的allDataList数组是否已经滚动到底部,全部呈现完了。 如果是那就执行else部分,请求的页数pageNumNow+1,然后调用addMes()请求数据,然后将新请求到的数据进行拼接到allDataList上 [代码]// 执行数据设置的相关任务, 滚动事件的具体行为 setDataStartIndex(data) { // console.log("scroll active") this.data.startIndex = ~~(data.detail.scrollTop / this.data.oneHeight); this.setData({ showDataList: this.data.allDatalist.slice(this.data.startIndex, this.data.endIndex) }) // 容器最后一个元素的索引 if (this.data.startIndex + this.data.containSize <= this.data.allDatalist.length - 1) { this.setData({ endIndex: (!this.data.allDatalist[this.data.endIndex]) ? this.data.allDatalist.length - 1 : this.data.startIndex + this.data.containSize }) } else { console.log("滚动到了底部"); this.data.pageNumNow++; this.addMes(); console.log(this.data.allDatalist.length) } }, // 根据接口数据来给数组添加真实数据 addMes: function () { this.list() .then(res => { // console.log(res.result.data); // 将接口获取到得所有数据存储起来 this.data.allDatalist = this.data.allDatalist.concat(res.result.data); // 设置初始显示列表 this.setData({ showDataList: this.data.allDatalist.slice(0, 5) }) }) }, // 以下为获取数据 list: function () { let pageNum = this.data.pageNumNow; let pageSize = 20; // console.log('当前请求的页码为:' + pageNum); return new Promise((resolve, reject) => { wx.cloud.callFunction({ name: 'teacher', data: { action: 'list', pageNum, pageSize } }).then(res => { resolve(res) }).catch(err => { reject(err) }) }) }, [代码] 到此处,高性能虚拟列表+分页请求的优化已经搞定了,下面开始节流的优化 (5)滚动事件节流定时器优化 由于监听滚动事件触发对应函数方法的频率是极高的,因此页面节流优化是必须的。 方法:在data中声明一个属性scro11State用来记录滚动状态,只有scro11State值为true的时候才会具体执行 PS:下面代码中定时器设置为17ms的原因是,由于小程序没有web的requestAnimationFrame的api,无法作滚动事件节流请求动画帧优化,因此可以手动计算显示器的帧率,大概一帧在17ms,因此定时器设置为17ms [代码]// 定义滚动行为事件方法 handleScroll(data) { //节流部分代码 if (this.data.isScrollStatus) { this.data.isScrollStatus = false; // 节流,设置一个定时器,1秒以后,才允许进行下一次scroll滚动行为 let mytimer = setTimeout(() => { this.data.isScrollStatus = true; clearTimeout(mytimer); }, 17) //节流部分代码 this.setDataStartIndex(data); } }, [代码] 到这为止,节流也搞定啦,觉得从中学到了许多的小伙伴不妨点个赞。 小程序如何引入computed计算属性请参考我的这篇文章:https://developers.weixin.qq.com/community/develop/article/doc/000a4442bd44c84e740d6b6b051413 在后续我也会将高性能虚拟列表+节流的优化封装成一个插件,给小伙伴们直接使用,欢迎关注我以便及时获取到我文章的更新~ 在这里也推荐一篇防抖和节流的性能优化知识介绍的文章:https://segmentfault.com/a/1190000018428170 觉得有帮助的小伙伴欢迎点赞,有其他问题也欢迎在评论区提出
2021-11-11 - 小程序中对初始时渲染太多wxml节点数做懒加载是很有必要的!这是一个真实案例。
前言 在写小程序时,有个页面由于要展示很多内容,光弹窗都6个,还有大量的列表,所以wxml节点数很多,超过了小程序建议的1000个节点以下。 懒加载示例项目代码已放到 github 和 码云 上 问题 这个内容很多的页面是分包的第一个页面,再此之前开发工具上能正常的运行,因此没有去管它。但是在上周六(2020年1月16日)后,开发工具上就出问题了!!!进入这个分包页面,视图半天才渲染,但事件是会立即执行的(看点击事件及接口请求情况得出的结论)。由于代码和前一天相比没有改动,并且手机上是正常的,所以第一时间就怀疑是不是开发工具出问题了,于是就删掉了工具,并重新下载了两次(一次最新稳定版,一次最新预发布版本),可还是不管用,依然这分包的这个页面,渲染不出来。 之后,又将手机的性能监听面板打开,发现再次渲染耗时那数值一直在闪动,但始终停留在0ms,就肯定的是小程序渲染出问题了。又怀疑只要是分包页面都有问题,就发现其他分包页面,并没有问题。 然后,想着之前用体验评分时,已经提示了wxml节点数过多,就试探性的注释了一些代码。好家伙,开发工具立马就正常了。 在确定问题前,没有第一时间考虑自己代码的问题,因为就过了一天开发工具就不能运行了,这确实很奇怪。 优化代码 初次渲染时,并不需要全部都渲染出来,所以可以做懒加载,显示时再渲染,通过 [代码]wx:if[代码] 来控制是否显示。 通过测试,做了懒加载后的体验评分在性能上是比不做要高的。下面是两张对比图: 没有使用懒加载 [图片] 使用了懒加载 [图片] 项目介绍: [图片] 如上图要实现的效果是,初始渲染时,只渲染玄幻栏目的数据,当往下滑动快触底时,再渲染下一个。如果初始渲染完成后,点击第四个都市栏目,则需把二三四个栏目都渲染出来。 懒加载原理 小程序通过setData后的数据,再放入wxml上就会被渲染,但是我们可以通过设置[代码]wx:if[代码]让,节点不显示,就不会去渲染。当滚动触底后,再通过一个标识值来判断后续的渲染情况。 此例子中与只有触底后渲染下面的视图,再加了点击左边的分类时,也做做渲染。 比如:初始时只渲染了第一组数据,当时我点击了第四个分类时,要将第二个,第三个,第四个的数据都显示出来。 这也是通过这个标识值来判断。 示意图: [图片] [图片] 项目地址: github地址 码云地址
2021-01-21 - 【小白入门文档】如何使用schema搭建考试后台管理系统
1、首先打开云开发的后台,点击【更多】【内容管理】,开通内容管理 [图片] 2、等待几分钟... [图片] 3、设置好了,就会有网站和账号密码,打开网址,登录后台 [图片] 4、创建一个新项目 [图片] 5、打开内容模型页面,找到代码根目录的cms.json文件,选择该文件导入 [图片] 6、导入完成以后,刷新后台,左侧就出现了后台管理的页面了 [图片]
2021-10-29 - 云数据库,数据量大了之后聚合函数查询超时,如何解决?
可以延长这个超时时间么?代码如下 db.collection('temperature') .aggregate() .lookup({ from: 'userInfo', let: { temp_sno: '$sno' }, pipeline: $.pipeline() .match(_.expr($.and([ $.eq(['$sno', '$$temp_sno']) ]))) .project({ _id: 0, _openid: 0, sdorm: 0, sname: 0 }) .done(), as: 'userInfo' }) .replaceRoot({ newRoot: $.mergeObjects([$.arrayElemAt(['$userInfo', 0]), '$$ROOT']) }) .match(_.expr($.and([ $.eq([event.date, '$date']) ]))) .count('uped') .end()
2021-11-04 - 还在傻傻分不清ES5、Es6数组方法?各大姿势来袭
前言 初衷: 在面试中,面试官经常问到说一下Es5和Es6的数组方法有哪些,有很多同学老是分不清楚,今天笔者就来分享一下。 适合人群: 前端初级开发 感兴趣的小伙伴点击链接,了解详情~ http://github.crmeb.net/u/yi Es5系列 indexOf 用途: 用于查找数组中是否存在某个值,如果存在则返回某个值的下标,否则返回[代码]-1[代码] let list = [1, 2, 3]; console.log(list.indexOf(2)) // 1 console.log(list.indexOf("蛙人")) // -1 map 用途: [代码]map[代码]是一个数组函数方法,接收三个参数,[代码]value[代码],[代码]index[代码],[代码]self[代码],返回值是处理完的结果。 let list = [1, 2, 3]; const res = list.map((value, key, self) => { console.log(value) // 1 2 3 console.log(key) // 0 1 2 console.log(self) // [1, 2, 3] return value * 2 }) console.log(res) forEach 用途: 用于遍历一个数组,接收三个参数,[代码]value[代码],[代码]index[代码],[代码]self[代码],返回值为[代码]undefined[代码] let list = [1, 2, 3]; const res = list.forEach((value, key, self) => { console.log(value) // 1 2 3 console.log(key) // 0 1 2 console.log(self) // [1, 2, 3] return 123 }) console.log(res) // undefined splice 用途: 用于数组删除或替换内容,接收三个参数: 第一个参数是,删除或添加的位置第二个参数是,要删除的几位,如果为0则不删除第三个参数是,向数组添加内容 let list = [1, 2, 3]; list.splice(0, 1) // 把第0个位置,给删除一位 console.log(list) // [2, 3] list.splice(0, 1, "蛙人") // 把第0个位置,给删除一位,添加上一个字符串 console.log(list) // ["蛙人", 2, 3] list.splice(0, 2, "蛙人") // 把第0个位置,给删除2位,添加上一个字符串 console.log(list) // ["蛙人", 3] slice 用途: 用于截取数组值,接收两个参数,第一个参数是要获取哪个值的下标,第二个参数是截取到哪个下标的前一位。 let list = [1, 2, 3]; let res = list.slice(1, 3) // 从第一位下标开始截取,到第三位下标的前一位,所以截取出来就是 [2, 3] console.log(res) // [2, 3] filter 用途: 用于过滤数组内的符合条件的值,返回值为满足条件的数组对象 let list = [1, 2, 3]; let res = list.filter(item => item > 1); console.log(res) // [2, 3] every 用途: 用于检测数组所有元素是否都符合指定条件,返回值为[代码]Boolean[代码] , 该方法是数组中必须全部值元素满足条件返回[代码]true[代码],否则[代码]false[代码] let list = [1, 2, 3]; let res = list.every(item => item > 0) console.log(res) // true let res1 = list.every(item => item > 1) console.log(res1) // false some 用途: 用于检测数组中的元素是否满足指定条件,返回值为[代码]Boolean[代码] , 该方法是只要数组中有一项满足条件就返回[代码]true[代码],否则[代码]false[代码] let list = [1, 2, 3]; let res = list.some(item => item > 0) console.log(res) // true reduce 用途: 该方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值。该方法回调函数接收四个参数 第一个参数:初始值, 或者计算结束后的返回值第二个参数:当前元素第二个参数:当前元素的索引第四个参数:当前元素所属的数组对象,本身 我们一般只用前两个就行,[代码]reduce[代码]第一个参数回调函数,第二个参数是初始值 let list = [1, 2, 3]; let res = list.reduce(( prev, cur ) => prev += cur, 0) console.log(res) // 6 reverse 用途: 用于数组反转 let list = [1, 2, 3]; let res = list.reverse(); console.log(res) // [3, 2, 1] join 用途: 用于数据以什么形式拼接 let list = [1, 2, 3]; let res = list.join("-"); console.log(res) // 1-2-3 let sum = eval(list.join("+")) console.log(sum) // 6 sort 用途: 用于将数组排序,排序规则看返回值 返回值为正数,后面的数在前面返回值为负数,前面的数不变,还在前面返回值为0,都不动 let list = [1, 2, 3]; let sort = list.sort((a, b) => b - a) console.log(sort) // [3, 2, 1] concat 用途: 用于合并数组原始 let list = [1, 2, 3]; let res = list.concat([1, 2, 3]) console.log(res) // [1, 2, 3, 1, 2, 3] push 用途: 向数组后面添加元素,返回值为数组的[代码]length[代码] let list = [1, 2, 3]; let res = list.push(1) console.log(res) // 4 pop 用途: 用于删除数组尾部的元素,返回值为删除的元素 let list = [1, 2, 3]; let res = list.pop() console.log(res) // 3 shift 用途: 用于删除数组的头部,返回值为删除的元素 let list = [1, 2, 3]; let res = list.shift() console.log(res) // 1 unshift 用途: 向数组的头部添加元素,返回值为数组的[代码]length[代码] let list = [1, 2, 3]; let res = list.unshift(1) console.log(res) // 4 toString 用途: 用于将数组内容转换为字符串 let list = [1, 2, 3]; let res = list.toString() console.log(res) // 1,2,3 Es6+ 系列 includes 用途: 检测数组中是否存在该元素,返回[代码]Boolean[代码]值 let list = [1, 2, 3]; let res = list.includes("蛙人") let res1 = list.includes(1) console.log(res, res1) // false true find 用途: 查找数组的元素,满足条件的返回单个值,按照就近原则返回 let list = [1, 2, 3]; let res = list.find((item) => item > 1) console.log(res) // 2, 按照就近原则返回 findIndex 用途: 查找数组中元素,满足条件的返回数组下标 let list = [1, 2, 3]; let res = list.findIndex((item) => item > 1) console.log(res) // 1, 按照就近原则返回下标 flat 用途: 用于拉平嵌套数组对象 let list = [1, 2, 3, [4, [5]]]; let res = list.flat(Infinity) console.log(res) // [1, 2, 3, 4, 5] fill 用途: 用于填充数组对象 let list = [1, 2, 3]; let res = list.fill(1) console.log(res) // [1, 1, 1] Array.isArray 用途: 检测对象是不是一个数组 let list = [1, 2, 3]; let res = Array.isArray(list) console.log(res) // true Array.from 用途: 将伪数组转换为真数组 let res = Array.from(document.getElementsByTagName("div")) console.log(res) // 转换为真数组就可以调用数组原型的方法 Array.of 用途: 用于生成一个数组对象,主要是用来弥补[代码]Array()[代码]的不足 let res = Array.of(1, 2, 3) console.log(res) // [1, 2, 3] 改变原始数组值的有哪些[代码]splice[代码]、[代码]reverse[代码]、[代码]sort[代码]、[代码]push[代码]、[代码]pop[代码]、[代码]shift[代码]、[代码]unshift[代码]、[代码]fill[代码] 结语这里[代码]keys[代码]、[代码]values[代码]、[代码]entries[代码]就不写啦,它们使用数组方式的话,返回的是[代码]Iterator[代码]遍历器对象。欢迎大家查漏补缺常用数组方法哦 感谢谢谢你读完本篇文章,希望对你能有所帮助,如有问题欢迎各位指正。 感兴趣的小伙伴点击链接,了解详情~ http://github.crmeb.net/u/yi 作者:蛙人
2021-04-02 - 小程序流量主运营之ECPM怎么提高
目前小程序流量主共有8个广告类型:banner、视频、插屏、前贴、激励、格子、模板、开屏。 首先说下ECPM是什么意思。ECPM指的是每一千次展示可以获得的广告收入。 ECPM是取决于用户点击广告次数,也就是1000次曝光里面点击广告的人数越多,ECPM值越高,不单单是曝光1000次的收益。 也就是说如果你曝光了1000次banner广告,但是没有用户点击,那么你banner广告位的ECPM就是0。 [图片] 然后我们说下今天的主题,ECPM怎么提高。 1、首先你流量主小程序里面的所有广告,都是广告主去投放的。广告主可投放的类型有很多种,根据广告主投放城市、行业等决定。 1-1、比如我在呼和浩特,我把广告投放到呼和浩特,如果我按竞价CPC的投放,一个点击最低1.06,除了微信要扣除流量主收益的50%之外,剩下的50%就是你的收益。 [图片] 1-2、我按合约广告投放,1000次曝光我花费的是15元,微信扣除50%,你的ECPM就是7.5,这么说懂了吧。 [图片] 1-3、所以基于广告主广告投放,你要选好广告,尽量避免第二条里面的1000次曝光这种CPM广告主,这种广告主一般都是大品牌,不在乎你点不点,就是让你看。所以我们要有犀利的眼光去判断屏蔽哪种广告位。 [图片] 2、ECPM的高低取决于你的小程序用户分布年龄 比如30岁IOS用户就是比20岁的IOS用户质量高,30岁左右的IOS用户属于高价值用户,对电商、游戏等付费能力会更强,会比20岁的人群要高,所以我们在开发小程序的时候,尽量往这个年龄段靠。还有30岁的女性电商用户,都是ECPM拉高点的关键用户群体。(此处仅仅是举例分析,无年龄歧视) 所以我们再开发、购买小程序时,选题材、素材的时候可以尽量往这个年龄段靠一下,ECPM会高一些。 3、广告设计体验 广告设计是一门艺术,但是不能违规。要优雅的设计广告,让用户看着舒服,看的清楚,主动去点击广告,这也是广告主投放广告的目的。最近有多人私信和我说买1000多个手机,弄1000多个微信去刷广告。真的是想瞎了心了,如果你是广告主,你投放出去的广告希望用户这样点击吗?再说了,微信广告的风控机制是你承受不住的,微信也得保护广告主投放的利益。如果全部都是这种样点广告,微信生态早就活不下去了。不要想着投机取巧,踏踏实实做流量才是王道。 [图片] 4、站外引流提升ECPM 所有的APP都缺流量,不要以为微信是国民产品就不缺流量。微信小程序也喜欢站外流量引进来,所以大家在引流的时候,可以从其他APP引流,这样你的ECPM也是很高。 [图片] [图片]
2020-12-19 - 哪种广告类型收益更高
哪种广告类型收益更高 ~也许这个标题很多人会感兴趣的 但是本文不是标题党,本文以实际运营的抽奖小程序具体来分析哪种广告类型收益更高 1 [图片] 1 [图片] 1 [图片] 1 上面三种截图是该小程序三种不同类型广告位的近七天收益,从图中不难看出,视频广告的eCPM在50上下,明显高于其他两种广告类型 [图片] 该小程序用户画像 [图片] 其
2021-01-08 - 如何实现快速生成朋友圈海报分享图
由于我们无法将小程序直接分享到朋友圈,但分享到朋友圈的需求又很多,业界目前的做法是利用小程序的 Canvas 功能生成一张带有小程序码的图片,然后引导用户下载图片到本地后再分享到朋友圈。相信大家在绘制分享图中应该踩到 Canvas 的各种(坑)彩dan了吧~ 这里首先推荐一个开源的组件:painter(通过该组件目前我们已经成功在支付宝小程序上也应用上了分享图功能) 咱们不多说,直接上手就是干。 [图片] 首先我们新增一个自定义组件,在该组件的json中引入painter [代码]{ "component": true, "usingComponents": { "painter": "/painter/painter" } } [代码] 然后组件的WXML (代码片段在最后) [代码]// 将该组件定位在屏幕之外,用户查看不到。 <painter style="position: absolute; top: -9999rpx;" palette="{{imgDraw}}" bind:imgOK="onImgOK" /> [代码] 重点来了 JS (代码片段在最后) [代码]Component({ properties: { // 是否开始绘图 isCanDraw: { type: Boolean, value: false, observer(newVal) { newVal && this.handleStartDrawImg() } }, // 用户头像昵称信息 userInfo: { type: Object, value: { avatarUrl: '', nickName: '' } } }, data: { imgDraw: {}, // 绘制图片的大对象 sharePath: '' // 生成的分享图 }, methods: { handleStartDrawImg() { wx.showLoading({ title: '生成中' }) this.setData({ imgDraw: { width: '750rpx', height: '1334rpx', background: 'https://qiniu-image.qtshe.com/20190506share-bg.png', views: [ { type: 'image', url: 'https://qiniu-image.qtshe.com/1560248372315_467.jpg', css: { top: '32rpx', left: '30rpx', right: '32rpx', width: '688rpx', height: '420rpx', borderRadius: '16rpx' }, }, { type: 'image', url: this.data.userInfo.avatarUrl || 'https://qiniu-image.qtshe.com/default-avatar20170707.png', css: { top: '404rpx', left: '328rpx', width: '96rpx', height: '96rpx', borderWidth: '6rpx', borderColor: '#FFF', borderRadius: '96rpx' } }, { type: 'text', text: this.data.userInfo.nickName || '青团子', css: { top: '532rpx', fontSize: '28rpx', left: '375rpx', align: 'center', color: '#3c3c3c' } }, { type: 'text', text: `邀请您参与助力活动`, css: { top: '576rpx', left: '375rpx', align: 'center', fontSize: '28rpx', color: '#3c3c3c' } }, { type: 'text', text: `宇宙最萌蓝牙耳机测评员`, css: { top: '644rpx', left: '375rpx', maxLines: 1, align: 'center', fontWeight: 'bold', fontSize: '44rpx', color: '#3c3c3c' } }, { type: 'image', url: 'https://qiniu-image.qtshe.com/20190605index.jpg', css: { top: '834rpx', left: '470rpx', width: '200rpx', height: '200rpx' } } ] } }) }, onImgErr(e) { wx.hideLoading() wx.showToast({ title: '生成分享图失败,请刷新页面重试' }) //通知外部绘制完成,重置isCanDraw为false this.triggerEvent('initData') }, onImgOK(e) { wx.hideLoading() // 展示分享图 wx.showShareImageMenu({ path: e.detail.path, fail: err => { console.log(err) } }) //通知外部绘制完成,重置isCanDraw为false this.triggerEvent('initData') } } }) [代码] 那么我们该如何引用呢? 首先json里引用我们封装好的组件share-box [代码]{ "usingComponents": { "share-box": "/components/shareBox/index" } } [代码] 以下示例为获取用户头像昵称后再生成图。 [代码]<button class="intro" bindtap="getUserInfo">点我生成分享图</button> <share-box isCanDraw="{{isCanDraw}}" userInfo="{{userInfo}}" bind:initData="handleClose" /> [代码] 调用的地方: [代码]const app = getApp() Page({ data: { isCanDraw: false }, // 组件内部关掉或者绘制完成需重置状态 handleClose() { this.setData({ isCanDraw: !this.data.isCanDraw }) }, getUserInfo(e) { wx.getUserProfile({ desc: "获取您的头像昵称信息", success: res => { const { userInfo = {} } = res this.setData({ userInfo, isCanDraw: true // 开始绘制海报图 }) }, fail: err => { console.log(err) } }) } }) [代码] 最后绘制分享图的自定义组件就完成啦~效果图如下: [图片] tips: 文字居中实现可以看下代码片段 文字换行实现(maxLines)只需要设置宽度,maxLines如果设置为1,那么超出一行将会展示为省略号 代码片段:https://developers.weixin.qq.com/s/J38pKsmK7Qw5 附上painter可视化编辑代码工具:点我直达,因为涉及网络图片,代码片段设置不了downloadFile合法域名,建议真机开启调试模式,开发者工具 详情里开启不校验合法域名进行代码片段的运行查看。 最后看下面大家评论问的较多的问题:downLoadFile合法域名在小程序后台 开发>开发设置里配置,域名为你图片的域名前缀 比如我文章里的图https://qiniu-image.qtshe.com/20190605index.jpg。配置域名时填写https://qiniu-image.qtshe.com即可。如果你图片cdn地址为https://aaa.com/xxx.png, 那你就配置https://aaa.com即可。
2022-01-20 - 在线答题小程序批量导入模板文件详解
在线答题小程序题库批量导入之前需要根据模板文件整理题库,那么目前小程序支持哪几种题型,本文主要讲述具体模板文件格式 该模板文件支持导入的题型有:单选、多选、判断、填空、简答五种题型 该模板文件于2020-05-20更新 [图片] 1、第一列的单选、多选、判断不要带"题"这个字,也就是说不能是单选题、多选题、判断题, 2、判断题的答案一列必须是A或者B,不能是对、错 3、解析一列如果不需要,可以没有 4、单选、多选题的选项一、二、三、四里面不要再包含A、B、C、D,会自动处理 5、前面题型不能省略,每一行都要有 6、单选、多选的选项可以是四个,系统支持任意选项,比如三个,比如七八个,都是可以支持的 7、题库文件的第一行一定是表头,这个不能缺少 8、第一列题型里面没有选择,只有单选和多选 9、填空题支持多个空,多个空时,答案中间用分割线分隔开来,比如答案是123|456 (2020-06-05支持) 具体文档,点击下面链接,选择其一就好,不同平台,存放的都是一份模板文件 腾讯文档 【腾讯文档】b55bcee1908fc19a https://docs.qq.com/sheet/DZk9WbFJpdlFCcHJ5?c=A14A0A0 语雀文档 https://www.yuque.com/docs/share/01f3cf96-931a-4f79-b412-c61f320cfa48?#77ab 金山文档 https://kdocs.cn/l/sCklbpGBn?f=130 [文档] b55bcee1908fc19a.xlsx 文件模板 模板文件一: 支持单选、多选、判断,选项为四个的模板,选项依次为A、B、C、D https://www.xiaomutong.com.cn/tpl20200501.xlsx 模板文件二: 支持单选、多选、判断,选项为七个的 模板,选项依次为A、B、C、D、E、F、G https://www.xiaomutong.com.cn/tpl20200505.xlsx 大家根据自己的题库选项酌情选择 海量题库管理多种方式支持导入题目、支持判断、单选、多选、填空、简答,可分类清晰管理和搜索。 [图片] 通过excel生成的题目,最后再界面上呈现如下 [图片] 2020-06-05 新增对填空题存在多个空的支持 比如某填空题有两个空位,答案分别为xiao和fei,那么在整理题库时,答案这里应该输入xiao|fei,中间用英文的分割线隔开。
2020-06-05 - 如何使用小程序云开发轻松支撑一场超过50000人的答题活动
原标题 小程序云开发如何在只消费12.9元的情况下,支撑一场超过50000人的答题活动 备注 该小程序答题活动得到当地省级领导的认可,得以在各地市推广,目前小程序答题活动已接近尾声,答题人次突破50000大关; 在阅读本文之前,希望您对小程序云开发,云开发套餐有一定的了解 云开发免费套餐有两个关键指标 单日5万次读 单日3万次写 也就是说单日如果流量不超过上述次数,便可以一直免费下去 ~~~ 话分两头,今天不来不想怂的,无奈今天参与答题的人数严重超过预期,最终还是交了保护费,既然不能不交,那么咱就要好好研究下,如何做到最少的保护费做最大的价值 接前文 宪法宣传周线上答题活动小程序复盘? - 微信开放社区 https://developers.weixin.qq.com/community/develop/article/doc/000886a4fb856062755b55ec357813 [图片] 下图为2020-12-02 18:00的云开发控制台截图 [图片] 通过上文我们清楚,小程序云开发免费的套餐能支持该小程序2000人的答题,但是截至18:00,单日答题用户已突破5000大关,创下我做答题小程序以来的最高历史记录 回想这一路走来,太多辛酸泪了,让我想起第一次支持答题活动,第一次支持党建答题的,那些情景还历历在目 第一次支持答题活动,是今年的五四活动,人数是1000人左右,当时小程序由于集合的_id,没有设计好,导致整个活动失败,给当时运营方带来 了非常大的麻烦 请移步下文 复盘一次小程序线上生产事故? - 微信开放社区 https://developers.weixin.qq.com/community/develop/article/doc/000460e99908e87ab34a463c256813 ~~ 第一次支持党建答题活动,是今年的七一党庆,由于是第一次做这么一个全新的产品,答题过程中出现了种种的问题,好在,最后收场的时候,满足了运营方的全部要求,可以给自己75分 请移步下文 党建答题活动小程序复盘回顾? - 微信开放社区 https://developers.weixin.qq.com/community/develop/article/doc/000a4296148320f6b68a852c851413 目前承载答题活动的小程序已趋于完善,不会像之前那样紧张,也不会在活动期间,对小程序进行修修补补,不需要任何干预,由活动方独立来完成 今天我借这篇文章主要分析文章标题的12元 这12元分别购买了两个套餐 1)云开发数据库资源包,原价270,首次购买优惠261,实际支付9元; 该资源包有3000万次读,1500万次写 2)云开发CDN流量包,该流量包包含了CDN流量100G 套餐一 [图片] 套餐二 [图片] 至于为什么 要买这两个套餐,要从下面的报警谈起 [图片] [图片] 细心的同学会发现,这上面两个截图是两个小程序的报警,其实对应的一个答题活动,小程序素材资源存储在截图一的小程序,所以其CDN流量超过预定的警戒值后就可以报警,应该是4/5 也就是免费的CDN流量是5G 第二张截图是本次小程序答题活动的承载小程序,主要补充的是读写的流量; 备注:由于答题活动正在如火如荼的开展中,为造成不必要的麻烦,有意在截图二对小程序名称进行脱敏处理 [图片] 本文总结 一路走来,死磕答题,几分执着,几分热血,此刻正当时,做一点有意义的事情。 [图片] 1
2020-12-05 - 实战:如何降低云开发服务器成本?
成本完全hold不住 [图片] 首先我们可以看到这幅图,CDN流量消耗和存储读请求次数特别大。 原因是因为在大量的登记里面上传图片需求非常高频,加上当时「群登记助手」日活高达4w。 我原本采购的 CDN 3(2199/月)套餐完成抗不住这个CDN消耗。 [图片] 所以我直接升级到旗舰 3 套餐。这个套餐每月4699/月(目前该套餐已下线) 作为一名独立开发者来说,这个成本非常高。 成本降低 1 倍 [图片] [图片] 于是我在「微信开放社区」进行了提问 我说什么时候能推出按需计费? 当天就得到了官方人员的回复,已经支持了。 然后我切换完成按量付费模式后,效果很明显。 成本直接从每个月4699降低到了2千多。 整体费用降低了 1 倍,但是这成本还是有点小贵。 再次降低 10 倍 [图片] 后来使用了云扩展的「图片处理」服务。 我使用了图片处理服务的「快速压缩模版」,使得成本再一次的降低。 [图片] 上次是 1 倍,这次是 10 倍,从原来的2249变成了现在的2百多。 总结 从每月4699套餐到每月208的成本,一共降低了20倍成本。当然本次案例是一个CDN流量消耗过大的案例,其他点也可以根据这个思路来优化。 优化思路 先找到消耗的关键点(如:本次案例为CDN) 如果是单个点很高,就按需收费。(套餐其他资源没被利用,也是浪费) 根据具体业务场景来优化次数。(如:请求次数,可以一次性放多点内容,不需要请求多次。CDN流量,可以把列表图片压缩,因为列表不需要看高清图片。)
2020-12-02 - 实战:图片处理服务之快速压缩模版
前言 在昨天发布的《实战:如何降低云开发服务器成本? 》文章,评论区有提到需要「关于cloudbase的扩展能力-图像处理-快速缩略模板的用法」今天我就来和大家分享一下具体用法和效果。 安装 地址:https://console.cloud.tencent.com/tcb/env/overview 选中「扩展能力」菜单下面的「扩展应用」 [图片] 选择「图片处理」服务进行「安装」 [图片] 安装过程一直「下一步」就行没有需要配置的地方,需要等待几分钟 [图片] 查看文档 安装完成之后我们就可以使用了 首先看下文档:https://cloud.tencent.com/document/product/876/42103 [图片] 找到我们要用的「快速压缩模版」。 地址:https://cloud.tencent.com/document/product/460/6929 使用方法,直接在图片后面来评价参数即可。 [图片] 实战使用 通常使用在列表场景中,本来就不要高清图,所以可以进行压缩也不会影响用户体验。 [图片] 我们找一个图片链接放在浏览器上来看 [图片] 然后使用下快速压缩模版拼接参数 ?imageView2/1/w/100/h/100 [图片] 把两张图片下载下来对比一下大小 [图片] 压缩后小了54倍 总结 这样以来不仅让用户能够更快的加载出图片,并且还能降低服务器资源成本。
2020-12-02 - 总结的一些血与泪的教训
云函数 云函数的上传流程 云函数必须先保存后上传,因为编译器不会在上传的时候自动保存。 保存的方法可以是CTRL+S或者是编译都可以,CTRL+SHIFT+S和编译是等效的;上传的方式可以是对文件的增量更新或者说部署云函数。主要是顺序必须先保存后上传。 云函数部署对于触发器并没有效果,触发器得单独上传(仍然需要先编译)。 如何知道云函数是否执行出错了 云开发→云函数→日志,读取日志,可以读取服务器返回的数据,所以云函数中最好用try{}catch(e){}结构,然后return JSON.stringify(e),当然你不这样也没问题,因为服务器会自动吧错误写进日志 云函数中console.log()的内容会写在日志的末尾。 当然,你也可以使用云函数的本地调试功能(来自Littlesnail大佬的分享),但是本地环境是缺少wx-server-sdk这个包的,需要自己用npm装,大家可以自行衡量。 如何知道云函数是否真正执行完了 之前一直在python平台的我根本没接触过node.js这种异步事件驱动的语言,被异步快整疯了 首先说结论,你得把所有返回Promise类型的方法都加上await关键字,并函数变为async类型。 所谓的Promise类型是指的一个虚的对象,他传递给某些函数后,对那些函数进行一个“保证”,保证过会会给那些函数数据。因此那些函数会先等着,等Promise返回数据后,提醒这些函数开始工作了,然后上一个工作完提醒下一个直到函数执行完。因为有些操作,比如数据库的读取是需要时间的,所以要是一直等着会阻塞整个线程。(node.js似乎是单线程的,这算是一直对多线程的补偿方案吧) Promise对象有三种状态。Pending,表示正在运行中;fulfilled,表示完成然后会自动提醒下一个函数;rejected,表示失败,当然如果你用try catch了就没事。 重点来了,对于async的云函数来说,执行到最后或者return就相当于结束了,然而这个时候如果函数内仍然有处于“保证中”(Pending)的Promise对象,云函数是不会等它执行完才返回的,它会直接返回。所以你必须加一个关键字,await,表示下面的语句都会等着这个异步操作完成再工作,也就是阻塞住。这样才能真正使得云函数完全执行完。 如何判断什么函数返回的是Promise对象,在开发者工具的console里面输入一下这个函数就行了,实在不行typeof。 PS:以前我曾经想过,如果吧云函数的async标签去掉,是否能实现不需要await也能正常运行呢,答案是,你想的美,去掉后人家该Promise还是Promise,不会给你变成现实(特朗普:恶意的zz隐喻,举报了)。所谓的async标签是指的我警告你我这玩意是异步的,你能用then,但是大多数情况你都不会去用then,所以,老老实实await吧。 你们就不要挣扎了,老老实实吧所有出现异步的位置都async和await吧 你标了async的函数,调用它的函数也得加async哦,而且调用的时候也得await 【async-await地狱】 小细节 云函数的运行环境中你不能获取到函数的信息,this.name,this.toString(),或者window.decodePathName都是undefine 数据库 为什么有时候数据库的get没用请确定你的权限是否有误,在 云开发控制台-数据库-权限设置 中可以调整,一般来说选第一个权限 为什么有时候数据库的update没用 首先当然是排查是不是因为在云函数中你却没有加await,数据库的几乎所有操作全是异步的,返回的基本都是Promise对象,另一点是小程序中的db.collection('seats')对象是动态对象,你不能这样写 db_seats=db.collection('seats') db_seats.where({}) 如果你在下面的语句继续用db_seats来直接对数据库做一些操作,相当于你创建了一个数据库的本地副本,你get()数据当然是有用的,但是可能不是最新的数据,但是你add()和update()都是没用的。你必须每次都用db.collection('xxx')来调用xxx数据库。 (实际上后来经过调查发现,add有时候是可以的,但是update保证每次都不行,不知道为啥) ————(以下为更新内容)———— 事实上还有一种更加广泛的错误 一定要注意,doc函数查到的是数据库的索引的_id字段,而不是_openid字段,你拿openid去doc一定找不到东西。 所以如果你一开始只有openid这个数据,那你最好先用where找到对应数据的_id,然后再doc它的id (为什么不直接where后接update?where返回的是一个数组,where接update相当于一次性修改一组数据,只有服务器端,也就是云函数有这个权限,小程序的前端只能先查询再修改) 数据库传入的数据 只有数据库的where(),update(),add(),传入的JSON数据中,只有data是必选属性,其他属性像是success,fail都是非必要的,如果你不用错误处理的话就不用加 then怎么用 数据库的then(),或者说所有的异步操作的then,你把函数放在then()里面表示到时间后完成某项操作,比如 //获取所有data const seats_list = await db.collection('seats').where({ used: true }) .get().then(res => { // return res.data console.log(res.data) }) 相当于吧收到的参数命名为res,然后等res异步返回后,会在=>{}里面处理一些只跟res有关的操作,这当然是有用的,但是问题在于then里面是一个闭包,你console.log当然是可以的,但是你要在里面用this.setData()就不行了,这个闭包的this可不是指的你这个外面的对象,他就是指的它点前面的对象,也就是get()后返回的对象。 所以想在外面用的话,正确操作是吧注释去掉,也就是return res或者return res.data,然后这样的话,外面的seats_list 就能被赋值为res或者res.data了,然后接下来的语句尽情对seats_list 操作,千万别忘了加await,否则你懂的。 WXML 为啥我的wx:if不管用 请注意一件事,那就是。。。WXML里面的任何函数部分,都是不能有空格的,比如这样写 点击选座位 点击选座位 点击选座位 点击选座位 点击选座位 无论你空格加在哪里,只要被""括起来,加空格都会使得wx:if失效,相信小伙伴们此时已经反应过来了,没错,这就是JS那个傻逼机制,非空字符串相当于true > Boolean(" ") > true 我猜wx:if的机制只是简单的做了下拆分,然后配对了下,并不能实际把它当程序运行(甚至可能正则都没用),所以如果遇到了它没拆成功的字符串,它就干脆暴力返回原字符串,当然被识别为true了。 整体 Js的所有变量都需要预先var数据库的Data和云函数的Data JS的Date()函数很迷,它返回的是tm一个String,当前时间的String,你必须用var new才能构造一个真正的Date对象而不是一个字符串,也就是按照下面方法使用,加注释的都是错误的用法。 var nowdate=new Date() // Date().getDate() // Date(一个Date对象).getDate() // Date(一个字符串).getDate() 另外就是无论你在哪调用这个函数,他创建的都是你调用端的时间,而不是云端的时间,就算你在云函数调用也一样,你电脑或者手机是几点他创建的就是几点,我都服了。 云端的时间只能用db.serverDate()来构建,但是问题在于db.serverDate()是tmd一个指令,他返回的不是一个Date,而是类似db.commend那种类型的东西,给传入服务器的data用。 //更新nowdate为服务器时间 await db.collection('seats').where().update({ data:{ nowdate:db.serverDate() } 也就是你平时用是没JB用的,你只能老老实实去用JS的Date方法请求世界时然后倒时区。 总结 以后想到了继续补充,欢迎大佬分享
2020-05-11 - 实现一个本地数据库
问题背景 对于一些变化性不强但可能多次使用的数据(如一些列表、文章信息等),如果每次都进行网络请求,不仅减慢了速度,也加重了服务器负担;这时往往需要通过 [代码]setStorage[代码] 和 [代码]getStorage[代码] 进行本地缓存,但也存在一些问题,一个是 [代码]storage[代码] 只能通过 [代码]key-value[代码] 的形式进行管理,无法进行更复杂的数据库操作,另外每次都从本地 [代码]storage[代码] 中读写效率不高 改进方式 改进读写方式 在 [代码]init[代码] 的时候读取本地 [代码]storage[代码],之后所有读写都在这个变量中进行操作,不必每次读取本地 [代码]storage[代码];在进行写入操作后,定期写回本地 [代码]storage[代码],也可以减少写入次数 存储结构 一个集合由一个 [代码]object[代码] 组成,里面的每一个键值对表示一个记录,通过 [代码]id[代码] 查询效率高([代码]id[代码] 可以自动生成,默认为 [代码]4[代码] 位由数字字母组成的随机值),通过 [代码]where[代码] 查询则需要遍历 实现数据库的 api 为减少上手难度,所有 [代码]api[代码] 都参照了 云数据库 的设置,几乎所有的方法都可以直接使用(但由于本地数据库存取都较快,所有方法都是同步方法,直接返回结果),通过这些 [代码]api[代码],可以大大便利数据的查询和设置 例程 [代码]const localDB = require('utils/localDB.js') const _ = localDB.command localDB.init() // 初始化 var articles = localDB.collection('articles') if(!articles) articles = localDB.createCollection('articles') // 不存在则先创建 // 按文章 id 查找 var doc = articles.doc('xxx') if(doc) { var data = doc.get() // 取得数据 } else { // 网络请求获取 data data._timeout = Date.now() + 15 * 24 * 3600000 // 设置过期时间为 15 天 articles.add(data) // 添加到本地数据库 } // 按类型查找 var data = articles.where({ type: 'xxx' }).get() // 正则查找 var data = articles.where({ title: /xxx/ // 标题中含有 xxx 的 }).get() // 分页查找 var page2 = articles.skip(10).limit(10).get() // 按时间查找 var data = articles.where({ date: _.gte('20200501').and(_.lte('20200510')) // 大于等于 20200501 小于等于 20200510 }).get() // 结果排序 var data = articles.orderBy('date', 'desc').get() // 按日期降序排序 // 清理过期数据 articles.where({ _timeout: _.lt(Date.now()) // 过期时间早于当前的 }).remove() [代码] github MpLocalDB
2020-05-09 - 关于云开发的一次性订阅消息
前段时间看到了这位老哥的一篇关于订阅消息的文章:https://developers.weixin.qq.com/community/develop/article/doc/0008802e8381e0eeabb92c9975b013 这篇文章对于程序员来说非常直观的说明了一次性订阅消息的逻辑:订阅1次,可以收到订阅消息一次,订阅10次,可以收到订阅消息10次。 但是我觉得这个方案对于一个普通用户来说,并不够友好,如果我是一个不懂订阅消息的普通用户,我根本不会花时间去点这样一个点1加1的订阅消息。我觉得对于开发者来说,用户能够点一次允许并且勾上不在询问就已经很不错了,剩下的完全可以交给程序来处理。下面是我的方案。让一次性订阅消息达到长期订阅的效果。 首先明确以下逻辑: 通过 wx.getSetting({ withSubscriptions: true }) 的 success 回调 res 可以得出订阅消息的以下5种状态[图片]当用户勾选了“不在询问”之后,不管你后面怎么调 wx.requestSubscribeMessage ,订阅消息的弹窗都是不会弹起的wx.requestSubscribeMessage 需要用户手动点击触发当得到以上几种状态之后,接下来就可以根据需要做自己想要做的操作 如我的小程序首页是一个版本列表 [图片] 我在列表的头部设计了一个跟小程序同风格的授权卡片,这样不会显得突兀同时告诉用户点击授权并且勾选“不在询问”,并告诉用户这样做的目的是什么。 然后根据上面得到的不同状态来显示不同的提示语: 总开关关闭了: [图片] 勾选了“不在询问”并且选项是取消 [图片] 接下来就是实现订阅消息+1的步骤,上面提到了当用户勾选了“不在询问”之后,不管你后面怎么调 wx.requestSubscribeMessage ,订阅消息的弹窗都是不会弹起的 这时在用户点击你应用中必点的操作时,比如知乎微博的点击列表进入详情,或者我这个小程序点击版本列表进入版本详情时就可以根据以上得到的状态来判断:当授权状态是“选择了不在询问并且选项是允许” 时,直接调用 wx.requestSubscribeMessage ,这时 wx.requestSubscribeMessage 的回调必定是success,而且不会出现授权弹窗,自然也就实现了+1效果。 最后把订阅次数+1记录到数据库,推送时推送订阅次数大于0的就ok了 [图片] 这样一个普通用户需要做的操作就只有点击授权-勾选不在询问-允许 这样一个步骤,同时就实现了无形中增加订阅次数的效果,替代让用户手动去点+1增加订阅消息的操作。 另外不用担心这种操作会使用户感觉像垃圾广告一样一直被推送,因为不管是在服务通知页面,还是在设置页面,用户都是可以很轻松的一键关闭通知。 [图片] 然后说下订阅消息的几个特殊情况: 1.当你的账号在开发者工具上面点过允许或取消的时候,wx.getSetting({ withSubscriptions: true }) 的 success 回调结果是这样的 [图片] 手机上的设置界面是这样的 [图片] 回调的itemSettings属性消失了,界面上有订阅消息的开关,但是订阅消息的选项却没了,正常情况应该是这样的 [图片] 这样的 [图片] 2.当用户点了“不在询问并允许”但是又手动通过服务通知页面,或者设置页面关闭了消息通知,这时就算该用户之前已经订阅过了很多次,都会被系统自动清0,这时你的数据库可能存的该用户还有比如5次订阅消息,但是通过cloud.openapi.subscribeMessage.send推送消息的时候,会进catch,errCode是43101。 3.当用户手速过快连续点击了授权按钮触发wx.requestSubscribeMessage时,会进入fail回调,errMsg是 requestSubscribeMessage:fail last call,这个文档是没写的。 最后可以扫码体验一下: [图片]
2020-07-14 - 只有三行代码的神奇云函数的功能之一:获取openid
这是一个神奇的网站,哦不,神奇的云函数,它只有三行代码:(真的只有三行哦) 云函数:login index.js: const cloud = require('wx-server-sdk') cloud.init() exports.main = async (event) => { return { ...event, ...cloud.getWXContext() } } 神奇功能之一:获取openid: 任何页面运行下面代码,已解决异步问题。 onLoad: async function (options) { app.globalData.openid=app.globalData.openid||(await wx.cloud.callFunction({name:'login'})).result.OPENID console.log(app.globalData.openid) }, 其他功能: 神奇功能之二:不用授权获取unionid: 不需要弹出授权框,直接获取unionid,但是不保证100%成功获取,有可能unionid为空。 https://developers.weixin.qq.com/community/develop/article/doc/000a0c6b580338e947f9db0c65b813 神奇功能之三:100%成功获取unionid: 保证100%成功获取unionid,需要用户信息授权。 https://developers.weixin.qq.com/community/develop/article/doc/00066a967c4e384949f93fe1151413 神奇功能之四:获取电话号码: 还是这三行代码,获取用户的电话号码。 https://developers.weixin.qq.com/community/develop/article/doc/0006a8ec7ac860c94bf90a34f5d813 神奇功能之五:获取群id: 将小程序分享到某群里,可获得该群的群id, https://developers.weixin.qq.com/community/develop/article/doc/000ea802c00f70894cf9fe72556013 [图片]
2020-10-20 - 免费ICP备案攻略。不花1分钱拥有一台云服务器并顺利ICP备案。
写在前面: 大家不要将ICP证和ICP备案搞混了。 ICP证指的是【电信增值业务经营许可证】,这个资质需要企业主体至少100万注金,去工信部办理,比较难办理;社交-交友需要ICP证。 而ICP备案,【非经营性互联网信息服务备案核准】仅仅是指企业主体的域名备案,可以简单的按以下步骤免费办理成功,其他社交类目如社区、论坛、笔记等,只需要ICP备案即可。 1、在腾讯云注册一个账号并认证企业主体(不吹不黑,开发小程序当然首选腾讯云,好用)。http://www.qcloud.com/ 如果你是个人主体,就不要往下看了,没必要折腾了。 2、找到腾讯云免费活动页:https://cloud.tencent.com/act/free?from=10107 3、选择一款云服务器,180天免费试用。 云服务器申请成功后,它的使命就完成了,没用了,让它自生自灭吧。 在整个备案过程中,也不需要部署网站(域名都没有备案,哪来的网站?)。 [图片] 云服务器180天到期后,可以自己决定是否续费,每个月也才99元,促销期甚至更低,完全可以接受吧。 备案成功后,该服务器就没什么作用了,让它180天后自然欠费销毁得了。 服务器销毁后会有什么影响?答:没有任何影响。 但是。。。。。 你备案的域名最后还得指向一个网站,因为腾讯云会应工信部的要求定期检查网站是否合规,所以你还是要建一个简单的网站,(备案期间,可以暂时不管网站的事,等将来需要的时候再管理)。 至于有多简单,答,多简单都行。此时你可以在七牛、腾讯云、阿里云租点免费的对象存储空间,做个简单的网站。 4、在进行ICP备案之前,你需要在腾讯云注册你的域名地址,如果你已有域名,但不在腾讯云,建议先将要域名过户到腾讯云的账号上。 5、进入控制台,开始ICP备案,这个流程就不介绍了,因为完全一看就懂。而且现在使用备案小程序后,不需要幕布或现场拍照了,极其方便,大家跟着流程走就一点问题没有,有人脸识别和在线拍一段小视频。另外,大家可以随便作,随便填,填错或者填得不合适也不用怕,会有专门的备案客服打电话告诉你哪哪要改,还会告诉你应该怎么填才更容易通过工信部的审核,客服的态度好得发指。 仅说一点其中的几个小坑: a、人脸识别的时候,白色背景、白色背景、白色背景,笔者在人脸识别的时候,满世界找白墙,结果还被打回来重拍了3次。 b、网站用途一律写:公司官网,好通过工信部审核。 6、腾讯云提交资料到工信部审核。这是一个漫长的让人无语的等待,20-30天。笔者最近两次都是20天才过审;不要幻想会有可能提前完成审核,这是政府部门在审核,提前完成说明某政府人员的工作安排有问题,会犯错误的。 7、备案成功后,会有短信通知你,但是,你需要去工信部网站查询结果,并将结果切屏拷贝下来,因为小程序类目审核需要上传这张图片。http://beian.miit.gov.cn/publish/query/indexFirst.action [图片] 把上面这张图片保存好,小程序类目审核的时候需要上传。收到通知后,如果在这里查不到结果,也别急,据说需要24小时。 8、接下来是小程序上线审核。 因为ICP备案的小程序内容肯定涉及到社交,最后小程序上线时还要提交到工信部审核,还需要7天左右的时间,加上前面ICP备案的时间,加起来怎么也得30-40天。大家估计时间,别影响小程序上线。这7天也是政府部门在审核,不要幻想会提前。 9、计算一下时间: 腾讯云注册账号和认证:1-3天; 域名备案:腾讯云环节:1-3天; 域名备案:工信部环节:20-30天; 小程序添加服务类目:社交类目审核:1-3天; 小程序上线审核:腾讯环节:1-2天; 小程序上线审核:工信部环节:7+天; 总天数:30-40天; 10、节省时间的一些建议: 在开发小程序之前,就开始备案工作,小程序可以同时开发,相互不影响; 在开发完成之前一、两星期之内,先发布一版小程序,别管功能是不是完整,能通过审核就行,这样会有7天的等待类目审核的时间,这个时间里,小程序可以照常开发,不影响进度; 只要是社交类,基本需要有文字和图片安全检查功能,别忘了加上,别到时审核通过不了。 11、结束。 [图片]
2021-01-19 - 【必收】精心整理!小程序开发资源汇总(附带源码)
很多小伙伴想在春节放假期间学小程序,但是小程序学习的资源和教程可能不太好找。所以小助手精心整理了一期,全是干货!认真学,开启美妙的小程序开发之旅,做一个属于自己的微信小程序。有需要的小伙伴收藏好这期文章哦~ 本文收集整理了微信小程序开发资源,包括官方文档,云开发训练营文档,视频教程以及实战源码推荐,会不间断更新。。 欢迎添加云开发小助手CloudBase微信:Tcloudedu1 ,一起加入技术交流群~ 小程序云开发官方公众号 [图片] 目录 官方文档 云开发训练营 视频教程 小程序·云开发Demo 技术交流群 官方文档 小程序开发者工具 小程序设计指南 小程序开发教程 小程序框架 小程序组件 小程序API 小程序开发者工具 小程序云开发文档 云开发训练营 小程序开发入门 小程序与JavaScript 云开发快速入门 [图片] 视频教程 腾讯云云开发B站:https://space.bilibili.com/447496276 [图片] 小程序·云开发Demo 技术博客小程序 包括文章的发布及浏览、评论、点赞、浏览历史、分类、排行榜、分享、生成海报图等。 网盘小程序 兼具文件存储与分享功能的专属网盘小程序。 教务助手小程序 用完即走,查个成绩和课表,无需下载app或去翻看公众号内的历史内容。 功能日历小程序 既能查看日历又能备注事项,看云开发如何支持功能性日历小程序的快速开发。 客户业务需求收集小程序 用云开发快速制作客户业务需求收集小程序,教你用云开发实现小程序版“朋友圈”的发布与展示。 小程序朋友圈 把朋友圈装进小程序需要几步?借助云开发实现小程序朋友圈的发布与展示。 南苑导览 一款由学生独立开发的以地图为载体,提供中山大学南方学院具体地点的位置信息、导航、校园历史及文化介绍的小程序。 互动打卡小程序 用云开发轻松构建精美互动打卡小程序,交互式双人打卡,快乐加倍。 个性头像小程序 别再@官方啦!云开发教你轻松制作个性头像小程序,趣味挂件、个性icon。 二手书商城小程序 云开发轻松制作二手书交易商城小程序,让智慧延续,让温暖传递。 后台数据批量导出 小程序开发过程中如何将云数据库中的数据批量导出至excel。 发送邮件 初学者福音,手把手教你用小程序云开发实现邮件发送功能。 高考查分小程序 实现高考分数轻松查,小程序源码。 mini论坛 仅需两天轻松搭建mini论坛小程序。 运动圈小程序 打造运动圈小程序(以乒乓球为例),实现球友间高效互动。 心情日记小程序 我能想到最浪漫的事,可能就是“你的心事我全知晓”。 最美恋爱小程序 小程序前端用的是taro框架写的,后台用的云开发。教你用云开发为心爱的人做个小程。 校园约拍小程序 校园场景下,小程序·云开发大显身手,校园约拍小程序源码。 体重记录小程序 只想记录每日体重还得下个APP,不用那么麻烦!用云开发做个专属体重记录小程序,看看你每天瘦了多少。 口袋工具 口袋工具之历史上的今天。一个基于云开发的小程序,看看历史上的今天都发生了啥。 迷你微博 独立做个精简版微博出来让你刷刷刷吗?而且,它还兼具搜索、点赞、主页的功能 多媒体小程序 使用小程序·云开发构建多媒体小程序。 技术交流群 交流技术为主,开发学习工作中遇到问题可以在群内交流,欢迎有需要的朋友加群。 添加小助手微信(Tcloudedu1),回复“技术群”,即可加入云开发技术群。 最后 如果你有关于使用腾讯云云开发相关的技术故事/技术实战经验想要跟大家分享,欢迎留言联系我们~ 关注腾讯云云开发,后台回复【源码】,获取更多微信小程序云开发实战源码。 [图片] [图片] [图片] 关注「腾讯云云开发」,后台回复【 源码 】,获取更多微信小程序云开发实战源码。 持续更新中… [图片]
2020-01-16