uni-app 开发微信公众号Bug记录,优化记录

流程方案

重定向静默授权

ios 使用 location.href 跳转返回空白页

内部页面跳转
1丶需使用Router跳转

this.$Router.push({ path: '/pages/test/test.vue`,params:{}})

2丶外部页面跳转解决ios白屏

window.history.pushState({},null,document.URL)
window.addEventListener('popstate',()=>{					
	 WeixinJSBridge.call('closeWindow');	
});

3丶使用iframe嵌套页面(最好的方法,但是不适合公众号文章和禁止iframe访问的页面)。

分享链接和打开链接不一致

用途嘛,懂的都懂

1丶配置router

//此时只要链接路径带有"testaliasPath "页面就会跳转到 "/pages/test/test.vue"这个页面

//pages.json和route都需要配置 pages/test/test.vue

//注意 router的页面path第一个字符为'/'

const aliasPath = "testaliasPath ";
{
    "path": "/pages/test/test.vue", // "*"为页面路径或文件名
    "aliasPath":*${aliasPath}*`//路由别名
}

2丶使用微信分享jssdk

注意:ios分享链接传到后台验证,需要使用刚打开单页面的初始链接,安卓直接使用localtion.href就行了
可以存在本地或者全局

uni.getSystemInfoSync().platform === 'ios'?this.$store.state.shareUrl:location.href

这个方法我封在utils里面

export function setPageConfig(pageConfig,success,fail) {
	try{
		
		const jsApiList = ['updateAppMessageShareData', 'updateTimelineShareData'];
		if (!isMobile()) {
			fail&&fail()
			return;
		}
		const res = pageConfig; //后台接口返回的参数;
		this.$wx.config({
			debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
			appId: res.appId, // 必填,公众号的唯一标识
			timestamp: res.timestamp, // 必填,生成签名的时间戳
			nonceStr: res.nonceStr, // 必填,生成签名的随机串
			signature: res.signature, // 必填,签名
			jsApiList // 必填,需要使用的JS接口列表
		})
			this.$wx.ready(res => {
				success && success(res)
			});
			this.$wx.error(function(err) {
				fail && fail(err)
			});
		}catch(err){
			fail && fail(err)
		}
}

这是一个随机字符串函数,方便随机分享路径

export const randomString = e => {  
      e = e || 32;
      var t = "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678",
      a = t.length,
      n = "";
      for (let i = 0; i < e; i++) n += t.charAt(Math.floor(Math.random() * a));
      return n
}

页面内部调用

	setPageConfig.call(this,await wxGetJsConfig({ url }),()=>{
				//config加载完毕执行
				//好友分享
				this.$wx.updateAppMessageShareData({
				title: `${this.info.title}】- ${this.info.share_memo}`, // 分享标题
				desc: this.info.share_memo, // 分享描述
				link: `${window.location.protocol}//${window.location.host}/'打包路径,为空则不写'/${randomString(3) + aliasPath + randomString(3)}?awaid=${this.info.id}`, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
				imgUrl: encodeURI(this.info.share_img), // 分享图标
				success: () => {
					// 设置成功
				}
				});
				//朋友圈
				this.$wx.updateTimelineShareData({
					title: `标题`, // 分享标题
					link: `${window.location.protocol}/${window.location.host}/'打包路径,为空则不写'/${randomString(3) + aliasPath + randomString(3)}`, // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
					imgUrl: encodeURI(this.info.share_img), // 分享图标
					success: () => {
						// 设置成功
					}
				});
			},()=>{
				//失败执行
			});

这样每个人的分享链接大部分都不一致

通用方法

获取经纬度

//获取当前经纬度 请不要重复调用存在全局状态
export const getLoc = () =>{
	return new Promise(reslove=>{
		let getLoc = () =>{
			getLoc = null;
			var geolocation = new BMap.Geolocation(); //获取位置定位
			geolocation.getCurrentPosition(r => {
				//r返回经纬度还有省市信息
				console.log(r)
				if(!r){
					showToast('用户拒绝访问位置信息');
					reslove(false)
				}else{
					reslove(r)
				}
			}, {
				enableHighAccuracy: true
			});
		}
		try{
			if(!window.BMap_loadScriptTime){
				window.BMap_loadScriptTime = (new Date).getTime(); 
				let script = document.createElement('script');
				script.src = 'https://api.map.baidu.com/getscript?v=3.0&ak=Ueqsa2Zlp0WeEWTM5jLRHnfPQYunzi33&services=&t=20200918142623' 
				script.onload = function(){
					getLoc()
				}
				document.querySelector('head').appendChild(script)
			}else{
				getLoc()
			}
		}catch(e){
			showToast('用访问位置信息失败');
			reslove(false)
		}
	})
}
//防止xss 转义字符
export const refundXss = (str='') =>{
	//防止script 和 a标签
	return str.replace(/(<script)|(<a)/g,(item,$1,$2)=>{
		if($1){
			return '&#60;script'
		}
		if($2){
			return '&#60;a'
		}
	})
}

加解密

npm i crypto-js
/**
 * 订单号token的加解密
 */
export const OrderTokenhashObj = {
	/**
	 * 加密
	 * @param {String} token 
	 * @param {Number} id 
	 */
	encrypt(token,id){
		let str = token;
		let tokenLength = str.length;
		let idLength = id.toString().length;
		let strEnd = randomString(1);
		let idOffsetL =  strEnd.charCodeAt() % tokenLength;
		str = token.slice(0,idOffsetL) + id.toString() + token.slice(idOffsetL)
		str +=  (idLength + strEnd);
		const _cipherText = crypto.getAES(str);
		let cipherText = _cipherText.slice(-1) +  _cipherText.slice(0,-1);
		console.log(`token:${token} id:${id},str:${str}`)
		return cipherText
	},
	/**
	 * 解密
	 * @param {String} cipherText  密文
	 * @returns {Object} 
	 */
	decipher(cipherText){
		cipherText = cipherText.slice(1) + cipherText.slice(0,1);
		console.log(cipherText)
		let webCipher = crypto.getDAes(cipherText);
		let idLength = Number(webCipher.substr(-2,1));
		let tokenLength = webCipher.length - 2 - idLength;
		let idOffsetL =  webCipher.slice(-1).charCodeAt() % tokenLength;
		let id = webCipher.substr(idOffsetL,idLength);
		let token = webCipher.slice(0,idOffsetL) + webCipher.slice(idOffsetL+idLength,-2)
		return {
			id,
			token
		}
	}
}
export const crypto = {
	options() {
	  return {
		key: CryptoJS.enc.Utf8.parse('shunliuliu168168'),
		iv: CryptoJS.enc.Utf8.parse('shunliuliu168168')
	  }
	},
  //加密
	getAES(data) { 
	  const { key, iv } = this.options()
	  const encrypted = CryptoJS.AES.encrypt(data, key, {
		iv: iv,
		mode: CryptoJS.mode.CBC,
		padding: CryptoJS.pad.Pkcs7
	  })
	  return encrypted.toString()
	},
  //解密
	getDAes(encrypted) {
	  const { key, iv } = this.options()
	  const decrypted = CryptoJS.AES.decrypt(encrypted, key, {
		iv: iv,
		mode: CryptoJS.mode.CBC,
		padding: CryptoJS.pad.Pkcs7
	  })
	  return decrypted.toString(CryptoJS.enc.Utf8)
	}
}

调起微信支付

//调起微信支付
/**
 * https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6
 */
/**
 * @param {String} param0
 * @param {String} param0.appId //公众号id
 * @param {*} param0.timeStamp  //时间戳,自1970年以来的秒数     
 * @param {*} param0.nonceStr //随机串     
 * @param {*} param0.package  //订单详情扩展字符串
 * @param {*} param0.signType //微信签名方式:     
 * @param {*} param0.paySign //微信签名 
 */
export const payMoney = param0 => new Promise(resolve => {
	let {appId, timestamp, nonceStr, signType, paySign} = param0;  
	WeixinJSBridge.invoke(
		'getBrandWCPayRequest', {
			appId, //公众号名称,由商户传入     
			timeStamp:timestamp, //时间戳,自1970年以来的秒数     
			nonceStr, //随机串     
			package:param0.package, //订单详情扩展字符串
			signType, //微信签名方式:     
			paySign //微信签名 
		},
		function (res) {
			if (res.err_msg == "get_brand_wcpay_request:ok") {
				resolve(1) //支付成功
			} else {
				resolve(0); //支付失败
			}
		});
});

速度优化

由于做的静默授权需要重定向,判断本地存储或者cookie没有token,就重定向到微信。
我们不应该uniap页面逻辑js代码里面调用,不然会加载更多的dom节点和其它js逻辑代码,从而新用户的加载速度。
解决方案
建立一个template.h5.html文件,(这个可以到官方去搜索怎么配置)
然后在DOM最顶部放入下面内容

//注意这里不支持es6的 `` 字符串
<script type="text/javascript">
	//获取参数
	var token,domain,pathname,reg,search,param,url,hascode;
	hascode = window.location.search.indexOf('code=') != -1;
	// localStorage.removeItem('userToken')
	token = localStorage.getItem("userToken");
	if(!token && !hascode){
	    //解决ios白屏
		window.history.pushState({},null,document.URL)
		window.addEventListener('popstate',()=>{			
			WeixinJSBridge.call('closeWindow');
		});
		
		//获取重定向参数
		domain = window.location.protocol + "//" + window.location.host;
		pathname = window.location.pathname.substring(4);
		reg = /[&]{0,1}code\=[a-zA-Z_0-9\&\=]+/g;
		search = window.location.search.replace(reg,"");
		param = encodeURIComponent(domain + '/hsh' + (pathname || 'index/index') + (search === '?' ? "" : search));
		url = domain + "/api/wx/index?url=" + param;
		//重定向
		window.location.replace(url);
	}
</script>

为什么我要放到DOM的顶部。网上也有人说脚本放在body里面最好,其实也有一定的道理,但是呢,要符合业务场景,做优化的时候,我们也可以舍弃一些规则,网站打开速度快才是关键!
知识点是死的,运用起来是活的,很多知识点我们都是知道的,如何合理的利用到项目中才时关键。

优化点
了解dom渲染机制
回流-绘画-复合。
1丶多使用this.$nextTick(()=>{})防止布局抖动,利用缓存
2丶动画尽量使用opacity和tansform,利用复合完成动画
3丶资源图片压缩。利用npm开源包
4丶资源整合(串行改为并行)。雪碧图,单文件,内容较少的几个文件可以合并成一个文件
5丶节流和防抖,长列表优化。
6丶利用canvas前端压缩图片上传
7丶DOM批量读写 fastDom
8丶异步加载组件和懒加载图片
9丶经常做动画的组件单独放一个层级。
10丶节点先display:none,在改变dom属性,减少绘画和回流
11丶减少dom节点嵌套,减少css选择器嵌套

其他实现:
实现了多个公众号一个收款公众号,利用加解密解决url参数传递过长的问题

Api通用封装

async function  awaitToken() {

	return new Promise(resolve=>{

		let i = 0;

		let newT = new Date().getTime();

		let key = 'awaitTokenTime'+newT;

		if(window[key])clearInterval(window[key]);

		window[key] = setInterval(()=>{

			if(getToken()){

				resolve(1);//获取token成功
				
				clearInterval(window[key]);

				window[key] = null


			}

			if(i>20){//10秒未获取到token,超时

				resolve(0);//获取token失败

				clearInterval(window[key])

				window[key] = null

			}

			i++;

		},500)

	})

}
function hanldfn(method, {
	url,
	data,
	isAuth = false,
	header = {}
}) {
	return new Promise(async (resolve, reject) => {
		let success = ({data}) => {
			try{
				data = JSON.parse(data);
			}catch(e){}
			if (data.code == 200) {
				resolve(data);
			} else {
				let e = '网络错误';
				if(data.code == -1 || data.code == -2){
					if(isAuth){
						return tokenExpired();
					}
					alert("未设置token")
				}
				if(data.msg){
					e = data.msg;
				}else if (String(data) ==  data) {
					e =  data;
				} else {
					try {
						e = JSON.stringify(data);
					} catch (e) {}
				}
				success = null;//清除闭包
				reject(e?e.slice(0,255):'网络错误');
			}
		};
		let fail = err => {
			let e = "网络错误";
			console.log(err)
			if (err && (err.msg || err.errMsg)) {
				e = err.msg || err.errMsg;
			} else if (String(err) == err) {
				e = err;
			} else {
				try {
					e = JSON.stringify(err);
				} catch (e) {}
			}
			fail = null;//清除闭包
			reject(e)
		};
		//验证权限是否足够
		if (isAuth) {
			header.token = header.token || getToken();
			if(!header.token){
				//等待获取token请求完毕
				let res = await awaitToken();
				if(!res){
					return reject('获取token超时');
				}
				//延时获取token
				header.token = getToken();
			}
		}
		const requestUrl = (window.$store().state.apiUrl || apiUrl)+url;
		console.log(requestUrl)
		if(method == 'PUT'){
			uni.uploadFile({
				url:requestUrl, 
				file: data.fileList[0],
				fail,
				success
			});
		}else{
			uni.request({
				url:requestUrl,
				data,
				method,
				header,
				fail,
				success
			})
		}

		
	});
}
/**
 * 验证请求权限是否足够
 */
function getToken() {
	return window.$store().state.token;
}

var api = {
	get(url, data, isAuth,header) {
		return hanldfn("GET", {
			url,
			data,
			isAuth,
			header
		});

	},
	post(url, data, isAuth, header) {
		return hanldfn("POST", {
			url,
			data,
			isAuth,
			header
		});

	},
	//文件上传
	put(url, data, isAuth, header) {
		return hanldfn("PUT", {
			url,
			data,
			isAuth,
			header
		});

	},
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
//微信充值 //支付接口测试 function balance(url, data) { uni.request({ url: cfg.originUrl + '/wx/mp/js_sig.do', data: { route: url }, method: 'GET', success: (res) => { jweixin.config({ debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来 appId: res.data.appId, // 必填,公众号的唯一标识 timestamp: res.data.timestamp, // 必填,生成签名的时间戳 nonceStr: res.data.nonceStr, // 必填,生成签名的随机串 signature: res.data.signature, // 必填,签名 jsApiList: ['chooseWXPay'] // 必填,需要使用的JS接口列表 }); jweixin.ready(function() { uni.request({ url: cfg.originUrl + '/wx/recharge/pay.do', method: 'POST', header: { 'Content-type': "application/x-www-form-urlencoded", }, data: JSON.stringify(data), success: function(res) { alert("下单成功"); alert(JSON.stringify(res)); alert(res.data.order_id); all.globalData.orderId = res.data.order_id; uni.setStorageSync('orderId', res.data.order_id); jweixin.chooseWXPay({ timestamp: res.data.payParams.timeStamp, // 支付签名时间戳 nonceStr: res.data.payParams.nonceStr, // 支付签名随机串 package: res.data.payParams.package, // 接口返回的prepay_id参数 signType: res.data.payParams.signType, // 签名方式 paySign: res.data.payParams.paySign, // 支付签名 success: function(e) { alert("支付成功"); alert(JSON.stringify(e)); // 支付成功后的回调函数 } }); } }) }); jweixin.error(function(res) { // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。 console.log("验证失败!") }); } }) }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值