我正在编写带有render
函数(特别是水平<Divider/>
组件)的轻量级functional component,在这里我只需要渲染一次就可以优化更新性能。而且它根本不需要是反应性的,因为它一旦呈现就应仅用作静态内容。但是,由于分隔符实际上是基于SVG构建的,因此它将需要一些基本的道具(例如color
,height
等)。
现在,使用single-file component中的模板,您可以在包含元素上使用v-once
并将其标记为functional
,并且基本上可以在初始渲染时设置道具从那时开始,它将变为静态且无渲染(对于此目的而言非常有用),如下所示:
<template functional>
<div class="divider" v-once>
<svg>
<g>
<path :fill="color" d="Mxx.xxx..."></path>
<!-- And more paths for complex dividers -->
</g>
</svg>
</div>
</template>
虽然可以正常工作,但似乎无法通过VNode.isOnce
函数在组件上设置相同的render
标志-进一步confirmed by Evan认为v-once
是编译级指令,因此在render
函数中不可用-考虑到如何使用基于模板的组件获得相同的效果,这太糟糕了。
functional
功能的render
组件:export default {
functional: true,
render(h, context) {
// As a non-static component, this function will always get called
// on each update! How can I apply some static flag at this point?
const data = {
style: {
textAlign: 'center'
}
};
const svgs = [
h('svg', ...)
];
return h(context.props.tag, data, svgs);
}
}
有人对此有任何看法或经验吗?
答案 0 :(得分:1)
v-once
不是“真实”指令。 Vue模板编译器将生成代码,以缓存标记有v-once
的模板的vnode。
由于您是手工编写渲染函数,因此必须注意缓存由渲染函数生成的vnode。
您可以尝试以下操作(未经测试,可能有问题):
const cache = new WeakMap()
export default {
functional: true,
render(h, ctx) {
// Get cache map from parent component instance
if (!cache.has(ctx.parent)) {
cache.set(ctx.parent, new Map())
}
const vnodeCache = cache.get(ctx.parent)
// Determine the cache key from the props (we only use one prop here)
const cacheKey = ctx.props.fill
// Get the cached vnode
let vnode = vnodeCache.get(cacheKey)
if (!vnode) {
// Render
vnode = h('div', `Fill is ${ctx.props.fill}`)
// This is necessary so Vue will reuse the previous DOM elements during patch
vnode.isStatic = true
vnode.isOnce = true
// Store in cache
vnodeCache.set(cacheKey, vnode)
}
return vnode
}
}
由于它的级别很低,因此我不能保证上面的代码在所有情况下都能正常工作,而且我不知道是否可以/应该以这种方式缓存vnode。根据您的要求,缓存代码可能比上面的代码复杂一些。
有趣的是,在功能组件中使用v-once
不会阻止模板子树的渲染代码完全执行(这是您要避免的问题),尽管vnode具有{ {1}}和isStatic
标记为true,Vue在修补DOM时将重用以前的呈现。
总而言之,这太过分了,我建议您不必担心isOnce
和缓存,除非您在分析后确定它是性能问题。