- [填坑手册]小程序目录结构和component组件使用心得
[图片] 小程序目录结构 关于小程序的目录结构,可以说一开始大家都有各自的开发习惯和命名规则,但一旦项目变得复杂庞大的时候,你就发现管理起来和后期维护变得很麻烦,如果是 协同开发 的话,更容易出现 “互坑” 的情况。 智库君在一年多的小程序开发中也跳过不少的坑,总结了一套还算好维护的目录结构跟大家分享(仅供参考,觉得好拿去,觉得不好欢迎提出意见),以下是实战项目中的结构示例: [代码]├─ app.js --- 小程序加载时优先加载的入口JS ├─ app.json ---入口文件和公共配置 ├─ app.wxss ---公共样式表 ├─ project.config.json ---小程序全局配置文件 ├─ sitemap.json ---允许微信索引文件 │ ├─cloud-functions ---云函数 │ └─setCrypto ---数据加密模块,用户加密一些数据 │ index.js │ package.json │ ... │ ... │ ├─components ---小程序自定义组件 │ ├─plugins --- (重点)可独立运行的大型模块,可以打包成plugins │ │ ├─comment ---评论模块 │ │ │ │ index.js │ │ │ │ index.json │ │ │ │ index.wxml │ │ │ │ index.wxss │ │ │ │ services.js ---(重点)用来处理和清洗数据的service.js,配套模板和插件 │ │ │ │ │ │ │ └─submit ---评论模块子模块:提交评论 │ │ │ index.js │ │ │ index.json │ │ │ index.wxml │ │ │ index.wxss │ │ │ │ │ └─canvasPoster ---canvas海报生成模块 │ │ index.js │ │ index.json │ │ index.wxml │ │ index.wxss │ │ services.js ---(重点)用来处理和清洗数据的service.js,配套模板和插件 │ │ ... │ │ ... │ │ │ └─templates ---(重点)模板,通过外部传参的容器,不做过多的数据处理 │ │ │ ├─slideshow ---滚屏切换模板 │ │ index.js │ │ index.json │ │ index.wxml │ │ index.wxss │ │ service.js ---(重点)用来处理和清洗数据的service.js,配套模板和插件 │ │ │ └─works ---作品模板 │ │ index.js │ │ index.json │ │ index.wxml │ │ index.wxss │ │ service.js │ │ │ ├─articlePlugin ---作品模板中的文章类型 │ │ index.js │ │ index.json │ │ index.wxml │ │ index.wxss │ │ │ ├─galleryPlugin ---作品模板中的九宫格类型 │ │ index.js │ │ index.json │ │ index.wxml │ │ index.wxss │ │ │ └─videoPlugin ---作品模板中的视频类型 │ index.js │ index.json │ index.wxml │ index.wxss │ ... │ ... │ ├─config ---自定义配置文件 │ config.js ---存放基础配置 │ constants.js ---存储常量 │ weui.wxss ---第三方文件wxss,js等 │ ... │ ... │ ├─pages ---小程序页面 │ ├─user ---用户页面 │ │ index.js │ │ index.json │ │ index.wxml │ │ index.wxss │ ├─news ---新闻页面 │ │ index.js │ │ index.json │ │ index.wxml │ │ index.wxss │ │ │ └─home ---首页 │ index.js │ index.json │ index.wxml │ index.wxss │ ... │ ... │ ├─request ---https请求管理(根据switch tab分类会比较好) │ common.js ---一些公共请求获取,如兑换openId,unionId 等 │ news.js │ uri.js --- (重点)总的URI请求管理,方便切换和配置DEV,QA,PROD环境 │ user.js │ ... │ ... │ └─utils ---功能组件 logger.js ---日志管理 util.js ---公共小组件库 ... ... [代码] 例如微信自己的wepy的官方文档,现在也添加了目录结构说明: [图片] 为什么一定要写这个目录结构呢? 不知道大家有没有发现,在以往的老项目交接和多人协同开发中,容易遇到别人写的模块,变量命名不准确,或者资料缺损,一次十来个方法/组件间的互相调用,直接把接(盘)手的人整懵逼了,所以智库君觉得,无论是独立开发,还是协同开发,留一份完整的目录说明文档是很有必要的,勿坑 他人 OR 未来的自己~~~ component使用心得 大家在开发过程中肯定会去看官方文档,但不可能全看完才开始写代码,大多数情况都是用到了再看,本人也是,所以下面抽一些开发中遇到的重点来讲: 一、引用组件模板页面的自定义 组件模板的写法与页面模板相同。组件模板与组件数据结合后生成的节点树,将被插入到组件的引用位置上。 在组件模板中可以提供一个 <slot> 节点,用于承载组件引用时提供的子节点。 [代码]<!-- 组件模板 --> <view class="wrapper"> <view>这里是组件的内部节点</view> <slot></slot> </view> [代码] [代码]<!-- page页/父页面引用组件的页面模板 --> <view> <component-tag-name> <!-- 这部分内容将被放置在组件 <slot> 的位置上 --> <view>这里是插入到组件slot中的内容</view> <view>在加载组件的页面里自定义内容,将没有复用性的内容写在这里</view> </component-tag-name> </view> [代码] 页面自定义部分默认是加载在组件上方。 为什么要在引用组件的页面添加这些内容呢? 因为组件其中一个重要的特点是复用性,但是有的时候可能要根据不同场景做一些自定义,如果在组件中写大量的场景/逻辑判断,会增加组件的冗余,而且这些方法只是被复用一次的话,完全可以不写到组件里。 二、“一键换肤”根据不同场景给组件引入外部样式 [代码]<!-- 外部引用组件的页面传入样式 --> <WorkComponent extra-class="style1" j-data="{{workData}}"></WorkComponent> [代码] [代码]//组件中js Component({ /** * 引入外部样式,可传多个class */ externalClasses: ['extra-class','extra-class2'], }) [代码] extra-class 从外部引入父级css,可用根据不同场景配置不同的样式方案,这样使得组件自定义能力更强。 三、数据清洗与容错 [代码]//service.js 思路示例 module.exports = { /** * 功能:处理作者列表 * @param list * @returns {Array} */ authorList: function (list = []) { let result = []; list.forEach(item => { result.push({ guid: item.recommend_obj_id || '', type: item.recommend_type || '', logo: (item.theme_pic || '').trim() || '', title: item.title || '' }); }); return result; } }; [代码] 如果外部传入的数据要分别导入多个组件中,可以在组件中建立一个对应的service.js,有2个作用: 清洗数据,避免setData()的时有过多的脏数据 错误数据的兼容,添加数据缺省值,增加代码健壮性 四、canvas在component组件中无法选中的问题 [代码] //这里只需要在后面 添加this对象 let ctx = wx.createCanvasContext('myCanvas', this); [代码] 其他一些默认组件,遇到类似的问题,一般只要引用时传入this对象即可解决。 五、组件之间的通讯 在实际生产环境中,我们常常需要控制各个组件之间的互相通信/传参,下面介绍下具体的用法: WXML 数据绑定:用于父组件向子组件的指定属性设置数据,仅能设置 JSON 兼容数据(自基础库版本 2.0.9 开始,还可以在数据中包含函数)。具体在 组件模板和样式 章节中介绍。 事件:用于子组件向父组件传递数据,可以传递任意数据。 如果以上两种方式不足以满足需要,父组件还可以通过 this.selectComponent 方法获取子组件实例对象,这样就可以直接访问组件的任意数据和方法。 设置监听事件: [代码]<!-- wxml 中 当自定义组件触发“myevent”事件时,调用“onMyEvent”方法 --> <component-tag-name bindmyevent="setMyEvent" /> <!-- 或者可以写成 --> <component-tag-name bind:myevent="setMyEvent" /> [代码] [代码]// index.js 父页面中 Page({ setMyEvent: function(e){ let self = this; if (e.detail) { // 自定义组件触发事件时提供的detail对象 switch (e.detail) { case "hidden": //隐藏 悬浮框上的评论 this.setData({ isFixCommentShow: false }); break; case "fixRefresh": //刷新悬浮框 this.setData({ fixRefresh: true }); break; case "commentRefresh": //刷新评论 this.setData({ commentRefresh: Math.random() }); break; case "createPoster": //生成海报组件 self.setPosterSave(); break; } } } }) [代码] 父页面引用子组件,子组件发送的信息,可以通过bind的方法监听到,来获取到具体的传参值。 触发事件 自定义组件触发事件时,需要使用 triggerEvent方法,指定事件名、detail对象和事件选项: [代码]<!-- 页面 page.wxml --> <another-component bindcustomevent="pageEventListener1"> <my-component bindcustomevent="pageEventListener2"></my-component> </another-component> <!-- 组件 another-component.wxml --> <view bindcustomevent="anotherEventListener"> <slot /> </view> <!-- 组件 my-component.wxml --> <view bindcustomevent="myEventListener"> <slot /> </view> [代码] [代码]//组件中js Component({ properties: {}, methods: { onTap: function(){ var myEventDetail = {} // detail对象,提供给事件监听函数 var myEventOption = {} // 触发事件的选项 this.triggerEvent('myevent', myEventDetail, myEventOption) //myEventOption的一些配置: this.triggerEvent('customevent', {}, { bubbles: true }) // 会依次触发 pageEventListener2 、 pageEventListener1 this.triggerEvent('customevent', {}, { bubbles: true, composed: true }) // 会依次触发 pageEventListener2 、 anotherEventListener 、 pageEventListener1 } } }); [代码] myEventOption 的配置: bubbles(Boolean):事件是否冒泡 composed(Boolean):事件是否可以穿越组件边界,为false时,事件将只能在引用组件的节点树上触发,不进入其他任何组件内部 capturePhase(Boolean):事件是否拥有捕获阶段 需要强调一点:建议大家不要在组件上bind太多的监听,一方面以后管理起来会比较麻烦,另一方面首次加载如果调用过多方法会引起数据渲染的卡顿。 Component官方文档: https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/ 往期回顾: [填坑手册]小程序Canvas生成海报(一) [拆弹时刻]小程序Canvas生成海报(二)
2021-09-13 - 如何彻底解决小程序滚动穿透问题
背景 俗话说,产品有三宝:弹窗、浮层加引导,足以见弹窗在产品同学心目中的地位。对任意一个刚入门的前端同学来说,实现一个模态框基本都可以达到信手拈来的地步,但是,当模态框里边的内容滚动起来以后,就会出现各种各样的让人摸不着头脑的问题,其中,最出名的想必就是滚动穿透。 什么是滚动穿透? 滚动穿透的定义:指我们滑动顶层的弹窗,但效果上却滑动了底层的内容。 具体解决方案分析如下: 改变顶层:从穿透的思路考虑,如果顶层不会穿透过去,那么问题就解决了,所以我们尝试给蒙层加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,近期开放。 [代码] my.setPageScrollable({ scrollable: true, success: res => { console.log(res); }, fail: err => { console.log(err); }, complete: res => { console.log(res); } }) [代码] 20250618. API已开放,支付宝小程序测试时发现bug,安卓设置禁止滚动后,弹窗内的可滚动区域也会被禁止,IOS正常,且该问题暂时无法解决。 原因: 由于系统实现层面的差异,安卓与 iOS 对于滚动禁止的层级控制存在区别: 安卓端采用 Webview 级滚动限制(全页面锁定),生效时界面及所有弹层均不可滚动; iOS 端采用组件级滚动限制(局部锁定),当弹层激活时会智能区分层级,仅限制底层页面滚动而保持弹层可滚动。
星期三 16:30 - 小程序EventChannel的一些理解和使用实践
EventChannel是什么通过官方文档和示例,实际可以发现EventChannel借助wx.navigateTo方法,在两个页面之间构建起了数据通道,互相可以通过“派发事件”及“注册这些事件的监听器”来实现基于事件的页面通信。 具体看下下面的官方示例: wx.navigateTo({ url: 'test?id=1', events: { // 为指定事件添加一个监听器,获取被打开页面传送到当前页面的数据 acceptDataFromOpenedPage: function(data) { console.log(data) }, someEvent: function(data) { console.log(data) } ... }, success: function(res) { // 通过 eventChannel 向被打开页面传送数据 res.eventChannel.emit('acceptDataFromOpenerPage', { data: 'test' }) } }) events: 注册将在目标页面触发(派发)的同名事件的监听器 success:跳转后进行可通过res.eventChannel 触发自定义事件 //test.js Page({ onLoad: function(option){ console.log(option.query) const eventChannel = this.getOpenerEventChannel() eventChannel.emit('acceptDataFromOpenedPage', {data: 'test'}); eventChannel.emit('someEvent', {data: 'test'}); // 监听 acceptDataFromOpenerPage 事件,获取上一页面通过 eventChannel 传送到当前页面的数据 eventChannel.on('acceptDataFromOpenerPage', function(data) { console.log(data) }) } }) 通过this.getOpenerEventChannel()获取eventChannel对象, 使用eventChannel.emit() 可以在任意时刻触发(派发)事件。当前页面即可执行对应的事件处理函数。 使用eventChannel.on() 即可监听上个页面emit的事件。 使用示例A页面为订单页面,B页面为填写发票信息页面,A页面跳转B页面携带开票金额(amount),A页面如果已填写,再次跳转B页面,需要携带发票信息(invoice),方便直接编辑B页面填写后返回更新A页面的发票信息(invoice)简单的实现是 A到B的传值使用query实现,B到A的传值使用globalData实现。 // A页面 onShow(){ if(globalData.temp_invoice_info){ this.setData({ invoice:globalData.temp_invoice_info },()=>{ globalData.temp_invoice_info=null; }) } }, //跳转填写发票表单信息 toFillOutInvoiceInfo() { wx.navigateTo({ url: `/pages/invoice/invoice-info-form/invoice-info-form?amount=${ this.data.detail.amount }&invoice=${ this.data.invoice.invoice_title ? JSON.stringify(this.data.invoice) : "" }`, }); }, //B页面 onLoad(options){ this.data.options = options; if (this.data.options.invoice) { this.setData({ invoice: JSON.parse(this.data.options.invoice) }) } }, submit(){ globalData.temp_invoice_info = this.data.invoice; wx.navigateBack(); } 如下EventChannel的实现 //A页面(注意这里需要使用箭头函数(访问this)) //跳转填写发票表单信息 toFillOutInvoiceInfo() { wx.navigateTo({ url: `/pages/invoice/invoice-info-form/invoice-info-form`, events:{ updateInvoice:(result)=>{ this.setData({invoice:result}) } }, success:(res)=>{ const params = { amount: this.data.amount, invoice:this.data.invoice.invoice_title ? JSON.stringify(this.data.invoice) : "" } res.eventChannel.emit('sendQueryParams',params) } }); }, //B页面 onLoad(){ this.getOpenerEventChannel().once('sendQueryParams',(params)=>{ const {amount,invoice} = parmas; this.setData({amount,invoice}) }) } submit(){ this.getOpenerEventChannel().emit('updateInvoice',this.data.invoice); wx.navigateBack(); } 还有一个额外的地方就是EventChannel可以在A-B-C多个页面直接建立数据通道。官方示例。 //注意this.getOpenerEventChannel() 只能在navigateTo模板页面使用,其他更多页面使用时, //可以保存在getApp()全局实例中以备其他页面使用 // 保留AB通道事件,已备C页面给A页面发送数据 // B页面 const eventChannel = this.getOpenerEventChannel() getApp().pageBEventChannel = eventChannel //C页面 getApp().pageBEventChannel.emit('PageAacceptDataFromPageC', { data: 'Page C->A' }); 有不对或扩展的地方欢迎大家批评交流。
2022-08-11 - 关于picker-view 的mask的背景渐变样式设置
如题,自己在使用picker-view组件时,想把picker-view的背景色改成另一个颜色,以符合视觉稿。通过一番摸索后发现,picker-view背景显示白色的原因是因为其上方的遮罩层设置了带透明度的白色,故自己想从设置mask的背景色来解决这个问题。但是当设置了mask的渐变背景后,发现渐变只覆盖了mask的上半部分。 [图片] - 请问如何设置能使渐变色覆盖mask的全部呢?。通过自己的观察发现,picker-view组件 中mask的默认背景渐变样式是从顶端开始到选中框上方,不透明度逐渐降低,而后选中框上方完全透明,再从选中框下方开始直到末尾不透明度逐渐增加,呈现一种以选中框为轴两端对称的渐变效果,请问是否有便捷方法能设置mask的样式以模仿这种效果,只是背景色稍加变换? [图片]
2018-07-24 - 小商店助手里出现了2个未命名的微信小商店账号,能否注销掉?
[图片][图片]
2024-06-06 - rich-text不能识别换行符吗?
<rich-text nodes="{{nodes}}"></rich-text> 第一种换行符不识别 nodes: [{ { name: "div", attrs: { class: "content", style: "width: 100%;font-size: 16px;" }, children: [{ type: 'text', text: '第一行文本\n第二行文本' }], }, }], 第二种换行符不识别 { name: "div", attrs: { class: "content", style: "width: 100%;font-size: 16px;" }, children: [{ type: 'text', text: '第一行文本<br/>第二行文本' }], }, 请问有谁知道怎么才能换行呢?
2024-06-04 - 管局已经通过了审核,但小程序还一直在审核中?
管局已经通过了小程序备案审核,但是小程序里面还是显示管局审核中? 小程序ID是wx4073f9ab71fb8a1a [图片]
2024-06-03 - 性能报告--性能数据查看与使用(一)
性能数据:提供官方统计的各项性能数据,包含启动、网络、运行、体验等多个维度的小程序性能统计指标,以及综合性的性能评估报告。分五个模块:性能报告、启动性能、网络性能、运行性能、体验分析 一、性能报告 性能报告:可选择按版本查看数据或者按周期查看数据。如下图 [图片][图片] 评估方式:性能报告将根据小程序的性能日志,对启动、网络、运行3类指标进行综合评估。评估等级分为“优秀”、“良好”、“一般”、“较差”4个档位(即综合评估),同时提供同类均值用作参考(即详细性能评估)。同类均值即同行业、同量级的相关小程序的性能指标均值。 按周期评估:将根据截至昨日的性能日志,进行评估。每日11点前进行更新。 按版本评估:为确保性能日志所表现出来的数据相对稳定,仅评估版本存续时间大于等于5天的小程序版本。 综合评估信息如下图 [图片] 启动性能:从以下几个方面来评估:性能评估、整体打开率、总启动耗时、首次渲染耗时、JS注入耗时、代码包下载耗时。如下图 [图片] 网络性能:从性能评估、请求耗时、请求错误率来评估。如下图 [图片] 运行性能:主要分性能评估和页面切换耗时评估,如下图 [图片] 业务影响:即该小程序影响到多少人,流失了多少次数。a、影响人次:所选周期/版本内。累计的单日影响人数。单日内的影响人数去重。隔天累计,不做额外去重。影响人次的定义为打开率折损的相关用户,同时如果用户有1次没打开,但后续有重新打开,仍然计算为影响人次。b、流失次数:所选周期/版本内,累计的单日流失次数。 [图片] 1.1、启动性能 启动性能:整体打开率(所选周期/版本内,在小程序冷启动的情况下,点击小程序到首个页面渲染完成的平均留存率)、总启动耗时(所选周期/版本内,在小程序冷启动的情况下,点击小程序到首个页面渲染完成的平均耗时)。如下图 [图片] 1.2、网络性能 网络性能:请求处理耗时(所选周期/版本内,所有网络请求的平均处理耗时(即不含等待耗时))、请求错误率(所选周期/版本内,网络请求报错的比例)。如下图 [图片] 1.3、运行性能 运行性能:页面切换耗时(所选周期/版本内,小程序运行过程中,切换到新页面的平均耗时)、异常退出率(所选周期/版本内,iPhone设备运行小程序的过程中,微信客户端主动回收小程序的比例)。如下图 [图片]
2024-04-23 - 未注册完成被冻结的小程序如何注销或解冻呢?(主体也已注销)
id:gh_b49cfacf158d 1、未注册完成 2、注册主体公司已注销 3、该账号已于2019年09月05日 17:03被系统冻结 求助:注销此小程序 谢谢
2024-04-29 - 使用skyline时picker-view双列时间滚轮,不能触发bindtap,bindchange
skyline渲染下,使用picker-view双列时间滚轮,第二列时间不停的向上滚动在动画未停止时,在第一列时间不停的向上滚动在快速向下滚动,多试几次会出现屏幕的按钮,switch滑块等都不能触发点击事件,出现bug的几率很大
2024-04-12 - Skyline模式下 Swiper组件指示点能否自定义位置? 实现类似文档中指示器效果演示的效果
[图片] 我想要实现类似文档这个视频的效果,把指示点单独拿出来,放到脱离 swiper 的其他复杂的排版位置去。 API 好像只有一个配置 margin 的参数,而且是固定的四个方向的 margin。 附:如果无法实现完全自定义位置的指示点, 自己模拟的话能否达到和原生相近的性能和效果? 指示点完全跟随 swiper-item 的移动,移回,取消移动,动作完全同步?
2024-04-24 - 微信小程序需怎么注销?原始ID:gh_17f32b371959,请官网小哥帮忙处理处理一下
微信小程序需怎么注销?原始ID:gh_17f32b371959,请官网小哥帮忙处理处一下,谢谢。[图片]一下
2024-04-24 - behaviors混入后,如何在page中获得behaviors内容提示,如何处理ts报错的问题?
behaviors混入后,如何在page中获得behaviors内容提示,如何处理ts报错的问题,提示无法找到方法等!
2024-04-12 - 安卓机,真机调试,skyline页面跳转webview页面,快速点击会多次跳转?
问题:两个页面不同的渲染方式,轮播图快速点击会跳转多次;相同的渲染模式则不会 代码片段:https://developers.weixin.qq.com/s/ZVkeinmM7sQ7 开发工具:Nightly 1.06.2404112 , 基础库:3.4.0 ,微信版本:8.0.48 注意:1.需开启开发者工具上的skyline渲染调试 2.需要在真机上调试,开发者工具没问题
2024-04-11 - 小程序在IOS上运行超级发热问题如何解决?
我们的appid是:wx95e894af4ebcadd3 该小程序在android老手机mate30上不会发热,还挺省电的。 但是在IOS上,运行不到1分钟,看了一个广告 就开始发热了。 目前测试的几款IOS机型都发热,机型包含iphone15,iphoneXMax
2024-04-06