具有一次效应的功能组件

时间:2019-03-17 09:10:40

标签: vue.js vuejs2

我正在编写带有render函数(特别是水平<Divider/>组件)的轻量级functional component,在这里我只需要渲染一次就可以优化更新性能。而且它根本不需要是反应性的,因为它一旦呈现就应仅用作静态内容。但是,由于分隔符实际上是基于SVG构建的,因此它将需要一些基本的道具(例如colorheight等)。

现在,使用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);
  }
}

有人对此有任何看法或经验吗?

1 个答案:

答案 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和缓存,除非您在分析后确定它是性能问题。