为HTMLImageElement魔改setAttribute的方法
JiaoXin2005 3/6/2021 JavaScript
# 背景
由于历史原因,前端升级 webp 图片的时候,需要给 img 的 src 地址加上形如:/xxxxx.png?webp=true 的参数。 但是不想改动特别多的代码,这里找到一个捷径记录一下。
核心:由于是在 react 环境中,像<img src='xxx' />
的 jsx 代码会被 babel 转译成 React.createElement("img", { src: "xxx" });
,最后通过setAttribute
给创建的HTMLImageElement
设置src
属性。
我们的思路就是想办法拦截setAttribute
的方法,拼接上?webp=true
的逻辑
# Object.getOwnPropertyDescriptor
Object.getOwnPropertyDescriptor(obj, prop)
返回指定对象obj
上一个自有属性prop
对应的属性描述符。
# Object.defineProperty
Object.defineProperty(obj, prop, descriptor)
方法会直接在一个对象obj
上定义一个新属性prop
,或者修改一个对象的现有属性,并返回此对象。其中descriptor
是属性的描述符
# 实现过程
思路:将目标对象的目标属性替换成传入进来的配置形式,同时添加一个以“$”开头的同名属性,用于调用这个属性的原始实现。经过尝试我们无法直接 getter.call(this),会报错,所以只能继续采用临时替换属性定义的方式来调用到属性的原始实现,所幸实现起来比直接调用 getter 更简单一些(直接调用 getter 的话还要考虑原始属性是否是 value 定义而非存取器)
export const changeProperty = (
obj: any,
key: string,
descriptor: PropertyDescriptor
): any => {
try {
let target = getTarget(obj, key);
// 保存要修改的属性装饰器
let originDescriptor: PropertyDescriptor =
Object.getOwnPropertyDescriptor(target, key) || descriptor;
// 魔改属性装饰器
Object.defineProperty(target, key, descriptor);
// 插入一个$开头的同名属性,用于外界使用,this.$xxx用来触发之前保存好的属性装饰器
Object.defineProperty(target, `$${key}`, {
configurable: originDescriptor.configurable,
enumerable: originDescriptor.enumerable,
get() {
/* 先恢复魔改之前属性,用于触发 */
Object.defineProperty(target, key, originDescriptor);
let res: any = this[key];
/* 在恢复魔改状态 */
Object.defineProperty(target, key, descriptor);
return res;
},
set(value: any) {
/* 先恢复魔改之前属性,用于触发 */
Object.defineProperty(target, key, originDescriptor);
this[key] = value;
/* 在恢复魔改状态 */
Object.defineProperty(target, key, descriptor);
},
});
} catch (error) {
console.log("changeProperty error: ", error);
}
};
/* 实现魔改setAttribute */
changeProperty(HTMLImageElement, "setAttribute", {
enumerable: true,
configurable: true,
writable: true,
value(...args: any[]) {
// do some magic things
let [key, imageSrc] = args;
let newArgs = [key, imageSrc + '?webp=true'];
this.$setAttribute(newArgs)
}
}
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
45
46
47
48
49
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
45
46
47
48
49