You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
< template v-render сгенерирует DOM узел template, который мы заменим, но в момент создания template сразу же попадет в dynamicChildren родителя, ситуация похожа как с patchFlag #1322, что мы заменяем что-то уже после создания, но Vue внутри у себя начал следить за узлом, который мы заменяем.
На выходе получаем следующий скомпилированный узел:
_withDirectives.call(_ctx,// Создаём ноду template(_openBlock.call(_ctx,),_createElementBlock.call(_ctx,"template",{key: el.uuid})),// Директива с её параметрами[[(_directive_render=_directive_render||_resolveDirective.call(_ctx,"render")),_ctx.renderChunk(_ctx.vdom.getRenderFn(el.component+'/'))({params}})]])
Из документации по миграции на Vue3:
template tags with no special directives (v-if/else-if/else, v-for, or v-slot) are now treated as plain elements and will
result in a native template element instead of rendering its inner content.
Предварительно я также модифицировал v-render, добавив возможность добавлять ключ для заменяемой VNode.
functionsetupBlock(vnode){// save current block children on the block vnodevnode.dynamicChildren=isBlockTreeEnabled>0 ? currentBlock||EMPTY_ARR : null;// close blockcloseBlock();// a block is always going to be patched, so track it as a child of its// parent blockif(isBlockTreeEnabled>0&¤tBlock){currentBlock.push(vnode);}returnvnode;}
В setupBlock попадает и создаваемый выше template, и VNode, которую вернёт v-render вместо template. Они обе попадают в условие if (isBlockTreeEnabled > 0 && currentBlock) т.к. у обеих есть родитель и их нужно будет сравнивать в процессе обновлений.
Отличие в том, что template попадает туда уже имея атрибут key, а VNode которая будет когда-то возвращена из v-render, попадает туда ещё не имея атрибута key. Это значит, что VNode, попадает туда до вызова v-render.
Таким образом, у нас в dynamicChildren родительского узла, попадают 2 элемента: template и узел создаваемый при вызове _ctx.vdom.getRenderFn(el.component + '/')({params}).
Проблема в том, что т.к. template мы подменили на новый узел, то у VNode template атрибуты anchor и el всегда будут null, потому что в настоящем доме этого узла не будет. А атрибут key и у template, и у новой VNode одинаковый. Это значит, что если измениться key, то под сравнение попадут оба узла, и они оба попадут под замену т.к. ключ будет отличаться. Далее vue будет пытаться найти опорный узел в реальном доме, для замены нашего template на новый, но не найдёт и получит ошибку Cannot read properties of null (reading 'nextSibling')
Если v-render используется совместно с template, то как правило template должен заменяться на узел, который вернула рендер функция, однако если же render функция вернет массив, то сейчас будет создан DOM узел template, где будут эти дочерние узлы, что в общем-то лишено смысла.
Возможно стоит v-render на template обрабатывать как не директиву, а конструкцию шаблона и сразу генерировать такой код, вместо узла с директивой:
// это массив children[
..._ctx.renderChunk(_ctx.vdom.getRenderFn(el.component+'/'))({params}})]
TL;DR
< template v-render
сгенерирует DOM узелtemplate
, который мы заменим, но в момент создания template сразу же попадет в dynamicChildren родителя, ситуация похожа как с patchFlag #1322, что мы заменяем что-то уже после создания, но Vue внутри у себя начал следить за узлом, который мы заменяем.Подробное описание
Рассмотрим проблему на примере шаблона:
На выходе получаем следующий скомпилированный узел:
Из документации по миграции на Vue3:
Предварительно я также модифицировал v-render, добавив возможность добавлять ключ для заменяемой VNode.
Видим в скомпилированном коде, что для создания template вызывается
_createElementBlock
А далее вызывается
setupBlock
В
setupBlock
попадает и создаваемый выше template, и VNode, которую вернётv-render
вместо template. Они обе попадают в условиеif (isBlockTreeEnabled > 0 && currentBlock)
т.к. у обеих есть родитель и их нужно будет сравнивать в процессе обновлений.Отличие в том, что template попадает туда уже имея атрибут key, а VNode которая будет когда-то возвращена из v-render, попадает туда ещё не имея атрибута key. Это значит, что VNode, попадает туда до вызова v-render.
Таким образом, у нас в
dynamicChildren
родительского узла, попадают 2 элемента: template и узел создаваемый при вызове_ctx.vdom.getRenderFn(el.component + '/')({params})
.Проблема в том, что т.к. template мы подменили на новый узел, то у VNode template атрибуты
anchor
иel
всегда будут null, потому что в настоящем доме этого узла не будет. А атрибутkey
и у template, и у новой VNode одинаковый. Это значит, что если изменитьсяkey
, то под сравнение попадут оба узла, и они оба попадут под замену т.к. ключ будет отличаться. Далее vue будет пытаться найти опорный узел в реальном доме, для замены нашего template на новый, но не найдёт и получит ошибкуCannot read properties of null (reading 'nextSibling')
Если резюмировать, то узел, который мы заменяем с помощью v-render, попадает в dynamicChildren родителя, что и является багом
The text was updated successfully, but these errors were encountered: