解决 :Uncaught (in promise) TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation
背景
写业务的过程中,项目中 axios 添加了一些业务的请求头,导致普通 get 会变成复杂请求,获取资源会跨域。所以在业务中使用原生的 fetch 先过渡一下。
后续业务迭代,代码需要兼容小程序,所以 JSON 数据的请求需要进行兼容,小程序中没有 fetch,需要自己用 wx.request 自己 fork 一下。
onMounted(() => {
dataControl.value = new OfficialDeliverControl(props.param, {
gatewayAxiosInstance: gatewayAxiosInstance,
fetchInstance: isWeapp ? wxFetch : fetch,
});
deliverForms.value = (dataControl.value?.data || []).filter(item => item.show);
});
执行后业务报错,且 JSON 数据也没有拿到。
寻找问题
Google 一下,在这篇问答中找到了答案:
Why does bind fix "failed to execute 'fetch' on 'Window' illegal invocation" error? - Stack Overflow
For some internal purpose, the fetch
function must have this
be the same as window
. When you create your own object and just assign the fetch function as one of its properties, the fetch function has no access to the window
object as this
.
The fetch
specification describes things the window
might be used for. You might be able to make your original code work by setting no-window
, but I have not tested that.
翻译过来:
fetch
函数必须 this
与 window
相同。当您创建自己的对象并仅将 fetch 函数分配为其属性之一时, fetch 函数无法像 this
那样访问 window
对象。
并给出了一个规范的地址: fetch.spec.whatwg.org/#concept-re…
可以确定 fetch
的 this
指向一定是 window
。
解决问题
修改代码 fetch
=> fetch.bind(window)
onMounted(() => {
dataControl.value = new OfficialDeliverControl(props.param, {
gatewayAxiosInstance: gatewayAxiosInstance,
fetchInstance: isWeapp ? wxFetch : fetch.bind(window),
});
deliverForms.value = (dataControl.value?.data || []).filter(item => item.show);
});
✅ 问题得到了解决。
继续探索
写一个 js 最小 DEMO 复现验证一下
class MyControl {
fetchInstance = null;
DATA_URL = "http://127.0.0.1:5500/data.json";
constructor(options = {}) {
this.fetchInstance = options.fetchInstance || fetch;
}
getData() {
this.fetchInstance(this.DATA_URL)
.then((res) => res.json())
.then((data) => {
console.log(data);
});
}
}
function init() {
const myControl = new MyControl({});
myControl.getData();
}
init();
在浏览器执行,会复现相同的报错。
如果我们加以修改,把 getData 放到 init 函数中,通过调整代码的位置将函数执行的作用域改成 window 下,验证一下。
class MyControl {
fetchInstance = null;
DATA_URL = "http://127.0.0.1:5500/data.json";
constructor(options = {}) {
this.fetchInstance = options.fetchInstance || fetch;
}
getFetchInstance() {
return this.fetchInstance;
}
}
function init() {
const myControl = new MyControl({});
const fetchInstance = myControl.getFetchInstance();
fetchInstance("http://127.0.0.1:5500/data.json")
.then((res) => res.json())
.then((data) => {
console.log(data);
});
}
init();
执行代码,我们获取到了我们想要的数据。
结论
fetch
的 this
需指向 window
。