1.链接: blog.csdn.net/qq_26585943…
www.jianshu.com/p/0078507e1…
微信小程序开发(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…
scriptimport 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)
图解布局:
小程序优化
juejin.im/post/5b496d…
离开页面
进入页面
打开小程序
项目目录分配 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;},复制代码













