【问题标题】:Functional component with v-once effect具有 v-once 效果的功能组件
【发布时间】:2019-03-17 09:10:40
【问题描述】:

我正在编写一个带有render 函数的轻量级functional component(特别是一个水平<Divider/> 组件),我只需要它渲染一次,以优化更新性能;而且它根本不需要反应,因为它应该只在渲染后作为静态内容。但是,它需要一些基本的道具(例如colorheight 等)作为分隔符,因为它实际上是建立在 SVG 之上的。

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

虽然按预期工作,但似乎无法使用render 函数在组件上设置相同的VNode.isOnce 标志——进一步confirmed by Evan v-once 是编译级指令,因此不可用在render 函数内部——考虑到如何使用基于模板的组件获得相同的效果,这太糟糕了。

带有render功能的functional组件:

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

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

【问题讨论】:

    标签: vue.js vuejs2


    【解决方案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 具有 isStatic并且isOnce 标记为true,Vue 将在修补DOM 时重用之前的渲染。

    总而言之,这太过分了,我建议您不必为v-once 和缓存而烦恼,除非您在分析后确定这是性能问题。

    【讨论】:

    • 感谢您的回复。到目前为止,这个低级组件并不是一个性能问题(因为你可能不会在一个页面上有那么多分隔符);考虑到它是模板的更接近编译器的替代方案,我只是认为这将是构建具有功能渲染的一次性、无渲染组件的好模式。考虑构建其他类型的组件的可能性,您确实希望将其视为纯粹的静态内容。
    • 我可能会求助于这个单独的 vnodes 模板缓存,尽管我认为确实应该有一种方法来避免无法在功能组件上附加“内部”指令的能力。
    猜你喜欢
    • 1970-01-01
    • 2015-02-14
    • 2020-08-07
    • 1970-01-01
    • 2020-08-14
    • 1970-01-01
    • 2015-12-02
    • 2016-07-04
    • 2012-02-25
    相关资源
    最近更新 更多