initData() {
this.videoFlag = false //是否显示视频
this.classDes = {}
this.promotionData = {}
this.activeIndex = 0
this.classDetailData = null
this.classId = null
this.isShow = false
this.networkType = 'wifi'
this.classVideoData = {}
}
需要在onUnload()函数中调用这个方法,这样才能保证下次进来会展示新的数据,这个问题只在mpvue框架会出现,在原生小程序中是不会出现的。mpvue的子元素生命周期很混乱,如果我们在频繁的更换页面详情得时候,会出现子元素数据不变缓存的问题,我们需要在onHide()中将传递给子元素的数据重置,然后在onShow()的时候在给赋值就可以了。
2. 在请求数据的时候,我们选用flyio.js来进行数据的请求获取,所以我们会对请求方法进行二次封装,在封装的时候我们会加入一个微信原生提供的loading方法(wx.showLoading())方法用于在请求接口时的等待显示,使用户体验会更好。这样做在其他请求页面请求接口还好,但是在列表页面下拉加载时,
onPullDownRefresh() {
this.refreshClassList();
},
refreshClassList() {
wx.request({
success: res => {
...
wx.hideLoading();
wx.stopPullDownRefresh();
}
})
}
由于小程序提供下拉刷新方法,所以使用原生的下拉刷新方法,但是下拉回弹的动画总是会过度回弹,导致顶部元素被遮挡,同时多次下拉会有几率触发页面卡下来的bug,通过查找,确认是两个loading共用导致,小程序的下拉刷新有顶部动画效果,所以下拉刷新时不需要做wx.showLoading的提示,最终在封装得请求中去掉了wx.showLoading()方法,在列表页面使用微信自带的下拉loading。
3. 微信小程序在最新版将不再提供默认弹出授权窗口,需要用户去点击触发才能弹出授权窗口,经过商讨,我们决定在添加一个授权页面,在app.vue的onLaunch()方法中判断是否授权,如果没有授权将跳到授权页面,点击按钮进行授权,反之,则继续执行正常操作,这个地方还有一个坑,就是分享出去的页面逻辑里面不要再去判断授权,要不然会弹出两个授权页面。
4. 我们在写组件的时候,往往会用到slot插槽,但是在嵌套组件中使用slot插槽无法正常显示渲染,最终在mpvue的Issues中找到解决办法,要在[email protected],[email protected],[email protected]的环境下嵌套组件的slot才可以正常使用。目前已降级到指定版本,暂时没有发现bug。
5. 在项目中有用到城市联级选择的情景,当时选用mpvue-picker插件来开发,因为联动需要循环,第一级或者第二级改变都需要遍历一遍数组,省市可能数据量比较大,然后就出现了卡顿,所以弃用,然后又找到了mpvue-citypicker,这个插件将citypicker单独出来,改变了城市数据结构,没有出现卡顿现象。但是这个插件里面的城市列表跟公司的城市不对应,返回的数据也不是我们想要的。所以我把它的原始数据给替换成了公司所需要的城市数据,并返回业务所需要的数据。考虑到这个小程序是多人开发,然后把这个插件整合了一下,发到了npm包上,供大家下载使用,npm包名为mpvue-citypicker-ht
6. 小程序分享朋友圈功能,首先请求后端生成的小程序码(生成小程序码需要access token,后端生成比较方便),要使用小程序码的图片路径,如果直接使用的话使用canvas是画不上去的,必须要通过wx.downloadFile这个api先把图片下载到本地,拿到临时路径
wx.getImageInfo({
src: _this.GenerateQrcode,
success: function(res) {
const background = '/static/canvas/canvas.png'
const GenerateQrcode_s = res.path
console.log(GenerateQrcode_s)
wx
.createSelectorQuery()
.select('.box')
.boundingClientRect(function(rect) {
const {
user,
title,
price,
teacher,
configs1,
configs2,
content,
message
} = _this.friendCircleParam
const shareModule = wx.createCanvasContext('shareCanvas')
const { width, height } = rect
const canvas_w = width * wx.getSystemInfoSync().windowWidth / 400 // 375
const canvas_h = height * wx.getSystemInfoSync().windowWidth / 380
const centerX = canvas_w / 2
const rpx = _this.rpxchange // rpx
// 初始化 canvas宽度
_this.shareCanvasWidth = `width:${canvas_w}px;height:${canvas_h}px;`
// 绘制背景
shareModule.drawImage(background, 0, 0, canvas_w, canvas_h)
// 绘制分享人
shareModule.setTextAlign('center')
shareModule.setFontSize(rpx(24))
shareModule.setFillStyle('#FFFFFF')
shareModule.fillText(user, centerX, rpx(48 + 24 + 6))
// 绘制文案
shareModule.setFontSize(rpx(60))
shareModule.setFillStyle('#FCE9A7')
shareModule.fillText(title, centerX, rpx(106 + 36 + 16))
// 绘制价格
shareModule.setFontSize(rpx(28))
shareModule.setFillStyle('#FFFFFF')
shareModule.fillText(price, centerX, rpx(216 + 28))
shareModule.setStrokeStyle('#fff')
shareModule.beginPath();
shareModule.arc(rpx(210) + rpx(20), rpx(210) + rpx(20), rpx(20), Math.PI, Math.PI * 3 / 2);
shareModule.lineTo(rpx(200) - rpx(20) + rpx(210), rpx(210));
shareModule.arc(rpx(200) - rpx(20) + rpx(210), rpx(20) + rpx(210), rpx(20), Math.PI * 3 / 2, Math.PI * 2);
shareModule.lineTo(rpx(200) + rpx(210), rpx(48) + rpx(210) - rpx(20));
shareModule.arc(rpx(200) - rpx(20) + rpx(210), rpx(48) - rpx(20) + rpx(210), rpx(20), 0, Math.PI * 1 / 2);
shareModule.lineTo(rpx(20) + rpx(210), rpx(48) +rpx(210));
shareModule.arc(rpx(20) + rpx(210), rpx(48) - rpx(20) + rpx(210), rpx(20), Math.PI * 1 / 2, Math.PI);
shareModule.closePath();
// shareModule.strokeRect(rpx(220), rpx(200), rpx(200), rpx(20))
// shareModule.fillText(price, centerX, rpx(220 + 38))
// 绘制描述
shareModule.font = `bold ${rpx(36)}px PingFangSC-Semibold`
shareModule.setFillStyle('#000')
_this.drawText(
shareModule,
content,
centerX,
rpx(340 - 42),
canvas_w * 0.9,
rpx(50)
)
// 绘制老师
shareModule.font = `normal ${rpx(24)}px PingFangSC-Semibold`
shareModule.setFillStyle('#888888')
// shareModule.fillText(teacher, centerX, rpx(410 + 24 + 6))
_this.drawText(
shareModule,
teacher,
centerX,
rpx(410 - 60),
canvas_w * 0.7,
rpx(30)
)
// 绘制中间提示文案
shareModule.font = `normal ${rpx(26)}px PingFangSC-Semibold`
shareModule.setFillStyle('#FF6d73')
shareModule.fillText(configs1, centerX, rpx(450 + 54 + 6))
shareModule.font = `normal ${rpx(26)}px PingFangSC-Semibold`
shareModule.setFillStyle('#FF6d73')
shareModule.fillText(configs2, centerX, rpx(480 + 74 + 6)) shareModule.drawImage(_this.GenerateQrcode, centerX, rpx(584), rpx(252), rpx(250))
console.log(_this.GenerateQrcode)
shareModule.drawImage(
GenerateQrcode_s,
canvas_w * 0.3,
rpx(594),
rpx(200),
rpx(200)
)
// 长按识别小程序码,一起省钱!
shareModule.setFontSize(rpx(26))
shareModule.setFillStyle('#Afafaf')
shareModule.fillText(message, centerX, rpx(824 + 28 + 8))
// line
shareModule.stroke()
shareModule.draw()
wx.hideLoading()
})
.exec()
},
fail: function(err) {
wx.hideLoading()
wx.showToast({
title: '生成失败,请重新尝试',
icon: 'none',
duration: 3000,
mask: true
})
}
})
_this.GenerateQrcode是从后台活动的小程序二维码,如果需要背景图的话,需要在本地保存一张图片当做背景图。切记不能使用线上图片,不起作用。
识别图片跳转到小程序相关页面时,需要在生成图片传递需要的参数,参数要在scene中传递。但是有些页面需要多个参数才可以打开,但是scene中只能传递一个参数,所以我们将参数用一个特殊符号拼接起来,比如
var par = `${this.$root.$mp.query.activityId}_${
this.$root.$mp.query.classId
}_${this.$root.$mp.query.isLive}`
this.GenerateParam = {
// 小程序二维码参数
scene: par,
page: `pages/activityInfo/components/activityDetail`,
width: 250,
auto_color: false,
line_color: { r: '236', g: '66', b: '76' },
is_hyaline: true
}
在对应的页面
onLoad(options) {
var that = this
if (options.scene) {
that.scene = decodeURIComponent(options.scene)
var arr = that.scene.split('_')
that.getPara = arr
console.log('value', arr)
}
},
用split()方法将参数分割成数组,在去拿对应的参数就可以了
7.mpvue在npm run build的后在ios低版本8, 9中会如下的错:
Page[pages/index/main] not found. May be caused by: 1.forgot to add page route in app.json. 2.Invoking Page{} in async task.
thirdScriptError
sdk uncaught third error
SyntaxError
line:16045,column:0,SynataxError:Unexpected keyword 'const'. Const declarations are not supported in strict mode. Stack:
报这个错得原因是因为我们的项目安装了mpvue-wxparse这个插件,因为微信小程序不能像vue一样用v-html解析带有html标签的数据,所以我们安装了这个插件。解决这个报错的方法是在webpack.base.conf.js文件中把
{
test: /\.js$/,
include: resolve('src'),
use: [
'babel-loader',
{
loader: '@f-loat/mpvue-loader',
options: {
checkMPEntry: true
}
},
]
},
替换成
{
test: /\.js$/,
include: [resolve('src'), resolve('test'), /mpvue-wxparse/],
use: [
'babel-loader',
{
loader: '@f-loat/mpvue-loader',
options: {
checkMPEntry: true
}
},
]
}
就可以成功的解决这个bug了。
8. 真机和模拟器的问题总结
10.页面连续点击之后跳转多次详情页面问题
在使用wx.navigateTo()方法跳转时,会有complete这个回调方法,
可以设置一个flag,默认为true,在点击的时候,将flag设置为false,然后在这个complete这个回调里再将flag设置为true,这样可以有效避免快速连续点击跳转多次详情页问题。还有一种是需要在点击的时候调接口做判断是否跳转,这种的情况用上述方法是不能解决的,目前想到的解决办法就是在调接口的时候加个loading,mark设置尾true,mark属性就是为了显示透明蒙层,防止触摸穿透
11.同路由跳转问题
在同一个路由下跳转,例如课程详情页跳转到另一个课程详情页时,在点击返回按钮时,数据还是停留在第二个详情页,这是mpvue存留的bug,暂时没人修复,只能通过以下代码去解决。
在utils里创建一个initData.js的文件,内容为:
const pageDatas = {}
export default {
install(_Vue) {
// 添加全局方法或属性
_Vue.prototype.$isPage = function isPage() {
return this.$mp && this.$mp.mpType === 'page'
}
_Vue.prototype.$pageId = function pageId() {
return this.$isPage() ? this.$mp.page.__wxWebviewId__ : null
}
// 注入组件
_Vue.mixin({
methods: {
stashPageData() {
return { data: { ...this.$data } }
},
restorePageData(oldData) {
Object.assign(this.$data, oldData.data)
},
},
onLoad() {
if (this.$isPage()) {
// 新进入页面
Object.assign(this.$data, this.$options.data())
}
},
onUnload() {
if (this.$isPage()) {
// 退出页面,删除数据
delete pageDatas[this.$pageId()]
this.$needReloadPageData = true
}
},
onHide() {
if (this.$isPage()) {
// 将要隐藏时,备份数据
pageDatas[this.$pageId()] = this.stashPageData()
}
},
onShow() {
if (this.$isPage()) {
// 如果是后退回来的,拿出历史数据来设置data
if (this.$needReloadPageData) {
const oldData = pageDatas[this.$pageId()]
if (oldData) {
this.restorePageData(oldData)
}
this.$needReloadPageData = false
}
}
},
})
},
}
在main.js˙中引入一下就可以了。
import initData from '@/utils/initData';
Vue.use(initData)
切记如果引入这段js,就不要再onShow()方法里获取数据了,要在mounted()方法中获取数据,这样才能解决上述bug