Мостовой Никита
@xnimorz
nik.mostovoy@gmail.com
6
6
6
6
6
6
6
8
Много
- Pure
- Pure
- Selectors
- Pure
- Cache \ memoization
- Selectors
6
6
6
6
6
6
8
Много
- Большой проект с 0
- Большой проект с 0
- Миграция hh.ru на react
Как мы поймем, что изменилось?
Как мы поймем, что изменилось?
+ Линейная сложность O(n)
+ Линейная сложность O(n)
+ В базовом варианте просты для реализации
+ Линейная сложность O(n)
+ В базовом варианте просты для реализации
– Мы полностью завязаны на порядок элементов
+ Линейная сложность O(n)
+ В базовом варианте просты для реализации
– Мы полностью завязаны на порядок элементов
! Не является полноценным алгоритмом сверки
+ Линейная сложность O(n)
+ В базовом варианте просты для реализации
– Мы полностью завязаны на порядок элементов
! Не является полноценным алгоритмом сверки
! В нашем случае является жадным алгоритмом
O(n )
2
[
]
[
]
- Iterable
[
]
- Iterable
- childNodes (HTMLCollection)
[
]
[
]
const Item = el.childNodes[1];
const Item = el.childNodes[1];
const Item = model.get('8');
const Item = el.childNodes[1];
const Item = model.get('8');
{}
Map
LinkedHashMap:
- Doubly linked list
- HashMap (key)
- Keyed элементы позволяют нам инвалидировать меньше
- Keyed элементы позволяют нам инвалидировать меньше
- Сравнение происходит поэлементно за 1 проход, без перебора
- Keyed элементы позволяют нам инвалидировать меньше
- Сравнение происходит поэлементно за 1 проход, без перебора
- Key — ключи хешмапы
3
3
3
8447 — O(n)
3
8447 — O(n)
71,351,809 — O(n )
2
3
8447 — O(n)
71,351,809 — O(n )
2
602,708,730,623 — O(n )
3
- Мы говорим про механизм сверки
- Мы говорим про механизм сверки
- Мы не говорим об оптимизациях для уровня компонентов (PureComponent \ SCU в реакте, автоматическое отслеживание данных во vue)
- Мы говорим про механизм сверки
- Подобный механизм сверки может работать как с DOM так и с VDOM
- Мы не говорим об оптимизациях для уровня компонентов (PureComponent \ SCU в реакте, автоматическое отслеживание данных во vue)
- Инвалидация узлов (сверка) — O(n)
- Инвалидация узлов (сверка) — O(n)
- Ререндер (внесение изменений в DOM) — O(n)
- Инвалидация узлов (сверка) — O(n)
- Layout
- Ререндер (внесение изменений в DOM) — O(n)
- Инвалидация узлов (сверка) — O(n)
- Layout
На ререндер \ layout мы можем повлиять только улучшая код конечного приложения
- Ререндер (внесение изменений в DOM) — O(n)
- Структура данных
- Структура данных
- "Инвалидируй, если не уверен"
- Структура данных
- "Инвалидируй, если не уверен"
- Поэлементное сравнение
import React, { useState } from "react";
import Component from "./Component";
function App() {
const [ articles, setArticles ] = useState([
{ id: 1, text: "foo" },
{ id: 2, text: "bar" },
{ id: 2, data: {} },
]);
return (
<div>
<Title>Hello world</Title>
<Content articles={articles} />
</div>
);
}
function Title({ children }) {
return <H1>{children}</H1>;
}
function Content({ articles }) {
return (
<div>
{articles.map((item) => {
if (item.text) {
return <article key={item.id}>{text}</article>;
}
return <Component key={item.id} article={item} />;
})}
</div>
);
}
import React, { useState } from "react";
import Component from "./Component";
function App() {
const [ articles, setArticles ] = useState([
{ id: 1, text: "foo" },
{ id: 2, text: "bar" },
{ id: 2, data: {} },
]);
return (
<div>
<Title>Hello world</Title>
<Content articles={articles} />
</div>
);
}
function Title({ children }) {
return <H1>{children}</H1>;
}
function Content({ articles }) {
return (
<div>
{articles.map((item) => {
if (item.text) {
return (
<article key={item.id}>
{text}
</article>
);
}
return (
<Component key={item.id} article={item} />
);
})}
</div>
);
}
import React, { useState } from "react";
import Component from "./Component";
function App() {
const [ articles, setArticles ] = useState([
{ id: 1, text: "foo" },
{ id: 2, text: "bar" },
{ id: 2, data: {} },
]);
const [ title, setTitle ] = useState('Hello world');
const invalidator = {
div: {
children: {
h1: {deps: [title]},
Content: {deps: [title ? title : 'stub', articles]},
},
deps: [],
}
}
return (
<div>
<h1>{title}</h1>
<Content title={title ? title : 'stub'} articles={articles} />
</div>
);
}
import React, { useState } from "react";
import Component from "./Component";
function App() {
const [ articles, setArticles ] = useState([
{ id: 1, text: "foo" },
{ id: 2, text: "bar" },
{ id: 2, data: {} },
]);
const [ title, setTitle ] = useState('Hello world');
const updater = {
h1: {
update: (ctx, $1) => {ctx.el.textContent = $1}
},
Content: {
update: () => {},
}
}
const invalidator = {
div: {
children: {
h1: {deps: [title]},
Content: {deps: [title ? title : 'stub', articles]},
},
deps: [],
}
}
return (
<div>
<h1>{title}</h1>
<Content title={title ? title : 'stub'} articles={articles} />
</div>
);
}
import React, { useState } from "react";
import Component from "./Component";
function App() {
const [ articles, setArticles ] = useState([
{ id: 1, text: "foo" },
{ id: 2, text: "bar" },
{ id: 2, data: {} },
]);
const [ title, setTitle ] = useState('Hello world');
const updater = {
h1: {
update: (ctx, $1) => {ctx.el.textContent = $1}
},
Content: {
update: () => {},
}
}
const invalidator = {
div: {
children: {
h1: {deps: [title]},
Content: {deps: [title ? title : 'stub', articles]},
},
deps: [],
}
}
const invertedInvalidator = {
title: [h1, Content],
articles: [Content],
}
return (
<div>
<h1>{title}</h1>
<Content title={title ? title : 'stub'} articles={articles} />
</div>
);
}
1) Обходим AST, по пути кешируя все Identifier
1) Обходим AST, по пути кешируя все Identifier
2) Сохраняем в переменную блок начала вывода компонента (jsx, template)
3) Ищем в выводе компонента обращения к Identifier
1) Обходим AST, по пути кешируя все Identifier
2) Сохраняем в переменную блок начала вывода компонента (jsx, template)
3) Ищем в выводе компонента обращения к Identifier
4) Помечаем эти identifier как аргументы для invalidator
1) Обходим AST, по пути кешируя все Identifier
2) Сохраняем в переменную блок начала вывода компонента (jsx, template)
3) Ищем в выводе компонента обращения к Identifier
4) Помечаем эти identifier как аргументы для invalidator
1) Обходим AST, по пути кешируя все Identifier
2) Сохраняем в переменную блок начала вывода компонента (jsx, template)
5) Перед выводом компонента описываем invalidator
<script>
import Title from "./Title";
import Content from "./Content";
</script>
<div>
<Title text="Hello world" />
<Content />
</div>
App.svelte
<script>
import Title from "./Title";
import Content from "./Content";
</script>
<div>
<Title text="Hello world" />
<Content />
</div>
App.svelte
<script>
export let text
</script>
<h1>
{text}
</h1>
Title.svelte
App.svelte
<script>
import Component from "./Component";
import articles from "./store";
</script>
<div>
{#each $articles as item (item.id)}
{#if item.text}
<article>{item.text}</article>
{:else}
<Component article={item} />
{/if}
{/each}
</div>
Content.svelte
<script>
export let text
</script>
<h1>
{text}
</h1>
Title.svelte
<script>
import Title from "./Title";
import Content from "./Content";
</script>
<div>
<Title text="Hello world" />
<Content />
</div>
App.svelte
<script>
import Title from "./Title";
import Content from "./Content";
</script>
<div>
<Title text="Hello world" />
<Content />
</div>
App.svelte
<script>
import Component from "./Component";
import articles from "./store";
</script>
<div>
{#each $articles as item (item.id)}
{#if item.text}
<article>{item.text}</article>
{:else}
<Component article={item} />
{/if}
{/each}
</div>
Content.svelte
import { writable } from "svelte/store";
export const articles = writable([
{ id: 1, text: "foo" },
{ id: 2, text: "bar" },
{ id: 3, data: { foo: "bar" } },
]);
Store.js
<script>
export let text
</script>
<h1>
{text}
</h1>
Title.svelte
Не совсем
1) Компонент описывает свою инвалидацию. Нужен механизм уведомления. В простом варианте — Event emitter
1) Компонент описывает свою инвалидацию. Нужен механизм уведомления. В простом варианте — Event emitter
2) Если компонент описывает свое обновление — встает вопрос согласованности обновления у всех компонентов на странице
3) Если нужны дополнительные свистелки (Suspense и пр. — также встает вопрос согласованности)
1) Компонент описывает свою инвалидацию. Нужен механизм уведомления. В простом варианте — Event emitter
2) Если компонент описывает свое обновление — встает вопрос согласованности обновления у всех компонентов на странице
- Полноценные алгоритмы сверки — крайне дорогие
- Полноценные алгоритмы сверки — крайне дорогие
- Современные фреймворки решают проблему либо в рантайме улучшенными алгоритмами обхода, либо пытаются прекомпилировать код
- Полноценные алгоритмы сверки — крайне дорогие
- Современные фреймворки решают проблему либо в рантайме улучшенными алгоритмами обхода, либо пытаются прекомпилировать код
- Прекомпилированный код позволяет решить вопрос инвалидации за O(1), но встает вопрос корректного согласования обновлений
- Полноценные алгоритмы сверки — крайне дорогие
- Современные фреймворки решают проблему либо в рантайме улучшенными алгоритмами обхода, либо пытаются прекомпилировать код
- Прекомпилированный код позволяет решить вопрос инвалидации за O(1), но встает вопрос корректного согласования обновлений
- Не обязательно решение с прекомпиляцией будет быстрее runtime решения на практике
Мостовой Никита
@xnimorz
nik.mostovoy@gmail.com