微信小程序开发总结
写在前面:小程序的开发需要精益求精,一起来努力探索吧
帮助文档:
https://developers.weixin.qq.com/miniprogram/dev/framework/
微信公众平台:
https://mp.weixin.qq.com/
开发环境:原生小程序+vscode+less+es7(async/await)
可安装如下插件:‘小程序开发助手’,‘minapp’,‘wechat-snippet’,‘wxapp-helper’
小程序中使用less
- 编辑器为vscode
- 安装插件‘Easy less’
- vscode中的setting.json中配置如下代码
"less.compile": {
"outExt": ".wxss"
},
- vscode中新建.less文件编辑即可,如自动生成对应的.wxss文件
使用async/await语法糖参考以下文章照做就好
https://www.cnblogs.com/replaceroot/p/11421713.html
基础和适配
750rpx = 开发屏幕宽度
swiper默认:宽100%,高150px’
imgae默认:宽320px,高240px
在小程序开发中:百分比,vw,vh,rpx会用的比较多,所以掌握这些基础知识是前提
小程序引入iconfont
在小程序项目中新建styles文件夹 新建iconfont.wxss文件 将刚才网页中打开的iconfont样式代码粘贴进去
在app.wxss中引入iconfont.wxss
@import './styles/iconfont.wxss';
使用字体图标
<view class="iconfont icon-gouwuche"></view>
封装小程序自带的请求接口wx.request
// 同时发送异步请求的次数
let ajaxNum = 0;
export const request = (params)=>{
ajaxNum++;
const baseUrl = "https://api-hmugo-web.itheima.net/api/public/v1";
wx.showLoading({
title: '加载中',
mask: true,
});
return new Promise((resolve,reject)=>{
wx.request({
...params,
url:baseUrl + params.url,
success: (result) => {
resolve(result)
},
fail: (err) => {
reject(err)
},
// 无论成功失败都会执行的回调
complete:()=>{
// 全部请求完毕,删除加载圈
ajaxNum--;
if(ajaxNum===0){ //*此处不能写成if(ajaxNum)
wx.hideLoading();
}
}
});
})
}
用户授权及获取用户信息
<button open-type="getUserInfo" bindgetuserinfo="handleGetUserInfo" type="primary" plain>登录</button>
// 获取用户信息
handleGetUserInfo({detail:{userInfo}}){
wx.setStorageSync('userinfo',userInfo);
wx.navigateBack({
delta: 1
});
}
获取登录的code以及小程序自带功能使用promise的封装示例
export const login = ()=>{
return new Promise((resolve,reject)=>{
wx.login({
timeout:10000,
success: (result) => {
resolve(result)
},
fail: (error) => {
reject(error)
}
});
})
}
export const showToast = ({title})=>{
return new Promise((resolve,reject)=>{
wx.showToast({
title,
success: (result) => {
resolve(result)
},
fail: (error) => {
reject(error)
}
});
})
}
const {code} = await login(); //拿到code,有时候在获取用户token值用到
await showToast({title:'添加成功'}); //showToast弹窗
获取用户收货地址
<button bindtap="handleChooseAddress" type="primary" plain>获取收货地址</button>
//一套组合拳
async handleChooseAddress(){
const res = await wx.getSetting();
const scopeAddress = res.authSetting['scope.address'];
if(scopeAddress===true || scopeAddress===undefined){
const res1 = await wx.chooseAddress();
this.setData({
address:{
username:res1.userName,
add:res1.provinceName+res1.cityName+res1.countyName+res1.detailInfo,
telNumber:res1.telNumber
}
});
// 下次方便存取
wx.setStorageSync("address",this.data.address);
}else{
const res2 = await wx.openSetting();
const res3 = await wx.chooseAddress();
}
},
wx.previewImage()实现图片预览
<swiper autoplay circular indicator-dots> //通常轮播图的三个属性
<swiper-item
wx:for="{{goods_detail.pics}}"
wx:key="pics_id"
data-url="{{item.pics_mid}}"
bindtap="handlePreview">
<image mode="widthFix" src="{{item.pics_mid}}"></image>
</swiper-item>
</swiper>
// 图片放大预览
handlePreview({currentTarget:{dataset:{url}}}){
const urls = this.goods_detail.pics.map(item=>item.pics_mid)
wx.previewImage({
current: url, // 当前显示图片的http链接
urls // 需要预览的图片http链接列表,支持滑动播放
})
},
联系客服和分享功能
<button open-type="contact">联系客服</button>
<button open-type='share'>分享</button>
效果图:
关于onLoad和onShow方法的获取页面参数
案例:从商品跳转到商品详情页面需要携带商品id:
<view class="first_tab">
<navigator
class="goods_item"
wx:for="{{goods_list}}"
wx:key="goods_id"
url="/pages/goods_detail/goods_detail?goods_id={{item.goods_id}}"
>
//...此处省略部分代码
</navigator>
</view>
onShow(){
let pages = getCurrentPages();
let options = pages[pages.length-1].options;
const {goods_id} = options;
// 处理收藏商品的逻辑
this.getGoodsDetail(goods_id);
},
onLoad(options){
const {goods_id} = options;
// 处理收藏商品的逻辑
this.getGoodsDetail(goods_id);
}
自定义组件使用block占位符类似插槽
wxml:
<!-- 导航栏开始 -->
<tabs tabs="{{tabs}}" bindtabItemClick="handleTabItemClick">
<block wx:if="{{tabs[0].isActive}}">
<view class="first_tab">
<navigator
class="goods_item"
wx:for="{{goods_list}}"
wx:key="goods_id"
url="/pages/goods_detail/goods_detail?goods_id={{item.goods_id}}"
>
<!-- 左侧图片容器 -->
<view class="goods_img_wrap">
<image mode="widthFix" src="{{item.goods_small_logo}}"></image>
</view>
<!-- 右侧商品容器 -->
<view class="goods_info_wrap">
<view class="goods_name">{{item.goods_name}}</view>
<view class="goods_price">¥{{item.goods_price}}</view>
</view>
</navigator>
</view>
</block>
<block wx:if="{{tabs[1].isActive}}">1</block>
<block wx:if="{{tabs[2].isActive}}">2</block>
</tabs>
<!-- 导航栏结束 -->
tabs组件:配合使用slot
<view class="tabs_content">
<slot ></slot>
</view>
类效果图:
搜索输入框input事件防抖处理(*想到一个点:hidden对设置了display:flex的元素不起作用)
防抖节流是一个类型的事件处理,百度上有很清晰的答案
timeId:-1,
//输入框input事件
handleInput({detail:{value}}){
// 防抖
clearTimeout(this.timeId);
this.timeId = setTimeout(() => {
this.search(value.trim());
}, 1000);
},
//方法
async search(query){
const res = await request({
url:'/goods/qsearch',
data:{query}
});
this.setData({
searchList:res.data.message
});
}
小程序选择文件和上传文件
// 选择图片事件
handleChooseImg(){
wx.chooseImage({
count: 9,
sizeType: ['original','compressed'],
sourceType: ['album','camera'],
success: (result)=>{
// 追加图片
this.setData({
imgList:[...this.data.imgList,...result.tempFiles]
});
}
});
},
imgList.forEach(item=>{
// 上传文件的api不支持多个文件同时上传,只能遍历
wx.uploadFile({
// 服务器路径
url: '',
// 文件路径
filePath: '',
// 文件名
name: '',
// 携带的文本信息
formData: {},
success:(result)=>{
//可以得到外网文件链接
console.log(result);
}
});
});
小程序登录页面的滑块动画功能
小程序类似登录界面的整体可以往下拉,然后滑动回去(样式也可以绑定);
注意:bind事件支持事件冒泡,catch不支持,所以在真机调试时,使用catch事件
<!-- 测试滑动效果开始 -->
<view class="moveContainer"
catchtouchstart='handleTouchStart'
catchtouchmove='handleTouchMove'
catchtouchend='handleTouchEnd'
style="transform:{{coverTransform}};transition:{{coverTransition}}">
<view class="item">1212</view>
<view class="item">1212</view>
<view class="item">1212</view>
<view class="item">1212</view>
<view class="item">1212</view>
<view class="item">1212</view>
</view>
<!-- 测试滑动效果结束 -->
data: {
// 位移距离
coverTransform:'',
// 过渡回去的效果
coverTransition:''
},
startY:0,
moveY:0,
moveDistance:0,
endY:0,
onShow: function () {
},
// 手指按下
handleTouchStart(e){
this.setData({
coverTransition:''
});
this.startY = e.touches[0].clientY;
},
// 手指移动
handleTouchMove(e){
this.moveY = e.touches[0].clientY;
this.moveDistance = this.moveY - this.startY;
if(this.moveDistance >= 120 || this.moveDistance < 0){
return;
};
this.setData({
coverTransform:`translateY(${this.moveDistance}rpx)`
});
},
// 手指离开回到原点
handleTouchEnd(e){
this.setData({
coverTransform:'translateY(0rpx)',
coverTransition:'transform 1s linear'
});
}
小程序对象名为变量时的setData使用
let type = e.currentTarget.id;
this.setData({
[type]:e.detail.value
})
使用getApp创建全局数据
/app.js
wx-App({
globalData:{
isMusicPlay:'全局数据'
},
})
/index.js
onShow: function () {
const app = getApp();
console.log(app.globalData.isMusicPlay,'全局数据');
},
小程序结合C3动画案例
<!-- 测试旋转动画开始 -->
<view class="rotateContainer rotate">
<image src="/static/image/logo.jpg"></image>
</view>
<!-- 测试旋转动画结束 -->
.rotateContainer{
margin: 20rpx auto;
width: 200rpx;
height: 200rpx;
image{
width: 100%;
height: 100%;
border-radius: 50%;
}
}
// 这样即可实现动画动态绑定
.rotate{
animation: rotate 4s linear infinite;
}
@keyframes rotate{
from{
transform: rotate(0deg);
}
to{
transform: rotate(360deg);
}
}
关于scroll-view
小程序scroll-view中的设置display:flex要设置enable-flex
两个属性用来设置滚动到第一个并且附加上动画;(见代码)
<!-- 测试scroll-view及动画开始 -->
<scroll-view
class="scrollContainer"
scroll-x
enable-flex
scroll-into-view="n1"
scroll-with-animation
>
<view>111</view>
<view>2222</view>
<view id="n1">3333</view>
<view>4444</view>
<view>5555</view>
<view>6666</view>
<view>7777</view>
<view>8888</view>
<view>9999</view>
</scroll-view>
<!-- 测试scroll-view及动画结束 -->
.scrollContainer{
display: flex;
view{
padding: 10rpx 30rpx;
}
}
效果:3333的自然就滚动到最前面
swiper的露出前后项
<!-- 轮播图区域开始-->
<swiper class="wx_swiper" autoplay circular indicator-dots
indicator-color="ivery"
indicator-active-color="#043c33"
previous-margin="30rpx"
next-margin="30rpx">
<swiper-item>
<image src="/static/image/logo.jpg"></image>
</swiper-item>
<swiper-item>
<image src="/static/image/logo.jpg"></image>
</swiper-item>
<swiper-item>
<image src="/static/image/logo.jpg"></image>
</swiper-item>
</swiper>
<!-- 轮播图区域结束-->
swiper{
height: 500rpx;
swiper-item{
width: 100%;
text-align: center;
image{
width: 96%;
height: 100%;
}
}
}
效果图
哈哈,又临时想到几个点:
- button等有默认padding,margin,包括图片,swiper的样式等
- 输入框文本域等赋值(类似vue中v-model的双向数据绑定)
<input type='text' value="{inputValue}"/> //我们只需要给inputValue赋值即可
3 小程序对于获取data中数据,一般要使用深拷贝比较严谨,但是浅拷贝不影响效果
对于小程序,接下来大家就一顿狂写就好了,多多参考官网就好了。
组件中监听properties中数据的变化
properties: {
activeIndex:{
type:Number,
default:0
}
},
observers: {
'activeIndex': function (val) {
if(val != 0){
this.setData({
active:val
})
}
}
},
获取用户信息(最新接口升级)
<button class="login-btn logOrResgister" type="primary" plain wx:if="{{canIUseGetUserProfile}}" bindtap="getUserProfile">登录/注册</button>
<button class="login-btn logOrResgister" type="primary" plain wx:else open-type="getUserInfo" bindgetuserinfo="getUserInfo">登录/注册</button>
onShow: function(){
if (wx.getUserProfile) {
this.setData({
canIUseGetUserProfile: true
})
};
},
getUserProfile(){
var that = this;
wx.getUserProfile({
lang: "zh_CN",
desc: "用于互动",
success: function (res) {
res.userInfo.id = (Math.random() * 10000);
that.setData({
userInfo:res.userInfo
});
wx.setStorage({
key: 'userInfo',
data: res.userInfo,
})
}
})
},
// 开发者工具中使用
getUserInfo(e) {
var that = this;
wx.getSetting({
success: (res) => {
if (res.authSetting['scope.userInfo']) {
wx.getUserInfo({
lang: "zh_CN",
success: function (res) {
res.userInfo.id = (Math.random() * 10000);
that.setData({userInfo:res.userInfo});
wx.setStorage({
key: 'userInfo',
data: res.userInfo
})
}
})
}
}
})
}