Функциональный компонент с эффектом v-Once - PullRequest
2 голосов
/ 17 марта 2019

Я пишу легкий функциональный компонент с функцией render (в частности, горизонтальный <Divider/> компонент), где он мне нужен для рендеринга только один раз, для оптимизации производительности обновления; и он вообще не должен быть реактивным, поскольку он должен служить статическим контентом только после рендеринга. Однако для делителя потребуются некоторые базовые параметры (например, color, height и т. Д.), Поскольку он фактически построен поверх SVG.

Теперь, с шаблонами в однофайловом компоненте , вы можете использовать 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 - далее подтвердил Эван , что v-once является компиляцией директива level и, следовательно, недоступна внутри 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 Ответ

1 голос
/ 17 марта 2019

v-once не является «настоящей» директивой.Компилятор шаблонов Vue сгенерирует код, который кэширует vnodes шаблонов, помеченных v-once.

Поскольку вы пишете функцию рендера вручную, вам придется позаботиться о кэшировании vnode, сгенерированных вашим рендером.function.

Вы можете попробовать что-то вроде этого (непроверенное и, вероятно, имеет проблемы):

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

Я не могу гарантировать, что приведенный выше код будет работать правильно во всех ситуациях, так как он довольнонизкий уровень, и я не знаю, можно ли / нужно кэшировать vnodes таким образом.Код кэширования может быть более или менее сложным, чем приведенный выше, в зависимости от ваших требований.

Интересно, что использование v-once в функциональном компоненте не помешает выполнению кода рендеринга этого поддерева шаблона в полном объеме (это то, чего вы хотите избежать в своем вопросе), хотя vnode имеет isStatic и isOnce, помеченные как true, Vue будет использовать предыдущий рендеринг при исправлении DOM.

В целом, этоэто довольно излишне, и я рекомендую вам не беспокоиться о v-once и кэшировании, если только вы не определили, что это проблема с производительностью после профилирования.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...