Учебный курс по React, часть 1: обзор курса, причины популярности React, ReactDOM и JSX

https://scrimba.com/playlist/p7P5Hd
  • Перевод
  • Tutorial
Представляем вашему вниманию первые 5 занятий учебного курса по React для начинающих. Оригинал курса на английском, состоящий из 48 уроков, опубликован на платформе Scrimba.com. Возможности этой платформы позволяют, слушая ведущего, иногда ставить воспроизведение на паузу и самостоятельно, в том же окне, в котором ведётся демонстрация, экспериментировать с кодом. Курс показался нам интересным, мы решили перевести его на русский и преобразовать в формат традиционных публикаций.



Часть 1: обзор курса, причины популярности React, ReactDOM и JSX
Часть 2: функциональные компоненты
Часть 3: файлы компонентов, структура проектов
Часть 4: родительские и дочерние компоненты
Часть 5: начало работы над TODO-приложением, основы стилизации
Часть 6: о некоторых особенностях курса, JSX и JavaScript
Часть 7: встроенные стили
Часть 8: продолжение работы над TODO-приложением, знакомство со свойствами компонентов
Часть 9: свойства компонентов
Часть 10: практикум по работе со свойствами компонентов и стилизации
Часть 11: динамическое формирование разметки и метод массивов map
Часть 12: практикум, третий этап работы над TODO-приложением
Часть 13: компоненты, основанные на классах
Часть 14: практикум по компонентам, основанным на классах, состояние компонентов
Часть 15: практикумы по работе с состоянием компонентов
Часть 16: четвёртый этап работы над TODO-приложением, обработка событий
Часть 17: пятый этап работы над TODO-приложением, модификация состояния компонентов
Часть 18: шестой этап работы над TODO-приложением

Полагаем, этот курс будет полезен всем, кто, что называется, «не умеет в React», но хочет научиться. В то же время, на то, чтобы превратить этот курс в обычные публикации, нужны немалые силы и время, поэтому мы, прежде чем принимать окончательное решение о запуске этого проекта, предлагаем всем желающим оценить курс и поучаствовать в опросе о целесообразности его перевода.

Занятие 1. Обзор курса и рекомендации по его освоению


Оригинал

Добро пожаловать на курс «Основы React». Меня зовут Боб Зиролл, я расскажу вам о том, как создавать фронтенд-проекты, используя один из самых популярных в мире веб-фреймворков. Я работаю в области компьютерного образования уже много лет, в частности, сейчас руковожу организацией учебного процесса в V School.

▍О процессе освоения курса


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

Для начала хочу отметить, что самый лёгкий и результативный способ изучить что угодно заключается в том, чтобы не жалеть сил и времени на практику. Если вы хотите научиться программировать — то чем раньше вы начнёте делать что-то сами, и чем чаще будете это делать — тем выше ваши шансы на успех.

Обычно, когда я ввожу в курс дела учащихся V School, я привожу им следующий пример из собственной жизни. Недавно меня потянуло на работу с деревом. Я читал книги, смотрел бесчисленные видео на YouTube, мне дарили инструменты. Но я не мог сделать ничего достойного до тех пор, пока не взял инструменты в руки. Только многие часы, потраченные на работу пилой и наждачной бумагой, на склеивание и свинчивание деталей, позволили мне приблизиться к цели. Собственно говоря, по такому же принципу устроено и освоение всего, чего угодно. Хотите изучить React? Пишите код.

Большинство занятий этого курса содержат упражнения. Ожидается, что вы постараетесь выполнять их самостоятельно. Если же вы, ознакомившись с заданием для самостоятельной работы, тут же перейдёте к описанию его решения, то вы, на самом деле, выберете самый сложный способ изучения React. Кроме того, не ждите, пока вам предложат попрактиковаться — берите инициативу на себя и пробуйте всё, о чём узнаёте. Старайтесь как можно больше самостоятельно работать с кодом. В частности, когда вы уже немного освоите React — создавайте нечто такое, что вам интересно, испытывайте всё, что вам любопытно испытать. Это позволит вам избежать такой неприятности, как «tutorial hell».

Ещё один важный момент моего подхода заключается в интервальном обучении и в повторении пройденного материала. Это — важнейшие вещи, которые позволяют по-настоящему запомнить то, чему вы учитесь. Не рекомендуется с головой бросаться в изучение курса. Это — путь в тот самый «tutorial hell». При таком подходе возникает ощущение, как будто вы и правда что-то узнали, а в реальности же вы просто не в состоянии запомнить то, что «изучили».

Поэтому, продвигаясь по материалам, делайте частые перерывы. Речь идёт не о периодических перерывах в 5-10 минут, а кое о чём более масштабном. Изучите пару принципов, попрактикуйтесь в их использовании, а затем денёк передохните. Когда вы вернётесь к курсу, будет очень полезно повторить уже изученные материалы. Конечно, при таком подходе на то, чтобы освоить курс, уйдёт больше времени, но это чрезвычайно благотворно скажется на вашем обучении.
Теперь давайте в двух словах обсудим то, чему вы научитесь, освоив этот курс.

▍Состав курса и предварительные требования


Вот перечень основных тем курса:

  • Компоненты. Говоря о React, нельзя избежать обсуждения концепции компонентов. Компоненты в React — это основной строительный блок для создания фрагментов HTML-кода, подходящих для повторного использования. И практически всё остальное, о чём мы будем говорить, имеет отношение к тому, как использовать эти компоненты для построения веб-приложений.
  • JSX. Это — синтаксическое расширение JavaScript, которое позволяет создавать компоненты, используя возможности HTML и JavaScript.
  • Стилизация компонентов. Стилизация позволяет придать компонентам привлекательный внешний вид.
  • Свойства и обмен данными в приложении. Свойства используются для передачи данных между компонентами.
  • Состояние. Механизмы состояния компонентов используются для хранения данных в приложении и для управления ими.
  • Обработка событий. События позволяют наладить интерактивные взаимоотношения с пользователями приложений.
  • Методы жизненного цикла компонентов. Эти методы позволяют программисту влиять на различные события, происходящие с компонентами.
  • Загрузка данных из внешних API с использованием протокола HTTP.
  • Работа с формами.

Для того чтобы продуктивно заниматься по этому курсу, вам нужно знать HTML, CSS и JavaScript (ES6).

Занятие 2. Учебные проекты


Оригинал

В процессе прохождения этого курса вы будете разрабатывать учебные проекты. Взглянем на некоторые из них.

Нашей первой разработкой будет стандартное TODO-приложение.


TODO-приложение

Может и выглядит оно скучновато, но в ходе его разработки будет задействовано множество возможностей, о которых мы будем говорить в курсе. По элементам списка дел можно будет щелкать, отмечая их как завершённые, и видеть, как меняется их внешний вид.


Отмеченные дела в TODO-приложении

А вот — наш курсовой проект — генератор мемов.


Генератор мемов

При работе с этим приложением в поля Top Text и Bottom Text вводят тексты, которые, соответственно, попадут в верхнюю и нижнюю часть изображения. По нажатию на кнопку Gen программа случайным образом выбирает изображение мема из соответствующего API и добавляет к нему текст. Вот пример работы этого приложения:


Готовый мем

Занятие 3. Зачем нужен React и почему его стоит изучать?


Оригинал

Зачем использовать React, если можно разработать веб-проект на чистом JavaScript? Если вы интересуетесь веб-разработкой, то, возможно, слышали о том, что React позволяет создавать очень быстрые приложения, производительность которых превышает то, что достижимо с использованием лишь JavaScript. Это достигается за счёт использования в React технологии, называемой Virtual DOM. Мы не будем вдаваться в подробности о Virtual DOM, если вы хотите познакомиться с этой технологией поближе, можете посмотреть это видео.

Сейчас нам достаточно знать о том, что Virtual DOM помогает веб-приложениям работать гораздо быстрее, чем если бы при их разработки использовался обычный JS. Ещё одно по-настоящему замечательное преимущество, которое даёт нам React — это возможность создавать веб-компоненты, подходящие для повторного использования. Рассмотрим пример.

У нас имеется стандартный элемент navbar (навигационная панель) из библиотеки Bootstrap.


Навигационная панель

Если вы раньше не пользовались Bootstrap, то знайте, что это просто CSS-библиотека, которая даёт веб-разработчику красиво оформленные элементы. Тут примерно четыре десятка строк кода, всё это выглядит довольно громоздко, ориентироваться в таком коде непросто. Если включить всё это в состав HTML-страницы, на которой и так имеется много всего, код такой страницы окажется попросту перегруженным различными конструкциями.

Веб-компоненты React позволяют брать фрагменты HTML-кода, оформлять их в виде самостоятельных компонентов, и, вместо того, чтобы добавлять на страницу эти фрагменты, включать в состав страниц нечто вроде особых HTML-тегов, указывающих на них. В нашем случае, вместо того, чтобы добавлять на страницу сорок строк HTML-разметки, достаточно включить в её состав компонент, содержащий эту разметку. У нас он называется MySweetNavbar.


Компонентный подход к формированию веб-страниц

Назвать такой компонент можно как угодно. Как видно, разметку страницы, основанную на компонентах, гораздо легче читать. Разработчик сразу видит общую структуру такой страницы. В данном случае, как можно понять из содержимого тега <body>, в верхней части страницы находится навигационная панель (MySweetNavbar), в середине размещено основное содержимое (MainContent), а в нижней части страницы имеется подвал (MySweetFooter).

Кроме того, компоненты не только улучшают структуру кода HTML-страниц. Они ещё и подходят для повторного использования. Как быть, если на нескольких страницах нужна одна и та же навигационная панель? Как быть, если такие панели на разных страницах немного отличаются друг от друга? Что делать, если всё та же панель используется на множестве страниц, а в неё нужно внести некое изменение? Без использования компонентного подхода трудно дать достойные ответы на эти и на многие другие вопросы.

Ещё одной причиной популярности React можно считать тот факт, что разработкой и поддержкой этой библиотеки занимается Facebook. Это, по меньшей мере, означает, что React на постоянной основе, занимаются квалифицированные программисты. Популярность React, и то, что проект это опенсорсный, опубликованный на GitHub, означает ещё и то, что вклад в проект делают множество сторонних разработчиков. Всё это позволяет говорить о том, что React, в обозримом будущем, будет жить и развиваться.

Говоря о React, и, в частности, о том, почему эту библиотеку стоит изучать, нельзя не вспомнить об огромном рынке труда, связанном с этой библиотекой. В наши дни React-специалисты пользуются устойчивым спросом. Если вы изучаете React с целью найти работу в сфере фронтенд-разработки — это означает, что вы на правильном пути.

Занятие 4. Среда разработки, ReactDOM и JSX


Оригинал

Здесь мы поговорим о том, как создать простейшее React-приложение с использованием ReactDOM и затронем некоторые ключевые моменты, касающиеся JSX. Но, прежде чем приступать к работе с кодом, поговорим о том, где этот код запускать.

▍Среда разработки


Для того чтобы экспериментировать с React-кодом, пожалуй, лучше всего будет развернуть полноценную локальную среду разработки. Для того чтобы это сделать, вы можете обратиться к недавно опубликованному нами материалу React.js: понятное руководство для начинающих, в частности, к его разделу Практика разработки React-приложений. А именно, для того чтобы приступить к экспериментам, нужно, с помощью create-react-app, создать новое приложение, после чего запустить локальный сервер разработки и приступить к редактированию кода. Если речь идёт о простейших примерах, то их код можно вводить прямо в стандартный файл index.js, убрав из него имеющийся в нём код или закомментировав его.

Содержимое файла index.html в проекте, создаваемом create-react-app, соответствует его содержимому в примерах, которые будут приводиться в данном курсе. В частности, речь идёт о наличии на странице элемента <div> с идентификатором root.

Ещё один вариант, который обычно подходит для каких-то совсем простых экспериментов, заключается в использовании онлайн-платформ наподобие codepen.io. Например, вот демонстрационный проект React-приложения Дэна Абрамова. Суть подготовки Codepen-проекта к экспериментам с React заключается в подключении к нему библиотек react и react-dom (это можно сделать, щёлкнув по кнопке Settings в верхней части страницы, перейдя в появившемся окне в раздел JavaScript и подключив к проекту, предварительно найдя их с помощью встроенной системы поиска, нужные библиотеки).

Вполне возможно, что вам, для экспериментов, будет удобно пользоваться возможностями Scrimba. Для этого можете просто открыть страницу соответствующего занятия. Ссылки на эти страницы можно найти ниже заголовков с номерами и названиями занятий.

▍Первая программа


Надо отметить, что в наших примерах будут использоваться возможности ES6 (ES2015), поэтому вам весьма желательно в них ориентироваться. В частности, для того чтобы импортировать в проект библиотеку react, служит такая конструкция:

import React from "react"

А так можно импортировать библиотеку react-dom:

import ReactDOM from "react-dom"

Теперь воспользуемся методом render() ReactDOM для того чтобы вывести что-то на экран:

ReactDOM.render()

Если вы решите использовать для экспериментов проект, созданный средствами create-react-app, то сейчас его файл index.js (открытый в VSCode), будет выглядеть так, как показано на следующем рисунке.


Ввод кода в index.js

Если у вас запущен сервер разработки и в браузере открыта страница http://localhost:3000/, то вы, сохранив такой index.js, вы увидите там сообщения об ошибках. Это, на данном этапе работы, совершенно нормально, так как мы пока не сообщили системе о том, что и куда мы хотим вывести командой render().

На самом деле, сейчас пришло время разобраться с тем кодом, который мы только что написали. А именно, здесь мы импортировали в проект React, потом — ReactDOM — для того, чтобы возможностями этой библиотеки можно было бы воспользоваться для вывода чего-то на экран.

Метод render() принимает два аргумента. Первый будет тем, что мы хотим вывести, а второй — будет тем местом, куда мы хотим что-то вывести. Если это записать в виде псевдокода, то получится следующее:

ReactDOM.render(ЧТО ВЫВОДИТЬ, КУДА ВЫВОДИТЬ)

То, что мы хотим вывести, должно быть каким-то образом привязано к некоей HTML-странице. Тот код, который мы будем писать, будет превращён в HTML-элементы, которые и попадут на страницу.

Вот как эта страница может выглядеть.

<html>
    <head>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <div id="root"></div>
        <script src="index.js"></script>
    </body>
</html>

Тут имеются все базовые элементы HTML-страницы, включая тег <link> и тег <script>.

Если вы пользуетесь create-react-app, то страница index.html будет выглядеть немного иначе. В частности, в её коде нет команды импорта index.js. Дело в том, что при сборке проекта связь index.html и index.js осуществляется автоматически.

Обратите внимание на элемент <div> с идентификатором root. Между открывающим и закрывающим тегами этого элемента React разместит всё, что мы создадим. Этот элемент можно считать контейнером для всего нашего приложения.

Если теперь вернуться к файлу index.js и к методу render() ReactDOM, его вторым аргументом, местом, куда надо выводить данные, будет указание на элемент <div> с идентификатором root. Тут мы воспользуемся обычным JavaScript, после чего второй аргумент метода Render будет выглядеть так:

ReactDOM.render(ЧТО ВЫВОДИТЬ, document.getElementById("root"))

При таком подходе метод render() берёт первый аргумент и выводит то, что он описывает, в указанное место. Теперь займёмся этим первым аргументом. Начнём с простого элемента <h1>. И, как это обычно бывает при написании первой программы, добавим в него текст Hello world!:

ReactDOM.render(<h1>Hello world!</h1>, document.getElementById("root"))

Если теперь обновить страницу браузера, то на ней будет выведен, в качестве заголовка первого уровня, заданный текст.


Результат работы первой программы

Тут у вас может возникнуть вопрос о том, почему это мы помещаем описание HTML-элемента в то место, где ожидается аргумент JavaScript-метода. Ведь JavaScript, столкнувшись с чем-то вроде <h1>Hello world!</h1>, вероятно, решит, что первый символ этого выражения представляет собой оператор «меньше», дальше, очевидно, идёт имя переменной, потом — оператор сравнения «больше». JavaScript не распознает в этой последовательности символов HTML-элемент, да он и не должен этого делать.

Разработчики React создали не только библиотеку, но и особый язык, который называется JSX. JSX очень похож на разновидность HTML. Дальше вы увидите, что практически весь JSX-код почти полностью совпадает с формируемой с его помощью HTML-разметкой. Различия между JSX и HTML, конечно, есть, и мы их постепенно обсудим.

Мы ввели довольно простую и короткую инструкцию, но в недрах React при её выполнении происходит много всего интересного. Так, эта инструкция преобразуется в её версию на JavaScript, осуществляется формирование HTML-кода, хотя в детали этого процесса мы тут не вдаёмся. Именно поэтому нам надо импортировать в проект не только react-dom, но и react, так как библиотека React — это именно то, что позволяет нам пользоваться JSX и сделать так, чтобы JSX-конструкции работали так, как ожидается. Если из нашего примера убрать строку import React from "react", сохранить файл скрипта и обновить страницу, будет выведено сообщение об ошибке. В частности, create-react-app сообщит нам о том, что без доступа к React пользоваться JSX нельзя ('React' must be in scope when using JSX  react/react-in-jsx-scope).

Дело в том, что даже хотя в нашем примере React напрямую не используется, библиотека применяется для работы с JSX.

Ещё кое-что, касающееся работы с JSX, на что я хочу обратить ваше внимание, заключается в том, что нельзя рендерить JSX-элементы, следующие друг за другом. Предположим, что после элемента <h1> нужно вывести элемент <p>. Попробуем воспользоваться такой конструкцией:

ReactDOM.render(<h1>Hello world!</h1><p>This is a paragraph</p>, document.getElementById("root")) //неправильно

Если после этого обновить страницу — будет выведено сообщение об ошибке (в create-react-app это выглядит как Parsing error: Adjacent JSX elements must be wrapped in an enclosing tag). Суть этой ошибки заключается в том, что такие элементы должны быть обёрнуты в какой-то другой элемент. То, что получится в итоге, должно выглядеть как один элемент с двумя вложенными в него элементами.

Для того чтобы наш пример заработал, обернём код <h1>Hello world!</h1><p>This is a paragraph</p> в элемент <div>:

ReactDOM.render(<div><h1>Hello world!</h1><p>This is a paragraph</p></div>, document.getElementById("root"))

Если теперь обновить страницу, то всё будет выглядеть так, как ожидается.


Два HTML-элемента на странице

Для того чтобы привыкнуть к JSX, потребуется некоторое время, но после этого его использование окажется гораздо легче и удобнее, чем работа с HTML-элементами с использованием стандартных средств JavaScript. Например, для того чтобы стандартными средствами описать элемент <p> и настроить его содержимое, понадобится примерно следующее:

var myNewP = document.createElement("p")
myNewP.innerHTML = "This is a paragraph."

Потом нужно подключить его к элементу, который уже есть на странице. Это — образец императивного программирования, а то же самое, благодаря JSX, можно делать в декларативном стиле.

Занятие 5. Практикум. ReactDOM и JSX


Оригинал

На прошлом занятии вы познакомились с ReactDOM и JSX, а теперь пришло время применить полученные знания на практике.

Все практические задания мы будем оформлять следующим образом. Сначала, в разделе с заголовком Задание, будет дано само задание, и, возможно, в разделе Подсказки, будут даны краткие рекомендации по его выполнению. Затем будет следовать раздел Решение. Рекомендуется приложить все усилия к тому, чтобы выполнить задание самостоятельно, а потом уже разбираться с приведённым решением.

Если вы чувствуете, что не справляетесь — вернитесь к предыдущему занятию, повторите соответствующий материал и попробуйте снова.

▍Задание


Напишите React-приложение, которое выводит на страницу маркированный список (тег <ul>). Этот список должен содержать три элемента (<li>) с любым текстом.

▍Подсказки


Сначала надо импортировать в проект необходимые библиотеки, а потом воспользоваться одной из них для вывода элементов, сформированных с помощью некоего JSX-кода, на страницу.

Решение
Для начала нужно импортировать в файл необходимые библиотеки. А именно — речь идёт о библиотеке react, и, так как мы собираемся выводить что-то на страницу, о библиотеке react-dom.

import React from "react"
import ReactDOM from "react-dom"

После этого надо воспользоваться методом render() объекта ReactDOM, передав ему описание элемента, который мы хотим вывести на страницу и указав место на странице, куда должен быть выведен этот элемент.

ReactDOM.render(
  <ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
  </ul>,
  document.getElementById("root")
)

Первый аргумент — это описание маркированного списка, второй — элемент страницы, в который он должен попасть — тег <div> с идентификатором root. HTML-код можно записать и в одну строку, но лучше оформить его так, как в нашем варианте решения.

Вот полный код решения:

import React from "react"
import ReactDOM from "react-dom"

ReactDOM.render(
  <ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
  </ul>,
  document.getElementById("root")
)

Со временем мы поговорим о том, как, используя аккуратные конструкции, выводить с помощью метода render() большие объёмы HTML-разметки.

Итоги


Сейчас, после того, как вы ознакомились с первыми занятиями курса по React, просим вас принять участие в опросе.

Спасибо за внимание!

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Нужно ли переводить учебный курс по React?

RUVDS.com
1020,00
RUVDS – хостинг VDS/VPS серверов
Поделиться публикацией

Комментарии 62

    +1
    Видел этот курс, благодарю за перевод! Будем учиться!
      +1
      Большое спасибо, всегда с интересом читаю ваши статьи и переводы
        +1
        Хотел дополнить комментарий, в общем потратил 10 минут и ужасно рад что все так просто и легко, по ощущениям проще чем Vue(хотя Vue показался тоже простым) и уж тем более проще AngularJS(делал на нем маленький проект замучился), в общем еще раз большое спасибо вам, как раз ищу легкий фреймворк, чтобы обрабатывать данные с бекенда (NodeJS + Express)

        P.S. Прошу не злиться адептов Angular и Vue, я не профессиональный разработчик, это скорее хобби, поэтому не воспринимайте мои слова близко к сердцу, что с меня взять.
          0
          по ощущениям проще чем Vue(хотя Vue показался тоже простым)

          Их нельзя так просто сравнивать. В vue реактивная магия из коробки, а реакте(по крайне мере в предложенном в статье виде) нет и всю магию(если она нужна, а если нет — заменитель) надо прикручивать сбоку.

          +4
          производительность которых превышает то, что достижимо с использованием лишь JavaScript

          Двойной фейспалм. Нет, ну вы это серьезно?


          React — это возможность создавать веб-компоненты

          Веб-компоненты — это вообще-то термин за которым стоит вполне конкретный набор стандартов, с Реактом не связанный, и даже являющийся ему альтернативой. Уверены, что с таким уровнем вообще можно кого-то обучать?

            0
            JSX. Это — синтаксическое расширение JavaScript, которое позволяет создавать компоненты, используя возможности HTML и JavaScript.

            Полная и несусветная глупость, у хтмл в сравнении с js нет никаких возможностей. JSX это просто попытка создать «нормальные» хтмл-шаблоны. И всё это во имя совместимости.

            Если вы интересуетесь веб-разработкой, то, возможно, слышали о том, что React позволяет создавать очень быстрые приложения, производительность которых превышает то, что достижимо с использованием лишь JavaScript.

            Выше сказали нелепости этой фразы, ведь каким образом либа на жс может быть быстрее жс. На самом деле всё куда глубже.

            Сейчас нам достаточно знать о том, что Virtual DOM помогает веб-приложениям работать гораздо быстрее, чем если бы при их разработки использовался обычный JS.

            Неверно, vdom является костылём(оптимизацией) изначально несостоятельной концепции(почему она используется — расскажу ниже), при этом эта оптимизация ничего не меняет.

            Есть совсем просто: «быстрый» обновляет значение любой ноды за O1, когда как vdom за O(n * const), где n это кол-во элементов в дереве компонентов/элементов, а const некая сложность обновление одного компонента/элемента. Очевидно, что это никоим образом не быстро и целиком и полностью ущербно. Сам vdom лишь влияет на контексту, т.е. сложность обновления в dom выше, чем в vdom. Именно поэтому он быстрее, но быстрее в сравнении с чем?

            Теперь про концепцию. Что из себя представляет реакт? Это некий шаблон, вызов которого генерирует dom, который в свою очередь генерирует картинку. Это некий типичный php-way. При каком-либо изменении данных шаблон перевызывается, генерируя новый dom. Очевидно, что такой путь крайне глупый и то решение, которое дал реакт — это не генерировать каждый раз дом, а генерировать vdom. А сам dom иметь мутабельным, накатывая на него обновления, которые есть разница предыдущего/текущего состояния vdom. Это очень типично для любых иммутабельных подходом — они зачастую оказывают несостоятельными.

            Почему выбран был такой подход? Ответ прост. Это привычный всем php-way, т.е. эмуляция обычных текстовых шаблонов. Для большинства людей это подход наиболее удобен и понятен.

            Очевидно, что из этого описания крайне просто выводится тот самый O1 подход. У нас есть данные, которые отображаются в каких-то элементах. Нам нужно просто запомнить какие данные с чем связаны. Какие данные обновились мы знаем и далее мы просто обновляем связанные с ними элементы. И нам абсолютно неважно сколько там у нас в дереве элементов, хоть миллион, хоть миллиард. Мы просто выкидываем ненужный vdom и изначально несостоятельный подход самого реакта.

            И так действительно делается, даже на уровне реакта с его контекстом. А так же на уровне любой state-библиотеки. Но всё это работает на уровне компонентов(ведь реакт не может отслеживать эти связи). Особенно хорошо это работает с реактивностью с тем же mobx.

            И все эти ухищрения нужны не просто так. Они — это прямое следствие того, что vdom целиком и полностью несостоятелен. И все как можно больше пытаются обновлять элементы напрямую, руками. И чем меньше там будет vdom — тем лучше.

              0

              Вы очень категоричны. Боюсь если рассуждать таким образом, то состоятельных решений не существует в природе, все в чём-нибудь кривые и косые. Нет реактивных решений с O(1). Если вы считаете, что у вас такое решение есть, то скорее всего вы либо не понимаете bigO, либо подводные камни того или иного решения. А возможно и то и другое.


              P.S. O(n * const) = O(n)

                0
                Боюсь если рассуждать таким образом, то состоятельных решений не существует в природе, все в чём-нибудь кривые и косые.

                На чём основаны эти заявления? Каким образом из «это костыль» следует «всё костыли»? А даже если всё, то каким образом это что-то меняет? Это ведь попросту ахинея, ведь даже если всё костыли, то мы можем измерять этот уровень костыля и лучшим решением будет мене костыльное, менее кривое.

                Самое смешное, что ровных поверхностей не существует в природе — что из этого следует? Я не могу что-то назвать кривым или косым?

                Нет реактивных решений с O(1)

                Я же уже показал O1 решение, либо вы не смогли его понять? Ну тогда объясню попроще — прямой биндинг есть O1 решение по определению. Если ещё проще, прямое изменение абстрактного value в dom-node является O1 решением.

                Самое интересное, что даже если вы не понимаете назначения vdom — я рассказал об этом выше. Но даже сейчас вы этого не понимаете. Мне ещё раз рассказать, либо вы сами в состоянии изучить теме до попыток спорить?

                Если вы считаете, что у вас такое решение есть, то скорее всего вы либо не понимаете bigO, либо подводные камни того или иного решения.

                Я услышу основания для этих заявлений, а так же примеры подводных камней? К тому же, сами эти рассуждения несостоятельны и не имеют смысла, т.к. любой vdom является прямым биндингом и все «подводные камни»(фентезийные) присущи и vdom. И раз тут они не мешают — они не будут мешать в рамках чего-то иного.

                P.S. O(n * const) = O(n)

                Приравнивая одно к другому вы множите на ноль смысл vdom, т.к. он асимптотически эквивалентен dom, как и сам dom любому другому. И причина тут проста, «понимаемый» вами bigO имеет смысл только для сравнения разных асимптотик.

                  0
                  Я же уже показал O1 решение, либо вы не смогли его понять? Ну тогда объясню попроще — прямой биндинг есть O1 решение по определению. Если ещё проще, прямое изменение абстрактного value в dom-node является O1 решением.

                  Действительно, в таком варианте вы получите О1, но это будет работать сильно медленнее, чем варианты реакта\ангуляра, т.к. самих операций апдейта будет на десятичные порядки больше в силу того, что апдейт вам надо будет делать на каждое изменение биндинга, а не "пакетами".

                    0
                    Утверждение является ошибочным, а причиной является неправильная трактовка назначения vdom. У vdom одна задача — поиск изменившихся нод и всё. Никакое пакетное обновление к vdom никакого отношения не имеет.

                    самих операций апдейта будет на десятичные порядки больше в силу того, что апдейт вам надо будет делать на каждое изменение биндинга, а не «пакетами».

                    Что это значит — неясно. Каким образом и из чего это следует? Я примерно предполагаю, что это какая-то смесь из нескольких популярных тезисов типа «всё дерево будет обновлять», «изменение каждой ноды(наверное имеется ввиду асинхронно) будет триггерить перерисовку, когда пакетное изменение будет триггерить одну перерисовку на пакет».

                    Очевидно, что первая цитата вообще несостоятельна, а вторая к vdom никакого отношения не имеет и в 10раз проще реализуется при прямом биндинге.

                    Если проще, то любой vdom будет пересоздаваться на каждое изменение, которых может быть сколько угодно. И даже если после этого изменения dom-node собирать в очередь и запускать пачками, то попросту мусор в сравнении с прямым биндингом.

                    В ситуации с прямым биндингом никакого vdom нет и на каждое изменение биндинга можно точно так же собирать операции в очередь, а далее исполнять их пачками.

                      0
                      изменение каждой ноды(наверное имеется ввиду асинхронно) будет триггерить перерисовку, когда пакетное изменение будет триггерить одну перерисовку на пакет

                      Ну вы же мягкое с тёплым сравниваете. Вы увидели, что в случае observable у вас 1 какой-нибудь прицельный setAttribute, innerText = и пр., и почему-то считаете, что в этом сложность решения и состоит. Мол пришло обновление и мы одной прицельной операцией сделали всё, что хотели. Я правильно понимаю ход ваших мыслей? Но ведь вы взяли только вершину айсберга, ту что над водой. В таком разрезе оно и правда может выглядеть очень привлекательно.


                      Но ведь есть та, что под водой. И она может быть как крошечной (1 observable + 1 computed), так и просто громадной (какой-нибудь хитрый граф зависимостей).


                      В различных ситуациях разные решения будут превалировать друг над другом. В особенности это зависит от прямоты рук разработчика. В случае immutable values + vDom разработчик будет минимизировать кол-во звеньев для ререндера vDom (всякие selector-ы, правильные key, денормализация и пр.). В случае observable разработчик будет тщательно обсустраивать свой observable-граф так, чтобы оно ворочалось побыстрее. И то и другое может быть нетривиальным. Сильно зависит от задачи.


                      Ну а формошлёпить можно с любым подходом с закрытыми глазами.

                        0
                        Утверждение является ошибочным, а причиной является неправильная трактовка назначения vdom.

                        А я не про вдом. Я про ваше предложение обновлять сразу по изменению биндинга.


                        Каким образом и из чего это следует?

                        Это следует непосредственно из того, что вы предлагаете. Если обновлять дом каждый раз как меняется биндинг — то группировать изменения не получится по определению.


                        Очевидно, что первая цитата вообще несостоятельна, а вторая к vdom никакого отношения не имеет

                        При чем тут вдом вообще? Я говорю про ваше О1 решение, а не про вдом.

                        0

                        Там не будет O(1). Там ведь трекинг зависимостей и вся машинерия по обновлению всего, что от этих зависимостей зависит. Скажем даже простейший перебор всех listeners уже O(n). А когда в дело включаются сложные переплетения различных видов observable то начинается такая кровавая баня, что порой проще утопиться, чем найти где же оно засбоило и как, откуда тут мать его race, и почему всё так тормозит. Оптимизировать реально сложный клубок бывает невыносимо "больно" )

                          –1
                          Там не будет O(1). Там ведь трекинг зависимостей и вся машинерия по обновлению всего, что от этих зависимостей зависит. Скажем даже простейший перебор всех listeners уже O(n).

                          На этом уже можно закончить, т.к. человек целиком и полностью несостоятелен. Ошибок очень много, начиная с того, что человек попытался подменить понятия, ведь в ситуации с vdom(в контексте create/diff дерева) под n понимается кол-во нод в дереве, когда как в данном случае кол-во listeners.

                          Заканчивая тем, что он начал спорить с голосами в голове, а не со мною, откуда-то взяв какие-то observable, которые никакого отношения к теме не имеют. Прямой биндинг подразумевает объект, который имеет лишь ссылку(и) на target, т.е. те node условное value которых привязано к value упомянутого объекта.
                            0

                            Вот ваши слова:


                            У нас есть данные, которые отображаются в каких-то элементах. Нам нужно просто запомнить какие данные с чем связаны. Какие данные обновились мы знаем и далее мы просто обновляем связанные с ними элементы

                            Вы описали паттерн "Наблюдатель". Ваши "запомнить какие данные с чем связаны" + "Какие данные обновились мы знаем " = tracking dependencies + notify dependencies.


                            То что вы не знаете, как оно называется не говорит, что я как человек "несостоятелен".


                            т.к. человек целиком и полностью несостоятелен

                            А вообще как вы сюда попали? Обычно людей которые настолько кислотны тут сливают в краткие сроки. Минус в карму за повторную грубость. Продолжайте в том же духе, будете readonly.

                            0
                            Там не будет O(1). Там ведь трекинг зависимостей и вся машинерия по обновлению всего, что от этих зависимостей зависит.

                            В варианте Pappagento никакого трекинга нет, просто обновили переменную и поправился дом. Представьте себе, что у вас есть класс-модель, и на полях этой модели стоит сеттер, который обновляет связанную с этим полем ноду. Присваиваете значение полю — обновляется нода. Все. Никаких listeners, вдомов, ченж трекингов и т.д. (ну или можно считать что у вас вырожденный observable с не более чем одним listener).


                            Все прекрасно, О1, но проблема в том, что если вы, например, в цикле совершили на модели 1000 изменений, то это вызовет 1000 апдейтов.

                              0

                              Вот же его слова:


                              У нас есть данные, которые отображаются в каких-то элементах. Нам нужно просто запомнить какие данные с чем связаны. Какие данные обновились мы знаем и далее мы просто обновляем связанные с ними элементы

                              Написано сумбурно, но я вижу в этом именно автоматический трекинг зависимостей. Иначе была бы формулировка вида "прописать руками в коде".


                              Но предположим я понял его превратно, и наш кислотный товарищ имел ввиду другое. Скажем если он имел ввиду, что у нас система вида каждый model.field хранит точный вбитый руками список своих dom.listeners, то это уже никакая не реактивная система. По сути примерно так писали на jQuery. Получив новое значение с какого-нибудь input мы прямо в коде прописывали все изменения, которые от этого значения должны произойти.


                              Все прекрасно, О1, но проблема в том, что если вы, например, в цикле совершили на модели 1000 изменений, то это вызовет 1000 апдейтов.

                              Ну формально можно их складировать в некую очередь, причём без повторений (скажем по ключу в хеше uniq-dom-id + attrubute-name|content|...). И выполнять уже на следующем тике. Но в целом такие ручные решения для систем сложнее простейшего формошлёпства нежизнеспособны и экономически не оправданы. Это ж ручной каторжный труд.


                              Вот теперь я, несостоятельный человек, правильно понял идею?


                              const n1 = { id: 'some-dom-id', type: 'attribute', value: fn1 };
                              const fn1 = () => mode.field1 + '!';
                              const model = {
                                field1: { value: 12, notifiers: [n1, n2, n3] }
                              }

                              где n1 это тип зависимости, fn1 это вычисление самого значения, notifiers это руками вбитый список что-где-и-как обновлять, указанный для каждого поля отдельно. Ну и всё это покрыто некой либой, которое это заводит. Так?

                                0
                                У нас есть данные, которые отображаются в каких-то элементах. Нам нужно просто запомнить какие данные с чем связаны.

                                Ну вот вы установили сеттер на поле и, т.о., запомнили, что это поле связано с нодой, которую этот сеттер потом обновит.


                                Иначе была бы формулировка вида "прописать руками в коде".

                                Очевидно, что прописать может фреймворк, автоматически. Вы в темплейте указали {{ a }} — и он автоматически нагенерит нужный сеттер для переменной а класса-модели. А в коде работаете с ней просто как с обычным полем.


                                у нас система вида каждый model.field хранит точный вбитый руками список своих dom.listeners, то это уже никакая не реактивная система.

                                Почему же? Это как раз система реактивная — мы обновляем поле и с ним реактивно обновляется дом. Нереактивная система — это в реакте, где реактивных обновлений нет :)


                                Ну формально можно их складировать в некую очередь, причём без повторений (скажем по ключу в хеше uniq-dom-id + attrubute-name|content|...)

                                Можно конечно. Только теперь можно заметить, что нам в принципе не надо делать промежуточных обновлений. Зачем сохроанять всю историю, если мы последовательно сделаем все ченжи и остановимся на последнем? Можно оставить только последний чендж, сделать его — и это будет нужный нам дом-стейт. Теперь у нас в биндингах хранятся пары (старое значение, новое значение), При этом при апдейте из-за такого что информация обычно представлена иерархически (в компоненту суем список, каждый элемент списка — это объект, который суется во вложенную компоненту, так объекты во вложенных и т.д....), то при перестройке этой иерархии 99% пар у нас будут вида (х, х) — то есть значение не изменилось. Надо ли нам апдейтить неизмененные значения? Совсем не надо, это лишние траты. Лучше пройтись по всем биндингам и апдейтить только те, где значения менялись. Кроме того — если мы складируем изменения а не апдейтим каждый раз, то надо теперь как-то определять, а когда именно эти изменения надо применить. Мы же так можем 5 минут складировать и дом не апдейтить, сомнительно что пользователю будет польза от такого поведения :)
                                Теперь поздравляю — у вас получился чендж детекшон, прямо как в реакте или в ангуляре :)


                                Это ж ручной каторжный труд.

                                Так можно это все автоматически делать.

                                  0

                                  О, я, кажется, начал что-то понимать. Т.е. у нас, скажем, условно есть шаблон вида: <div id="1">{{ field1 }}</div>. Система автоматически выцепляет field1 и его принадлежность к div#1, и в field1.notifiers записывает это как div#1#text?


                                  Да, такая автоматика, конечно, возможна. Правда она одноуровневая. Т.е. у нас div#1 зависит от field1, который, в свою очередь, если и зависит от чего-либо ещё, то мы это тут не рассматриваем. Хотя, на мой взгляд, это самое интересное.


                                  Нашему кислотному товарищу не понравилось, что я сравниваю систему в комплексе, а не отдельно vDom и другие рендер подходы. Дескать, я приплетаю в этот вопрос лишнее, не относящееся к делу. Но я точно так не считаю. Ведь по сути, вот смотрите:


                                  Говоря о react с его vDom в наиболее идиоматичном виде мы говорим об immutable-values (context, redux, state, ...) + render + vDom + reconcilation + commit. В этой цепочке у нас есть 1 важная особенность, в render мы НЕ ЗНАЕМ какие поля изменились. Мы знаем, лишь то, что что-то изменилось (SCU), ведь это plain objects (POJO). Причём измениться могло оно на вложенном уровне наших данных. И даже если мы как-нибудь (например babel-plugin) распарсим наш JSX в конструкцию вида domNode -> notifiers of particular value, то воспользоваться мы ей не сможем, т.к. мы не знаем при рендере, изменился наш particular value или нет. Мы конечно можем пробежаться по всем задействованным путям в данных, но это уже никакой не O(1).


                                  Работа с POJO до render-а и selector-ов очень быстрая, простая, предсказуемая и т.д… Это крошечная часть под водой. А тяжёлая это как раз наш vDom. В случае если у нас сами данные реактивные (в крутом понимании этого слова), то у нас и возможностей больше. Но платим мы за это тяжёлыми обвязками и графом зависимостей, трекингом (вне render-а в данном случае) и т.д.


                                  Ну т.е. не получается рассматривать plain objects (типовой react-way) и такой подход в отрыве от "несущественных деталей" о том, что это за данные и что мы о них знаем.


                                  Тогда как в случае observable мы знаем более-менее, что именно у нас изменилось. И наш model.field1 может стриггерить свои dom-манипуляции. И для observable подхода мы можем построить такую цепочку обновлений.


                                  Я не прав?

                                    0
                                    если и зависит от чего-либо ещё, то мы это тут не рассматриваем.

                                    Если он зависит от чего-то, то мы его обновим через field1 = f(что-то). Вы, конечно, можете навернуть какую-то внутреннюю логику апдейтов вашей модели, можете через обсервабле, можете через setState(), можете редаксом обмазать. Аналогично как в реакте можете прикрутить те же обсерваблы или внутренние мутабельное двигло для моделей. Это же уже с рендерингом не связано никак вообще, так что не совсем понятно, как к обсуждению относится. У нас есть модель, когда модель меняется — мы должны обновить дом. Вот этот вот этап же (апдейт дома вслед за моделью) мы и рассматриваем. А как вы апдейтите саму модель? Ну как хотите так и обновляйте.


                                    Работа с POJO до render-а и selector-ов очень быстрая, простая, предсказуемая и т.д… Это крошечная часть под водой.

                                    Дык и в рассматриваемом случае то же самое — работа непосредственно внутри модели, то есть апдейт самой переменной field1 — это легко и быстро, медленно — это когда данный апдейт потом тригерит обновление соответствующей ноды.


                                    Вы там выше писали про иерархию. Я вот так сходу-спрыгу не могу придумать, как можно построить такую очередь обновлений грамотно, чтобы не потерять наш O(1)

                                    Так никак :)


                                    Еще раз — вы либо апдейтите дом на каждый чих за О(1), либо делаете свою "очередь", которая естественным образом оптимизируется до чендждетекшона за O(n) :)

                                      0
                                      работа непосредственно внутри модели

                                      Эмм… А какой смысл рассматривать исключительно работу внутри модели с плоской иерархией модели. Ну да, пока у нас существует только небольшой список полей с сеттером, мы в шоколаде. Но who cares? Как это связано с реальными задачами? Формочка авторизации?

                                        0
                                        Эмм… А какой смысл рассматривать исключительно работу внутри модели с плоской иерархией модели.

                                        А что меняет "плоскость" иерархии?

                                          0

                                          Ну когда у нас всякие мутабельные массивы идут всё становится сложнее. Значения добавляются, удаляются, всякие переборы этих массивов (построение diff-ов). В общем мути много. Помню я очень давно много копался и дебажил observableArray в knockout и соответствующую часть обновлений DOM. В итоге написал свой собственный binding который в моих задачах давал ускорение на 1 десятичный порядок. В голове отложилось: эффективный рендер изменяемого массива это сложно.


                                          Но мой посыл выше был больше иерархию вычисляемых полей. Ну что это за проект такой, где нам не нужны computed?! Всё время есть какой-нибудь граф данных, зачастую очень большой и сложный с хитрыми переплетениями. Вот я давеча был вынужден переписать проект со Vue на React + Redux только из-за странностей трекинга зависимостей во Vue. У меня шёл счёт на несколько тысяч observables и большое древо computed-полей на основе этих obersables. Из-за особенностей vue watcher.demand мне пришлось всё снести :(


                                          А тут у нас вообще речь про простые get+set поля. Рассматриваем только передовую (единичные реактивные поля), забыв про тылы, и наслаждаемся тем, как же быстро работает setAttribute минуя vDom. Осталось только маску сварщика с головы снять и оглянуться… )

                                            –1
                                            Ну когда у нас всякие мутабельные массивы идут всё становится сложнее. Значения добавляются, удаляются, всякие переборы этих массивов (построение diff-ов).

                                            Никакой diff ненужен, никакой перебор не нужен. Перебор может быть нужен только тогда, когда мы оптимизирует случай упомянутый выше случай, вернее его подслучай, а именно «очень много изменений структуры в цикле» и все эти изменения перекрывают друг друга.

                                            И именно на этом этапе и нужен diff, хотя даже тут спорно. Во-первых далеко не факт, что вся эта муть будет быстрее нативного dom, а во-вторых это целиком и полностью искусственный кейс и разбирать его не имеет смысла.

                                            Но мой посыл выше был больше иерархию вычисляемых полей. Ну что это за проект такой, где нам не нужны computed?! Всё время есть какой-нибудь граф данных, зачастую очень большой и сложный с хитрыми переплетениями. Вот я давеча был вынужден переписать проект со Vue на React + Redux только из-за странностей трекинга зависимостей во Vue. У меня шёл счёт на несколько тысяч observables и большое древо computed-полей на основе этих obersables. Из-за особенностей vue watcher.demand мне пришлось всё снести :(

                                            Опять рассуждения не относящиеся к теме, на которые я уже отвечал. Все эти связи существуют в нагромождениях поверх react(vdom) и будут в любом случае, если использовать этот подход.

                                            А тут у нас вообще речь про простые get+set поля. Рассматриваем только передовую (единичные реактивные поля), забыв про тылы, и наслаждаемся тем, как же быстро работает setAttribute минуя vDom. Осталось только маску сварщика с головы снять и оглянуться… )

                                            Любая нода это и есть get/set и больше ничего. А все ваши попытки говорить о чём-то ещё — это попытки уйти с обсуждаемой темы, на тему проблем дефолтных подходов. Т.е. говорить о теме в доступных(привычных) вам понятиях, т.к. за рамками оных вы теряетесь.

                                              0
                                              Но мой посыл выше был больше иерархию вычисляемых полей. Ну что это за проект такой, где нам не нужны computed?!

                                              Ну вот в реакте нету computed и прекрасно живут люди с этим.

                                                0

                                                Кто живёт? Как живёт? Люди цепляют redux-ы, селекторы, mobx-ы и т.д. React-приложение без подобных штук либо предельно простое, либо его не существует в природе. Ну либо это какой-то лютый неподдерживаемый страх господень.


                                                Вот на примере распространённой связки react + redux + reselect (и др. мемоизация): Селекторы = те самые computed. Строятся деревьями. Без них (или аналогов) нормальное больше приложение написать нельзя. К тому же считается крайне рекомендуемым подход с нормализацией данных, так что мем-селекторы просто must-have.


                                                В случае mobx, rxjs и других реактивных подходов — там совсем всё очевидно.


                                                Просто React выступает как остов для компонент и работа с DOM. Само приложение находится за пределами React-а. И при этом React работает с POJO. Т.е. требований к этому приложению вне React-а куда меньше. Но из-за этого никаких прицельных обновлений сделать нельзя.

                                                  0
                                                  Кто живёт? Как живёт? Люди цепляют redux-ы, селекторы, mobx-ы и т.д. React-приложение без подобных штук либо предельно простое, либо его не существует в природе.

                                                  Ну так вот во всех случаях, кроме мобх, нету никаких computed.


                                                  Селекторы = те самые computed.

                                                  А, ну так можете тогда просто любые ф-и объявить cmomputed и все.


                                                  Без них (или аналогов) нормальное больше приложение написать нельзя.

                                                  Не надо просто пихать все в глобальный стейт, тогда и проблем не будет :)

                                                    0
                                                    А, ну так можете тогда просто любые ф-и объявить cmomputed и все.

                                                    Могу. Главное что они есть. Они тяжёлые. И их нужно кешировать. Можно не кешировать, но будет тормозить. В случае observable кеширование из коробки. В случае мемоизаторов — тоже. Если голые функции — ну вы ССЗБ. Суть да — это вычисляемые поля. Часто образуются деревья вычислений + мемоизация.


                                                    Не надо просто пихать все в глобальный стейт, тогда и проблем не будет :)

                                                    Будет. И не важно глобальный он или нет. Тут главное, что стейт есть. И приложение от него зависит. Стейт меняется. Приложение должно адаптироваться под изменения. Взаимосвязи и хитросплетения сложные. Ну и т.д..


                                                    В общем шаг влево шаг в право от всяких формошлёпств (скажем большой редактор, или какой-нибудь excel) — и вы никуда не денетесь с подводной лодки.


                                                    А форму логина можно написать и без "этих ваших реактов" )

                                                      0
                                                      Могу. Главное что они есть. Они тяжёлые. И их нужно кешировать. Можно не кешировать, но будет тормозить. В случае observable кеширование из коробки. В случае мемоизаторов — тоже. Если голые функции — ну вы ССЗБ. Суть да — это вычисляемые поля. Часто образуются деревья вычислений + мемоизация.

                                                      Это все прекрасно, но при чем тут рендеринг и вообще V из MVC?


                                                      Будет. И не важно глобальный он или нет. Тут главное, что стейт есть.

                                                      Так тот, что есть и не глобальный, можно спокойно хранить внутри компонента, изолировано, без редаксов и мобиксов :)


                                                      В общем шаг влево шаг в право от всяких формошлёпств (скажем большой редактор, или какой-нибудь excel) — и вы никуда не денетесь с подводной лодки.

                                                      Если нужен какой-нибудь excel, то это уж не для реакта кейз. Для кровавого ынтырпрайза со сложной логикой, нетривиальным уи, и такое прочее есть ангуляр. Реакт — это больше про ленту твитов и вот это вот все :)

                                                        0
                                                        Это все прекрасно, но при чем тут рендеринг и вообще V из MVC?

                                                        Я же выше описал. В случае observable мы можем дёрнуть те самые get-set значения. В случае POJO не можем. Применимость vDom для true-реактивных значений под вопросом (можно сильно прогадать). Применимость обсуждаемого решения для immutable pojo ― просто невозможна.


                                                        Грубо говоря — ну не получается обсуждать это в отрыве. Мы не можем вот так вот взять оторвать V здесь и сказать — смотрите какой vDom плохой. Это совсем не конструктивно.


                                                        Т.е. не просто причём — а по сути самый главный вопрос в этом и состоит.


                                                        Если нужен какой-нибудь excel, то это уж не для реакта кейз

                                                        Ха-ха. Ок :)

                                                          0
                                                          > В случае observable мы можем дёрнуть те самые get-set значения. В случае POJO не можем.

                                                          Ну а рендер тут при чем?

                                                          > Грубо говоря — ну не получается обсуждать это в отрыве. Мы не можем вот так вот взять оторвать V здесь и сказать — смотрите какой vDom плохой.

                                                          Эм… почему не можем?

                                                          > Т.е. не просто причём — а по сути самый главный вопрос в этом и состоит.

                                                          Я все еще не могу понять, где связь. Вы четко можете ответ на этот вопрос сформулировать?
                                                            0
                                                            Ну а рендер тут при чем?

                                                            Render будет тогда и только тогда, когда мы дёрнем setter. А setter мы никогда не дёрнем, ибо нечем нам его дёргать. В итоге рендера нет. Либо дёргаем все сеттеры всегда… Получаем ерунду.


                                                            Вы четко можете ответ на этот вопрос сформулировать?

                                                            Боюсь, что более понятно, чем комментарием ниже не могу.

                                                          0

                                                          Попробую более понятно. Скажем у нас есть фреймворк работающий по обсуждаемой схеме. Статичная структура дом, плоская структура полей в модели (view-model). И ежу понятно, что мало-мальски сложное приложение так не построить. Но, начинаем рассматривать это как отдельный V-слой и игнорируем откуда и как к нам приходят данные. По сути у нас к внешней оболочке приложения только 1 требование: нам нужно чтобы оно дёргало сеттеры наших полей модели.


                                                          И теперь мы встаём перед вопросом. Как этого добиться?


                                                          В случае observable решений всё просто — subscribe-ся от конкретных значений, которые нас интересуют. Что-то вроде маппинга конкретных observable к конкретным setter-ам из V. Делается относительно тривиально. Поменялось поле в реальной модели -> observable.notify -> setter -> setAttribute. Ляпота.


                                                          А теперь у нас подход ака functional-like. POJO объекты и ссылочная целостность. Нам нужно дёрнуть изменённое поле? Откуда нам знать какое поле изменилось? А не откуда. Только пробежаться по всем полям и проверить. Т.е. к слою V мы добавляем ещё 1 слой V2, который будет держать руками вбитые привязки и актуализировать значения через сеттеры. Ну в общем что попало. Ещё пойди извернись и сделай это автоматически, не написав свой собственный observable.


                                                          При этом подход с obserable тяжёл до рендера, а с immutalbe pojo во время.


                                                          Надеюсь теперь моя позиция предельно понятна. vDom ведь придуман как раз для того, чтобы не было никакого трекинга в графе значений. Трекинг там как раз в dom-е. При наличии трекинга в данных вопрос в применимости vDom как раз весьма актуален. Нужен ли он там. Можно ж ведь прицельно стрелять.

                                                            0
                                                            Статичная структура дом, плоская структура полей в модели (view-model).

                                                            Почему плоская-то?


                                                            В случае observable решений всё просто — subscribe-ся от конкретных значений, которые нас интересуют.

                                                            В случае без обсерваблов все в точности так же — просто добавляем сеттер, который будет делать ровно то же, что делал бы обсервер.


                                                            Нам нужно дёрнуть изменённое поле? Откуда нам знать какое поле изменилось?

                                                            Так на поле сеттер стоит. Он и дернет все, что нужно.

                                                              0

                                                              Druu вы меня не понимаете. Или я вас. И похоже совсем. Какой setter? Куда добавляем? Откуда он возьмётся? Кто его дёрнет? О чём вы? )


                                                              Вот есть у нас V. У неё есть 10 полей. Т.е. 10 setter-ов. Есть у нас приложение, со своей бизнес-логикой. Работает по схеме immutable pojos (скажем мы сюда redux запихали или что-нибудь типа elm). Приложение автоматически определяет, что наш V должен обновиться, т.к. что-то из его зависимостей изменилось. Что именно — не известно. Какой setter из 10 будем дёргать? Все? Делать full comparison, а потом дёргать?


                                                              Это же сама парадигма рендера другая в vDom. Мы не знаем что изменилось и знать не хотим, позволяя внешнему приложению, работающему с исходными данными, ничего не трекать. Мы просто используем V как pure-function от данных. Никаких заморочек по определению какие именно данные изменились. Просто гоняем туда сюда immutable объекты. И за счёт многочисленных сравнений накатываем изменения на реальный DOM. Такая вот парадигма рендера. Да всё вверх-ногами, да необычно. Но именно так.


                                                              И ну это просто бессмысленно рассматривать и критиковать такую схему работу с данными и V в отрыве друг от друга. В таком случае vDom всегда будет слабым звеном, которое ну разве что на свалку.


                                                              Боюсь ещё подробнее я описать это не в состоянии )

                                                                0
                                                                Какой setter из 10 будем дёргать? Все? Делать full comparison, а потом дёргать?

                                                                В каждом из десяти сеттеров уже есть весь апдейт.Ничего дополнительно дергать не надо.


                                                                Это же сама парадигма рендера другая в vDom.

                                                                Ну с vdom да. Но мы же не про vdom?


                                                                И ну это просто бессмысленно рассматривать и критиковать такую схему работу с данными и V в отрыве друг от друга.

                                                                Так смысл разделения на вид и модель именно в том и состоит, чтобы их можно было рассматривать по отдельности.

                                                                  0
                                                                  В каждом из десяти сеттеров уже есть весь апдейт.Ничего дополнительно дергать не надо.

                                                                  Не понял. Ничего там нет. Надо явным образом его вызывать с новым значением.


                                                                  Ну с vdom да. Но мы же не про vdom?

                                                                  Мы тут про vDom vs "o(1) get-set".


                                                                  Так смысл разделения на вид и модель именно в том и состоит, чтобы их можно было рассматривать по отдельности.

                                                                  До каких-то пределов. В случае React на входе можно подать любые данные. В случае… как бы это назвать, нашего V:O(1), входных данных вообще по сути нет. Есть setter-ы которые надо вызывать "когда нужно". При этом "когда нужно" решает не V. Годится для observable (они знают "когда нужно").


                                                                  Либо я, либо вы, либо мы оба, держим в голове АБСОЛЮТНО разную ментальную картинку происходящего. Как на разных языках говорим.

                                                                    0
                                                                    Мы тут про vDom vs "o(1) get-set".

                                                                    Ну вот в "o(1) get-set" у вас все есть в геттерах сеттерах, конец истории. Нет?


                                                                    Либо я, либо вы, либо мы оба, держим в голове АБСОЛЮТНО разную ментальную картинку происходящего.

                                                                    У меня тоже такое ощущение :)


                                                                    вы какой вообще тезис пытаетесь сформулировать?

                                                                      0
                                                                      Ну вот в "o(1) get-set" у вас все есть в геттерах сеттерах, конец истории. Нет?

                                                                      Нет, там будет только то, что мы туда положим. Ничего не положим, ничего не будет.


                                                                      Но честно говоря мне сильно надоела эта дискуссия. Вот если бы это было за кружкой пива, да с листком бумаги. А так...


                                                                      Вот родит наш кислотный товарищ готовую библиотеку, тогда и продолжим, ок? )

                                                    0

                                                    Правда с приходом нового иммутабельного контекста, новых точечных useState хуков и примитивов для мемоизации из коробки в react, мы можем обойтись стандартными возможностями либы. Но используя мемоизацию мы построим всё те же computed-ы, просто называться они будут по-другому.

                                              0
                                              Еще раз — вы либо апдейтите дом на каждый чих за О(1), либо делаете свою "очередь", которая естественным образом оптимизируется до чендждетекшона за O(n) :)

                                              Ну тогда можно обновлять на каждый чих без всяких очередей. Мне кажется это довольно странно по сотне раз к ряду обновлять какое-то поле разными значениями. Нетипичный сценарий.

                                            0
                                            Зачем сохроанять всю историю, если мы последовательно сделаем все ченжи и остановимся на последнем?

                                            Условно говоря, несмотря на то, что setAttribute крайне быстр, значение для setAttribute может быть гхм… более тяжёлым. Ну так себе случай, но всё же :)


                                            Вы там выше писали про иерархию. Я вот так сходу-спрыгу не могу придумать, как можно построить такую очередь обновлений грамотно, чтобы не потерять наш O(1) в случае ветвлений в иерархии DOM (всякие if, else, switch, ...). По сути мы не можем в произвольном порядке накатить произвольное изменение, т.к. нужной domNode может вовсе не оказаться ещё, т.к. её изменение на её создание стоит в очереди глубже. Тут надо думать… Все мои мысли приводят к циклам и падению асимптотики )

                                              0
                                              Вы там выше писали про иерархию. Я вот так сходу-спрыгу не могу придумать, как можно построить такую очередь обновлений грамотно, чтобы не потерять наш O(1) в случае ветвлений в иерархии DOM (всякие if, else, switch, ...). По сути мы не можем в произвольном порядке накатить произвольное изменение, т.к. нужной domNode может вовсе не оказаться ещё, т.к. её изменение на её создание стоит в очереди глубже. Тут надо думать… Все мои мысли приводят к циклам и падению асимптотики )


                                              Спустя столько времени до вас наконец-то дошло(а возможно вы прочитали) проблема структуры. Только сообщаю вам, что никакая произвольная структура не нужна и эта та самая дыра в вашем понимании.

                                              Структура вся заранее описана, структура изменяется только в очень редких случаях — 90% кейсов это массивы. Очевидно, что любая операция над тем же массивом знает то, как она его изменяет и таким образом может изменить dom-отображение.

                                              Хотя я уже отвечал на этот вопрос, никакая иерархия dom не нужна — это рудимент, рудимент несостоятельный целиком и полностью.
                                            0
                                            Написано сумбурно, но я вижу в этом именно автоматический трекинг зависимостей. Иначе была бы формулировка вида «прописать руками в коде».

                                            Опять подмена понятий. Автоматизированный — это не только известный вам примитивный паттерн.

                                            Вот теперь я, несостоятельный человек, правильно понял идею?

                                            Слишком много лишнего в понимании.

                                            id: 'some-dom-id'

                                            Фундаментальный проблема номер раз — хтмл-мышление, либо текст-мышление.

                                            notifiers это руками вбитый список

                                            Фундаментальный проблема номер два — скриптуха-мышление. Проявляется это в непонимании различий между структурой и лапшой. Структура уже существует в коде, её не нужно никак отслеживать и прочее.

                                            codepen.io/anon/pen/yGXOjM — минимальный пример.

                                            0
                                            В варианте Pappagento никакого трекинга нет, просто обновили переменную и поправился дом. Представьте себе, что у вас есть класс-модель, и на полях этой модели стоит сеттер, который обновляет связанную с этим полем ноду. Присваиваете значение полю — обновляется нода. Все. Никаких listeners, вдомов, ченж трекингов и т.д. (ну или можно считать что у вас вырожденный observable с не более чем одним listener).

                                            Это бесполезно, но раз кто-то понял — я объясню нюансы. Мне лень объяснить их тем, кто целиком и полностью ничего не понимает и понимать не желает.

                                            В данном схеме существует одна проблема, фундаментальная проблема, которую и решает vdom(на халяву) — это структура. Дело в том, что в базовом подходе данные не просто обновляют, а изменяют структуру. Тем самым может появиться значение, которое вообще ни с чем не связано.

                                            Да, это решается построением объектов, которые сами всем этим управляют и проблем нет, но в дефолтной(обобщённой) ситуации это проблема. И если и пытаться критиковать этот подход, то именно с этой стороны.

                                            Расскажу ещё немного про решение. Дело в том, что в дефолтном подходе некий хтмл(т.е. структура некоего поддерева) попросту захардкорена, а даже если мы использует так называемый «компонентный подход», то это мало что меняет. В конечном итоге нижняя структура первичная, а верхняя вторична.

                                            Именно это и создаёт проблемы, но решение есть — это строить структуры данных таким образом, что они и будут представлять конечную структуру, а уже далее это будет преобразовано в ту самую dom-структуру.

                                            По-сути реакт(и vdom в частности) и является чем-то подобным, но плохой он потому, что он экспортирует наверх структуру «под», т.е. ненужную нам структуру dom, когда мы работаем со структурами данных. Именно с преобразованием структура данных -> структура dom и возникают проблемы.

                                            Если же наши данные сами генерируют структуру, то мы итак знаем что и куда менять. Подобные решения и используются в тех же observable структурах данных. Допустим, если мы имеем array в который мы добавили элемент, то мы из этого без всяких vdom можно вывести те операции, которые нужно произвести в dom для воспроизведения нужной структуры.

                                            Допустим, если мы делаем push(value), то мы точно знаем, что нам нужно создать ноду и добавить её как child другой ноде, которая и представляет наш array, которая напрямую связана с array.

                                            Тот же vdom этого сделать не может потому, что там произвольная структура(представляющая структуру dom, а не наши данные), да и вообще это несостоятельное immutable.

                                            Все прекрасно, О1, но проблема в том, что если вы, например, в цикле совершили на модели 1000 изменений, то это вызовет 1000 апдейтов.

                                            Проблема целиком и полностью надуманная. Но даже она имеет элементарное решение — ввод инварианта(в рамках описанного мною выше решения) одно обновление на ноду.

                                            Если изменения будут изменять структуру, то в самом примитивном варианте туда без проблем впиливается diff. Более вменяемые решения реализуются уже для конкретных структур данных и в рамках их логики.
                                              0
                                              Но даже она имеет элементарное решение — ввод инварианта(в рамках описанного мною выше решения) одно обновление на ноду.

                                              Какое из тысячи?
                                              Вот я написал a = 1, a = 2, a = 3, a = 4, и у меня биндинг на а, с-но я а изменил 4 раза. Какое из изменений должно отработать?


                                              Если изменения будут изменять структуру, то в самом примитивном варианте туда без проблем впиливается diff.

                                              И как только вы ввели дифф у вас получился реакто/ангуляр.

                                                0
                                                Какое из тысячи?

                                                Очевидно, что последние.

                                                Вот я написал a = 1, a = 2, a = 3, a = 4, и у меня биндинг на а, с-но я а изменил 4 раза. Какое из изменений должно отработать?

                                                Код не имеет смысла, проблема не имеет смысла.

                                                И как только вы ввели дифф у вас получился реакто/ангуляр.

                                                Неверно. Здесь нет произвольной структуры, здесь нет структуры пляшущей от dom и нет всех этих проблем и тормозов.

                                                К тому же, почему вдруг обновление ноды на каждый чих вообще стало является проблемой? Проблемой является перерисовка, а не обновление.

                                                  0
                                                  Очевидно, что последние.

                                                  Ну так я же уже описывал дальнейшие проблемы. Во-первых, вам надо как-то явно дергать апдейт, раз он не происходит на просто set. Во-вторых — у вас почти все биндинги это (х, х), с-но в качестве оптимизации вы прикрутите цд, который будет дергаться по триггеру. И получили то, с чего начинали :)


                                                  Неверно.

                                                  Как же неверно, если описанная вами конструкция — это в точности то, что используется в современных фреймворках?


                                                  Здесь нет произвольной структуры, здесь нет структуры пляшущей от dom и нет всех этих проблем и тормозов.

                                                  Дык и в ангуляро-реакте ее нет. Там есть структура которая содержит новое состояние биндингов, та же структура, что и у вас. Можете назвать ее виртуальный дом. Домом она, конечно, не является и вообще дому в том или ином смысле соответствовать не должна. Единственное ее предназначение — узнать, что изменилось с прошлого раза (какие биндинги заапдейтились).


                                                  К тому же, почему вдруг обновление ноды на каждый чих вообще стало является проблемой? Проблемой является перерисовка, а не обновление.

                                                  Поскольку обновление ведет к перерисовке — оно стало проблемой, да.

                                                    0
                                                    Поскольку обновление ведет к перерисовке — оно стало проблемой, да.

                                                    Не обязательно. Смотрите:


                                                    const domNode = ...();
                                                    domNode.innerText = '1';
                                                    domNode.innerText = '2';
                                                    domNode.innerText = '3';

                                                    Сколько было настоящих перерисовок (reflow)? 1-а. Когда она произошла? Условно — на следующий тик. Почему? Ну браузер ждёт пока JS прекратит свои издевательства и только потом делает reflow.


                                                    Ну и можно всю малину испортить, если где-нибудь посередине дёрнуть что-то типа domNode.offsetWidth. Тогда будет досрочный reflow.

                                                      0
                                                      Дык и в ангуляро-реакте ее нет. Там есть структура которая содержит новое состояние биндингов, та же структура, что и у вас

                                                      Вы не могли бы пояснить, что вы имеете ввиду на примере react? Там ведь как раз структура, которая пляшет от dom (только ненастоящего браузерного, а супер-облегчённого pojo). И там не хранятся состояния байндингов, нет. Там хранится всё. Ну словно у нас есть в шаблоне 5 нод, и из динамики 1 innerText и 1 setAttribute. При том что реальных аттрибутов и пр. (включая статичные) там в 10 раз больше. Так вот, react будет хранить ВСЁ ЦЕЛИКОМ, а не два наших байндинга. Такие дела :)

                                                        0
                                                        Вы не могли бы пояснить, что вы имеете ввиду на примере react? Там ведь как раз структура, которая пляшет от dom (только ненастоящего браузерного, а супер-облегчённого pojo). И там не хранятся состояния байндингов, нет.

                                                        Это просто способ представления, который показался удобен разработчикам для реализации диффа. По факту, от вдома не требуется ничего, кроме фиксации биндингов.

                                                          0
                                                          По факту, от вдома не требуется ничего, кроме фиксации биндингов.

                                                          Согласен. Но только при условии что хотя бы сам шаблон статичен. Какой был при первом рендере, такой и остался. Но это не про React, конечно. Там хоть random вытворяй.

                                          0
                                          На чём основаны эти заявления?

                                          Я имею некоторое представление как работают разные подходы в обсуждаемом вопросе. Ни один из них не работает при помощи магии. Вот, скажем, самый необычный подход.


                                          Ну тогда объясню попроще — прямой биндинг есть O1 решение по определению. Если ещё проще, прямое изменение абстрактного value в dom-node является O1 решением.

                                          Надо рассматривать систему в комплексе. Судя по вашему описанию выше, вы описали observable подход. См. скажем, knockoutjs. А у него асимптотика "render"-а зависит не от количества dom-нод, а от сложности графа зависимостей. И очень сильно от реализации трекинга зависимостей. Т.е. условно, при vDom у вас асимптотика O(n), а при observable aka knockoutjs у вас O(m). И разные константы при этом. Константа у observable большая, и сама структура, обеспечивающая такую работу — тоже.


                                          Т.е. если попроще: несмотря на то, что какой-нибудь knockoutjs может прицельно поменять какой-нибудь аттрибут dom-ноды в зависимости от какого-нибудь computed-а, а не строить кусок псевдо-древа и потом его реконсилировать, делать такую "простую" задачу он может как дольше, так и быстрее. И это сильно зависит от реальной зависимости этого computed-а. И от трекинга зависимостей. И от прямоты рук разработчика.


                                          Возможно вы очень плохо понимаете как они работают. Если это так, то попробуйте реализовать простейший observable-примитив самостоятельно. Довольно быстро увидите, что O(1) там и не пахнет.


                                          P.S. ничего против "паттерна наблюдатель" не имею. На нём основано множество библиотек и фреймворков. Скажем тот же redux — это observable. Vue — observable, KnockoutJS — observable. В каком-то виде observable сквозит из каждой щели, это удобный примитив. Вопрос лишь в том, является ли он у вас главным кирпичиком в системе, или нет. И касательно рендера, некоторые библиотеки с observable, к примеру Vue, несмотря на observable используют именно virtualDOM для рендера. Т.е. они целенаправленно отказались от подхода описанного выше. Вероятно сделав замеры посчитали vDom более быстрой альтернативой. Кто знает — я не копал в эту сторону.


                                          И последнее. Не стоит писать в таком агрессивном стиле. Мы не на lor-е.

                                            –1
                                            Я имею некоторое представление как работают разные подходы в обсуждаемом вопросе. Ни один из них не работает при помощи магии. Вот, скажем, самый необычный подход.

                                            Я имею как минимум не меньшее представление, а про магию вам никто не говорил.

                                            Надо рассматривать систему в комплексе.

                                            Неверно. vdom не является комплексом.

                                            Судя по вашему описанию выше, вы описали observable подход.

                                            Неверно.

                                            См. скажем, knockoutjs. А у него асимптотика «render»-а зависит не от количества dom-нод, а от сложности графа зависимостей.

                                            Опять подмена понятий. Все графы зависимостей там существуют между данными, которые никакого отношения к render не имеют, и такие же зависимости будет с vdom и без него.

                                            Никаких графов между dom-node и ассоциированным с ним значением/объектом — нет. И именно тут происходит подмена понятий, а именно рассуждения на тему «как связано это конечное значение и неким абстрактным состоянием приложения». Всё это не имеет отношения к делу и vdom тут никак не помогает.

                                            И очень сильно от реализации трекинга зависимостей. Т.е. условно, при vDom у вас асимптотика O(n), а при observable aka knockoutjs у вас O(m).

                                            Как это удобно, манипулировать а потом забывать, делая вид, что ничего не было. А ведь и там и там было O(n).

                                            И разные константы при этом. Константа у observable большая, и сама структура, обеспечивающая такую работу — тоже.

                                            Дак констант же нет? А теперь уже есть. И да, с чего вдруг она большая? Опять какие-то нелепые тезисы.

                                        0
                                        Приятно, что хоть кто-то ещё в мире называет костыль «костылем». Не подскажете, есть ли хоть какие-то более-менее популярные фреймворки, которые не пытаются эмулировать декларативность через императивщину? Из непопулярных-то я знаю. DerbyJS имеет замечательную архитектуру с честными декларативными шаблонами. Но интересно, существует ли какой-нибудь более популярный проект?
                                          0
                                          Вы не поняли. Если я называю это костылём, то это не значит, что я выступаю за какую-то «декларативность». В том и проблема этих решений, что вся эта декларативность(в моём понимании) несостоятельна и причин тут много. Во-первых это очень абстрактное — нет никаких конкретных критериев для отличения одного от другого. Во-вторых сами декларации несостоятельны т.к. любой набор деклараций есть императивщина по определению.

                                          Поэтому я вижу только одно решение — выпилить и забыть о несостоятельной декларативной концепции. И тут наши взгляды расходятся и я вам никак не помогу. Это значит, что я считаю подход jsx куда более вменяемым, нежели любые признаки текстовых шаблонов. А т.к. текстовые шаблоны несостоятельны, то все они императивны и по-сути имплементируют какой-то имеративный недоязык в рамках себя. Соответственно, любой из этих недоязыков является мене состоятельным через язык реальный, а значит язык реальный куда более подход для выражения всего того, что выражается через этот недоязык.

                                            0
                                            Соответственно, любой из этих недоязыков является мене состоятельным через язык реальный

                                            С чего бы вдруг? Специально заточенный под задачу язык, очевидно, более удобен и эффективен для решения этой задачи, чем язык общего назначения. Точно так же, как ЯП более эффективен для программировании, чем универсальный ЕЯ.

                                              0
                                              Специально заточенный под задачу язык, очевидно, более удобен и эффективен для решения этой задачи, чем язык общего назначения.

                                              Правильно, а в данном случае этим самым специально заточенным языков — является язык общего назначения.

                                              А специально заточенным языком в общем случае будет является api на том самом языке. Да, js очень слаб для этого, но это проблема чисто js.
                                                0
                                                Правильно, а в данном случае этим самым специально заточенным языков — является язык общего назначения.

                                                Нет, никакой язык общего назначения не заточен под генерацию дом. Если он станет заточен — то сразу перестанет быть, собственно, языком общего назначения.


                                                Да, js очень слаб для этого, но это проблема чисто js.

                                                А какой язык не слаб?

                                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                      Самое читаемое