小程序目录结构
关于小程序的目录结构,可以说一开始大家都有各自的开发习惯和命名规则,但一旦项目变得复杂庞大的时候,你就发现管理起来和后期维护变得很麻烦,如果是 协同开发 的话,更容易出现 “互坑” 的情况。
智库君在一年多的小程序开发中也跳过不少的坑,总结了一套还算好维护的目录结构跟大家分享(仅供参考,觉得好拿去,觉得不好欢迎提出意见),以下是实战项目中的结构示例:
├─ 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 ---公共小组件库 ... ... 复制代码
为什么一定要写这个目录结构呢? 不知道大家有没有发现,在以往的老项目交接和多人协同开发中,容易遇到别人写的模块,变量命名不准确,或者资料缺损,一次十来个方法/组件间的互相调用,直接把接(盘)手的人整懵逼了,所以智库君觉得,无论是独立开发,还是协同开发,留一份完整的目录说明文档是很有必要的, 勿坑 他人 OR 未来的自己~~~例如微信自己的wepy的官方文档,现在也添加了目录结构说明:
component使用心得
大家在开发过程中肯定会去看官方文档,但不可能全看完才开始写代码,大多数情况都是用到了再看,本人也是,所以下面抽一些开发中遇到的重点来讲:
一、引用组件模板页面的自定义
组件模板的写法与页面模板相同。组件模板与组件数据结合后生成的节点树,将被插入到组件的引用位置上。 在组件模板中可以提供一个 节点,用于承载组件引用时提供的子节点。
复制代码 这里是组件的内部节点
复制代码 这里是插入到组件slot中的内容 在加载组件的页面里自定义内容,将没有复用性的内容写在这里
页面自定义部分默认是加载在组件上方。
为什么要在引用组件的页面添加这些内容呢?
因为组件其中一个重要的特点是复用性,但是有的时候可能要根据不同场景做一些自定义,如果在组件中写大量的场景/逻辑判断,会增加组件的冗余,而且这些方法只是被复用一次的话,完全可以不写到组件里。二、“一键换肤”根据不同场景给组件引入外部样式
复制代码
//组件中jsComponent({ /** * 引入外部样式,可传多个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 方法获取子组件实例对象,这样就可以直接访问组件的任意数据和方法。
设置监听事件:
复制代码
// 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对象和事件选项:
复制代码
//组件中jsComponent({ 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官方文档:
往期回顾: