|
| 1 | +--- |
| 2 | +title: preact-root-fragment |
| 3 | +description: Независимая реализация параметра `replaceNode` (устаревшего в Preact 10) для Preact 10+ |
| 4 | +--- |
| 5 | + |
| 6 | +# preact-root-fragment |
| 7 | + |
| 8 | +preact-root-fragment — это независимая и более гибкая реализация параметра `replaceNode` (устаревшего в Preact 10) для Preact 10+. |
| 9 | + |
| 10 | +Она предоставляет способ рендеринга или гидратации дерева Preact с использованием подмножества дочерних элементов внутри родительского элемента, переданного в `render()`: |
| 11 | + |
| 12 | +```html |
| 13 | +<body> |
| 14 | + <div id="root"> ⬅ передаем это в render() как родительский DOM-элемент... |
| 15 | + |
| 16 | + <script src="/etc.js"></script> |
| 17 | + |
| 18 | + <div class="app"> ⬅ ... но мы хотим использовать это дерево, а не скрипт |
| 19 | + <!-- ... --> |
| 20 | + </div> |
| 21 | + </div> |
| 22 | +</body> |
| 23 | +``` |
| 24 | + |
| 25 | +--- |
| 26 | + |
| 27 | +<toc></toc> |
| 28 | + |
| 29 | +--- |
| 30 | + |
| 31 | +## Зачем это мне? |
| 32 | + |
| 33 | +Это особенно полезно для [частичной гидратации](https://jasonformat.com/islands-architecture/), которая часто требует рендеринга нескольких отдельных деревьев Preact в один и тот же родительский DOM-элемент. Представьте сценарий ниже — какие элементы мы должны передать в `hydrate(jsx, parent)`, чтобы каждый виджет `<section>` гидратировался без перезаписи других? |
| 34 | + |
| 35 | +```html |
| 36 | +<div id="sidebar"> |
| 37 | + <section id="widgetA"><h1>Виджет A</h1></section> |
| 38 | + <section id="widgetB"><h1>Виджет B</h1></section> |
| 39 | + <section id="widgetC"><h1>Виджет C</h1></section> |
| 40 | +</div> |
| 41 | +``` |
| 42 | + |
| 43 | +Preact 10 предоставлял несколько непонятный третий аргумент для `render` и `hydrate` под названием `replaceNode`, который можно было использовать в приведённом выше случае: |
| 44 | + |
| 45 | +```jsx |
| 46 | +render(<A />, sidebar, widgetA); // вставить результат в <div id="sidebar">, обрабатывая только <section id="widgetA"> |
| 47 | +render(<B />, sidebar, widgetB); // то же самое, но только для widgetB |
| 48 | +render(<C />, sidebar, widgetC); // то же самое, но только для widgetC |
| 49 | +``` |
| 50 | + |
| 51 | +Хотя аргумент `replaceNode` оказался полезным для обработки сценариев вроде приведённого выше, он был ограничен одним DOM-элементом и не мог работать с деревьями Preact, имеющими несколько корневых элементов. Кроме того, он плохо справлялся с обновлениями, когда несколько деревьев монтировались в один и тот же родительский DOM-элемент, что, как оказалось, является ключевым сценарием использования. |
| 52 | + |
| 53 | +В будущем мы предоставляем эту функциональность в виде независимой библиотеки под названием `preact-root-fragment`. |
| 54 | + |
| 55 | +## Как это работает |
| 56 | + |
| 57 | +`preact-root-fragment` предоставляет функцию `createRootFragment`: |
| 58 | + |
| 59 | +```ts |
| 60 | +createRootFragment(parent: ContainerNode, children: ContainerNode | ContainerNode[]); |
| 61 | +``` |
| 62 | + |
| 63 | +Вызов этой функции с родительским DOM-элементом и одним или несколькими дочерними элементами возвращает «Фрагмент с сохранением состояния». Это фальшивый DOM-элемент, который притворяется, что содержит предоставленные дочерние элементы, сохраняя их в их существующем реальном родительском элементе. Его можно передать в `render()` или `hydrate()` вместо аргумента `parent`. |
| 64 | + |
| 65 | +Используя предыдущий пример, мы можем заменить устаревшее использование `replaceNode` на `createRootFragment`: |
| 66 | + |
| 67 | +```jsx |
| 68 | +import { createRootFragment } from 'preact-root-fragment'; |
| 69 | + |
| 70 | +render(<A />, createRootFragment(sidebar, widgetA)); |
| 71 | +render(<B />, createRootFragment(sidebar, widgetB)); |
| 72 | +render(<C />, createRootFragment(sidebar, widgetC)); |
| 73 | +``` |
| 74 | + |
| 75 | +Поскольку мы создаем отдельные родители «Фрагментов с сохранением состояния» для каждой передачи в `render()`, Preact будет относиться к каждому как к независимому дереву Virtual DOM. |
| 76 | + |
| 77 | +## Несколько корневых элементов |
| 78 | + |
| 79 | +В отличие от параметра `replaceNode` из Preact 10, `createRootFragment` может принимать массив дочерних элементов, которые будут использоваться как корневые элементы при рендеринге. Это особенно полезно при рендеринге дерева Virtual DOM, которое генерирует несколько корневых элементов, например, фрагмент или массив: |
| 80 | + |
| 81 | +```jsx |
| 82 | +import { createRootFragment } from 'preact-root-fragment'; |
| 83 | +import { render } from 'preact'; |
| 84 | + |
| 85 | +function App() { |
| 86 | + return ( |
| 87 | + <> |
| 88 | + <h1>Example</h1> |
| 89 | + <p>Hello world!</p> |
| 90 | + </> |
| 91 | + ); |
| 92 | +} |
| 93 | + |
| 94 | +// Используем только последние два дочерних элемента внутри <body>: |
| 95 | +const children = [].slice.call(document.body.children, -2); |
| 96 | + |
| 97 | +render(<App />, createRootFragment(document.body, children)); |
| 98 | +``` |
| 99 | + |
| 100 | +## Поддержка версий Preact |
| 101 | + |
| 102 | +Эта библиотека работает с Preact 10 и 11. |
0 commit comments