项目背景
19年在之前公司负责的一个ToB的房产行业项目,18年开发完了PC端,然后要同步开发小程序。 另外一个小程序项目是之前公司的PC端门户官网,也要同步开发小程序。
本文为整理记录本人使用微信小程序开发产品的过程中,针对项目中业务需求所遇到的各种难点、API问题、BUG、及教程整理(不定时更新)😁
正文
1、wxml页面的 Mustache 语法(双大括号)数据绑定是不能使用全局js方法的,例如utils.js里面定义的时间日期格式化,价钱格式化等等公共函数方法。
只能用.wxs文件,这是文档:developers.weixin.qq.com/miniprogram… 并且.wxs文件只能使用es4语法,不支持es6语法,以及一些符号,比如我在vue项目里常用的这个邮箱正则就会报错。
const emailReg = /[\w!#$%&'*+/=?^_`{|}~-]+(?:.[\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\w](?:[\w-]*[\w])?.)+[\w](?:[\w-]*[\w])?/;
所以想要在.wxml页面使用全局方法的话,就先定义好.wxs文件的全局方法,然后如下引入就可以使用util.wxs里面的全局方法了,<wxs src="../../utils/util.wxs" module="util" />
。
但是用.wxs来做全局方法的话有局限性,例如moment.js
和accounting.js
,这种使用率比较大的第三方插件,.wxs文件是不能引用js文件的,只能引用.wxs文件。
所以这点很烦,只能继续使用utils.js
在里面定义好全局方法,然后在app.js入口引入,如下:
/**
* 全局公共方法
* @type {function}
*/
const utils = require('./utils/utils.js');
App({
// 全局公共方法
utils: utils,
})
接着,假如在一个列表页或详情页里,需要用到单位换算,例如需要使用全局方法来处理数据的美元、RMB价钱以及平方英尺、平方米换算。使用方法很简单:在test.js里,在data里声明好变量,然后就可以在下面使用全局方法来处理数据了。 例子:
/* test.js */
// 获取全局应用程序实例对象
const app = getApp();
Page({
/**
* 页面的初始数据
*/
data: {
price: null,
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
//js里只能这样使用util全局方法,结合data声明的变量处理数据
//此处是判断服务器接口返回的数据字段是否为null,如res.data.price = 123
this.setData({ price: app.utils.isNull0(res.data.price) });
},
})
/* test.wxml */
<view>{{ price }}</view>
<!-- 最终页面呈现为<view>123</view>,如果服务端接口返回的price字段是null的话,那么经过全局方法判断后,页面这里应该显示<view>-</view> -->
以上这些就是小程序page子路由页面如何使用utils全局方法的全部内容了,这一点算是我入手小程序这几天以来遇到的比较恶心🤢的了,相比用vue开发webapp项目,小程序这一点真的是反人类,很不方便😱。
2、UI库的使用,我用了有赞的vant-weapp和iView-weapp。
使用方法:
vant-weapp:youzan.github.io/vant-weapp/…
iView-weapp:weapp.iviewui.com/docs/guide/…
把代码包下载到本地后,把里面的dist目录放到小程序的components目录下即可,然后在app.json里面定义好usingComponents字段,用到哪个组件,按照其官方文档的使用指南,把组件引入到usingComponents里面就行了,注意相对路径要设置好,不然会报错。 例如我目前开发的小程序项目暂时用到了这几个组件,之后开发完以后,dist目录里没有用到的组件可以删掉。
"usingComponents": {
"van-row": "../components/vant-weapp/dist/row/index",
"van-col": "../components/vant-weapp/dist/col/index",
"van-toast": "../components/vant-weapp/dist/toast/index",
"van-notify": "../components/vant-weapp/dist/notify/index",
"van-tab": "../components/vant-weapp/dist/tab/index",
"van-tabs": "../components/vant-weapp/dist/tabs/index",
"van-search": "../components/vant-weapp/dist/search/index",
"i-spin": "../components/iView-weapp/dist/spin/index",
"i-button": "../components/iView-weapp/dist/button/index",
"i-drawer": "../components/iView-weapp/dist/drawer/index"
}
不过这种方式已经落后不推荐了,这是18年我刚开始学小程序时,那时候vant文档是介绍用这种下载代码包然后手工静态引入的方式,现在vant官网文档已经是只有npm一种安装方式了,详见上面的vant官方文档 -> 快速上手教程。
我之前公司小程序项目最新的引入vant的方式可参考截图:
3、小程序的页面间跳转有些规则的,vue开发wabapp项目,跳转路由一般使用编程式导航:
this.$router.push({path: "/houseDetails", query: {id: id}});
//传参在query里定义。
而小程序的跳转有4种:
//保留当前页面,跳转到应用内的某个页面。但是不能跳到 tabbar 页面。使用 wx.navigateBack 可以返回到原页面。
wx.navigateTo(Object object);
//关闭当前页面,跳转到应用内的某个页面。但是不允许跳转到 tabbar 页面。
wx.redirectTo(Object object);
//关闭所有页面,打开到应用内的某个页面
wx.reLaunch(Object object);
//跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面
wx.switchTab(Object object);
刚开始开发首页的时候,用bindtap点击事件绑定一个方法,在里面用 navigateTo
怎么点都不跳转,没反应,第二天仔细看文档才发现,要跳转的页面是 tabBar 页面,所以向 tabBar 页面跳转的时候,不能使用navigateTo
,要用 switchTab
方法来跳转😳。
4、安装npm第三方插件
参考此文档: developers.weixin.qq.com/miniprogram…
具体步骤:
(1)、先在本项目里打开命令行,然后npm install --production
(2)、跑完以后,比如要安装moment.js 则 npm install moment --save
(3)、安装完后 去微信开发者工具里找到菜单栏 -> 工具 -> 构建npm 点击执行,等待完成。
(4)、构建完后,项目根目录里会多出一个miniprogram_npm目录,里面就是安装好的moment.js插件。
然后在utils.js引用:
// moment时间格式化
import moment from 'moment';
然后就可以在下面的全局方法配置文件utils.js里的 momentFormat
(函数名自己取的)里利用moment.js
的API格式化日期数据了,参考下图:
5、js里的方法不能用es6的箭头函数,只能用function(),否则会报错this指向,查看报错信息里,this是undefined,暂不知是什么原因。
例如:
keywordChange: app.utils.debounce((e) => {
this.setData({ searchKeyword: e.detail });//此时的this是undefined 所以这行会报错
if (app.utils.getByteLen(e.detail) > 3) {
this.setData({ searchLoaderShow: true });
}
}, 500)
keywordChange: app.utils.debounce(function(e) {
this.setData({ searchKeyword: e.detail });//不用es6的箭头函数就没问题
if (app.utils.getByteLen(e.detail) > 3) {
this.setData({ searchLoaderShow: true });
}
}, 500)
6、bindtap点击事件的方法传参要这么写才行:
必须要用data-开头,自定义属性,data-后面的就是你命名的key
<view bindtap="gotoDetails" data-searchType="{{ item.id }}"></view>
//方法里这样获取点击事件要用的key-value
gotoDetails(e){
let url = '/pages/details/details?id='+ e.currentTarget.dataset.searchtype;
//这里有个坑,绑定事件的设置参数,如果参数名有大写字母的话,下面获取的时候要用小写的,不然会报错undefined。例如:data-searchType -> e.currentTarget.dataset.searchtype
wx.navigateTo({
url: url
})
}
//不能像vue项目那样,直接在括号里跟参数,会报错,哎,小程序有些地方真的反人类😝
<view bindtap="gotoDetails(id)"></view>
7、报错:wx.switchTab: url 不支持 queryString
意思是tabBar页面不支持url传参,只能利用app.js里的globalData
里的query参数
,在要跳转的页面里赋值,就可以了。
比如要从搜索页跳转到房源列表页,就通过app.js里的globalData
里houseListQuery.searchKeyword
来给房源列表页传参,参考下图:
8、吐槽下微信开发者工具的版本管理,真TMD难用,还不如直接用命令行操作git来的方便,跟intellij IDEA 内置的Git管理工具差远了。
附个微信开发者工具的版本管理的设置教程:static.oschina.net/news/99140/…
9、微信小程序设为体验版后,不能加载数据,进去首页后会弹个Toast轻提示
报错:Object Object
经过百度搜索,可能是这些原因:域名已经备案、https已经配置、ssl证书在1.2以上版本、小程序后台已经配置服务器域名
10、小程序遍历对象要用wx:for-index属性
<view wx:for="{{ obj }}" wx:for-item="item" wx:for-index="key">
<view class='aside'>{{ key }}</view>
</view>
11、小程序的自定义组件有些像vue的组件化,比如详情页类型的内容结构复杂的页面或者是有2处及以上的业务页面模块有复用需求的,可以用自定义组件来实现,具体教程可参考:
developers.weixin.qq.com/miniprogram… blog.csdn.net/qq_41813695…
注意:自定义组件不能用wxs。
12、如果小程序某个dashboard统计页或详情类页面要做echarts图表可视化功能,但是echarts图表有很多,需要进行封装,这种情况最好还是采用小程序的webview来实现
具体教程可参考: developers.weixin.qq.com/miniprogram… www.jianshu.com/p/292f73512…
比如拿我19年在之前公司开发一个ToB项目的小程序举例,我当时的情况是这样的:这个项目已经开发完了PC端,但是要同步开发小程序。详情页会有很多echarts图表📈,各种类型图表,柱状图📊、饼图、折线图、面积图等等,接近上百个,这种情况就很适合用webview。
我刚开始的时候不知道webview这个概念,我记得当时echarts图表有专门用于小程序的插件echarts-for-weixin,然后在研究构思怎么基于这个插件封装全局可使用的line、bar、pie、面积图这四种公共图表自定义组件,当时折腾了几天没啥思路,很麻烦。后来CTO大佬告诉我这种大数据量图表页面,可以用webview来变相实现。
然后我就马上去看官方文档对于webview的介绍,后来决定采用小程序webview的技术方案,我只要新建一个vue移动端项目,这个项目就是专门服务于小程序采用webview的,项目git仓库命名为"某某某-Wechat-Charts",然后把现成的PC端详情页的业务模块的图表相关代码及目录结构,照搬copy过来到移动端项目就行了,只需要稍作改动即可。
可以在小程序里定义为9个page页面,以详情页里图表业务模块来命名,然后点击每个模块时跳转到这个模块对应的page页面,并且把这个模块下所有图表所需的参数(比如商品id),通过url传过去。然后这个page的wxml里放webview组件,动态设置src。js里获取从详情页点击模块跳转过来时所携带的query参数,拼接好以后,setData设置webviewURL。
比如小程序详情页这个地区概况业务模块:
用户在点击时会跳转到houseDetails_RegionSituation
页面,然后自定义的query参数
会携带过去,houseDetails_RegionSituation
页面的js会接收到,并在setData
里处理拼接好上面vue移动端图表项目该业务模块所需的接口url
,然后会赋值给webview
的src属性
,参考如下代码:
<!--图表模块的采用webview组件的page页面——wxml-->
<view class="page-body">
<view class="page-section page-section-gap">
<web-view src="{{ webviewURL }}"></web-view>
</view>
</view>
<!--图表模块的采用webview组件的page页面——js-->
data: {
id: null, //商品id
zip: null,
},
onLoad: function (options) {
// console.log(options)
//获取url参数
this.setData({
id: options.id || null,
zip: options.zip || null
});
let url = `https://-----此处为你配置的业务域名-------/situation?id=${this.data.id}&zip=${this.data.zip}`;
// situation为模块页面名称,随便你怎么定义。后面就是query参数
this.setData({
webviewURL: url
});
},
【之所以项目详情页里有9个图表模块,小程序就要新建9个page页面,是因为一个page页面只能放一个webview组件,并且会覆盖掉其他组件。】
然后在vue移动端图表项目里获取到小程序webview src跳转的时候携带的query参数,然后就是vue语法来获取query参数,进行接口请求数据,配置图表生成实例,移动端页面的该业务模块的图表数据就可以正常在小程序里展示了,交互动效也没啥问题,整个流程逻辑就完事儿了。
记得退出页面时也要销毁图表实例释放内存,这个应该也会占用小程序内存的,vue项目的生命周期是这样的:
beforeDestroy () {
if(this.myEcharts){
this.myEcharts.dispose();
}
},
具体流程就是以上这些了,并不复杂。
这样下来节省了大量的工作,图表的业务模块加起来有9个,加起来近百个echarts图表,采用webview的技术方案,从无到开完自测完毕,只用了三四天时间,我就不用头疼麻烦的用小程序的自定义组建来封装实现需求了,而且那种会有性能问题。
然后再分享个关于页面性能和交互优化的方案:就比如PC端项目的详情页,图表的业务模块加起来有9个,一共近百个echarts图表,如果用户初次进入页面就加载全部的图表接口,要全量渲染这么多图表DOM结构和js,那么浏览器内存会很高,用户会卡死几秒的时间,体验很差。所以后来优化为:用户初次进入页面是默认不加载图表相关接口的,采用每个图表业务模块做成折叠面板的方案,点击展开该页面模块时,才请求该模块下的所有图表接口然后处理数据渲染echarts实例和DOM结构,收起时销毁图表实例释放内存,并且只在第一次展开时加载接口,后面收起再展开时就不需要重复请求接口了,这样优化完后,详情页加载时间极大缩短。
13、image组件默认图片的宽度300px、高度225px,贼鸡儿恶心,要实现宽度100%,高度自适应效果,要使用mode="widthFix"参数
参考API文档:developers.weixin.qq.com/miniprogram…
14、实现 Anchor 锚点定位,类似楼层导航的功能,主要使用scroll-view组件的scroll-into-view属性
参考教程:developers.weixin.qq.com/miniprogram… blog.csdn.net/weixin_4267…
15、使用txv-video腾讯视频小程序插件,实现腾讯视频在小程序里的播放功能。
【txv-video标签的方案】
1️⃣:登录上微信公众平台,然后第三方设置里,添加腾讯视频插件。
2️⃣:在app.json里添加如下代码
//version是腾讯视频插件的版本号,我今天安装完,查看最新更新的版本是1.5.2
//provider是小程序的AppID,填对应自己开发的小程序AppID即可
"plugins": {
"tencentVideo": {
"version": "1.5.2",
"provider": "wxa75............"
}
}
3️⃣:在要用txv-video标签的页面的json文件里添加如下代码, 比如在详情页要用腾讯视频播放功能,那么就在details目录里的index.json里添加如下代码:
"usingComponents": {
"navigationBarTitleText": "详情页",
"txv-video": "plugin://tencentVideo/video"
}
4️⃣:取出业务接口返回的video src
中的vid
,具体逻辑看自己怎么实现吧,我的代码放上来仅供参考,可能有点儿傻,18 19年那时候的技术还比较菜😝
再吐槽下:我们项目的接口,video字段返回的是embed标签
,PC端vue的v-html
指令,直接渲染出来,然鹅,小程序不能用rich-text
渲染embed标签
🤑
//取出video中src
let videoSrc;
if (res.data.video.includes("src=")) {
videoSrc = res.data.video.split('src=')[1].split('allowFullScreen')[0];
}
console.log(videoSrc)
//取出videoSrc中vid的值
let vid;
if (videoSrc.includes("vid=")) {
vid = videoSrc.split('vid=')[1].split('&auto')[0];
}
this.setData({
vid: vid
});
console.log(vid)
console.log(this.data.vid)
5️⃣:然后在wxml里使用txv-video标签,就完事儿了,蛋疼的是有广告😒
//playerid名字随便取,类似于这个标签id,是唯一的。
//如果页面里有多处业务模块都要播放腾讯视频,那么要放多个txv-video标签,注意每个标签的playerid不能重复。
<txv-video vid="{{ vid }}" playerid="txvVideo1"></txv-video>
到此就算完事儿了,播放腾讯视频的业务功能已经完成,至于产品需求有多个视频/有视频列表,要做动态切换的话,就自己慢慢摸索吧,应该没什么难度。小程序的播放腾讯视频的解决方案,目前看来最方便的就是用txv-video标签,然后传过去vid值,就可以实现播放腾讯视频的功能了,比下面的video组件的方案好,video组件的方案太麻烦,而且已经过去很长时间了,属于取巧的一种方法吧。
【video组件实现播放腾讯视频的方案】
具体流程可以参考这篇大佬的教程:blog.csdn.net/qq_41629498…
前面取vid值的逻辑是一样的,只不过后面要发接口vv.video.qq.com,再根据返回的字段,切割拼接出完整的video url,就是这么个逻辑。
注:这种方案我没顺利完成,因为卡在了配置服务器域名这一步,
tcb-api.tencentcloudapi.com、https://vv.video.q… 这两个域名我都添加配置好了,
但是请求接口时会报错:vv.video.qq.com 不在以下 request 合法域名列表中,请参考文档:developers.weixin.qq.com/miniprogram…
刚开始我以为是配置服务器域名,小程序有缓存,要等10分钟,后来还是不行,不纠结果断放弃该方案了。
16、点击图片预览大图,一般详情页的轮播图使用
参考文档:developers.weixin.qq.com/miniprogram…
<swiper indicator-dots circular indicator-active-color="#fff" current wx-if="{{ !housePhotoNoDataShow }}">
<block wx:for="{{ housePhotoItem }}" wx:for-item="item" wx:key="index">
<swiper-item>
<image src='{{ item }}' lazy-load mode="aspectFill" data-index='{{index}}' bindtap='previewImg'></image>
</swiper-item>
</block>
</swiper>
previewImg(e) {
var currentImgIndex = e.currentTarget.dataset.index;
var imgArr = this.data.housePhotoItem;
// console.log(currentImgIndex, imgArr);
wx.previewImage({
current: imgArr[currentImgIndex], //当前图片地址
urls: imgArr, //所有要预览的图片的地址集合-数组形式
success: function (res) {
console.log(res)
},
fail: function (res) {
console.log(res)
},
complete: function (res) {
console.log(res)
},
})
},
17、app.js新增更新线上版本的用户友好提示(用于热启动时及时提示用户有新版本更新,冷启动时会自动下载更新)
参考官方文档:developers.weixin.qq.com/miniprogram…
App({
onLaunch: function (options) {
// 获取小程序更新机制兼容
if (wx.canIUse('getUpdateManager')) {
const updateManager = wx.getUpdateManager();
//监听向微信后台请求检查更新结果事件。微信在小程序冷启动时自动检查更新,不需由开发者主动触发。
updateManager.onCheckForUpdate(function (res) {
// 请求完新版本信息的回调
console.log(res.hasUpdate)
});
//监听小程序有版本更新事件。客户端主动触发下载(无需开发者触发),下载成功后回调
updateManager.onUpdateReady(function () {
wx.showModal({
title: '新版本更新提示~',
content: '本小程序新版本已经准备好,是否重启应用?',
success: function (res) {
if (res.confirm) {
// 新版本已经下载好,调用 applyUpdate 应用新版本并重启
updateManager.applyUpdate()
}
}
});
});
//监听小程序更新失败事件。小程序有新版本,客户端主动触发下载(无需开发者触发),下载失败(可能是网络原因等)后回调
updateManager.onUpdateFailed(function () {
// 新版本下载失败
wx.showModal({
title: '已经有新版本了哟~',
content: '本小程序最新版本已经上线啦~,请您在小程序列表删除当前小程序,重新搜索关键词打开本小程序即可体验最新版本哟~',
})
});
} else {
// 提示用户在最新版本的客户端上体验您的小程序
wx.showModal({
title: '提示您:',
content: '当前微信版本过低,无法正常使用小程序,请升级到最新微信版本后重试。'
})
}
},
})
18、自定义组件是默认不继承app.wxss全局样式的,不过可以在自定义组件的js里设置addGlobalClass: true
参考官方文档:developers.weixin.qq.com/miniprogram…
包括使用阿里iconfont图标库的场景,我遇到的情况是icon始终不能显示,微信开放社区和百度搜了很多文章,并不是base64引入的问题,也不是at.alicdn.com跨域的问题,后来突然想起来自定义组件的样式继承问题,才终于跳坑了,哎,又浪费了一上午🤑......
Component({
options: {
addGlobalClass: true,// 可以继承使用父级组件样式或app.wxss全局样式
},
})
19、2020-12月份记:时隔一年半多时间,最近又重新开始做小程序项目了,上家干了快3年的小公司19年底裁员解散团队了,20年上旬在阿卡索做了几个月又跳了,中旬来鹅厂干外包咯,在滨海总部办公贼爽,最近接手一个鹅厂内部使用的小程序,要开发新需求,最近看微信开发者工具更新了好多功能,越来越像vscode了,居然有了自动生成骨架屏功能,简直太赞了。
用法参考官方文档即可,developers.weixin.qq.com/miniprogram…