Vue JSX 属性透传的问题

参见以下代码:

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>
  )
}

createButtons(this.$createElement, [
  {
    text: "修改",
    onClick: this.handleModify,
    size: "small",
    disabled: (props) => {
      return auditStatus.isOffline(props.row.status);
    },
  },
]);

编译后

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]
      );
    }),
  ]
);

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

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代码的字符串,也就是

function () { [native code] }

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

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

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

function () { [native code] }

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

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

参考: