扫一扫优化
This commit is contained in:
4
dist/index.d.ts
vendored
4
dist/index.d.ts
vendored
@@ -22,6 +22,10 @@ interface ScanConfigOptions {
|
||||
* 页面 URL 变化后是否自动重新初始化微信 JSSDK(默认 true,避免扫一扫失效)
|
||||
*/
|
||||
wxJssdkAutoReinitOnUrlChange?: boolean,
|
||||
/**
|
||||
* URL 轮询检测间隔(毫秒),用于捕获未走 history API 的 SPA 路由;默认 1000,设为 0 关闭
|
||||
*/
|
||||
wxJssdkUrlPollInterval?: number,
|
||||
/**
|
||||
* 桥接是否启用,默认启用
|
||||
*/
|
||||
|
||||
2
dist/index.js
vendored
2
dist/index.js
vendored
File diff suppressed because one or more lines are too long
59
src/_core.js
59
src/_core.js
@@ -7,7 +7,7 @@ import {
|
||||
startScan, stopScan, scanImage, scanImageFromFile, clear
|
||||
} from './services/provider/scan';
|
||||
import { setConfig, getVersion } from './services/config';
|
||||
import { initWxJssdk } from './services/wx';
|
||||
import { initWxJssdk, installWxJssdkUrlWatcher, resetWxJssdkState } from './services/wx';
|
||||
import {
|
||||
isSupportWebScan,
|
||||
prepareWebScanBarcodeDetector,
|
||||
@@ -15,6 +15,13 @@ import {
|
||||
} from './services/web';
|
||||
import { printDebug, printWarn } from './utils/logger';
|
||||
import { requestPayment, closePaymentDialog } from './services/payment';
|
||||
import {
|
||||
getSdkRuntimeUrl,
|
||||
setSdkRuntimeUrlBaseline,
|
||||
isSdkRuntimeUrlChanged,
|
||||
setSdkRuntimeUrlChangeHandler,
|
||||
bindSdkConfigReadyPromise,
|
||||
} from './services/sdkLifecycle';
|
||||
|
||||
let _readyPromise = null;
|
||||
let _calledReady = false;
|
||||
@@ -23,20 +30,26 @@ export function isReadyCalled() {
|
||||
return _calledReady;
|
||||
}
|
||||
|
||||
function config(config) {
|
||||
if (config) {
|
||||
setConfig(config);
|
||||
}
|
||||
if (_readyPromise) {
|
||||
return _readyPromise;
|
||||
}
|
||||
function invalidateSdkReadyState() {
|
||||
_readyPromise = null;
|
||||
_calledReady = false;
|
||||
setSdkRuntimeUrlBaseline("");
|
||||
bindSdkConfigReadyPromise(null);
|
||||
resetWxJssdkState();
|
||||
}
|
||||
|
||||
function runSdkConfigInit() {
|
||||
const urlAtStart = getSdkRuntimeUrl();
|
||||
setSdkRuntimeUrlBaseline(urlAtStart);
|
||||
_readyPromise = Promise.resolve().then(() => {
|
||||
initWxJssdk().catch(err => {
|
||||
installWxJssdkUrlWatcher();
|
||||
return initWxJssdk().catch(err => {
|
||||
printDebug('init wx jssdk failed:', err && err.message ? err.message : err);
|
||||
});
|
||||
}).then(() => {
|
||||
printDebug('-------------------------------------');
|
||||
printDebug('sdk_version:', getVersion());
|
||||
printDebug('sdk_runtime_url:', getSdkRuntimeUrl());
|
||||
printDebug('support_list:', supportList.map(item => item.name + ':' + item.support).join(', '));
|
||||
printDebug('-------------------------------------');
|
||||
if (isSupportWebScan() || isSupportImageScan()) {
|
||||
@@ -46,13 +59,39 @@ function config(config) {
|
||||
}
|
||||
}).then(() => {
|
||||
_calledReady = true;
|
||||
setSdkRuntimeUrlBaseline(getSdkRuntimeUrl());
|
||||
}).catch(err => {
|
||||
_readyPromise = null;
|
||||
invalidateSdkReadyState();
|
||||
throw err;
|
||||
});
|
||||
bindSdkConfigReadyPromise(_readyPromise);
|
||||
return _readyPromise;
|
||||
}
|
||||
|
||||
function config(config) {
|
||||
if (config) {
|
||||
setConfig(config);
|
||||
}
|
||||
if (isSdkRuntimeUrlChanged()) {
|
||||
invalidateSdkReadyState();
|
||||
}
|
||||
if (_readyPromise) {
|
||||
return _readyPromise;
|
||||
}
|
||||
return runSdkConfigInit();
|
||||
}
|
||||
|
||||
setSdkRuntimeUrlChangeHandler(() => {
|
||||
if (!isSdkRuntimeUrlChanged()) {
|
||||
return;
|
||||
}
|
||||
printDebug('sdk runtime url changed, reinitializing config:', getSdkRuntimeUrl());
|
||||
invalidateSdkReadyState();
|
||||
runSdkConfigInit().catch((err) => {
|
||||
printDebug('sdk reconfig failed:', err && err.message ? err.message : err);
|
||||
});
|
||||
});
|
||||
|
||||
export default Object.assign({}, {
|
||||
config,
|
||||
onScanListener,
|
||||
|
||||
@@ -18,6 +18,7 @@ import { startScanner, stopScanner } from "../scanner";
|
||||
import { getConfig } from "../config";
|
||||
import { toAny } from "../../utils/toany";
|
||||
import { printDebug, printWarn } from "../../utils/logger";
|
||||
import { waitForSdkConfigReady } from "../sdkLifecycle";
|
||||
import { forwardEmbedScanResultIfNeeded, forwardEmbedScanErrorIfNeeded } from "../embedScanBridge";
|
||||
|
||||
let _scan_status = "ready";
|
||||
@@ -81,6 +82,9 @@ function __fallbackScanAfterWxFailure(err) {
|
||||
}
|
||||
|
||||
function __startNonBridgeScan(err) {
|
||||
if (isWxEnv()) {
|
||||
return __startWxScan();
|
||||
}
|
||||
if (isSupportWebScan()) {
|
||||
return __startWebScan();
|
||||
}
|
||||
@@ -744,17 +748,17 @@ export function startScan(key) {
|
||||
return;
|
||||
}
|
||||
unlockScanBeep();
|
||||
Promise.resolve().then(() => {
|
||||
Promise.resolve().then(() => waitForSdkConfigReady()).then(() => {
|
||||
__beginScanSession(key);
|
||||
__scanning();
|
||||
let scannerPromise = new Promise(resolve => {
|
||||
_scan_resolve = resolve;
|
||||
});
|
||||
let scanPromise = Promise.resolve();
|
||||
if (inRuntime()) {
|
||||
if (isWxEnv()) {
|
||||
scanPromise = __startWxScan().catch(__fallbackScanAfterWxFailure);
|
||||
} else if (inRuntime()) {
|
||||
scanPromise = __startBridgeScan().catch(__fallbackScanAfterBridgeFailure);
|
||||
} else if (isSupportWxScan()) {
|
||||
scanPromise = __startWxScan();
|
||||
} else if (isSupportWebScan()) {
|
||||
scanPromise = __startWebScan(true);
|
||||
} else if (isSupportImageScan()) {
|
||||
|
||||
59
src/services/sdkLifecycle.js
Normal file
59
src/services/sdkLifecycle.js
Normal file
@@ -0,0 +1,59 @@
|
||||
import { getConfig } from "./config";
|
||||
import { resolveUseParentProxy } from "./embedProxy";
|
||||
|
||||
let _runtimeUrlBaseline = "";
|
||||
let _runtimeUrlChangeHandler = null;
|
||||
let _configReadyPromise = null;
|
||||
|
||||
export function bindSdkConfigReadyPromise(promise) {
|
||||
_configReadyPromise = promise || null;
|
||||
}
|
||||
|
||||
export function waitForSdkConfigReady() {
|
||||
if (_configReadyPromise) {
|
||||
return _configReadyPromise;
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
* SDK 运行时 URL(微信 JSSDK 签名、config 缓存均以此为准,不含 hash)
|
||||
*/
|
||||
export function getSdkRuntimeUrl() {
|
||||
const override = getConfig("wxJssdkSignatureUrl");
|
||||
if (override) {
|
||||
return String(override).split("#")[0];
|
||||
}
|
||||
if (resolveUseParentProxy()) {
|
||||
try {
|
||||
if (typeof window !== "undefined" && window.parent && window.parent !== window) {
|
||||
return window.parent.location.href.split("#")[0];
|
||||
}
|
||||
} catch (e) {
|
||||
// 跨域父页不可读 location
|
||||
}
|
||||
}
|
||||
if (typeof window === "undefined" || !window.location) {
|
||||
return "";
|
||||
}
|
||||
return window.location.href.split("#")[0];
|
||||
}
|
||||
|
||||
export function setSdkRuntimeUrlBaseline(url) {
|
||||
_runtimeUrlBaseline = url ? String(url) : "";
|
||||
}
|
||||
|
||||
export function isSdkRuntimeUrlChanged() {
|
||||
const current = getSdkRuntimeUrl();
|
||||
return !!(_runtimeUrlBaseline && current && _runtimeUrlBaseline !== current);
|
||||
}
|
||||
|
||||
export function setSdkRuntimeUrlChangeHandler(handler) {
|
||||
_runtimeUrlChangeHandler = typeof handler === "function" ? handler : null;
|
||||
}
|
||||
|
||||
export function notifySdkRuntimeUrlChanged() {
|
||||
if (typeof _runtimeUrlChangeHandler === "function") {
|
||||
_runtimeUrlChangeHandler();
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,11 @@ import { resolveUseParentProxy } from "../embedProxy";
|
||||
import { request } from "../../utils/request";
|
||||
import { toAny } from "../../utils/toany";
|
||||
import { printDebug } from "../../utils/logger";
|
||||
import {
|
||||
getSdkRuntimeUrl,
|
||||
isSdkRuntimeUrlChanged,
|
||||
notifySdkRuntimeUrlChanged,
|
||||
} from "../sdkLifecycle";
|
||||
|
||||
const WX_JS_SDK_URL = "https://res.wx.qq.com/open/js/jweixin-1.6.0.js";
|
||||
const WX_SCAN_API = "scanQRCode";
|
||||
@@ -11,7 +16,11 @@ const WX_SCAN_API = "scanQRCode";
|
||||
let _wxReadyPromise = null;
|
||||
let _wxReady = false;
|
||||
let _wxSignatureUrl = "";
|
||||
let _wxPendingSignatureUrl = "";
|
||||
let _wxInitGeneration = 0;
|
||||
let _urlWatchInstalled = false;
|
||||
let _urlPollTimer = null;
|
||||
let _urlReinitTimer = null;
|
||||
|
||||
function getWx() {
|
||||
if (typeof wx !== "undefined") {
|
||||
@@ -51,25 +60,9 @@ function loadWxScript() {
|
||||
});
|
||||
}
|
||||
|
||||
/** 微信 JS-SDK 签名 URL:嵌入且走父页代理时用父页地址(与微信内打开的页面一致);可配置 wxJssdkSignatureUrl 覆盖 */
|
||||
/** 微信 JS-SDK 签名 URL:与 SDK 运行时 URL 一致 */
|
||||
function getPageUrlForWxJssdkSignature() {
|
||||
const override = getConfig("wxJssdkSignatureUrl");
|
||||
if (override) {
|
||||
return String(override).split("#")[0];
|
||||
}
|
||||
if (resolveUseParentProxy()) {
|
||||
try {
|
||||
if (typeof window !== "undefined" && window.parent && window.parent !== window) {
|
||||
return window.parent.location.href.split("#")[0];
|
||||
}
|
||||
} catch (e) {
|
||||
// 跨域父页不可读 location,由调用方配置 wxJssdkSignatureUrl
|
||||
}
|
||||
}
|
||||
if (typeof window === "undefined") {
|
||||
return "";
|
||||
}
|
||||
return window.location.href.split("#")[0];
|
||||
return getSdkRuntimeUrl();
|
||||
}
|
||||
|
||||
function isWxJssdkAutoReinitEnabled() {
|
||||
@@ -96,11 +89,82 @@ function getWxJssdkWatchWindow() {
|
||||
function resetWxJssdkState() {
|
||||
_wxReady = false;
|
||||
_wxReadyPromise = null;
|
||||
_wxPendingSignatureUrl = "";
|
||||
_wxInitGeneration += 1;
|
||||
}
|
||||
|
||||
export { resetWxJssdkState };
|
||||
|
||||
function isWxSignatureUrlChanged() {
|
||||
const current = getPageUrlForWxJssdkSignature();
|
||||
return !!(_wxSignatureUrl && current && _wxSignatureUrl !== current);
|
||||
const baseline = _wxSignatureUrl || _wxPendingSignatureUrl;
|
||||
return !!(baseline && current && baseline !== current);
|
||||
}
|
||||
|
||||
function getWxJssdkUrlPollInterval() {
|
||||
const interval = getConfig("wxJssdkUrlPollInterval");
|
||||
if (interval === 0 || interval === false) {
|
||||
return 0;
|
||||
}
|
||||
if (typeof interval === "number" && interval > 0) {
|
||||
return interval;
|
||||
}
|
||||
return 1000;
|
||||
}
|
||||
|
||||
function scheduleWxJssdkReinitIfNeeded() {
|
||||
if (!isWxEnv() || !isWxJssdkAutoReinitEnabled()) {
|
||||
return;
|
||||
}
|
||||
if (!isSdkRuntimeUrlChanged() && !isWxSignatureUrlChanged()) {
|
||||
return;
|
||||
}
|
||||
if (_urlReinitTimer) {
|
||||
return;
|
||||
}
|
||||
_urlReinitTimer = setTimeout(() => {
|
||||
_urlReinitTimer = null;
|
||||
if (!isSdkRuntimeUrlChanged() && !isWxSignatureUrlChanged()) {
|
||||
return;
|
||||
}
|
||||
printDebug("sdk runtime url changed, notify reconfig:", getSdkRuntimeUrl());
|
||||
notifySdkRuntimeUrlChanged();
|
||||
}, 50);
|
||||
}
|
||||
|
||||
function stopWxJssdkUrlPoll() {
|
||||
if (_urlPollTimer) {
|
||||
clearInterval(_urlPollTimer);
|
||||
_urlPollTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
function startWxJssdkUrlPoll() {
|
||||
const interval = getWxJssdkUrlPollInterval();
|
||||
if (!interval || _urlPollTimer) {
|
||||
return;
|
||||
}
|
||||
_urlPollTimer = setInterval(() => {
|
||||
scheduleWxJssdkReinitIfNeeded();
|
||||
}, interval);
|
||||
}
|
||||
|
||||
export function installWxJssdkUrlWatcher() {
|
||||
if (!isWxJssdkAutoReinitEnabled()) {
|
||||
return;
|
||||
}
|
||||
const watchWin = getWxJssdkWatchWindow();
|
||||
if (!watchWin || !watchWin.addEventListener) {
|
||||
return;
|
||||
}
|
||||
if (!_urlWatchInstalled) {
|
||||
_urlWatchInstalled = true;
|
||||
watchWin.addEventListener("popstate", scheduleWxJssdkReinitIfNeeded);
|
||||
watchWin.addEventListener("hashchange", scheduleWxJssdkReinitIfNeeded);
|
||||
patchHistoryMethod(watchWin.history, "pushState", scheduleWxJssdkReinitIfNeeded);
|
||||
patchHistoryMethod(watchWin.history, "replaceState", scheduleWxJssdkReinitIfNeeded);
|
||||
}
|
||||
startWxJssdkUrlPoll();
|
||||
}
|
||||
|
||||
function patchHistoryMethod(history, methodName, callback) {
|
||||
@@ -120,36 +184,6 @@ function patchHistoryMethod(history, methodName, callback) {
|
||||
history[methodName] = patched;
|
||||
}
|
||||
|
||||
function installWxJssdkUrlWatcher() {
|
||||
if (_urlWatchInstalled || !isWxJssdkAutoReinitEnabled()) {
|
||||
return;
|
||||
}
|
||||
const watchWin = getWxJssdkWatchWindow();
|
||||
if (!watchWin || !watchWin.addEventListener) {
|
||||
return;
|
||||
}
|
||||
_urlWatchInstalled = true;
|
||||
|
||||
const scheduleReinit = () => {
|
||||
if (!isWxEnv() || !isWxJssdkAutoReinitEnabled()) {
|
||||
return;
|
||||
}
|
||||
if (!isWxSignatureUrlChanged()) {
|
||||
return;
|
||||
}
|
||||
printDebug("wx jssdk url changed, reinitializing:", getPageUrlForWxJssdkSignature());
|
||||
resetWxJssdkState();
|
||||
initWxJssdk().catch((err) => {
|
||||
printDebug("wx jssdk reinit failed:", err && err.message ? err.message : err);
|
||||
});
|
||||
};
|
||||
|
||||
watchWin.addEventListener("popstate", scheduleReinit);
|
||||
watchWin.addEventListener("hashchange", scheduleReinit);
|
||||
patchHistoryMethod(watchWin.history, "pushState", scheduleReinit);
|
||||
patchHistoryMethod(watchWin.history, "replaceState", scheduleReinit);
|
||||
}
|
||||
|
||||
function fetchWxConfig() {
|
||||
let initWechatJssdk = toAny(getConfig("initWechatJssdk"), {});
|
||||
if (!!initWechatJssdk.sdkConfig) {
|
||||
@@ -227,6 +261,9 @@ export function initWxJssdk() {
|
||||
if (_wxReadyPromise) {
|
||||
return _wxReadyPromise;
|
||||
}
|
||||
const generation = _wxInitGeneration;
|
||||
const signatureUrlAtStart = getPageUrlForWxJssdkSignature();
|
||||
_wxPendingSignatureUrl = signatureUrlAtStart;
|
||||
_wxReadyPromise = Promise.all([loadWxScript(), fetchWxConfig()]).then(items => {
|
||||
const wx = items[0];
|
||||
const config = items[1];
|
||||
@@ -242,13 +279,27 @@ export function initWxJssdk() {
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
wx.ready(() => {
|
||||
if (generation !== _wxInitGeneration) {
|
||||
return;
|
||||
}
|
||||
const currentUrl = getPageUrlForWxJssdkSignature();
|
||||
if (signatureUrlAtStart && currentUrl && signatureUrlAtStart !== currentUrl) {
|
||||
resetWxJssdkState();
|
||||
initWxJssdk().then(resolve).catch(reject);
|
||||
return;
|
||||
}
|
||||
_wxReady = true;
|
||||
_wxSignatureUrl = getPageUrlForWxJssdkSignature();
|
||||
_wxSignatureUrl = currentUrl;
|
||||
_wxPendingSignatureUrl = "";
|
||||
resolve(wx);
|
||||
});
|
||||
wx.error(err => {
|
||||
if (generation !== _wxInitGeneration) {
|
||||
return;
|
||||
}
|
||||
_wxReady = false;
|
||||
_wxSignatureUrl = "";
|
||||
_wxPendingSignatureUrl = "";
|
||||
_wxReadyPromise = null;
|
||||
reject(err);
|
||||
});
|
||||
@@ -257,9 +308,12 @@ export function initWxJssdk() {
|
||||
}));
|
||||
});
|
||||
}).catch(err => {
|
||||
if (generation === _wxInitGeneration) {
|
||||
_wxReady = false;
|
||||
_wxSignatureUrl = "";
|
||||
_wxPendingSignatureUrl = "";
|
||||
_wxReadyPromise = null;
|
||||
}
|
||||
throw err;
|
||||
});
|
||||
return _wxReadyPromise;
|
||||
|
||||
4
types/index.d.ts
vendored
4
types/index.d.ts
vendored
@@ -22,6 +22,10 @@ interface ScanConfigOptions {
|
||||
* 页面 URL 变化后是否自动重新初始化微信 JSSDK(默认 true,避免扫一扫失效)
|
||||
*/
|
||||
wxJssdkAutoReinitOnUrlChange?: boolean,
|
||||
/**
|
||||
* URL 轮询检测间隔(毫秒),用于捕获未走 history API 的 SPA 路由;默认 1000,设为 0 关闭
|
||||
*/
|
||||
wxJssdkUrlPollInterval?: number,
|
||||
/**
|
||||
* 桥接是否启用,默认启用
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user