去年在公司的时候负责了一个浙里办的微应用 个人感觉还是挺坑的大概说一下。
主要技术栈为React
1、支付宝 浙里办app单点登录
var sUserAgent = window.navigator.userAgent.toLowerCase();
var bIsDtDreamApp = sUserAgent.indexOf('dtdreamweb') > -1;
var bIsAlipayMini =
sUserAgent.indexOf('miniprogram') > -1 &&
sUserAgent.indexOf('alipay') > -1;
var url = '';
if (bIsAlipayMini) {
// 支付宝小程序
url = `https://puser.zjzwfw.gov.cn/sso/alipay.do?action=ssoLogin&servicecode=xxxxx`;
} else {
// 浙里办app
url = `https://puser.zjzwfw.gov.cn/sso/mobile.do?action=oauth&scope=1&servicecode=xxxxx`;
}
window.location.href = url;
浙里办单点登录的逻辑大概就是在用户进入微应用的时候会先判断地址栏上是否携带ticket令牌,假如没有携带ticket令牌就会重定向到对应端的单点登录地址,单点登录完成后会往栈地址中加入一层跳转到你配置的回调地址,在这一步的时候会引生出一个问题也就是二次回退。
2、二次回退
经过测试在进行跳转登录地址的时候无论使用assign、replace、push都会默认往栈结构加一层导致回退的时候需要二次。
二次回退产生流程大概是这样 不是勿喷 本人理解。(较为抽象忍忍就好)
解决方案
在浙里办官方文档中给出了解决方案 使用pageshow事件判断在第一次回退的时候在1节点时候上一步是刷新,回退,还是首次进入,假如是回退的话就使用浙里办的jsapi删除当前的页面就回到了app首页。
pageshow事件描述:
window.performance.navigation.type 包含了3个值
0 : TYPE_NAVIGATE(用户通过常规导航方式访问页面,比如点一个链接,或者一般的get方式)
1 : TYPE_RELOAD(用户通过刷新,包括JS调用刷新接口等方式访问页面)
2 : TYPE_BACK_FORWARD(用户通过后退按钮访问本页面)
因为pageshow事件是在页面在React生命周期执行前就得进行监听,所以把监听的代码直接写在了项目当中index.html script标签当中
function login() {
if (!window.location.href.includes('ticket')) {
console.log('去登录');
var sUserAgent = window.navigator.userAgent.toLowerCase();
var bIsDtDreamApp = sUserAgent.indexOf('dtdreamweb') > -1;
var bIsAlipayMini =
sUserAgent.indexOf('miniprogram') > -1 &&
sUserAgent.indexOf('alipay') > -1;
var url = '';
if (bIsAlipayMini) {
url = `https://puser.zjzwfw.gov.cn/sso/alipay.do?action=ssoLogin&servicecode=xxxx`;
} else {
url = `https://puser.zjzwfw.gov.cn/sso/mobile.do?action=oauth&scope=1&servicecode=xxxx`;
}
console.log('准备跳走');
window.location.href = url;
location.replace;
}
}
window.addEventListener(
'pageshow',
function (event) {
console.log(
'进入pageshow事件,___________________________________________________-',
event.persisted ||
(window.performance && window.performance.navigation.type == 2)
);
if (
event.persisted ||
(window.performance && window.performance.navigation.type == 2)
) {
ZWJSBridge.close()
.then((result) => {
console.log(result);
})
.catch((error) => {
console.log(error);
});
}
login();
},
false
);
3、对接微信小程序端
微信小程序端使用的获取用户信息的参数并不是ticket而是ticketId,后端获取用户信息的接口也并不是同一个(俩个token并不互通),也就是说需要前后端写俩套获取用户信息的代码,浙里办官方文档写的并不是很详细,我大概整理了一下我目前已经上架成功了的方法。
```
判断是否在微信环境
const ua = navigator.userAgent.toLowerCase();
if (ua.indexOf('micromessenger') !== -1) {
window.wx.miniProgram.getEnv((res) => {
console.log('html检测小程序', res);
if (res.miniprogram) {
console.log('html在微信小程序内');
} else {
console.log('html不在小程序内');
}
});
} else {
console.log('不在微信里');
}
```
有了此宝物可以在原本pageshow函数调用login函数的地方加上一个判断,假如不在微信环境内就执行原本的那一套
// index.html文件当中
const isWx = (isLoad = () => {}) => {
var ua = navigator.userAgent.toLowerCase();
if (ua.indexOf('micromessenger') !== -1) {
console.log('html文件中在微信');
} else {
console.log('不在微信里');
login();
}
};
window.addEventListener(
'pageshow',
function (event) {
console.log(
'进入pageshow事件,___________________________________________________-',
event.persisted ||
(window.performance && window.performance.navigation.type == 2)
);
if (
event.persisted ||
(window.performance && window.performance.navigation.type == 2)
) {
window.ZWJSBridge.close()
.then((result) => {
console.log(result);
})
.catch((error) => {
console.log(error);
});
}
isWx(login);
},
false
);
// 在框架初始化的时候
const getUserWxData = async () => {
if (ZWJSBridge.ssoTicket) {
const ssoFlag = await ZWJSBridge.ssoTicket({});
if (ssoFlag && ssoFlag.result === true) {
// 使用 IRS“浙里办”单点登录组件
if (ssoFlag.ticketId) {
console.log(ssoFlag.ticketId, 'ticketId,这里进行调接口操作');
// TODO 应用方服务端单点登录接口
} else {
// 当“浙里办”单点登录失败或登录态失效时调用ZWJSBridge.openLink方法重 新获取 ticketId。
ZWJSBridge.openLink({ type: 'reload' }).then((res) => {
console.log(res.ticketId, 'ticketId这里进行调接口操作');
});
}
}
}
};
useEffect(() => {
const ua = navigator.userAgent.toLowerCase();
if (ua.indexOf('micromessenger') !== -1) {
window.wx.miniProgram.getEnv((res) => {
console.log('html检测小程序', res);
if (res.miniprogram) {
console.log('html在微信小程序内');
getUserWxData();
}
});
} else {
这里就使用从地址栏获取ticket的方法进行获取用户信息
}
}, []);
特别注意: 在index.html当中使用浙里办ZWJSBridge的时候必须先进行初始化不然会报错。请看代码
<script
type="text/javascript"
src="//assets.zjzwfw.gov.cn/assets/ZWJSBridge/1.1.0/zwjsbridge.js"
></script>
<script>
ZWJSBridge.onReady(() => {
console.log('初始化完成后,执行bridge方法');
});
</script>
微信调试直接进入微信小程序扫irs通用码就可以!
旧埋点
在html文件中
<script>
(function (w, d, s, q, i) {
w[q] = w[q] || [];
var f = d.getElementsByTagName(s)[0],
j = d.createElement(s);
j.async = true;
j.id = 'beacon-aplus';
j.src = 'https://d.alicdn.com/alilog/mlog/aplus.js?id=202951085';
f.parentNode.insertBefore(j, f);
})(window, document, 'script', 'aplus_queue');
aplus_queue.push({
action: 'aplus.setMetaInfo',
arguments: ['aplus-waiting', 'MAN'],
});
aplus_queue.push({
action: 'aplus.setMetaInfo',
arguments: ['aplus-rhost-v', 'alog.zjzwfw.gov.cn'],
});
aplus_queue.push({
action: 'aplus.setMetaInfo',
arguments: ['aplus-rhost-g', 'alog.zjzwfw.gov.cn'],
});
aplus_queue.push({
action: 'aplus.setMetaInfo',
arguments: ['appId', '60506758'],
});
aplus_queue.push({
action: 'aplus.setMetaInfo',
arguments: ['_hold', 'BLOCK'],
});
aplus_queue.push({
action: 'aplus.setMetaInfo',
arguments: ['_hold', 'START'],
});
</script>
在单点登录获取到用户信息的地方
// 获取位置信息
const getLocationData = await window.ZWJSBridge.getLocation();
// 获取用户类型
const getUserTypeData = await window.ZWJSBridge.getUserType();
window.aplus_queue.push({
action: 'aplus.sendPV',
arguments: [
{
is_auto: false,
},
{
miniAppId: '这里填应用id',
miniAppName: '这里填应用名称',
long: getLocationData?.longitude,
lati: getLocationData?.latitude,
userType: getUserTypeData?.userType,
},
],
});
window.aplus_queue.push({
action: 'aplus.setMetaInfo',
arguments: ['_user_nick', 这里输入单点登录获取到的用户名],
});
window.aplus_queue.push({
action: 'aplus.setMetaInfo',
arguments: ['_user_id', 这里输入单点登录获取到的用户ID],
});
新埋点(我的项目还没更新所以不确定是否可以 看看尝试一下吧)
在html中
<script
type="text/javascript"
src="//assets.zjzwfw.gov.cn/assets/zwlog/1.0.0/zwlog.js"
/>
<script>
var _per = window.performance;
function getmb(size) {
return Math.floor(size / 1024 / 1024, 4) + 'mb';
}
function getsec(time) {
return time / 1000;
}
console.log(
'加载时长' + getmb(_per.timing.domComplete - _per.timing.domLoading)
);
console.log('内存占用:' + getmb(_per.memory.usedJSHeapSize));
console.log(
'TCP链接耗时:' +
getsec(_per.timing.connectEnd - _per.timing.connectStart)
);
console.log(
'响应耗时' + getsec(_per.timing.responseEnd - _per.timing.responseStart)
);
window.onload = () => {
const zwlog = new window.ZwLog();
zwlog.onReady(function () {
zwlog.sendPV({
miniAppId: '输入应用Id',
t2: getsec(_per.timing.domComplete - _per.timing.domLoading),
t0: getsec(_per.timing.responseEnd - _per.timing.responseStart),
pageName: '输入应用名称',
miniAppName: '输入应用名称',
pageId: '输入应用ID',
log_status: '02',
});
});
// 埋点的浏览时长我的想法是直接起一个定时器 页面关闭或在跳走的时候在关闭定时器埋入timeNum的值 又更好的方法麻烦评论告诉一下
window.timeNum = 0;
window.responseTime = setInterval(() => {
window.timeNum += 1;
}, 1000);
};
</script>
在单点登录获取用户信息后
const zwlog = new window.ZwLog({
_user_id:"用户ID",
_user_nick:"用户名称"
});
zwlog.onReady(function () {
zwlog.sendPV({
miniAppId: '输入应用Id',
// Page_duration: '用户从进入到离开当前页面的时长',
pageName: '输入应用名称',
miniAppName: '输入应用名称',
pageId: '输入应用Id',
log_status: '02',
_user_id: 用户ID,
_user_nick: 用户名称,
});
});
页面关闭 或者 外链跳走
clearInterval(window.responseTime);
const zwlog = new window.ZwLog();
zwlog.onReady(function () {
zwlog.sendPV({
// 用户从进入到离开当前页面的时长
Page_duration: window.timeNum,
});
});
希望可以帮到您