小程序开发的喜怒哀乐【玩转前端】
前言
好久没写原生小程序了,最近新项目,重新体验了一把,感觉还挺好。
小程序生态发展到现在,功能很全,但是正因为它的全,所以,在开发者初次开发的时候会不太适应,很多API都不熟悉,包括一些API的历史变迁也不了解。所以,我趁着新项目开发的间隙,将一些常用的功能整理下来。即方便后面使用时查阅,也希望为大家开发提供一点帮助。
一个“五脏俱全”的小程序
我对新技术应用可以说是非常喜爱的。我对于一门新技术的学习,如果只是一味的学习,我的吸收不能达到最佳状态,后续就会出现疲惫心理。但是如果结合实践,我的大脑会比较兴奋,这个时候对技术的理解和吸收都会有显著提升。
所以我之前也专门写过我对SVG的学习方法是边学边做,掌握程度也会比单纯的看技术点高很多。
如何拥有一个小程序
这一章是将小程序从无到有的过程拆解成多个步骤,主要是写给第一次做小程序开发的朋友,可以通过下面的步骤熟悉小程序的开发生态。有经验的开发朋友,可以跳过这一章。
新建小程序
微信开发者工具自带创建小程序的功能,如果还没有申请小程序可以使用测试号的方式获取AppID。小程序从申请到使用开发者工具开发可以查看微信的官方文档大而全。
2.1.2 新增页面
根据微信小程序的开发文档,一个小程序页面由四个文件组成,分别是:
实际的文件结构如下图:
那么每次新增页面都需要新增四个文件吗?其实不用,只需要定义好需要新增的文件路径放到app.json文件中pages数组中即可
"pages": [
"pages/home/home"
],
新增之后保存,微信开发者工具边帮大家自动生成文件了,为微信开发者工具点赞(哈哈哈,我好像没见过什么世面的样子)。
底部 tab 栏
1.基础底部 tab 栏
底部导航依旧是在app.json文件中配置,我再自己的小程序里面配置了两个入口,首页和我的,这两个入口链接、展示文字、未选中的icon、选中的icon、选中文字的颜色都设置了,这样基本就满足了一个小程序对底导航的需求。
"tabBar": {
"selectedColor": "#007AF5",
"list": [
{
"pagePath": "pages/home/home",
"text": "首页",
"iconPath": "/images/icon/home-unselected.png",
"selectedIconPath": "/images/icon/home-selected.png"
},
{
"pagePath": "pages/mine/mine",
"text": "我的",
"iconPath": "/images/icon/mine-unselected.png",
"selectedIconPath": "/images/icon/mine-selected.png"
}
]
},
实际可以配置的属性有很多,微信开发文档我把参数都复制出来放到下面:
属性 |
类型 |
必填 |
默认值 |
描述 |
最低版本 |
color |
HexColor |
是 |
tab 上的文字默认颜色,仅支持十六进制颜色 |
||
selectedColor |
HexColor |
是 |
tab 上的文字选中时的颜色,仅支持十六进制颜色 |
||
backgroundColor |
HexColor |
是 |
tab 的背景色,仅支持十六进制颜色 |
||
borderStyle |
string |
否 |
black |
tabbar 上边框的颜色, 仅支持 black / white |
|
list |
Array |
是 |
tab 的列表,详见 list 属性说明,最少 2 个、最多 5 个 tab |
||
position |
string |
否 |
bottom |
tabBar 的位置,仅支持 bottom / top |
|
custom |
boolean |
否 |
false |
自定义 tabBar,见详情 |
2.自定义底部 tab 栏
还可以根据业务需求,进行tabBar的自定义开发,微信的官方文档也给了很详细的开发步骤,我们来一起写一个自定义的底导航
首先按照文档提供的方案,在代码根目录下添加入口文件:
custom-tab-bar/index.js
custom-tab-bar/index.json
custom-tab-bar/index.wxml
custom-tab-bar/index.wxss
目录结构如下:
index.js
相较官方提供的代码,我加入了tabChange方法,因为直接使用官方的代码,会出现底部tab高亮不准的问题(点击我的,高亮让在首页)。大家可以注释掉tabChange方法试试就能发现问题了。
Component({
data: {
selected: 0,
color: '#A0A3B1',
selectedColor: '#007AF5',
list: [
{
pagePath: '/pages/home/home',
iconPath: '/images/icon/home-unselected.png',
selectedIconPath: '/images/icon/home-selected.png',
text: '首页',
},
{
pagePath: '/pages/mine/mine',
iconPath: '/images/icon/mine-unselected.png',
selectedIconPath: '/images/icon/mine-selected.png',
text: '我的',
},
],
},
ready() {
this.tabChange();
},
attached() {},
methods: {
switchTab(e) {
const data = e.currentTarget.dataset;
const url = data.path;
wx.switchTab({ url });
},
tabChange() {
const pages = getCurrentPages(); //获取加载的页面
const currentPage = pages[pages.length - 1]; //获取当前页面的对象
const url = currentPage.route; //当前页面url
const list = this.data.list;
let selected = 0;
list.forEach((item, index) => {
if (item.pagePath.indexOf(url) != -1) {
selected = index;
}
});
this.setData({
selected: selected,
});
},
},
});
index.wxml
相较官方文档,我把cover-view替换成了view,把cover-image替换成了image,因为我在滑动页面的时候发现底部会出现两个tab,所以将视图容器进行了更换。微信的官方文档也有关于cover-view替换成view的建议,可以查看cover-view的官方文档
<!--miniprogram/custom-tab-bar/index.wxml-->
<view class="tab-bar">
<view class="tab-bar-border"></view>
<view wx:for="{{ list }}" wx:key="index" class="tab-bar-item" data-path="{{ item.pagePath }}" data-index="{{ index }}" bindtap="switchTab">
<image src="{{ selected === index ? item.selectedIconPath : item.iconPath }}"></image>
<view style="color: {{ selected === index ? selectedColor : color }}">{{ item.text }}</view>
</view>
</view>
index.wxss
.tab-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 48px;
background: white;
display: flex;
padding-bottom: env(safe-area-inset-bottom);
z-index: 99;
}
.tab-bar-border {
background-color: rgba(0, 0, 0, 0.06);
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 1px;
transform: scaleY(0.5);
}
.tab-bar-item {
flex: 1;
text-align: center;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.tab-bar-item image {
width: 24px;
height: 24px;
}
.tab-bar-item view {
font-size: 10px;
}
别忘了app.json中加入"custom": true,自定义底部tab才能生效
"tabBar": {
"custom": true,
"list": [
{
"pagePath": "pages/home/home",
"text": "首页"
},
{
"pagePath": "pages/mine/mine",
"text": "我的"
}
]
},
经过上面几步,一个基础的小程序就完成了。可以根据实际的开发需求进行功能开发了。
小程序提测和上线步骤
第一步:代码发布
1.在开发者工具 的右上角有发布按钮,点击可以将小程序发布到小程序后台。
2.点击之后会提示当前提交会覆盖之前的提交且为体验版,点击确定。
3.确定之后会让选择更新类型、版本号、项目备注,这三项均根据实际需要进行选择或者填写,确认无误之后点击上传,上传成功之后,需要进入小程序后台找到体验版二维码,通过二维码进行后续的测试流程。
注意:
- 小程序的代码有大小限制,单个主包不能超过2M,大家开发的时候一些资源文件尽量传到七牛云。官方文档
- 提测之后的git项目管理规范请遵守我们的:小程序Git使用规范
第二步:小程序测试
1.登录小程序后台,在管理->版本管理中找到体验版二维码。所有后续的测试流程都是通过扫体验版二维码进行的。
注意:
- 项目测试必须走真机测试,不可以用开发者工具进行真机模拟测试;
- 给所有需要验收的人绑定体验权限,这个由小程序管理员去做即可;
第三步:提交审核
小程序测试无误之后,需要进行提交审核操作,选择需要提交的版本,点击提交审核按钮。提交成功之后,审核版本处会有一条记录。审核一般需要一段时间,所以尽量提前进行提交审核操作,避免因为审核时间等待过长影响上线。
注意:
1.在设置->基本设置->功能设置里有一个用户隐私保护指引设置的项,这一项需要提前完善,只有该项是已完善状态,小程序才能正常进行提交审核,否则小程序无法提交审核。
第四步:小程序发布
小程序审核通过之后,点击提交发布,就可以将小程序发布到线上了。小程序提供了两种发布模式:全量发布和分阶段发布。具体使用哪种发布模式,可以提前跟产品确认。小程序发布官方文档
如何拥有一个功能齐全的小程序
用户微信信息授权
大多数时候,我们的产品经理希望开发获取用户的微信头像和昵称,用于小程序内的信息展示,前端会根据微信提供的API进行用户授权、信息展示、数据缓存等系列操作。今年4月份,微信对授权功能进行了调整,调整通知可见官方文档使用新的wx.getUserProfile(可以参见官方文档)替换之前的wx.getUserInfo。
由于wx.getUserProfile需要用户操作进行授权,所以我的处理是获取授权之后,将数据进行缓存,避免需要用户频繁操作授权的不佳体验。如果页面有退出登录操作,还需要清除缓存数据。
mine.wxml
<view class="mine-header" bindtap="getUserProfile">
<view class="mine-header-img">
<image src="{{ userInfo.avatarUrl }}"></image>
</view>
<view class="mine-header-name">
<text class="mine-header-nickname">{{ userInfo.nickName }}</text>
<image class="mine-header-edit" src="../../images/mine/edit.png"></image>
</view>
</view>
<view class="mine-content-btn">
<button class="btn-gray" bindtap="loginOut">退出登录</button>
</view>
mine.js
- getUserInfo方法是在页面加载时调用的,用户回显缓存中的数据;
- getUserProfile方法是调取用户授权的操作,授权成功之后需要将授权数据进行缓存处理;
添加完这两个方法一个完整的授权处理就完成了。如果有退出功能,需要再退出的方法中清除缓存(这个具体功能视业务实际需求而定,我遇到的业务需求一般是需要清除授权信息缓存展示默认界面UI的)。
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
this.getUserInfo();
},
// 设置用户信息
getUserInfo() {
let userInfo = wx.getStorageSync('userInfo') || {};
if (JSON.stringify(userInfo) === '{}') {
userInfo = {
nickName: '可爱的网友',
avatarUrl: '../../images/mine/defaultAvatarUrl.jpeg',
};
}
this.setData({
userInfo: userInfo,
});
},
// 获取用户的授权信息
getUserProfile(e) {
// 推荐使用wx.getUserProfile获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认,开发者妥善保管用户快速填写的头像昵称,避免重复弹窗
wx.getUserProfile({
desc: '展示用户信息', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
success: res => {
console.log(res, 'getUserProfile');
const userInfo = res.userInfo;
wx.setStorageSync('userInfo', userInfo);
this.setData({
userInfo: userInfo,
});
},
});
},
// 退出登录
loginOut() {
// 清除缓存,并回到首页
wx.setStorageSync('userInfo', {});
wx.switchTab({
url: '../home/home',
});
},
小程序内页面跳转
小程序内的页面跳转主要有两个方法wx.redirectTo和wx.switchTab,这两个的主要区别是wx.switchTab跳转到 tabBar 页面,而非tabBar 页面需要用wx.redirectTo进行跳转,且不能混用,混用会失效。
如果在一个公共的处理路由跳转的工具类方法中怎么区别使用两个方法呢?
我在以往的功能开发中加了switchTab的路由列表,如果检测出当前页面的路由在列表中则使用wx.switchTab,不在列表中则使用wx.redirectTo。这种处理一般在需要登录拦截的业务下会用到,比如某些页面需要登录才能查看到数据,比如旅游网站的酒店订单等,如果订单入口配在了公众号上面,那么跳转的时候小程序里面要做拦截处理,判断当前用户是否已登录,如果订单属于底部tab,那么就要使用wx.switchTab了。下面的代码就是我写的一个工具类:
/**
* 公共跳转处理
* @param {string} url 最终跳转链接
* @return {void} 无
*/
const commonRedirectToNext = url => {
/** @name 底部tab的路由 */
const tabList = ['pages/mine/mine', 'pages/home/home'];
// =>true: 属于底部tab的路由使用wx.switchTab
if (tabList.filter(item => url.indexOf(item) !== -1).length) {
wx.switchTab({
url: '/' + url,
});
} else {
wx.redirectTo({
url: '/' + url,
});
}
};
动态更换页面标题
常规的页面标题
以首页pages/home/home为例,页面的标题在home.json中设置,navigationBarTitleText可以设置页面标题。可见官方文档页面配置项:
{
"usingComponents": {},
"navigationBarTitleText": "说走就走"
}
如下图为我为页面设置的标题
动态设置页面标题
比如旅游的攻略文章,前端开发使用的同一个页面渲染不同的文章,标题需要动态的设置为文章标题,需要用wx.setNavigationBarTitle这个API设置页面标题。
我在页面中通过详情接口请求到详情数据,调用设置标题的setBarTitle方法设置页面标题:
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
httpUtil.http(configUtil.travelDetailById, { id: options.id }, res => {
this.setBarTitle(res.title);
let detail = res;
detail.announceTime = util.dateFormatter(res.announceTime || 0, 'yyyy-MM-dd');
this.setData({
detail: detail,
});
});
},
// 设置页面标题
setBarTitle(title) {
wx.setNavigationBarTitle({
title: title,
});
},
对于旅游文章页,不同的文章,页面会展示不同的标题,下面两个图分别是故宫和青城山的跳转:
退出小程序
退出小程序有两种方式
navigator
navigator导航组件也可以实现退出小程序的功能,将open-type设置为exit,且配合`target="miniProgram"`时生效。(官方文档)
页面使用navigator,点击按钮会退出小程序,注意真机模拟才有效。
mine.wxml
<view class='mine-content-btn'>
<navigator class='btn' open-type='exit' target='miniProgram'>
退出登录
</navigator>
</view>
wx.exitMiniProgram
wx.exitMiniProgram可以实现退出小程序公布,在点击事件中调用,不过这个方法需要基础库 2.17.3及以上才行。(官方文档)
使用很简单,成功之后会直接关闭小程序。注意真机模拟才有效。
mine.wxml
<view class="mine-content-btn">
<button class="btn-gray" bindtap="loginOut">退出登录</button>
</view>
mine.js
// 退出登录
loginOut() {
wx.exitMiniProgram({
success: function (res) {
// 成功之后关闭小程序
console.log(res);
},
});
}
总结
个人小程序git地址:https://github.com/wxmp-project/wxmp-travel
目前功能开发的还比较简单,后续会持续更新,文章也会持续更新,因为还有些功能由于还在摸索着,所以暂时没加。
作者:非职业「传道授业解惑」的开发者叶一一
简介:「趣学前端」、「CSS畅想」系列作者,华夏美食、国漫、古风重度爱好者,刑侦、无限流小说初级玩家。
如果看完文章有所收获,欢迎点赞👍 | 收藏⭐️ | 留言📝。
- 点赞
- 收藏
- 关注作者
评论(0)