Vue JSX 属性透传的问题

参见以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function createButtons = (h, buttons) => props => {
return (
<span style="display: flex; justify-content: space-between;">

<!-- more -->
{buttons.map((item, index) => (
<mtd-button
{...{ attrs: item }}
key={index}
onClick={() => item.onClick(props)}
disabled={item.__disabled}
>
{item.text}
</mtd-button>
))}
</span>
)
}
1
2
3
4
5
6
7
8
9
10
createButtons(this.$createElement, [
{
text: '修改',
onClick: this.handleModify,
size: 'small',
disabled: props => {
return auditStatus.isOffline(props.row.status)
},
},
])

编译后

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
renderedButtons.forEach(function (item) {
if (typeof item.disabled === 'function') {
item.__disabled = item.disabled(props);
} else if (typeof item.disabled === 'boolean') {
item.__disabled = item.disabled;
}
});
return h("span", {
"style": "display: flex; justify-content: space-between;"
}, [renderedButtons.map(function (item, index) {
return h("mtd-button", {
"attrs": _objectSpread({}, item, {
"disabled": item.__disabled
}),
"key": index,
"on": {
"click": function click() {
if (typeof item.onClick === 'function') {
item.onClick(props);
}
}
}
}, [item.text]);
})]);

最后,在我们点击修改按钮的时候,控制台报错:

1
Uncaught SyntaxError: Function statements require a function name

在这里createButtons 中的某一项传递了onClick这个参数,那么在对应的渲染函数里面,最后传递给组件的是什么样的呢?

handleModify 很好理解,就是我们希望绑定的事件,但是在上面的listeners里面,还添加了click事件,其对应的函数为

因此,上面的方式,最后会把 onClick 封装到 attrs里面。

在 Vue 组件挂载会后,首次更新时,Vue 会把相应的属性更新到 DOM 上.

因此,最后会调用 ele.setAttribute(key, value) 来设置组件的属性。这里的key value 都应该是一个DOM String。在这里的场景下, key 是对应的 onClick 字符串,而 value 是一个函数,它被定义为了 Vue 组件上的一个方法。而 Vue 在实例化组件的时候,通过initMethods方法,把对应的 method 通过bind 的方式绑定了vm的上下文。因此,这里的 value,最后是一个Bound function。因此这里会把相应的函数转换为DOM String,具体的转换方法没有找到相应的文档,但是从转换结果来看,是调用了函数的toString方法。而bind返回的函数,它的toString方法,最后会返回一个表示native代码的字符串,也就是

1
function () { [native code] }

因此,最后输出到html里的就变成了以下内容:

1
<button onclick="function () { [native code] }">测试</button>

我们知道,在一个元素定义了onclick属性的情况下,点击的时候,会将其属性值当作js脚本来执行。因此,当我们点击按钮的时候,会执行上面的脚本

1
function () { [native code] }

然而这个脚本是非法的,不说函数体,定义一个函数有函数表达式和函数声明两种方式,而上面的这种方式只能作为匿名函数被调用,在这里被当成了函数声明,因此就报错了。

Vue updateAttrs
el.setAttribute(key, value)

总结:
当出现问题并且不知道导致问题发生的因素时,应先通过二分法快速定位到导致问题的代码,然后通过控制变量,减少场景的影响因子,在控制变量的时候,更需要注意双边其他因子的一致性,找到问题出现的原因,待解决问题后,再寻根溯源,找到产生问题的根本因素。

参考:

  • https://html.spec.whatwg.org/multipage/webappapis.html#handler-onclick
  • https://w3c.github.io/uievents/#event-type-click