TamperMonkey遇到「function is not defined」的解决办法

Posted by Eric on June 22, 2020

Tampermonkey 是一款免费的浏览器扩展和最为流行的用户脚本管理器,它适用于 Chrome, Microsoft Edge, Safari, Opera Next, 和 Firefox

虽然有些受支持的浏览器拥有原生的用户脚本支持,但 Tampermonkey 将在您的用户脚本管理方面提供更多的便利。 它提供了诸如便捷脚本安装、自动更新检查、标签中的脚本运行状况速览、内置的编辑器等众多功能, 同时Tampermonkey还有可能正常运行原本并不兼容的脚本。

昨天写了一段脚本,目的用于爬取网页图片的资源信息并格式化成markdown格式,调用 document.queryselector(selector)方法

1
2
3
4
5
6
7
8
9
window.onload = function(){
  ...
}
function getPicInfo() {

	...


}

导入tampermonkey后,window.onload的代码正常运行,试过按钮事件,console调用,getPicInfo()方法完全无法调用,搞了很久最后看了一下后台,写一点笔记。

TamperMonkey允许引用外部JS(如jQuery),但是所有的代码都在一个「big」function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
(function(that) {
    ((context,fapply,console)=>{
        with (context) {
            (module=>{
                "use strict";
                try {
                    fapply(module, context, [, , context.CDATA, context.uneval, context.define, context.module, context.exports, context, context.unsafeWindow, context.console, context.cloneInto, context.exportFunction, context.createObjectIn, context.GM, context.GM_info]);
                } catch (e) {
                    if (e.message && e.stack) {
                        console.error("ERROR: Execution of script (脚本名称) " + e.message);
                        console.log(e.stack.replace(/(\\(eval at )?<anonymous>[: ]?)|([\s.]*at Object.tms_[\s\S.]*)/g, ""));
                    } else {
                        console.error(e);
                    }
                }
            }
            )(function tms_e59a9a41_b6ee_417f_8d88_33fXXXXX5cef(context, fapply, CDATA, uneval, define, module, exports, window, unsafeWindow, console, cloneInto, exportFunction, createObjectIn, GM, GM_info) {
              
//***********
//外部引用JS
//***********

              
                              (function() {

                    'use strict';
//***********
//用户代码
//***********
                                
                })();

            })
        }
    }
    )(that.context, that.fapply, that.console);
    
)((()=>{
    const k = "__u__5907087.XXXXXXXXX"
      , r = this[k];
    delete this[k];
    return r;
}
)())

使用的是ES6的语法,部分代码也经过混淆不是很能推理出其作用。结合TamperMonkey的参数设置Line 7为读取代码中CDATA/module/export/console等特定的关键词,便于正常调用。

文件结构看,用户输入代码的位置Line 28在两层函数嵌套下,局部方法的作用域是私有的。所以访问不到

解决方法

如果需要添加按钮事件,可以在window.onload中对按钮进行事件监听addEventListener,直接在内部写函数即可,如果需要调用方法,需要在监听事件中定义。

这个方法的问题是代码的可维护性比较差,没办法,先这样凑合着吧。以后有新的解决办法再写。

2020.6.24

想到一个新方法,实测好用,而且可全局调用:

TamperMonkey只需要写一下代码,require和脚本都一样。引用文件在前。

❗️module引入时注意添加type="module”(上次被这个玩意坑惨了)

1
2
3
4
5
6
7
8
window.onload = function(){
    function addJS(src){
        var srcObj = document.createElement("script")
        srcObj.setAttribute("src",src)
        document.body.prepend(srcObj)
    }
    addJS("")
} 简单说就是把script直接在文档加载时Load进去。需要注意当服务器启用HTTPS访问时,所访问的资源也需要是HTTPS的(localhost除外),否则会被[CORS](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS)直接blocked。