createElement 动态加载
把原来的 <script> 标签写法:
<script async src="https://docs.opencv.org/4.x/opencv.js"
onload="onOpenCvReady();" type="text/javascript"></script>
转成 JS 动态创建:
const script = document.createElement("script");
script.src = "https://docs.opencv.org/4.x/opencv.js";
script.async = true;
script.onload = () => {
onOpenCvReady();
};
document.head.appendChild(script);
避免重复加载
if (!window.cv) {
const script = document.createElement("script");
script.src = "https://docs.opencv.org/4.x/opencv.js";
script.async = true;
script.onload = () => {
console.log("OpenCV loaded");
onOpenCvReady();
};
document.head.appendChild(script);
} else {
onOpenCvReady();
}
onload 不等于 OpenCV 初始化完成
script.onload 只表示 JS 文件下载解析完毕,但 OpenCV.js 内部有一个 WebAssembly 初始化过程,cv 对象此时可能还未就绪。
如果在 onload 里立刻调用 new cv.Mat() 会报错。正确做法是等 cv.onRuntimeInitialized:
script.onload = () => {
cv["onRuntimeInitialized"] = () => {
// 这里才能安全使用 cv API
const mat = new cv.Mat();
console.log("OpenCV Ready");
};
};
封装成 Promise
function loadOpenCV() {
return new Promise((resolve, reject) => {
if (window.cv) {
resolve(window.cv);
return;
}
const script = document.createElement("script");
script.src = "https://docs.opencv.org/4.x/opencv.js";
script.async = true;
script.onload = () => {
cv["onRuntimeInitialized"] = () => {
resolve(cv);
};
};
script.onerror = reject;
document.head.appendChild(script);
});
}
loadOpenCV().then((cv) => {
console.log("Ready");
const mat = new cv.Mat(100, 100, cv.CV_8UC3);
console.log(mat);
});
油猴脚本:@require 绕过 CSP
有些网页设置了 Content-Security-Policy(CSP),会阻止加载非白名单来源的外部脚本。这时用 createElement 注入 script 会直接被浏览器拦截。
油猴的 @require 是在油猴自身的沙箱里加载,不经过页面的 CSP,可以绕过这个限制:
// ==UserScript==
// @name Load OpenCV
// @namespace http://tampermonkey.net/
// @version 1.0
// @match *://*/*
// @require https://docs.opencv.org/4.x/opencv.js
// @grant none
// ==/UserScript==
(function () {
'use strict';
cv.onRuntimeInitialized = () => {
console.log('OpenCV Ready');
const mat = new cv.Mat(100, 100, cv.CV_8UC3);
console.log(mat.rows, mat.cols);
};
})();
@require 里的脚本在油猴加载阶段执行,cv 全局变量在脚本运行时已经存在。
两种方式对比
| 方式 | 适用场景 | CSP 限制 |
|---|---|---|
createElement + onload | 普通页面、自己控制的网页 | 受页面 CSP 限制 |
@require | 油猴脚本 | 不受页面 CSP 影响 |
如果网页没有 CSP,两种都能用,@require 写法更简洁;有 CSP 时只能用 @require。
