优化iframe内使用的优化
This commit is contained in:
2
dist/index.d.ts
vendored
2
dist/index.d.ts
vendored
@@ -8,7 +8,7 @@ interface ScanConfigOptions {
|
|||||||
scanRestartDelay?: number,
|
scanRestartDelay?: number,
|
||||||
/**
|
/**
|
||||||
* iframe 场景下是否将 API 调用转发到父页面同名 SDK(postMessage)。
|
* iframe 场景下是否将 API 调用转发到父页面同名 SDK(postMessage)。
|
||||||
* - `'auto'`(默认):处于子 frame(`parent !== window`)即转发,对外 API(含 `startScan`)均由父页 SDK 执行
|
* - `'auto'`(默认):处于子 frame 时 `startScan` 等由父页执行;`onScanListener` 仅注册在 iframe 内,识别结果由父页 `postMessage` 回传
|
||||||
* - `true` / `'on'` / `'parent'`:存在父 window 时强制转发
|
* - `true` / `'on'` / `'parent'`:存在父 window 时强制转发
|
||||||
* - `false` / `'off'` / `'local'`:始终在本页执行(子页自己要跑扫码时用)
|
* - `false` / `'off'` / `'local'`:始终在本页执行(子页自己要跑扫码时用)
|
||||||
*/
|
*/
|
||||||
|
|||||||
2
dist/index.js
vendored
2
dist/index.js
vendored
File diff suppressed because one or more lines are too long
155
src/_export.js
155
src/_export.js
@@ -10,37 +10,23 @@ import {
|
|||||||
resolveUseParentProxy,
|
resolveUseParentProxy,
|
||||||
} from "./services/embedProxy";
|
} from "./services/embedProxy";
|
||||||
import { createUUID } from "./utils/uuid";
|
import { createUUID } from "./utils/uuid";
|
||||||
import { dispatchHardwareScanResult } from "./services/provider/scan";
|
import {
|
||||||
import { startScanner, stopScanner } from "./services/scanner";
|
dispatchEmbedScanResult,
|
||||||
|
acknowledgeEmbedScanConsumed,
|
||||||
|
} from "./services/provider/scan";
|
||||||
|
import { setEmbedScanResultForwarder } from "./services/embedScanBridge";
|
||||||
|
|
||||||
const EMBED_SOURCE = "IScanEmbed";
|
const EMBED_SOURCE = "IScanEmbed";
|
||||||
const EMBED_V = 1;
|
const EMBED_V = 1;
|
||||||
|
|
||||||
/** 嵌入子页已注册的监听 key(与父页监听对应,用于 refcount 决定是否在本页挂载扫码枪按键监听) */
|
/** 已向父页发起过 invoke 的嵌入子 frame(用于父页识别结果回传) */
|
||||||
const childEmbedScanGunKeys = new Set();
|
const embedChildSources = new Set();
|
||||||
/** offScanListener(fn) 时反查对应的 key */
|
|
||||||
const embedScanGunWeakListenerKey = new WeakMap();
|
|
||||||
|
|
||||||
function syncEmbedScanGunForwardFromChild() {
|
const EMBED_LISTENER_METHODS = new Set([
|
||||||
if (typeof window === "undefined" || !resolveUseParentProxy()) {
|
"onScanListener",
|
||||||
return;
|
"offScanListener",
|
||||||
}
|
"clear",
|
||||||
if (childEmbedScanGunKeys.size === 0) {
|
]);
|
||||||
stopScanner();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
startScanner((result) => {
|
|
||||||
window.parent.postMessage(
|
|
||||||
{
|
|
||||||
source: EMBED_SOURCE,
|
|
||||||
v: EMBED_V,
|
|
||||||
kind: "forwardScanGun",
|
|
||||||
result,
|
|
||||||
},
|
|
||||||
"*"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function isEmbedMessage(data) {
|
function isEmbedMessage(data) {
|
||||||
return data && data.source === EMBED_SOURCE && data.v === EMBED_V;
|
return data && data.source === EMBED_SOURCE && data.v === EMBED_V;
|
||||||
@@ -119,36 +105,24 @@ function hydrateEmbedParams(params, messageSource, targetOrigin) {
|
|||||||
return params.map(walk);
|
return params.map(walk);
|
||||||
}
|
}
|
||||||
|
|
||||||
function serializeEmbedInvokeResult(methodKey, result) {
|
function broadcastScanResultToEmbedChildren(result) {
|
||||||
if (
|
if (embedChildSources.size === 0 || result == null || result === "") {
|
||||||
methodKey === "onScanListener" &&
|
return;
|
||||||
result &&
|
|
||||||
typeof result === "object" &&
|
|
||||||
typeof result.key === "string"
|
|
||||||
) {
|
|
||||||
return { __IScanEmbedListenerRef__: true, key: result.key };
|
|
||||||
}
|
}
|
||||||
return result;
|
embedChildSources.forEach((source) => {
|
||||||
}
|
try {
|
||||||
|
source.postMessage(
|
||||||
function deserializeEmbedInvokeResult(methodKey, raw) {
|
{
|
||||||
if (
|
source: EMBED_SOURCE,
|
||||||
methodKey === "onScanListener" &&
|
v: EMBED_V,
|
||||||
raw &&
|
kind: "forwardScanResult",
|
||||||
raw.__IScanEmbedListenerRef__ &&
|
result,
|
||||||
typeof raw.key === "string"
|
},
|
||||||
) {
|
"*"
|
||||||
const key = raw.key;
|
);
|
||||||
return {
|
} catch (e) {
|
||||||
key,
|
}
|
||||||
cancel: () => {
|
});
|
||||||
childEmbedScanGunKeys.delete(key);
|
|
||||||
syncEmbedScanGunForwardFromChild();
|
|
||||||
embedInvoke("offScanListener", [key]).catch(() => {});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return raw;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let embedWxProbeScheduled = false;
|
let embedWxProbeScheduled = false;
|
||||||
@@ -207,6 +181,23 @@ function embedChildOnMessage(ev) {
|
|||||||
setParentWxEnvReport(!!data.wx);
|
setParentWxEnvReport(!!data.wx);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (data.kind === "forwardScanResult") {
|
||||||
|
if (typeof data.result === "string") {
|
||||||
|
const consumed = dispatchEmbedScanResult(data.result);
|
||||||
|
if (consumed && resolveUseParentProxy()) {
|
||||||
|
window.parent.postMessage(
|
||||||
|
{
|
||||||
|
source: EMBED_SOURCE,
|
||||||
|
v: EMBED_V,
|
||||||
|
kind: "scanResultConsumed",
|
||||||
|
result: data.result,
|
||||||
|
},
|
||||||
|
"*"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (data.kind === "invokeResult") {
|
if (data.kind === "invokeResult") {
|
||||||
const pending = pendingInvokes[data.id];
|
const pending = pendingInvokes[data.id];
|
||||||
if (!pending) {
|
if (!pending) {
|
||||||
@@ -214,7 +205,7 @@ function embedChildOnMessage(ev) {
|
|||||||
}
|
}
|
||||||
delete pendingInvokes[data.id];
|
delete pendingInvokes[data.id];
|
||||||
if (data.ok) {
|
if (data.ok) {
|
||||||
pending.resolve(deserializeEmbedInvokeResult(data.methodKey, data.result));
|
pending.resolve(data.result);
|
||||||
} else {
|
} else {
|
||||||
pending.reject(new Error(data.error || "[IScan embed]: invoke failed"));
|
pending.reject(new Error(data.error || "[IScan embed]: invoke failed"));
|
||||||
}
|
}
|
||||||
@@ -277,7 +268,6 @@ function handleEmbedHostInvoke(lib, ev) {
|
|||||||
return out;
|
return out;
|
||||||
})
|
})
|
||||||
.then((result) => {
|
.then((result) => {
|
||||||
const serializedResult = serializeEmbedInvokeResult(methodKey, result);
|
|
||||||
ev.source.postMessage(
|
ev.source.postMessage(
|
||||||
{
|
{
|
||||||
source: EMBED_SOURCE,
|
source: EMBED_SOURCE,
|
||||||
@@ -286,7 +276,7 @@ function handleEmbedHostInvoke(lib, ev) {
|
|||||||
id,
|
id,
|
||||||
methodKey,
|
methodKey,
|
||||||
ok: true,
|
ok: true,
|
||||||
result: serializedResult,
|
result,
|
||||||
},
|
},
|
||||||
ev.origin
|
ev.origin
|
||||||
);
|
);
|
||||||
@@ -316,6 +306,7 @@ export function installEmbedHost(lib) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
embedHostInstalled = true;
|
embedHostInstalled = true;
|
||||||
|
setEmbedScanResultForwarder(broadcastScanResultToEmbedChildren);
|
||||||
window.addEventListener("message", (ev) => {
|
window.addEventListener("message", (ev) => {
|
||||||
const data = ev.data;
|
const data = ev.data;
|
||||||
if (!isEmbedMessage(data)) {
|
if (!isEmbedMessage(data)) {
|
||||||
@@ -338,15 +329,13 @@ export function installEmbedHost(lib) {
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/** 嵌入子页焦点下扫码枪的 keydown:子页转成字符串后透出,再走主页面监听与嵌入回调链路 */
|
if (data.kind === "scanResultConsumed") {
|
||||||
if (data.kind === "forwardScanGun") {
|
|
||||||
if (!ev.source || ev.source === window) {
|
if (!ev.source || ev.source === window) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (typeof data.result !== "string") {
|
if (typeof data.result === "string") {
|
||||||
return;
|
acknowledgeEmbedScanConsumed(data.result);
|
||||||
}
|
}
|
||||||
dispatchHardwareScanResult(data.result);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (data.kind !== "invoke") {
|
if (data.kind !== "invoke") {
|
||||||
@@ -358,6 +347,7 @@ export function installEmbedHost(lib) {
|
|||||||
if (!ev.source || ev.source === window) {
|
if (!ev.source || ev.source === window) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
embedChildSources.add(ev.source);
|
||||||
handleEmbedHostInvoke(lib, ev);
|
handleEmbedHostInvoke(lib, ev);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -420,42 +410,11 @@ function freezeObj(obj) {
|
|||||||
function createInvokeTransport(lib, method, methodName, initNames) {
|
function createInvokeTransport(lib, method, methodName, initNames) {
|
||||||
return function IScanInvokeProxy(...params) {
|
return function IScanInvokeProxy(...params) {
|
||||||
if (resolveUseParentProxy()) {
|
if (resolveUseParentProxy()) {
|
||||||
if (methodName === "onScanListener") {
|
if (EMBED_LISTENER_METHODS.has(methodName)) {
|
||||||
const listener = params[0];
|
if (!isReadyCalled() && initNames && initNames.indexOf(method) < 0) {
|
||||||
const key = params[1];
|
throw `[IScan]:Can't call the "IScan.${method}" method, because "IScan" not ready, please confirm that "IScan.ready()" has been called. params: ${JSON.stringify(params)}`
|
||||||
if (!key || typeof key !== "string" || typeof listener !== "function") {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
embedScanGunWeakListenerKey.set(listener, key);
|
return _exec(lib, methodName, ...params);
|
||||||
childEmbedScanGunKeys.add(key);
|
|
||||||
syncEmbedScanGunForwardFromChild();
|
|
||||||
embedInvoke(methodName, params).catch(() => {});
|
|
||||||
return {
|
|
||||||
key,
|
|
||||||
cancel: () => {
|
|
||||||
childEmbedScanGunKeys.delete(key);
|
|
||||||
syncEmbedScanGunForwardFromChild();
|
|
||||||
embedInvoke("offScanListener", [key]).catch(() => {});
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (methodName === "offScanListener") {
|
|
||||||
const p0 = params[0];
|
|
||||||
if (typeof p0 === "string") {
|
|
||||||
childEmbedScanGunKeys.delete(p0);
|
|
||||||
} else if (typeof p0 === "function") {
|
|
||||||
const rk = embedScanGunWeakListenerKey.get(p0);
|
|
||||||
if (rk != null && rk !== "") {
|
|
||||||
childEmbedScanGunKeys.delete(rk);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
syncEmbedScanGunForwardFromChild();
|
|
||||||
return embedInvoke(methodName, params);
|
|
||||||
}
|
|
||||||
if (methodName === "clear") {
|
|
||||||
childEmbedScanGunKeys.clear();
|
|
||||||
syncEmbedScanGunForwardFromChild();
|
|
||||||
return embedInvoke(methodName, params);
|
|
||||||
}
|
}
|
||||||
return embedInvoke(methodName, params);
|
return embedInvoke(methodName, params);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ let _defineConfig = {
|
|||||||
let _defConfig = {
|
let _defConfig = {
|
||||||
/**
|
/**
|
||||||
* iframe / 嵌入场景下是否把调用转发到父页面的同名 SDK(postMessage)。
|
* iframe / 嵌入场景下是否把调用转发到父页面的同名 SDK(postMessage)。
|
||||||
* - 'auto'(默认):只要处于子 frame(parent !== window)即转发,含 startScan 等均走父页逻辑
|
* - 'auto'(默认):处于子 frame 时 startScan 等走父页;onScanListener 仅注册在 iframe 内,识别结果由父页回传
|
||||||
* - true | 'on' | 'parent':在存在父 window 时强制转发
|
* - true | 'on' | 'parent':在存在父 window 时强制转发
|
||||||
* - false | 'off' | 'local':始终在本页执行(子页自己要跑扫码时用)
|
* - false | 'off' | 'local':始终在本页执行(子页自己要跑扫码时用)
|
||||||
*/
|
*/
|
||||||
|
|||||||
12
src/services/embedScanBridge.js
Normal file
12
src/services/embedScanBridge.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/** 父页识别到扫码结果时,向嵌入 iframe 转发的回调(由 installEmbedHost 注册) */
|
||||||
|
let embedScanResultForwarder = null;
|
||||||
|
|
||||||
|
export function setEmbedScanResultForwarder(fn) {
|
||||||
|
embedScanResultForwarder = typeof fn === "function" ? fn : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function forwardEmbedScanResultIfNeeded(result) {
|
||||||
|
if (embedScanResultForwarder && result != null && result !== "") {
|
||||||
|
embedScanResultForwarder(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import { startScanner, stopScanner } from "../scanner";
|
|||||||
import { getConfig } from "../config";
|
import { getConfig } from "../config";
|
||||||
import { toAny } from "../../utils/toany";
|
import { toAny } from "../../utils/toany";
|
||||||
import { printDebug } from "../../utils/logger";
|
import { printDebug } from "../../utils/logger";
|
||||||
|
import { forwardEmbedScanResultIfNeeded } from "../embedScanBridge";
|
||||||
|
|
||||||
let _scan_status = "ready";
|
let _scan_status = "ready";
|
||||||
let _scan_status_listener = null;
|
let _scan_status_listener = null;
|
||||||
@@ -93,6 +94,7 @@ function __match(result, match) {
|
|||||||
|
|
||||||
function __result(result) {
|
function __result(result) {
|
||||||
result = parseBarcodeString(result);
|
result = parseBarcodeString(result);
|
||||||
|
forwardEmbedScanResultIfNeeded(result);
|
||||||
let matched = false;
|
let matched = false;
|
||||||
for (let i = 0; i < _scan_listener_list.length; i++) {
|
for (let i = 0; i < _scan_listener_list.length; i++) {
|
||||||
const item = _scan_listener_list[i];
|
const item = _scan_listener_list[i];
|
||||||
@@ -157,18 +159,38 @@ function __stopCurrentScan() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 由嵌入子页转发过来的扫码枪按键结果,与 startScanner 走同一匹配与回调链路
|
* 父页通过 postMessage 将识别结果投递到嵌入 iframe 时调用(与本地扫码枪/监听同一链路)。
|
||||||
*(仅在父页的 installEmbedHost 处理 forwardScanGun 时调用)。
|
* @returns {boolean} 是否有监听消费了该结果
|
||||||
*/
|
*/
|
||||||
export function dispatchHardwareScanResult(raw) {
|
export function dispatchEmbedScanResult(raw) {
|
||||||
const result =
|
const result =
|
||||||
typeof raw === "string" ? parseBarcodeString(raw) : raw;
|
typeof raw === "string" ? parseBarcodeString(raw) : raw;
|
||||||
__scannerResult(result);
|
return __scannerResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 嵌入 iframe 已消费扫码结果时通知父页结束当前识别(关闭摄像头/UI 等)。
|
||||||
|
*/
|
||||||
|
export function acknowledgeEmbedScanConsumed(raw) {
|
||||||
|
const result =
|
||||||
|
typeof raw === "string" ? parseBarcodeString(raw) : raw;
|
||||||
|
if (!isScanning()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const resolve = __finishScan();
|
||||||
|
_scan_closing = true;
|
||||||
|
_scan_next_start_time = Date.now() + getScanRestartDelay();
|
||||||
|
__stopCurrentScan().then(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
_scan_closing = false;
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
|
resolve && resolve({ result });
|
||||||
}
|
}
|
||||||
|
|
||||||
function __scannerResult(result) {
|
function __scannerResult(result) {
|
||||||
if (!__hasMatchedListener(result)) {
|
if (!__hasMatchedListener(result)) {
|
||||||
return;
|
return __result(result);
|
||||||
}
|
}
|
||||||
if (isScanning()) {
|
if (isScanning()) {
|
||||||
const resolve = __finishScan();
|
const resolve = __finishScan();
|
||||||
@@ -178,13 +200,13 @@ function __scannerResult(result) {
|
|||||||
_scan_closing = false;
|
_scan_closing = false;
|
||||||
}, 0);
|
}, 0);
|
||||||
});
|
});
|
||||||
__result(result);
|
const matched = __result(result);
|
||||||
resolve && resolve({
|
resolve && resolve({
|
||||||
result
|
result
|
||||||
});
|
});
|
||||||
return;
|
return matched;
|
||||||
}
|
}
|
||||||
__result(result);
|
return __result(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
function __startBridgeScan() {
|
function __startBridgeScan() {
|
||||||
|
|||||||
2
types/index.d.ts
vendored
2
types/index.d.ts
vendored
@@ -8,7 +8,7 @@ interface ScanConfigOptions {
|
|||||||
scanRestartDelay?: number,
|
scanRestartDelay?: number,
|
||||||
/**
|
/**
|
||||||
* iframe 场景下是否将 API 调用转发到父页面同名 SDK(postMessage)。
|
* iframe 场景下是否将 API 调用转发到父页面同名 SDK(postMessage)。
|
||||||
* - `'auto'`(默认):处于子 frame(`parent !== window`)即转发,对外 API(含 `startScan`)均由父页 SDK 执行
|
* - `'auto'`(默认):处于子 frame 时 `startScan` 等由父页执行;`onScanListener` 仅注册在 iframe 内,识别结果由父页 `postMessage` 回传
|
||||||
* - `true` / `'on'` / `'parent'`:存在父 window 时强制转发
|
* - `true` / `'on'` / `'parent'`:存在父 window 时强制转发
|
||||||
* - `false` / `'off'` / `'local'`:始终在本页执行(子页自己要跑扫码时用)
|
* - `false` / `'off'` / `'local'`:始终在本页执行(子页自己要跑扫码时用)
|
||||||
*/
|
*/
|
||||||
|
|||||||
Reference in New Issue
Block a user