传统架构与前后端分离架构
传统架构优点:更有利于浏览器命中静态页面及服务端返回的HTML页面,搜索引擎SEO友好
缺点:直接生成html页面,那么终端需要有浏览器来渲染页面,导致实现的终端设备单一
前后端分离架构优点:服务端返回json数据,前后端分离,接口调用灵活
缺点:由于服务端不直接返回一个页面,而浏览器大部分无法解析js,导致访问者无法搜索到想要的页面
实现切换tab栏
给wxml定义好循环遍历的数组DOM
然后再js里面定义一个data为判断变量,变量值跟着点击事件event.currentTarget.dataset.index变
DOM元素wx:if判断变量是否等于当前元素的index值
记得在DOM元素设置属性data-index="{{index}}" 不然取不到
监听手指滑动切换页面
插槽知识点
解决问题:内容不确定、样式不确定
要不要在插槽外面嵌套view,取决于:需不需要在组件内实现对该插槽的样式书写,不一定有的的插槽不需要在组件内写样式,在哪里用就在那里写样式就可以了
什么时候使用请求数据什么时候定义js数据?
当数据不多时,且后端接口返回的数据无法直接取到想要的数据(有,但需要自己筛选),这时候可以酌情自己定义js数据
比如慕慕到家服务广场的服务类型导航栏,只有三种服务类型(全部,在提供,正在找),可以用js定义数组,遍历数组即可
注意:这种js数据一般定义在使用的page或component上,不然会降低组件的通用性
重构知识点
复用性高的代码,整理成组件:不止是html内容可以在组件里面更改,css样式也可以,可以在html里设置内联样式,然后通过js来定义属性可以实现从外部传入自定义样式;也可以设置显示状态组件,通过wx:if="{{}}"实现
可以设置成组件的东西:
加载成功与加载失败,封装一个show-status组件
使用不同的字体图标,封装wxss(从iconfont拿到的代码,图一)、html和css(通过给icon组件传不同的属性实现使用不同的iconfont,图二)
骨架屏的使用
记得在project.config.json里面配置骨架屏字段skeletonConfig
减少使用硬编码
方便后续维护,项目迭代
组件
分类:通用组件和业务组件
通用组件:和别人没关系,自己干自己的,如数据在自己这,绑定的事件也不包括其他组价的自定义事件
很多东西都可以设置为组件
屏幕兼容性组件
状态组件,当没有时显示暂无
字体图标组件,可以封装在一起,通过传名字参数实现不同icon
开发中应该避免的问题
一.js与html的频繁交互如事件通知(比如touch事件),如果有数据绑定甚至会导致页面频繁渲染,浪费资源
用wxs可以减少不必要的逻辑层与页面层的交互
wxs使用场景
1.如上,频繁地js与页面的交互时
一般用wxs文件而不直接写在wxml里,不然会导致无法复用
2.需要对数据进行复杂的格式化处理时
3.处理后端数据返回的枚举值,枚举成字符串,比如状态码变文字状态
总的来说,wxs的使用场景偏向于渲染时候对数据进行转换
wxs通信
利用在wxs里返回值
wxs响应小程序事件 ownerInstance.callMethod('响应的事件名',要传递的数据)
二.在js文件生命周期函数里面实现业务,这一般是一个入口,不应该实现过多的业务,一般在这里调用一个个函数
解决方法:在js文件里把业务定义成一个个私有函数
Or
对业务或数据进行归纳总结,最终对外提供若干函数方法,即Js面向对象机制:模型类
模型的意义:分离调用与内部实现,实现功能解耦,隔离会变化的部分(如请求的域名)
三.慎用面向对象类(如model)的静态属性
静态属性相当于共享数据,开发时被多个地方调用时,后续可能数据莫名其妙就变了,是个大隐患
四.字符串和变量拼接
尽量用模板字符串,用加号容易导致出错
导出方式不同,导入方式不同
默认导出时,import xxx from "path" xxx不需要加花括号
命名导出时,import {xxx} from "path" xxx需要加花括号
回调函数解决异步响应问题
app.json是个好东西
通用的组件如icon组件,可以定义在app.json里面,这样所有components和pages都能用,不用重复定义了
面向对象的意义
当一个对象有不同的调用,或者调用时对象参数应该不同时,可以使用重置函数reset()实现不同的调用
如上拉触底得到新数据和下拉刷新得到最新数据
在同一个service对象model里面用reset()方法,可以实现service在上拉触底和下拉刷新等不同场景的应用。
一个类里面使用实例方法还是静态方法
使用场景:
有状态(属性)就用实例方法,实例方法可以this.xxx获取到状态,没有状态就用静态方法
原理:
实例方法本质上是在调用实例对象的方法,而静态方法是在调用类方法
静态属性
静态方法可以调用静态属性,静态属性是共享的,不要随便调,所以开发时候一般不用静态方法调用静态属性,总结:要获取到状态还是使用实例方法
联动的原理
后端传入index参数不同,请求数据一样,传参不同获得不同的数据(如点击不同tab或category得到不同panel数据)
微信开发者工具——开发辅助骨架屏
在app.json里面配置骨架屏字段
配完在小程序生成骨架屏文件,然后在要实现骨架屏页面导入文件即可
节流与防抖函数
节流:规定时间内只执行一次
防抖:只执行最后一次
闭包与this
这里的this指向最后一次调用function的地方,即使用节流函数的page页面,把this指回page
两个page之间的数据通信
注意在页面中调用接口的形式不同,比如两个相关页面(慕慕service和service-detail)的数据调用同一个接口,那么需要实现两个page页面的数据通信
以下是实现的方法
1.缓存:在有数据的page页面设置setStorageSync,需要拿数据的页面getStorageSync
缺点:实时性差,所以推荐使用只拿index值,传index获得不同的数据,如下
两个相关页面(旧岛book和book-detail)的数据调用不同接口(book页面调用hot-list的api,book-detail调用detail、comment、likeStatus的api),通过book索引实现传参调用api,传参不同那么获得的detail数据不同,可以点击不同书本封面拿到不同书本detail数据
以下是实现方法
1.页面的options:有数据的页面可以通过在路径中传参xxx,让没有数据的页面可以通过options.xxx拿到,xxx是指需要传的index键
相关页面的跳转:通过点击跳转路由传递参数
跳转前页面(即有数据的页面)
使用wx.navigateTo(
url:'url?data_index=' + data.index_value
)
注意:data_index的命名由后端固定了,记得看看叫啥
css样式问题
一、通过给两个不同大小的元素,各自包裹在不同的container,这两个container设置align-self:center可以实现两个不同大小的元素对齐
比如用户头像和用户昵称显示的时候应该对齐
二、
<view class=“row”></view>
<view class=“row”></view>
<view class=“row”></view>
每个row本来就是column排列,不用给外层加column,可以给row直接定义flex-direction:row 和 justify-content:space-between,大部分是就可以设置,不是的多用一个view包裹起来就可以了
存本地和发起一个新的请求
存本地:更新相对不频繁的数据,比如当前登录的用户id
发请求:实时性高的数据,比如某个服务的信息(可能随时会更改,比如服务的状态)
提高可维护性的方法
再次强调分层面向对象的好处:分层model,让调用者不需要知道model的实现,方便下次model的改变
避免硬编码:采用enum文件夹枚举法
在model文件里面引入类(如utils/http.js)和使用继承(如extends base基类)
这两个都能引入,那么应该用哪个呢?
首先,导入格式上,继承会比引入更方便,this.xxx就可以取到继承的类的对象。如果不想new就用继承;
其次,使用对象上,继承比较偏向给公共类,即很多地方都要用到的类使用,毕竟model只是一个分离实现的包,有的实现类并不常用,对于这种类,推荐在一个调用者page的js里引入类即可
api专项
图片预览api
urls:一个数组,此次预览访问哪些图片
current:图片,用数组[index]拿到,其中index获取如下
点击图片拿到索引,进而显示图片
设置自定义属性data-id="index",获得数组的索引,进而点击事件event.currentTarget.dataset.id拿到图片索引,并进行显示
swiper组件
在这个组件里不能定义class样式!定义了用不了
多插槽记得在js文件上加上配置
options: {
multipleSlots: true
}
当后端没返回汇总数据但又需要展示时
在定义一个方法,调用原来的api之后手动用unshift等数组分进行数据添加
定义一个触摸切换功能
步骤:
首先搞清楚是哪一个js文件需要触摸切换页面,如本案例中的tab.js需要切换,所以先在js里定义切换函数
该切换函数用direction进行切换
而direction需要用wx的触摸事件(touchstart和touchend)计算出数据
数据得出后,由于wxs的特性:可以使用调用者的函数,即tab.js的handleTouchMove方法,且通过该函数传数据direction:ownerInstance.callMethod('handleTouchMove',{direction})
串起来就是一个触摸切换功能
特别注意:当时哦你wxs方法的时候,记得给wxs命名.方法外面加上花括号。毕竟人家是变量来着!
异常与错误
区别:
异常,会中断后续代码的执行
错误则不会
为了方便调试和控制代码的执行顺序,我们可以将res.data.message抛出异常
异常需要被捕获的格式:try {需要成功但可能不能成功的语句} catch{不成功的处理}
模块要注意static方法是不能被实例访问到的!!
setData异步
this.setData是异步的,所以我们需要用的更新的数据时,需要确认是否在一个函数内,不是的话它们的执行顺序是不确定的
页面间通信emit
在给数据的页面触发事件login
获得数据的那一边监听
js的深拷贝和浅拷贝
对于引用数据类型如对象,我们无法直接赋值,因为得到的是地址,解决方法有两个
一是转换成普通数据类型的赋值,数据 = 引用数据类型.属性
二是使用第三方类库Lodash的cloneDeep()方法
对wx.uploadFile进行二次封装,作用效果类似于http.js
api参数:url(文件上传路径),filePath(文件的路径),name(唯一标识,一般为key)
uploadFile没有进行promise的转换,所以取res的时候,需要自己序列化,即JSON.prase(res.data)这样才能取得到特定的值如error_code等
组件的生命周期函数和使用者的生命周期函数执行顺序不一定
在这方面可能会发生逻辑冲突,可以通过打印日志的方式检查两个地方的执行顺序
weui表单组件的使用
表单组件mp-form 和 mp-cell
表单常用元素标签:(嵌套于mp-cell,在mp-cell中也可以使用自定义组件)
1.picker自带change事件,可嵌套view,是显示的数据
2.switch勾选标签
3.textarea文本标签
4.input表单输入标签
5.button按钮
表单数据渲染
分两种,展示初始化信息(修改表单)和初始化信息为空(创建新表单)
修改表单:需要传一个值(对象)给formData,且需要考虑深拷贝浅拷贝问题
对象传参
当传参是一个对象时,需要先转换成字符串,作为参数传过去,然后在另一边转换回对象再接收该对象参数
表单校验
首先配置rules(在data里配置)
再通过获取组件实例(即表单组件),注意,通过给wxml标签配置id以及rules规则,然后在js文件this.selectComponents(id:'#xxx')获取
表单组件mp-form会有一个方法,validate,通过这个方法,里面调用箭头函数,可以拿到校验结果
组件与页面间通信的另一种方法:
在外部直接调用自定义组件内部的方法,这时候就可以通过this.selectComponent('#页面元素id')
方法获取组件实例对象,这样就可以直接访问组件的任意数据和方法
wxml显示提示
第一步是tips,可以通过组件mp-toptips来显示,属性有msg,show,type等
第二步是给每一项cell单独配置show-error和prop属性
在使用lib第三方库的时候,由于moment.js无法实现切换页面后重置掉校验不通过,所以我们需要手动通过定义条件渲染并且在生命周期函数里面(show和hide)通过更新showForm的值,来手动实现检验信息清空
但是这又带来另一个问题,即我们在调用chooseImage时,由于该原生api的特性,选择上传图片时,会调用hide,选择结束后会调用show生命周期函数,所以当选择结束后,创建服务的表单数据会清空
解决方法:
在初始化表单or更改showForm为false之前增加判断条件,通过一个值来动态决定chooseImage时页面不切换即可
单例模式
希望实例化对象在全局内只有一份时,可以使用单例模式
即定义一个静态属性为空
一个静态方法,当没有该静态属性时,则给该静态属性赋值一个实例(该小程序是Tim实例)
SDK初识
sdktim实例在Tim的构造函数里面创建
通过get方法可以让Tim实例方法获得sdk实例,避免创建sdk重复代码
SDK即时通信的getMessageList(异步,返回promise)需要传三个参数
其一:回话类型 converstationID: 聊天类型(如单聊C2C)${聊天对象id}
其二:下一次拉取消息列表时的消息id nextReqMessageID
其三:拉取数量上限 count
即时通讯登录sdk的tim实例下login方法,所需参数:用户id和用户签名
用户id指的是当时登录小程序的用户id
而用户签名则需要通过专门的获取签名函数来获取
D:\卓哥yyds\miniprogram-1\lib\tim\generate-test-usersig.js
D:\卓哥yyds\miniprogram-1\lib\tim\lib-generate-test-usersig.min.js
观察者模式
应用:全局状态管理
全局状态:被多个不同功能模块使用且使用方需要主动观察到其变化的数据
在页面中获取全局状态的语法
全局状态绑定工具tecent的Mobx,其createStoreBindings方法可以绑定自定义的全局状态,之后就可以this.data.xxx访问到全局状态属性了
全局store调用model方法,model再在类方法里面调用constructor里面tim实例的方法
在组件中获取全局状态的语法
通过behaviors配置引入,然后配置一个storeBindings对象为配置,在里面拿到全局状态就可以了
SDK IM实时通讯包 import TIM from 'tim-wx-sdk-ws'
两个tim文件的区别
model里的tim文件:为了限制不要重复创建SDK实例设置成一个类文件,在构造函数里面创建sdk实例,然后通过类方法用单例设计模式间接获得即可
store 全局状态仓库 用来存全局用到的,比如实时通讯的属性,方法等
组件高阶
定义组件间关系,形成组合组件
relations:{
xxx组件路径:{
type:'关系声明(child/parend)'
}
}
注意:声明组件关系的组件是相互的,两个js文件都需要定义以上代码
还可以通过自定义组件给的方法getRelationNodes来获取子组件的数量
事件监听的冒泡
在父组件里实现对子组件的监听,然后在页面通过父组件拿到子组件的配置
子组件定义事件冒泡及穿透组件边缘为true
父组件定义监听事件,设置正常的事件触发,触发子组件的自定义事件
wxml bind:子组件自定义的事件="处理函数"
js 处理函数 触发事件传递子组件的配置给页面即可
云开发数据库事务
应用场景:跨集合且用到这些集合的功能有一致性要求时(比如签到成功则积分+5)
原子性介绍:要么全部成功要么全部失败
实物开始: db.runTransaction( async transaction => {执行的原子性函数})
注意:主函数之外的函数传入参数transaction后记得把db的普通操作改成transaction事务操作(增和改需要,查不需要) transaction.collection('xx').xxx()
事务机制 (其所有操作是对数据快照的操作,抛出异常之后则把快照撤销掉)
并发事务操作
小程序解决并发操作冲突:加事务锁,提交事务后再解锁
事务实践注意事项:
1.避免大事务
大事务的定义:跨多个集合或嵌套了耗时业务逻辑的事务;
2.一旦事务逻辑可以提前退出,技师提交事务或者触发回滚以释放锁
3.使用db.runTransaction() 原因:可以自动回滚事务和提交事务;冲突时自动重试3次