架构分析
- 软件应用架构包括:
数据层、业务逻辑层、服务处、控制层、展示层、用户,小程序属于展示层,通常还需要其他层次提供支持
- 主体文件:
app.js,app.json,app.wxss,前两者是必须存在再根目录下,app.wxss可以不要
- 页面文件:
一般位于pages目录下,同一个目录下的四个文件必须同名a.wxml,a.js,a.wxss,a.json,前两者是必须存在的,后两者可以不要
- 其他文件:其他静态资源可以放在目录下的任何位置
- 页面的配置文件 只能配置全局app.json文件中的window配置项中内容
- 小程序属于表现层,但又可以细分为逻辑层和视图层
- 注册
提供App方法来注册程序(只能注册一次),page方法来注册页面
- 框架并非运行在浏览器中,一些document,window等无法使用
- 编译
所有的代码都被打包为一份javascript,在小程序启动时运行,直到销毁。类似ServerWorker,所以逻辑层又称为AppServer
- block
并不是一个元素组件,仅仅是一个包装元素,不会在页面做任何渲染,只接受控制属性。嵌套组件的渲染必须用block或repeat包装,否则可能会出现问题
- 模板
模板拥有自己的作用域,只能使用data传入的数据 -
引入文件的三种方式:
- import方式引入的是模板文件,如<import src="a.wxml"/>,不会嵌套引入;
- include方式是将除模板之外的代码全部引入到当前位置,如<include src="h.wxml"/>;
- @import方式可引入外部wxss文件,如@import a.wxss。注意:必须放在最前面,放在代码后不会生效
- target与currentTarget对象区别:
多数情况下两者的返回值时一样的,但是当组件嵌套时,并且内外层组件都定义了事件处理函数,那么在触发内存组件时,这两个对象的返回值就不太一样了
- 尺寸单位
建议使用小程序扩展的两种rpx和rem
- 数据绑定
被绑定的数据必须在data属性里先做定义,否则可能不会正常渲染
线程架构
- 每个小程序分为2个线程,view和appServer。其中view线程负责页面的解析和渲染(包括wxml和wxss),appServer线程负责运行js。appServer线程运行在jsCore中(安卓下运行在X5中,开发者工具中运行在nwjs中)。由于js并不运行在web-view里,所以就无法操作BOM和DOM,这就是小程序没有window全局对象的原因
生命周期
app生命周期——始终优先于page声明周期
- onLaunch:小程序初始化完成就调用,只被触发一次
- onShow:小程序启动、从后台进入前台显示,被触发
- onHide:小程序从前台进入后台,被触发
- onError:小程序出错、api调用失败、被触发
- -onPageNotFound:打开的页面不存在,被触发
- 其他:开发者可以添加任意函数或数据到Object对象中,用this访问
注意:
- App() 必须在 app.js 中注册,且不能注册多个
- 不要在定义于 App() 内的函数中调用 getApp() ,使用 this 就可以拿到 app 实例
- 不要在 onLaunch 的时候调用 getCurrentPages(),此时 page 还没有生成
- 通过 getApp() 获取实例之后,不要私自调用生命周期函数
page生命周期
- onLoad: 页面在加载时,只被触发一次
- onReady: 页面初次渲染完成,只被触发一次
- onShow: 页面显示时,被触发
- onHide: 页面隐藏时,被触发(触发左上角的退出箭头时,仅触发app.onHide
- onUnload: 生命周期函数--监听页面卸载,只被触发一次
- onPullDownRefresh: 页面相关事件处理函数--监听用户下拉动作
- onReachBottom: 页面上拉触底事件的处理函数
- onShareAppMessage: 用户点击右上角转发
- onPageScroll: 页面滚动触发事件的处理函数
- onTabItemTap: 当前是 tab 页时,点击 tab 时触发
- 其他 Any 开发者可以添加任意的函数或数据到 object 参数中,在页面的函数中用 this 可以访问
注意:
- 执行顺序:onLoad => onShow => onReady
**一个完整的小程序执行的生命周期如下:
app.onLaunch => app.onShow => page1.onLoad => page1.onShow => page1.onReady (打开程序,第一个页面page1加载完成) => page1.onHide => page2.onLoad => page2.onShow => page2.onReady (从第一个页面新打开page2) => page2.onUnload => page1.onShow => ... => app.onUnload (关闭page2,返回page1...退出小程序)
视图层注意事项
- 请使用官方提供的wxss选择器,其他选择器可能也没问题,但是不推荐。
例如`.cls1 .cls2`级联样式,微信团队说会破坏组件结构,级联最终会被取消,未来会推出新的方案。
- 所有的组件和属性都需要使用小写
踩坑指南
网络相关问题
- 无法抓到请求
charles或fiddler抓取不到开发者工具的请求,打开代理127.0.0.1:8888
- 提示非法域名
一般情况都是出现了不在合法域名中的域名,打开开发者工具中的“不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书”
- Get和Post请求的返回数据不支持二进制流
小程序支持通过post获取小程序码(有点像菊花)。尝试通过wx.request向微信服务器获取小程序码的图片,结果发现返回的结果无法显示。开始怀疑代码有问题,调试之后,发现微信服务器返回的结果正确,而小程序会自动把二进制结果转码。更郁闷的是,这种转码丢失了文件内容,并且转换是不可逆的。
于是,我们改方案,把服务器当中转站,让小程序使用wx.downloadFile从服务器下载图片。在收到小程序下载图片的请求之后,服务器直接和微信服务器获取小程序码的图片,然后以附件的形式返回给小程序。
参考:小程序开发,那些我们跳过的坑
-
断网或者与正常网络互相切换时问题
如果需要根据网络状态判断是否发送异步请求,可以通过官方文档提供的onNetworkStatusChange和``两个方法来处理
wx.getNetworkType({
success(res) { if(res.networkType === 'none') { globalData.netNotConnected = true; } }
})
wx.onNetworkStatusChange(res => {
if (res.isConnected) { globalData.netNotConnected = false; } else { globalData.netNotConnected = true; tip.alert("网络已断开,请连接后重试"); }
});
组件相关
- 自定义字体
无法使用本地的字体文件,必须使用网络地址
- 背景图片问题
background-image背景图片不支持本地图片,只能用网络url或者base64;本地图片要用image标签才行
-
button样式
- 去掉默认样式
小程序的button是用伪元素实现的,去掉其默认样式需要用button::after{ border: none; }
-
设置背景图片
-
方法1:
<button open-type="share" class="btn" style="background-image: url(11.png);" plain="true" > </button> .btn{ width: 30rpx; height: 30rpx; padding: 0 20rpx; position: absolute; right: 32rpx; top: 0; bottom: 0; margin: auto; background-size: 30rpx 30rpx; background-repeat: no-repeat; border: none; }
-
- 去掉默认样式
参考:https://www.jianshu.com/p/b1d8a62da876 - 方法2: ``` .btn { position: relative; &::after { display: inline-block; content: ""; position: absolute; top: 0; right: 20rpx; left: 0; background: url(11.png) no-repeat 0 0; background-size: 715rpx 120rpx; background-size: 100% 100%; z-index: -1; } } ```
- input长度
value值的默认最大长度是140个字符(不包括空格),如果长度长度超过最大长度,需要设置maxlength=-1即可。
H5中input的value默认没有长度限制
- text组件的/n问题
view组件不识别/n,text可以。保存的文本中有换行,如果读取的时候将数据放入view中换行没有效果,放入text中就好了。原则上text标签与其中的内容不能有换行或空格,否则会有大片空白
wx.showModal方法的换行问题:在文本中添加\r\n,但需要在真机上才能生效,开发者工具不行
- 多个空格的问题
小程序中通过多个 是无法正常显示多个空格的,解决方法:用中文全角空格部 门(shift+space)
-
textarea问题
- 设置了auto-height="true"不设置maxlength=“-1”,输入长度会遭限制;
- 页面中只能有一个<textarea/>或<input/>设置auto-focus属性;
- textarea苹果和安卓的显示高度不一致,不给textarea设置宽度,则默认是300px;
- 设置auto-height时,style.height不生效,并且ios和android高度显示不一致
- button的边框是用:after方式实现的,用户如果在button上定义边框会出现两条线,需用:after的方式去覆盖默认值。
-
scroll-view
- tip: 请勿在 scroll-view 中使用 textarea、map、canvas、video 组件
- tip: scroll-into-view 的优先级高于 scroll-top
- tip: 在滚动 scroll-view 时会阻止页面回弹,所以在 scroll-view 中滚动,是无法触发 onPullDownRefresh
- tip: 若要使用下拉刷新,请使用页面的滚动,而不是 scroll-view ,这样也能通过点击顶部状态栏回到页面顶部
- 注意:使用竖向滚动时,需要给<scroll-view>一个固定高度height(如100vh),设置百分数无效
-
清除黑色滚动条(竖向滚动时)
::-webkit-scrollbar { width: 0; height: 0; color: transparent; }
参考:小程序填坑之路
-
cover-view
- cover-view的内容只有放置在原生组件里才能显示
- cover-view的内容不能超过原生组件,否则会被强制截取
- 微信文档中说只支持最外围的cover-view的fixed,经测试内围absolute也可使用,但有时候会看到用absolute的标签不见了,这时候你就要查看包括这个不见的标签的父元素的高度了,估计是absolute的标签超高了被父元素截取了
- cover-image不支持absolute
由于小程序里面video标签的层级是最高的无法覆盖。所以cvoer-view应运而生。它就是用于盖在video标签上面,进行对video标签的周遭加以装饰的利器。 例如在cover-view上面使用相对定位,当video标签大小发生变化的时候,cover-view上面的元素就乱七八糟。 又譬如圆角的不起效等等。 具体的问题大家可以在开发者社区看看。[cover-view定位问题](https://developers.weixin.qq.com/search?action=list&t=search/index&search_type=1&key=cover-view&token=&lang=zh_CN) 避坑方法:尽量在cover-view上不使用定位,其他的bug只能等官方优化,大家谨慎使用。
- 关于live-pusher,live-player
<live-player />’渲染失败,错误原因: insertLivePlayer:fail:access denied
原因:微信的权限里的相机和麦克风没有权限导致的,到手机 设置-应用-微信-权限里设置
- 键盘与输入框距离
input,textarea 可以通过标签的cursor-spacing属性设置键盘与输入框的距离
- 循环渲染
有嵌套关系的组件需要被循环渲染时,一定 要用block或repeat,否则会出现一些奇怪问题,如可能取不到item对象的属性值等
- dataset属性问题
属性名必须全部小写,不支持驼峰方式。如data-id必须小写。data-author-name在e.currentTarget.dataset中会自动转换authorName - 视频标签黑边
objectFit去除小程序视频标签的黑边(没试过)
-
swiper问题
- 初次使用swiper的的时候可能遇到当图片自动轮播到最后的时候,跳转到第一页的效果不友好,此时需要添加 circular=“true” 无缝衔接
**问题2:** 轮播图显示异常问题:A页面有`swiper`图片轮播,跳转到B页面编辑并删除某张轮播图片,再返回A页面后,轮播图显示空白,添加新图片时没问题;初步猜测:返回A页面后,`swiper`的`current`属性的当前值状态值指向不存在导致。但在A页面`onShow`时重置`current`值也不行,暂没找到原因 1. 省市县镇级联选择时,当有多个`swiper-item`时,点击第二个及以后的省获取市时显示空白 解决方法:在点击第二个`swiper-item`里的省获取市之前先把`swiper`组件重置,如设置渲染条件先不让他渲染,获取到市数据后再设置条件让`swiper`组件渲染出来 1. `swiper`滑动过一次后,无法再动态的设置`current`的值解决方案 给`swiper`添加`change`监听事件,当滑动`swiper`时通过`e.detail.current`记录`current`,下次再返回时设置`current`为上次保存的值。 参考:[swiper bug, swiper滑动过一次后,无法动态改变current值](https://openclub.alipay.com/read.php?tid=2919&fid=51)
-
canvas问题
canvas层级最高,其他元素无论z-index设置多大,都不能盖在canvas上,解决方法:让canvas定位后设置比较大的偏移值,或设置hidden
绘图时的图片必须是已经下载好的图片,不能使用网络图片或base64,可以用微信提供的getImageInfo或downloadFile,示例:
async createPoster() { const imgPromise1 = return new Promise((resolve, reject) => { wx.getImageInfo({ src, success(res) { resolve(res); }, fail(error) { reject(error); } }) }); Promise.all([imgPromise1, imgPromise2]).then(res => { const [ res1, res2 ] = res; ... const ctx = wx.createCanvasContext('poster-canvas-id'); ctx.drawImage(res1.path, x, y, ...); }) }
-
海报(二维码)
手机扫描分享的海报二维码,只能跳转到小程序线上版本,可以通过抓包获取跳转时的启动参数,放到开发者工具中模拟线下环境跳转
如:通过scene值来实现:手机扫码生成海报二维码,抓包这个请求https://activity.12345.com/wxa/town/load?scene=hvORN4dgYy,记下这个scene值,在开发者工具中配置路径'pages/index/index' 参数scene=hvORN4dgYy即可 原理:getAppCode方法中,会把页面加载所需要的参数作为scene值来获取二维码;当扫描二维码时,这个二维码中含有scene值,先进入XX首页时会通过上面那个抓包请求获取这个scene,然后从scene中解析并调整到pagetype,从而先通过跳转首页后再跳转到指定的页面
-
其他 - Canvas和Image对图片的各种不支持
- 扫码签到中用到了二维码
在开始的版本中,我们准备在Canvas上直接绘制二维码,接着使用wx.canvasToTempImage函数保存为image文件,然后通过Image组件加载。
经过调试,一切顺利。运行的时候呢,发现有时候在绘制完图片之后,调用wx.canvasToTempImage函数失败。这种情况在调试无法重现,运行的时候偶尔出现,不稳定。
仔细检查了代码,没问题啊。Google之后,有网友提出了解决方案,在drawImage完成之后,最好等3秒钟再调用wx.canvasToTempImage,以保证保存成功。
- Canvas和Image都不支持Base64图片,Image无法保存
最初的方案中,我们自己生成二维码,后来为了兼容微信的“扫一扫”功能,我们决定改用小程序码。
开始,我们把Image的src设置为Base64格式,从服务器上通过request获取图片的Base64编码。小程序开发工具和iPhone上面测试都没有问题,唯独Android手机上无法正常显示图片。哦,原来在Android上,Canvas和Image都不支持Base64图片。可是...小程序开发文档中并没有这方面的说明啊。
怎么解决Android手机上的这个问题呢?如果把Image改成URL形式呢,小程序无法保存图片,以致即使是相同的图片,每次都要从服务器获取,这又加重了服务器的负担。这样吧,使用wx.downloadFile把文件下载到本地,然后再处理
参考:Canvas和Image对图片的各种不支持
- 扫码签到中用到了二维码
- 凌晨定时清理storage
可以把日期(年/月/日)存储到storage;在页面onLoad时,判断当前日期和storage存储的日期(转时间戳)是否相等,否则就视为第二天
事件 & api 相关
- 无法通过event事件对象修修改dataset属性值???
试过好像不行
- 无法通过event事件对象修修改class类属性值???
试过好像不行
- tabBar问题
tabBar里面的图标 只能是本地图片,不能是网络图片,否则无法显示。至少2个,最多5个
-
路由跳转问题
- 不要尝试修改页面栈,会导致路由以及页面状态错误。
- 不要在 App.onLaunch 的时候调用 getCurrentPages(),此时 page 还没有生成。
----------- - navigateTo, redirectTo 只能打开非 tabBar 页面。 - switchTab 只能打开 tabBar 页面。 - reLaunch 可以打开任意页面。 - 页面底部的 tabBar 由页面决定,即只要是定义为 tabBar 的页面,底部都有 tabBar。 - 调用页面路由带的参数可以在目标页面的onLoad中获取。 ----------- - **注意:最多跳转5个页面** - 在路由跳转的时候,模拟器偶尔会系统报错。频繁跳转出现概率比较高,但仅仅在模拟器出现。可以无视 **经过测试,因为页面跳转是有动画时效的,在动画进行中当前页面还能操作。如果双击当前跳转按钮机会进行两次跳转。第二次点击的时候小程序内置的路由栈是已经定位到了新的页面了,而这时候在按照原来的路由栈去定位当前页面,因此会报错。所以页面跳转按钮需要进行短时间多次点击的限制** 详见:[小程序路由](https://developers.weixin.qq.com/miniprogram/dev/framework/app-service/route.html)
- 上下拉页面刷新问题
bindscrolltoupper 还是 bindscrolltoupper做上拉下拉刷新都需要注意这两个事件是会多次调用的。需要一个标识符来拦截多次调用
-
缓存问题
- 缓存最大支持 10M,可以写入多种类型数据number、boolean、array、string、object 等
- 调用同步方法写缓存时,经常会报错(如下图提示),官方建议尽量使用异步方式写入缓存,编码同步方式;可以添加try catch finally处理
**注意:** 你在开发小程序过程中,是否遇到,自己已经删除了体验版小程序,但是缓存依然存在? 那是因为,同一个小程序的开发版、体验版、线上版的缓存是共用的,你需要同时删除这三个版本的小程序,缓存才会被删除。 参考:[小程序缓存踩过的坑](https://blog.csdn.net/weixin_42133469/article/details/81875125)
- 涂层 & 弹框 问题
设置当前涂层和弹框的@touchmove.stop="func"即可,不要在func内添加preventDefault 或 cancelBabel(不支持)
- 网络状态变化问题onNetworkStatusChange
网络由正常状态变为断开状态时,发布帖子会报错;这时网络又恢复正常,点击发布按钮却没反应,也没触发请求。
解决方案:在注册小程序的onLaunch中通过wx.onNetworkStatusChange监听网络变化,同时把该状态存储到globalData全局变量中;当发帖时,首先判断网络状态,如果是断网的情况,就给出toast提示并禁止发布;如果是网络正常,就正常发布。这样即可解决断网又联网时导致不能发布的问题,示例:
// app.wpy ``` onLaunch() { wx.onNetworkStatusChange(res => { if (res.isConnected) { globalData.netNotConnected = false; } else { globalData.netNotConnected = true; tip.alert("网络已断开,请连接后重试"); } }); } ``` // formSubmit.wpy 表单提交 ``` if(globalData.netNotConnected) { tip.alert("网络已断开,请连接后重试"); return; } ... ```
Java PropertyChangeListener - 编程问答
wepy相关
- this.$apply方法
wepy中的this.$apply()方法一般用于组件从头传值的.sync中,其他状态更新的地方没必要添加该方法
-
组件问题
wepy的组件是静态组件,是以ID作为唯一标识,每一个ID都对应一个组件实例,当页面引入两个相同ID的组件时,这两个组件共用同一个实例与数据,当其中一个组件数据变化时,另外一个也会一起变化。如果需要避免这个问题,则需要分配多个组件ID和实例。
components = { //为两个相同组件的不同实例分配不同的组件ID,从而避免数据同步变化的问题 ErrorComponent: ErrorComponent, ExceptionComponent: ErrorComponent };
-
组件传值问题
- 只能传递字符串类型,可以传object,array等,但需要先序列号;注意:如果传递是object,内部不能再有嵌套的复合数据类型了,负责子组件无法正确渲染(手机),array类型的可以
-
不要再在子组件中修改props中属性的数据类型,否则当该属性需要频繁获取时,页面会出现抖动,体验极差(如省市县镇的级联选择时,当前显示的areaData都是通过props中的areaData获取时,不能在子组件中修改其数据类型)
... props = { areaData: { type: String, default: "" } } computed = { this.areaData = JSON.parse(areaData); // 不可以,页面抖动 this.$apply(); } ...
其他
-
1.模拟器和真机的差异
在模拟器上表现正常的,在真机未必正常,问题也很多。譬如
- 动画的使用 - cover-view上面使用定位 - wepy 组件传值 - canvas定位、绘图 - 背景图片路径
原因:造成这些错乱主要是pc端和移动端不同的内核导致的。 避坑方式:开发过程中,要时不时地用真机也看一下效果。
- 无效的appID
重启开发者工具;
描述:凡是公众平台服务端的修改,都需要重启开发者工具才能生效,包括https域名设置,开发者添加,appid使用,等等
- **开发者工具提示‘无法创建新项目’
文件夹不是空文件夹
- 开发
以iphone6的rpx来进行小程序的布局最为方便,iphone6的宽度是750rpx
- 搜索
小程序不支持模糊匹配,只能精确搜索
- 数据
单词设置的数据大小不能超过1024KB,否则会崩溃
-
校验
var wxReg=new RegExp("^[a-zA-Z]([-_a-zA-Z0-9]{5,19})+$") //微信号正则验证 var qqReg=new RegExp("[1-9][0-9]{4,}") //QQ号正则验证 var phReg=new /^1[345678]\d{9}$/ //手机号正则验证 var nameReg=new RegExp("^[\u4e00-\u9fa5]{2,4}$") //2-4位中文姓名正则验证
-
引入iconfont问题
H5方式:@font-face {font-family: "iconfont"; src: url('iconfont.eot?t=1485242349767'); /* IE9*/ src: url('iconfont.eot?t=1485242349767#iefix') format('embedded-opentype'), /* IE6-IE8 */ url('iconfont.woff?t=1485242349767') format('woff'), /* chrome, firefox */ url('iconfont.ttf?t=1485242349767') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ url('iconfont.svg?t=1485242349767#iconfont') format('svg'); /* iOS 4.1- */ } .iconfont { font-family:"iconfont" !important; font-size:16px; font-style:normal; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
小程序使用这种方式无法引入字体,解决方案如下:
- 进入iconfont选择自己需要的icon,并且下载到本地,找到后缀名为.ttf的文件 - 打开https://transfonter.org/,将字体文件转化成base64的格式 - 转化无完成后将生成的stylesheet.css拷贝到微信小程序项目中,通过@import方式引入,在需要引入的地方 ``` #icons:before{ font-family: "iconfont"; /* color: red; */ font-size: 40rpx; content: "\e60b" } ``` 参考:[微信小程序踩坑日志](https://blog.csdn.net/marko_zheng/article/details/79130076)
分享,二维码,小程序码
-
分享相关
- 小程序中的分享只能分享到聊天或群里,不能分享到朋友圈。
- 扫描所分享的二维码图片,只能跳转到正式版,无法跳转到线下版本
- 扫码相关
小程序支持扫二维码进入,但不支持长按识别
授权 & 登录
授权和登录是两回事。登录是用户无感知的,获取到code 和后端通信获得openid来定位用户。而授权才能获取用户头像和名字等信息
开发板 & 体验版 & 正式版
小程序第一次提交审核的时间比较后面的长,第一次审核时间一般1-2天
- 开发版本只限定管理员扫码预览,不支持其他开发者或体验者预览
- 体验版不支持其他开发者预览,仅支持被授权的体验者预览
其他:
- 小程序社群=>社区 (又小程序开发联盟)
- 微信开发社区-搜索
- 微信小程序开发跳坑(1-50)
- 微信小程序开发跳坑指南(51-100)
- 两百条微信小程序开发跳坑指南(不定时更新)
- 小程序坑及案例
- 开发小程序时遇到的坑
- 微信小程序开发之路上遇到的那些坑
- 小程序踩坑日志(一)
- 微信小程序之Canvas填坑
- 小程序canvas踩坑