本篇文章仅用于学习目的。不可用于商业用途,若有侵权,请告知,立刻删除。
源码一览
去掉了一些与基础框架无关的接口、函数、变量
/**
* 6.0.2
* 关于 6.0.2 的相关文档说明:
* - 为什么6.0.1版本config:ok,但是6.0.2版本之后不ok
* (
* 因为6.0.2版本之前没有做权限验证,所以config都是ok,
* 但这并不意味着你config中的签名是OK的,
* 请在6.0.2检验是否生成正确的签名以保证config在高版本中也ok。
* )
* - 是否需要对低版本自己做兼容
* (
* jssdk都是兼容低版本的,
* 不需要第三方自己额外做更多工作,
* 但有的接口是6.0.2新引入的,只有新版才可调用
* )
*/
!(function (e, n) {
"function" == typeof define && (define.amd || define.cmd)
? define(function () {
return n(e);
})
: n(e, !0);
})(this, function (entryO, entryE) {
if (!entryO.jWeixin) {
var n;
var global = {
config: "preVerifyJSAPI",
};
/** 类似转码,把global中的 value 作为新obj的key,global中的 key 作为新 obj 的 value */
var createObjectByGlobal = (function () {
var tempObj = {};
for (var n in global) {
tempObj[global[n]] = n;
}
return tempObj;
})();
var oDocument = entryO.document;
var userAgent = navigator.userAgent.toLowerCase();
var platform = navigator.platform.toLowerCase();
var isMacOrWin = !(!platform.match("mac") && !platform.match("win"));
var isWxdebugger = -1 != userAgent.indexOf("wxdebugger");
var isMicromessenger = -1 != userAgent.indexOf("micromessenger"); // 通过userAgent是否包含MicroMessenger来判断是否在微信内置浏览器打开网页
var isAndroid = -1 != userAgent.indexOf("android");
var isIphone =
-1 != userAgent.indexOf("iphone") || -1 != userAgent.indexOf("ipad");
var micromessengerVersion = (n =
userAgent.match(/micromessenger\/(\d+\.\d+\.\d+)/) ||
userAgent.match(/micromessenger\/(\d+\.\d+)/))
? n[1]
: "";
var help = {
version: 1,
appId: "",
initTime: 0,
preVerifyTime: 0,
networkType: "",
isPreVerifyOk: 1,
systemType: isIphone ? 1 : isAndroid ? 2 : -1,
clientVersion: micromessengerVersion,
url: encodeURIComponent(location.href),
};
var vConfig = {};
var SEvents = { _completes: [] };
var stateSetting = { state: 0, data: {} };
/** 要对外暴露的接口api对象 */
var wechatJsEvents = {
/**
* 所有需要使用JS-SDK的页面必须先注入配置信息,
* 否则将无法调用
* (
* 同一个url仅需调用一次,
* 对于变化url的SPA的web app可在每次url变化时进行调用,
* 目前Android微信客户端不支持pushState的H5新特性,
* 所以使用pushState来实现web app的页面会导致签名失败,
* 此问题会在Android6.2中修复
* )。
* @param {*} inputConfig
*/
config: function (inputConfig) {
emptyConfigParamWhenDebug("config", (vConfig = inputConfig));
/**
* vConfig.check !== false
* 不允许转换,意思 truly 那一套失效,必须为 false 或其他任何值
*
* comment:
* 目前来看 vConfig.check 均为 undefind,所以 isCheckOfConfig 为 true。
* 因为没找到传递此参数的地方,且 config 文档中没有此变量
*
* true
*/
var isCheckOfConfig = !1 !== vConfig.check;
micromessengerAddEventListener(function () {
if (isCheckOfConfig) {
installWeixinJSBridge(
global.config,
{
verifyJsApiList: getList(vConfig.jsApiList),
verifyOpenTagList: getList(vConfig.openTagList),
},
// 立即执行函数IIFE
(function () {
/**
* 初始时:var SEvents = { _completes: [] };
*
* _complete
*
* success
* fail
* complete
*/
/** 声明 _complete 函数 */
SEvents._complete = function (completeData) {
stateSetting.state = 1;
stateSetting.data = completeData;
};
/** 声明 success 函数 */
SEvents.success = function (e) {
help.isPreVerifyOk = 0;
};
/** 声明 fail 函数 */
SEvents.fail = function (e) {
SEvents._fail ? SEvents._fail(e) : (stateSetting.state = -1);
};
var seventCompletes = SEvents._completes;
seventCompletes.push(function () {
// IIFE 立即执行函数
!(function () {
/**
* &&
* !isMacOrWin 非mac非win浏览器
* !isWxdebugger 不是微信开发者工具
* !vConfig.debug 未开启debug模式
* micromessengerVersion >= '6.0.2' 微信公众号版本号大于等于 6.0.2
* help.systemType >= 0 (苹果或者安卓手机)苹果手机:1;安卓手机:2;默认:-1
*/
if (
!(
isMacOrWin ||
isWxdebugger ||
vConfig.debug ||
micromessengerVersion < "6.0.2" ||
help.systemType < 0
)
) {
// 声明 image
var tempImage = new Image();
// 设置 appid
help.appId = vConfig.appId;
wechatJsEvents.getNetworkType({
isInnerInvoke: !0,
success: function (getNetworkTypeObj) {
help.networkType = getNetworkTypeObj.networkType;
tempImage.src =
"https://open.weixin.qq.com/sdk/report?v=" +
help.version +
"&o=" +
help.isPreVerifyOk +
"&s=" +
help.systemType +
"&c=" +
help.clientVersion +
"&a=" +
help.appId +
"&n=" +
help.networkType +
"&i=" +
help.initTime +
"&p=" +
help.preVerifyTime +
"&u=" +
help.url;
},
});
}
})();
});
// 接口调用完成时执行的回调函数,无论成功或失败都会执行。
SEvents.complete = function () {
for (var index = 0; index < seventCompletes.length; ++index) {
seventCompletes[index](); // 顺序执行 _completes 数组中所有函数
}
SEvents._completes = []; // 执行完毕后,清空 _completes 数组
};
return SEvents;
})()
);
} else {
// 状态设置为完成
stateSetting.state = 1;
var sEventsCompletes = SEvents._completes;
for (n = 0; n < sEventsCompletes.length; ++n) {
// 顺次执行所有 complete 中事件
sEventsCompletes[n]();
}
// 执行完complete之后清空complete数组
SEvents._completes = [];
}
});
// 要对外暴露的接口api对象 有 invoke 属性时不再执行,
// 若无 invoke 属性时添加 invoke 和 on 两个属性
wechatJsEvents.invoke ||
((wechatJsEvents.invoke = function (invokeFunc, invokeN, invokeI) {
entryO.WeixinJSBridge &&
WeixinJSBridge.invoke(invokeFunc, createXObj(invokeN), invokeI);
}),
(wechatJsEvents.on = function (onFunc, onN) {
entryO.WeixinJSBridge && WeixinJSBridge.on(onFunc, onN);
}));
},
getNetworkType: function (e) {
installWeixinJSBridge(
"getNetworkType",
{},
((e._complete = function (e) {
e = (function (e) {
var n = e.errMsg;
e.errMsg = "getNetworkType:ok";
var eSubtype = e.subtype;
if ((delete e.subtype, eSubtype)) e.networkType = eSubtype;
else {
var t = n.indexOf(":");
var colonNextStr = n.substring(t + 1);
switch (colonNextStr) {
case "wifi":
case "edge":
case "wwan":
e.networkType = colonNextStr;
break;
default:
e.errMsg = "getNetworkType:fail";
}
}
return e;
})(e);
}),
e)
);
},
/**
* config信息验证后会执行ready方法,
* 所有接口调用都必须在config接口获得结果之后,
* config是一个客户端的异步操作,
* 所以如果需要在页面加载时就调用相关接口,
* 则须把相关接口放在ready函数中调用来确保正确执行。
* 对于用户触发时才调用的接口,
* 则可以直接调用,
* 不需要放在ready函数中。
* @param {*} readyFunc
*/
ready: function (readyFunc) {
0 != stateSetting.state
? readyFunc() // 状态为完成状态,直接执行
: (SEvents._completes.push(readyFunc),
!isMicromessenger && vConfig.debug && readyFunc());
// 1. 推送到 _completes 数组中
// 2. 非微信公众号 且 开启debug 执行 e
},
error: function (e) {
micromessengerVersion < "6.0.2" ||
(-1 == stateSetting.state
? e(stateSetting.data)
: (SEvents._fail = e));
},
};
var T = 1;
var k = {};
return (
// document.addEventListener
oDocument.addEventListener(
"error",
function (e) {
if (!isAndroid) {
var n = e.target;
var eTargetTagName = n.tagName;
var t = n.src;
if (
"IMG" == eTargetTagName ||
"VIDEO" == eTargetTagName ||
"AUDIO" == eTargetTagName ||
"SOURCE" == eTargetTagName
)
if (-1 != t.indexOf("wxlocalresource://")) {
e.preventDefault(), e.stopPropagation();
var o = n["wx-id"]; // e.target['wx-id']
if ((o || ((o = T++), (n["wx-id"] = o)), k[o])) return;
(k[o] = !0),
wx.ready(function () {
wx.getLocalImgData({
localId: t,
success: function (e) {
n.src = e.localData;
},
});
});
}
}
},
!0
),
oDocument.addEventListener(
"load",
function (e) {
if (!isAndroid) {
var n = e.target,
eTargetTagName = n.tagName; // e.target.tagName
n.src;
if (
"IMG" == eTargetTagName ||
"VIDEO" == eTargetTagName ||
"AUDIO" == eTargetTagName ||
"SOURCE" == eTargetTagName
) {
var t = n["wx-id"]; // e.target['wx-id']
t && (k[t] = !1); // k[e.target['wx-id']] = false
}
}
},
!0 // true
),
entryE && (entryO.wx = entryO.jWeixin = wechatJsEvents),
wechatJsEvents
);
}
/** 挂载部分函数到微信浏览器内置函数 WeixinJSBridge 上 */
function installWeixinJSBridge(
jsbridgeParamName,
jsbridgeObj,
jsbridgeParam
) {
entryO.WeixinJSBridge
? WeixinJSBridge.invoke(
jsbridgeParamName,
createXObj(jsbridgeObj),
function (jsbridgeInvokeObj) {
main(jsbridgeParamName, jsbridgeInvokeObj, jsbridgeParam);
}
)
: emptyConfigParamWhenDebug(jsbridgeParamName, jsbridgeParam);
}
/** 设置一个 createXObj 对象 */
function createXObj(xObj) {
(xObj = xObj || {}).appId = vConfig.appId;
xObj.verifyAppId = vConfig.appId;
xObj.verifySignType = "sha1";
xObj.verifyTimestamp = vConfig.timestamp + "";
xObj.verifyNonceStr = vConfig.nonceStr;
xObj.verifySignature = vConfig.signature;
return xObj;
}
/**
* SEvents
*/
function main(paramName, obj, jsbridgeParam) {
("openEnterpriseChat" != paramName && "openBusinessView" !== paramName) ||
(obj.errCode = obj.err_code),
delete obj.err_code,
delete obj.err_desc,
delete obj.err_detail;
var objErrMsg = obj.errMsg;
/**
* objErrMsg
* - 存在 后面不执行
* - 不存在 后面执行
*/
objErrMsg ||
// #1
// #1-1
((objErrMsg = obj.err_msg),
// #1-2
delete obj.err_msg,
// #1-3
(objErrMsg = (function (paramName, objErrMsgParam) {
var errMsgFuncFirstParam = paramName,
t = createObjectByGlobal[errMsgFuncFirstParam];
t && (errMsgFuncFirstParam = t);
var okParam = "ok";
if (objErrMsgParam) {
var colonIndex = objErrMsgParam.indexOf(":");
"confirm" == (okParam = objErrMsgParam.substring(colonIndex + 1)) &&
(okParam = "ok"),
"failed" == okParam && (okParam = "fail"),
-1 != okParam.indexOf("failed_") &&
(okParam = okParam.substring(7)),
-1 != okParam.indexOf("fail_") && (okParam = okParam.substring(5)),
("access denied" !=
(okParam = (okParam = okParam.replace(
/_/g,
" "
)).toLowerCase()) &&
"no permission to execute" != okParam) ||
(okParam = "permission denied"),
"config" == errMsgFuncFirstParam &&
"function not exist" == okParam &&
(okParam = "ok"),
"" == okParam && (okParam = "fail");
}
return (objErrMsgParam = errMsgFuncFirstParam + ":" + okParam);
})(paramName, objErrMsg)), // 立即执行函数
// #1-4
(obj.errMsg = objErrMsg)), // 设置 errMsg 为 objErrMsg
// #2
(jsbridgeParam = jsbridgeParam || {})._complete &&
(jsbridgeParam._complete(obj), delete jsbridgeParam._complete), // 执行了(1: 设置 state 为 1)
// #3
(objErrMsg = obj.errMsg || ""),
// #4
vConfig.debug &&
!jsbridgeParam.isInnerInvoke &&
alert(JSON.stringify(obj));
var colonIndex = objErrMsg.indexOf(":");
switch (objErrMsg.substring(colonIndex + 1)) {
case "ok":
jsbridgeParam.success && jsbridgeParam.success(obj);
break;
case "cancel":
jsbridgeParam.cancel && jsbridgeParam.cancel(obj);
break;
default:
jsbridgeParam.fail && jsbridgeParam.fail(obj);
}
// ✨✨✨✨✨✨✨✨✨✨ 关键 ✨✨✨✨✨✨✨✨✨✨
jsbridgeParam.complete && jsbridgeParam.complete(obj);
// ✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨✨
}
/**
* 获取列表
* verifyJsApiList: getList(vConfig.jsApiList)
* verifyOpenTagList: getList(vConfig.openTagList)
*/
function getList(list) {
if (list) {
for (var index = 0; index < list.length; ++index) {
var t = list[index];
var o = global[t];
o && (list[index] = o);
}
return list;
}
}
/**
* ("config", (vConfig = e));
*
* function installWeixinJSBridge(n, e, i)
* (n, i)
* if (vConfig.debug && (!obj || !obj.isInnerInvoke))
* debug模式下调用config却未传入参数时触发,设置一个默认的config函数,不执行所有ready函数
*/
function emptyConfigParamWhenDebug(paramKey, obj) {
if (!(!vConfig.debug || (obj && obj.isInnerInvoke))) {
var temp = createObjectByGlobal[paramKey];
temp && (paramKey = temp);
obj && obj._complete && delete obj._complete;
console.log('"' + paramKey + '",', obj || "");
}
}
function micromessengerAddEventListener(e) {
isMicromessenger &&
(entryO.WeixinJSBridge
? e()
: oDocument.addEventListener &&
oDocument.addEventListener("WeixinJSBridgeReady", e, !1)); // !1 (false)
}
});
源码梳理
/**
* 模仿JSSDK框架结构
*
*/
/** 状态设置 */
let stateSetting = { state: 0, data: {} };
/** complete对象 */
let oriComplete = { _completes: [] };
/** 获取 complete 对象 */
function _getComplete() {
/** 声明 _complete 函数 */
oriComplete._complete = function (completeData) {
stateY.state = 1;
stateY.data = completeData;
};
const seventCompletes = oriComplete._completes;
// ✨✨✨ 接口调用完成时执行的回调函数,无论成功或失败都会执行。
oriComplete.complete = function () {
for (var index = 0; index < seventCompletes.length; ++index) {
seventCompletes[index](); // 顺序执行 _completes 数组中所有函数
}
oriComplete._completes = []; // 执行完毕后,清空 _completes 数组
};
return oriComplete;
}
function config() {
const completeData = {};
let complete = _getComplete();
// complete = complete || {} 相当于先判断complete是否为undefined,
// 若为则重新将complete赋值为{},避免后面 a.b 因为 a 为 undefined 报错
const _complete = (complete = complete || {})._complete;
if (_complete) {
complete._complete(completeData);
delete complete._complete;
}
// ✨✨✨ 在这里执行了 complete 事件
complete && complete(completeData);
}
/**
* config信息验证后会执行ready方法,
* 所有接口调用都必须在config接口获得结果之后,
* config是一个客户端的异步操作,
* 所以如果需要在页面加载时就调用相关接口,
* 则须把相关接口放在ready函数中调用来确保正确执行。
* 对于用户触发时才调用的接口,
* 则可以直接调用,
* 不需要放在ready函数中。
* @param {*} readyFunc
*/
function ready(readyFunc) {
// 以下为原始代码,难以理解
// 0 != stateSetting.state
// ? readyFunc() // 状态为完成状态,直接执行
// : (oriComplete._completes.push(readyFunc),
// !isMicromessenger && vConfig.debug && readyFunc());
// 重新写作 if-else 形式
if (stateSetting.state != 0) {
// config注入成功,直接执行 readyFunc
readyFunc();
} else {
// ✨✨✨ config未注入成功,推送入complete._completes数组中,待config执行完毕,再执行数组中的事件
oriComplete._completes.push(readyFunc);
if (!isMicromessenger && vConfig.debug) {
// 非微信内置浏览器 且 开启debug,直接执行
readyFunc();
}
}
}
源码抽离
/** complete对象 */
let oriComplete: Record<string, any> = { _completes: [] as Function[] };
/** 获取 complete 对象 */
function _getComplete() {
const seventCompletes = oriComplete._completes;
// ✨✨✨ 接口调用完成时执行的回调函数,无论成功或失败都会执行。
oriComplete.complete = function () {
for (var index = 0; index < seventCompletes.length; ++index) {
seventCompletes[index](); // 顺序执行 _completes 数组中所有函数
}
oriComplete._completes = []; // 执行完毕后,清空 _completes 数组
};
return oriComplete;
}
function config() {
let complete = _getComplete();
SDK.initialized = true;
// ✨✨✨ 在这里执行了 complete 事件
complete && complete.complete && complete.complete();
}
function readyCallback(func: Function) {
SDK.initialized ? func && func() : oriComplete._completes.push(func);
}
// use
Sdk.readyCallback(() => {
console.log('readyCallback: before config');
});
Sdk.config();