Vue的h函数切换渲染innerHTML和innerText异常,这应该是Vue源码的一个缺陷吧

557 阅读1分钟

下面这段代码为异常测试代码。通过isHtml开关控制h函数切换渲染innerHTML和innerText。

<script>
  import { h } from 'vue';

  export default {
    name: 'JsonString',
    data() {
      return {
        isHtml: true,
      };
    },
    methods: {
      change() {
        this.isHtml = !this.isHtml;
      },
    },
    render() {
      let domItem = {};
      if (this.isHtml) {
        domItem.innerHTML = ' <div>现在是innerHTML</div>';
      } else {
        domItem.innerText = '<div>现在是innerText</div>';
      }
      console.log(domItem);

      return h('span', {}, [
        h('div', { innerText: 'Vue的h函数切换渲染innerHTML和innerText异常:' }),
        h('span', domItem),
        h('button', {
          onClick: this.change,
          innerText: '切换',
        }),
      ]);
    },
  };
</script>

<style scoped>
  div {
    margin: 10px;
  }
  span {
    margin-right: 20px;
  }
</style>


测试结果发现:

  • 进入页面后正常渲染innerHTML如下

企业微信截图_16619338455879.png

企业微信截图_16619338686592.png

  • 点击【切换】按钮,切换innerText后'<div>现在是innerText</div>'这部分没有渲染出来。

企业微信截图_16619338936745.png

企业微信截图_16619339194184.png

 const patchProps = (el, vnode, oldProps, newProps, parentComponent, parentSuspense, isSVG) => {
 // oldProps = {innerHTML: " <div>现在是innerHTML</div>"}
 // newProps = {innerText: "<div>现在是innerText</div>" }
        if (oldProps !== newProps) {
            // 先遍历newProps
            for (const key in newProps) {
                // 此时遍历的key为innerText
                // empty string is not valid prop
                if (isReservedProp(key))
                    continue;
                const next = newProps[key];
                // next = "<div>现在是innerText</div>"
                const prev = oldProps[key];
                // prev = "";
                // defer patching value
                if (next !== prev && key !== 'value') {
                    hostPatchProp(el, key, prev, next, isSVG, vnode.children, parentComponent, parentSuspense, unmountChildren);
                    // 到此处时会渲染出 <div>现在是innerText</div>
                }
            }
            if (oldProps !== EMPTY_OBJ) {
            // 再遍历oldProps
                for (const key in oldProps) {
                    // 此时的key为innerHTML
                    if (!isReservedProp(key) && !(key in newProps)) {
                        hostPatchProp(el, key, oldProps[key], null, isSVG, vnode.children, parentComponent, parentSuspense, unmountChildren);
                        // 此处会渲染innerHTML,而newProps的innerHTML为空,故最终渲染为空
                    }
                }
            }
           ...
        }
    };

总结: Vue在最终渲染时,会先遍历新的newProps对象,将新的newProps中属性值都渲染出来;再遍历旧的oldProps对象,将旧的oldProps[key] = newProps[key],上例中,遍历newProps时,将innerText渲染为<div>现在是innerText</div>,遍历oldProps时,将innerHTML渲染为空。 所以最终变成了渲染成了空。

目前我的解决方式是,都用innerHTML渲染好了。不知道读者朋友们有什么更好的方案吗?

给官方提了一个BUG:Vue's H function switches rendering innerHTML and innerText exceptions. · Issue #6571 · vuejs/core (github.com)