1.链接: blog.csdn.net/qq_26585943…
微信小程序开发(wepy框架)
布局: rpx 设计稿750px, 设计稿多大就写多大的
一、生命周期(app.js)
- onLaunch 用户首次打开小程序,触发 onLaunch(全局只触发一次)这里做第三方开发平台自定义配置项 注意: ext.json也可以覆盖本地的配置项目,例如小程序跳转小程序就是在ext.json可以进行配置,手机扫码会进行读取ext.json进行覆盖本地的配置
- wx.getExtConfigSync() 动态读取ext 字段自定义的数据字段
- onLoad[(options)] 加载小程序,options参数拦截,模块只能触发一次,tabbar菜单, 页面模块进行切换不进行调用了, 页面是每次都会调用的
涉及到模块页面是否是是及时更新,如果及时更新放到onShow里面
- onHide 进入后台,进程没有杀死
- onError 错误监听函数 当小程序发生脚本错误,或者 api 调用失败时,会触发 onError 并带上错误信息
- 顺序 onLuach => onLoad => onHide
页面生命周期(page.js):
- onLoad --监听页面加载
- onShow --监听页面显示
- onReady --监听页面初次渲染完成
- onHide --监听页面隐藏
- onUnload ---监听页面卸载
-
其中,打开小程序后会依次执行onLoad => onShow => onReady
-
前后台切换会分别执行onHide和onShow方法,
-
当小程序页面销毁时会执行 onUnload方法
export default class index extends wepy.page { config = {}; //配置信息 components = {}; //組件配置 data = {}; //页面数据 methods = {}; //元素绑定事件区域 events = {}; //子组件$emit给父组件发数据,触发父组件方法 watch = {}; //监听数据变化 //==> 生命周期 onLoad(options) { // scene=decodeURIComponent(options.scene); 二维码参数 // 一般参数 {} } onReady() { // Do something when page ready. } onShow() { // Do something when page show. } onHide() { // Do something when page hide. } onUnload() { // Do something when page close. } //下拉 onPullDownRefresh() { // Do something when pull down. } //上拉 onReachBottom() { // Do something when page reach bottom. } //分享 onShareAppMessage() { // return custom share data when user share. }, //页面滚动 onPageScroll() { // Do something when page scroll } onResize() { // Do something when page resize } //==> 自定义方法 getQuery(){ ... } createImage() { .... } 复制代码
-
二、配置文件. 小程序配置config
app.wpy文件
- pages: [] 页面,小程序所页面,必要的页面,
- subPackages:[] 分包配置,将模块页面放入,上面就可以不用配置了
- window:{} 所有页面配置
- backgroundTextStyle
- navigationBarBackgroundColor
- navigationBarTextStyle
- onReachBottomDistance: 240 //实现无感加
- navigationStyle:default/custom 微信小程序自定义顶部导航栏 ,顶部导航栏就会消失,保留右上角胶囊状的按钮
- tabBar:[] 小程序下面切换配置,可以自己写
- 最多五个,最少2个,每次修改完要提交微信审核
- navigateToMiniProgramAppIdList: [] 小程序跳转小程序配置, 目前最多10个
- globalData全局数据 [类似于vuex redux 数据共享],一般外链文件
- methods 全局方法 , 所有页面都可以共用的,一般外链文件
三、页面配置信息 conifig (重点)
app.json配置如果和页面配置冲突了,那么会采用页面配置的
- navigationBarTitleText title
- navigationBarTextStyle 字体颜色 ["white" /"blank"]
- enablePullDownRefresh 是否下拉属性
- disableScroll 是否可以滚动
- disableSwipeBack 是否可以IOS左滑返回上一级,或着关闭小程序
四、一个wpy文件的组成
- template模板,结构
- script脚本
- data页面数据 this.xxx = xxx; 进行修改
- methods 页面方法 @绑定的 内置的方法
- events 是子组件传递的方法 $emit子元素传父元素事件 .sync props对象进行接收设置twoWay: true父元素下发的数据更新子元素跟着改变
- watchs 监听数据 数据发生改变进行处理
五、 微信授权登录流程
wepy登录流程: blog.csdn.net/weixin_4156…
<script>
import wepy from 'wepy'
import 'wepy-async-function'
import { setStore } from 'wepy-redux'
import configStore from './store'
const store = configStore()
setStore(store)
export default class extends wepy.app {
config = {
pages: [
'pages/index',
'pages/charts',
'pages/test'
],
window: {
backgroundTextStyle: 'light',
navigationBarBackgroundColor: '#fff',
navigationBarTitleText: 'WeChat',
navigationBarTextStyle: 'black'
}
}
globalData = {
userInfo: null,
ret:null,
config:null,
apiUrl:'后台地址'
}
constructor () {
super()
this.use('requestfix')
this.use('promisify')
}
onLaunch() {
this.testAsync()
this.init();
}
// 初始化获取config
init(){
wepy.request('初始化后台api').then(
(ret)=>{
this.globalData.config = ret.data.config;
console.log('======初始化成功=======');
//初始化成功判断是否授权
this.checkSettingStatus();
}
)
}
//判断登录状态/是否授权
async checkSettingStatus(){
try {
let auth = await wepy.getSetting(); //调用getSetting Api
let authSetting = auth.authSetting; //获取authSetting用来判断是否授权
console.log('---开始判断---');
if(authSetting['scope.userInfo']){
console.log('---已经授权---');
//已经授权情况下直接获取userInfor
let userInfo= await wepy.getUserInfo();
this.globalData.ret = userInfo;
//调用登录
this.login();
}else{
//如果没有授权跳转至授权页进行授权(新版只能通过调用按钮来调出授权框)
//授权页面button需要赋予open-type='getUserInfo'属性
//bindgetuserinfo='scope' bind方法用来授权/获取userInfor
console.log('---用户未授权---');
wepy.navigateTo({ url: './test' });
}
} catch (error) {
console.log(error)
}
}
//登录方法
async login(){
try {
console.log('---调用login方法---')
let token = wepy.getStorageSync('token') || '';
let {code:code} = await wepy.login(); //通过调用login获取code 判断是否开始登录
if(code){
console.log('---获取信息发送网络请求---');
let ret = this.globalData.ret
wepy.request({
url: '', //开发者服务器接口地址",
data: {
code : code,
rawData : ret.rawData,
token:token
},
method:'POST',
header:{
'Content-Type':'application/x-www-form-urlencoded'
}
}).then((res)=>{
console.log('---网络请求返回成功---')
console.log(res)
let response = res.data;
if(response.code == 1){
console.log('---登录正常,返回值1---');
console.log(response);
this.globalData.userInfo = response.data.userInfo;
wepy.setStorageSync('token',response.data.userInfo.token);
console.log(this.globalData.userInfo );
}else{
console.log('---登录异常---');
wepy.setStorageSync('token','');
}
});
}else{
console.log('---login返回异常---')
}
} catch (error) {
console.log(error)
}
}
sleep (s) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('promise resolved')
}, s * 1000)
})
}
async testAsync () {
const data = await this.sleep(3)
console.log(data)
}
getUserInfo(cb) {
const that = this
if (this.globalData.userInfo) {
return this.globalData.userInfo
}
}
}
</script>
复制代码
=> 进入首页判断用户是否登录(是否有用户信息) => 没有登录 => 进入登录页面
1.第一步 进入首页检测
HttpRequest.checkSessionAndLogin(this.$parent); //进行判断是否登录,是否登录过期
2. 第二步,进行判断
static async checkSessionAndLogin(app) {
let that = this;
if (app.globalData.userInfo) {
let session = await wepy.checkSession(); //是否过期了,
if (!session) {
that.login(app);
}
} else {
that.login(app); //没有登录,去登录页面
}
}
//3.登录授权
static async login(app) {
let that = this;
let res_login = await wepy.login(); //登录
if (res_login.code) { // code => 用户登录凭证(有效期五分钟)
app.globalData.code = res_login.code;
let res_setting = await wepy.getSetting(); //=> //调用getSetting Api 返回API对象
if (res_setting.authSetting['scope.userInfo']) { ///获取authSetting用来判断是否授权
that.getUserLoginInfo(app, res_login.code);
return true;
} else { //没有授权, 进入登录页面
wepy.navigateTo({
url: 'login'
});
wepy.hideLoading();
}
}
}
//4. 已经授权, 取得用户信息进行存储
static async getUserLoginInfo(app, code) {
let that = this;
// 已经授权,可以直接调用getUserInfo获取头像昵称,不会弹框
let res_userInfo = await wepy.getUserInfo();
if (res_userInfo) {
app.globalData.userInfo = res_userInfo.userInfo;
let invite_app_user_id = app.globalData.invite_app_user_id; //首页进入URL参数放入数据池的
let step_prize = app.globalData.step_prize; //步数邀请 //首页进入URL参数放入数据池的
let punch_the_clock = app.globalData.punch_the_clock; //打卡邀请
let query = {
code: code,
app_id: app.globalData.configInfo.appid,
raw_data: res_userInfo.rawData,
iv: res_userInfo.iv,
};
if (invite_app_user_id) { //如果是空则不传
query.invite_app_user_id = invite_app_user_id;
}
if (step_prize) { //步数邀请,该值为1
query.step_prize = step_prize;
}
if (punch_the_clock) { //打卡邀请,该值为1
query.punch_the_clock = punch_the_clock;
}
//调用后台接口
that.request({
query: query,
url: app.globalData.basePath + '/auth',
success: function (backData) {
if (backData.data.status == "1") {
app.globalData.userInfo = backData.data.data;
// getUserLoginInfo是网络请求,可能会在Page.onLoad之后才返回,所以此处加入callback以防止这种情况
if (app.userInfoReadyCallback) {
app.userInfoReadyCallback(backData.data.data);
}
wepy.hideLoading();
} else {
wepy.hideLoading();
}
}
})
}
}
微信授权button按鈕
<button id="getUserInfo" open-type="getUserInfo" hidden="true" bindgetuserinfo="bindGetUserInfo"></button>
bindGetUserInfo(e) {
//用户单击授权按钮确定,进行返回首页
if (e.detail.errMsg == "getUserInfo:ok") {
wepy.navigateBack();
}
}
复制代码
六、项目问题总结:
- 获取公共数据
- 页面内采用: this.$parent ==> [ app = getApp() ]
- 组件内部: this.parent
- 小程序跳转小程序配置 在app.json里面配置navigateToMiniProgramAppIdList:[]
- 弹框出来阻止页面滑动,
<view class="outer_chain_pop" @tap="hidePopModel" catchtouchmove='stopPageScroll'></view>
stopPageScroll () {
return false;
},
复制代码
- 弹框单击, 单击弹框区域外隐藏弹框,利用事件冒泡
<view class="share_pop" @tap="hidePopModel" catchtouchmove='stopPageScroll'>
<view class="share_pop_box {{shareObj.slideUp}}" @tap.stop='preventEvent'></view>
</view>
//单击弹框区域外, 弹框消失
hidePopModel() {
this.shareObj.isShowSharePop = false;
this.isPageNoScroll = '';
this.isShowCanvas = false;
this.isShowChainPop = false;
},
//单击弹框,阻止事件
preventEvent() {
return false;
},
复制代码
- button按钮样式重置
button {
background: transparent !important;
}
button::after {
border: none;
}
复制代码
- 微信修改了分享机制,导致开发者拿不到任何分享后的回调(成功、失败、完成)
- 用 scroll-view 如果要横向滚动的话,除了要设置 scroll-x 属性,还要设置 white-space: nowrap; 的样式,子元素设置 display: inline-block
- 如果你用了一个定时器,在退出页面的时候要记得清除,不然这个定时器还会一直执行
- 小程序分享图片必须是5:4比例,如果不是,则进行补图, 首先用canvas画图,转为图片,这张图片进行分享图片
- 回到顶部,安卓会有抖动的效果,动画时间直接设为0, IOS没事
- IOS下拉会出现白色背景,盖一层,定位到上面,优化
- IOS在模块内左滑会关闭小程序,在config对象设置disableSwipeBack:true 不关闭小程序
- 生成专属图片分享,canvas生成图片进行分享
- 邀请好友,分享一律query参数
- 参数接受问题:
- 跳转?后面的参数在onLoad里面用options接受,返回的是一个对象 eg:pages/pagesWpy/index?from='step' {}
- 小程序二维码扫码跳转进来的,在onLoad里面使用var scene = decodeURIComponent(options.scene)获取,他返回的是一个字符串 ?后面的参数 "",在进行参数格式化返回一个的对象,进行处理
-
wx.navigateTo(导航切换) 和wx.switchTab(控制 tabBar 的切换)
-
自定义底部导航和内置tabBar导航
-
自定义组件
-
解析 HTML 的三种方法(www.qinziheng.com/xiaochengxu…)
- wxParse 解析富文本 插件处理,引用
- rich-text 解析富文本 标准HTML都可以解析
- web-view 解析富文本,公众号文章展示,显示网页内容,组件权限最高
- juejin.im/post/5bb86a…
-
使用微信开发者工具–>添加项目,项目目录请选择dist目录。
-
微信开发者工具–>项目–>关闭ES6转ES5。 重要:漏掉此项会运行报错。
-
微信开发者工具–>项目–>关闭上传代码时样式自动补全。 重要:某些情况下漏掉此项也会运行报错。
-
微信开发者工具–>项目–>关闭代码压缩上传。 重要:开启后,会导致真机computed, props.sync 等等属性失效。(注:压缩功能可使用WePY提供的build指令代替,详见后文相关介绍以及Demo项目根目录中的wepy.config.js和package.json文件。)
-
WePY中的methods属性只能声明页面wxml标签的bind、catch事件,不能声明自定义方法
-
onload只会在页面加载时候执行,比如用navigateBack回到之前的页面的时候,之前那个页面不会再执行onload, 所以我们要触发某些函数的时候,我们可以放在onshow里面,即使是navigateBack回来也会执行
-
同一个页面想要2个分享?
- 需要在Page中添加onShareAppMessage方法,否则点右上角菜单不会出现转发选项 除了右上角菜单外,可以使用button组件的open-type="share" button组件和右上角的点点点都是调用onShareAppMessage方法 通过参数中的from字段区分事件来源是菜单menu还是按钮button(某需求要求两个分享不同) 通过onShareAppMessage方法返回的对象来定制转发界面显示的内容
-
全屏蒙版弹窗遮不住tabBar?
- tabBar的层级还是很高的,当出现全屏蒙版弹窗时,是无法盖住tabBar的, 可以调用微信的hidetabbar,不过需要注意兼容低版本 wx.hideTabBar({}) / wx.hideTabBar({})
-
canvas生成分享图
- 创建canvas对象,画图,draw()花完,进行canvas转图片, 回调里面, wx.canvasToTempFilePath(options)
图解布局:
小程序优化
离开页面
进入页面
打开小程序
项目目录分配 src是源文件夹, dist是打包完的文件夹
HttpRequest.js封装
import wepy from 'wepy'
import globalData from '@/infrastructure/globalData/global' //公用数据
import dialog from '@/assert/utils/dialog';
const params = globalData.parapms; //接口公共参数
const basePath = globalData.basePath; //域名
class HttpRequest {
static setPromise(url, method, options = {}, type) {
if(type !=='md') { //其他接口
options = Object.assign(options, params);
url = (basePath + url);
}
return wepy.request({
url ,
method,
dataType: 'json',
data: options,
header: {
'content-type':'application/json'
}
});
}
//统一接口拦截,请求是否成功
static validation(res) {
return new Promise((resolve, reject) => {
if (typeof res !=='undefined') {
let code = res.data.status;
if (code == 1) {
resolve(res.data);
} else if(code == 0) {
//错误处理
let timer = null;
clearInterval(timer);
if(res.data.error=="活动已关闭,该页面已隐藏"){
reject(res.data);
}else{
wepy.showToast({
title: res.data.error,
icon: 'none',
duration: 2000
});
timer = setTimeout(() => {
dialog.hideLoading();
reject(res); //返回错误信息
}, 2000);
}
}
}
});
}
static get(url, options = {}, type) {
return HttpRequest.setPromise(url, "get",options,type);
}
static post(url, options = {}) {
return HttpRequest.setPromise(url, "post", options);
}
static delete(url, options = {}) {
return HttpRequest.setPromise(url, "delete", options);
}
static put(url, options = {}) {
return HttpRequest.setPromise(url, "put",options);
}
}
export default HttpRequest;
复制代码
七、常用方法封装
- 获取URL参数
function getQuery(key, url) {
url = url || window.location.href + '';
if (url.indexOf('#') !== -1)
url = url.substring(0, url.indexOf('#'));
let rts = [],
rt;
let queryReg = new RegExp('(^|\\?|&)' + key + '=([^&]*)(?=&|#|$)', 'g');
while ((rt = queryReg.exec(url)) != null) {
rts.push(decodeURIComponent(rt[2]));
}
if (rts.length == 0) return null;
if (rts.length == 1) return rts[0];
return rts;
}
复制代码
2.保存图片到手机
```
<block wx:if="{{shareObj.scopeFlag}}">
<view class="" @tap.stop="saveImg" @tap.stop='preventEvent'>
<button class="canvas_image_btn_save" @tap.stop='handleSetting1'>去授权</button>
</view>
</block>
<block wx:if="{{shareObj.settingFlag}}">
<button class="canvas_image_btn_save" open-type="openSetting" bindopensetting='handleSetting'>去设置</button>
</block>
//保存图片,判断是否授权,没有授权则进入授权页面进行授权
function checkoutSaveImg() {
wx.getSetting({
success: res => {
//进行去授权
if (!res.authSetting['scope.writePhotosAlbum']) {
if(this.shareObj.scope) { //授权完毕了, 包括失败和成功
this.shareObj.scopeFlag = false;
this.shareObj.settingFlag = true;
} else {
this.shareObj.scopeFlag = true;
this.shareObj.settingFlag = false;
}
this.shareObj.saveImgSettingFlag = false;
this.$apply();
} else { //授权完毕,进入设置页面进行设置
this.shareObj.saveImgSettingFlag = true;
this.shareObj.settingFlag = false;
this.shareObj.scopeFlag = false;
this.isShowCanvas = true;
this.$apply();
}
}
});
}
//调取授权弹框进行授权
function handleSetting1(e) {
//判断是否开启授权
wx.getSetting({
success: res => {
//进行去授权
if (!res.authSetting['scope.writePhotosAlbum']) {
wx.authorize({
scope: 'scope.writePhotosAlbum',
success: () => {
this.shareObj.saveImgSettingFlag = true;
this.shareObj.scopeFlag = false;
this.shareObj.settingFlag = false;
this.isShowCanvas = true;
this.shareObj.scope = true;
this.$apply();
},
fail: () => {
debugger
this.shareObj.scope = true;
this.shareObj.settingFlag = true;
this.shareObj.saveImgSettingFlag = false;
this.shareObj.scopeFlag = false;
this.$apply();
}
});
} else { //授权完毕,进入设置页面进行设置
if (!e.detail.authSetting['scope.writePhotosAlbum']) {
Tips.alert('不授权无法保存', 2000);
this.shareObj.saveImgSettingFlag = false;
this.shareObj.settingFlag = true;
this.shareObj.scopeFlag = false;
this.isShowCanvas = true;
} else {
this.shareObj.saveImgSettingFlag = true;
this.shareObj.settingFlag = false;
this.shareObj.scopeFlag = false;
this.isShowCanvas = true;
}
this.$apply();
}
}
});
},
```
复制代码
3.分享图片
//邀请注册,和页面默认转发功能,进入首页
function setShare(options) {
options = options || {};
let imageUrl = options.imageUrl || 'https://xiaomengtong.oss-cn-beijing.aliyuncs.com/statics/xiaomengtong-small-program/share_img_one.png'; //默认当前页面截取
let title = options.title || '阅读新体验,活动玩不停';
let path = options.path || '/pages/pagesWpy/index'; //默认回到首页
return {
title,
imageUrl,
path,
}
}
复制代码
4.随机生成字符串
function getRandomString() {
let str = "",
range = 30,
arr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
// 随机产生
for (let i = 0; i < range; i++) {
str += arr[Math.round(Math.random() * (arr.length - 1))];
}
return str;
},
复制代码