下面这段代码为异常测试代码。通过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如下
- 点击【切换】按钮,切换innerText后
'<div>现在是innerText</div>'
这部分没有渲染出来。
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渲染好了。不知道读者朋友们有什么更好的方案吗?