做框架的时候因为跨域问题,需要将用户的请求目标进行hook,拦截用户代码的Image.src操作并重定向到自己的url进行代理访问。为此进行了一些研究。
解决方案:
- 利用Proxy包装并返回Proxy,通过handler拦截。
- 利用Object.defineProperty监听变化并拦截
利用Proxy方法拦截有个弊端,由于返回的是Proxy对象,虽然能够拦截src并进行修改,但是将无法通过drawImage绘制到Canvas上。
正常的绘制方法:
1 2 3 4 5 6 7 8
| let canvas2d = document.createElement('canvas').getContext('2d') let img = new Image() image.src='a.jpg'
image.onload = ()=>{ canvas2d.drawImage(img,0,0) }
|
加了Proxy拦截之后:
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
| function hookFunction(src){ } function FakeImage(){ const img = new Image() const handler = { set(obj, prop, value) { if ((prop === 'src')) { console.log('Hook set src',value); obj[prop] = hookFunction(src) } else { return Reflect.set(...arguments); } } } return new Proxy(img,handler) }
let canvas2d = document.createElement('canvas').getContext('2d') let img = new FakeImage() image.src='a.jpg'
image.onload = ()=>{ canvas2d.drawImage(img,0,0) }
|
虽然更改了src但无法绘制到canvas上,我的解决办法是利用Object.defineProperty。
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
| function hookFunction(src){ } function FakeImage(){ const img = new Image()
const originalSet = Object.getOwnPropertyDescriptor(img.__proto__,'src').set
Object.defineProperty(img,'src',{ set:(src)=>{ console.log('Hook set src',value) originalSet.call(img,value) } }) return img }
let canvas2d = document.createElement('canvas').getContext('2d') let img = new FakeImage() image.src='a.jpg'
image.onload = ()=>{ canvas2d.drawImage(img,0,0) }
|
warning 一定要记得保存原有的setter
在defineProperty设置setter之前,先通过Object.getOwnPropertyDescriptor保存原来的setter,否则无法触发image元素的自动请求!!特别注意,被坑了好久!