Pull to refresh
17
0

User

Send message

На мой взгляд в сравнении фреймвоков и самописных решений упущен очень важный момент — количество кода и зависимостей и при при прочих равных когда баги есть и в устаревших фреймворках и в самописных решениях второй вариант лучше потому что там просто меньше кода. Для наглядности — возьмем популярные шаблоны для создания приложений. При создании нового пустого проекта с популярным нынче create-react-app устанавливается 969 зависимостей, а с vue-cli устанавливается 1434 зависмости. 1434, карл!!! Мне кажется, имея 1434 пакетов, придется чуть ли не каждый день изучать ченджлоги потому что обновился пакет и исправил какие-то баги или добавил какие-то фичи. А что насчет безопасности? Сколько вы потратите времени на аудит, учитывая что каждый из этих пакетов может либо содержать увязвимости, либо быть скомпрометированным и достаточно добавить post-install-скрипт в один из этих 1434 зависимостей чтобы при выполнении команды "npm i" этот скрипт получит полный доступ к компьютеру с возможностями начиная от воровства токенов закачивая шифровальщиками или всякими вирусами. И при всем этом я что-то ни в одном проекте не видел чтобы зависимости коммитили в гит (чтобы избежать подмены исходников на npm, компрометации, или вообще взлома серверов по ссылках в lock-файлах) А какой процент фич от этих тысячи пакетов реально будет использоваться? Учитывая что каждый пакет имеет тенденцию добавлять все новые фичи на все возможные случаи, то чем больше комбинаций из этих пакетов — тем больше будет дублирования этих фич и попытка для большого бизнес-проекта, которому нужны гибкие и специализированные решения и быстрое развитие, выбрать какой-то жирный фреймворк как минимум столкнется с конфликтами, ограничениями и костылями. А теперь если сравнить с самописными решениями? Реакт (до 16 версии имел 20-25к теперь 18к строк) можно написать на 200 строчек кода, получив diff виртуального дома + компоненты. Имея js-парсер можно на 100 строчках реализовать сборку в бандл и webpack вместе с 431 зависимостями будет не нужен. В итоге, если сравнивать по количеству багов, то для того чтобы исправить какой-то баг — что проще — разобраться в устаревшем фреймворке на 20к строчек (собранный вдобавок из кучи других зависимостей) или в маленьком файле на 200 строчек? Я уверен что самописные решения (хотя правильно это было бы назвать "специализированный инструмент заточенный под нужды бизнеса") всегда будут гибче и проще как в поддержке так и развитии

там не передаются инстансы — посмотрите в дебагере — слот заменяется на объекты VNode который является обычным объектом описывающий верстку — точно так же как в реакте. Другое дело что vuejs прикрепляет к этим объектам ссылки на инстансы компонентов, но это уже недостаток реализации самого vuejs
O: Хотя React это позволяет, я не думаю, что это хорошая идея.

Странное мнение. Как по мне это самый большой недостаток vuejs и вместе с тем нарушение компонентного подхода. Есть компоненты которые могут принимать данные через пропсы. Никто же нас не ограничивает в том сколько раз мы эти данные отобразили в компоненте, так почему теперь, когда вы решили передать компоненту несколько div-элементов в качестве пропса-слота, мы жестко ограничены в том что больше одного раза отобразить его в компоненте мы не можем? Вот в реакте сделали настоящий компонентный подход — передав элементы через пропсы (либо вложенные либо через отдельный пропс) <Component someContent={<div>...</div>}/> — компонент может сколько угодно раз отрендерить то что ему передали а не один раз как во vuejs.

Интересно где у этого фреймворка mapStateToProps или хотя бы возможность сделать вложенные actions? На приложениях вроде счетчика или тодошек все фреймворки выглядят сносно но вот ближе к реальности начинаются ограничения и костыли. В данном случае допустим мы хотим добавить фичу вложенных тодошек, чтобы юзер мог разбить большую подзадачу на мелкие а те в свою очередь на более мелкие и не нужно ограничивать юзера в количестве уровней. Как эта задача решается в hyperapp? Если идти по пути вложенности (хранить в объекте todo массив вложенных тодошек) то как обновить текст какой-то тодошки на n-уровне вложенности? Нам нужно вернуть новый объект состояния и нужно как-то рекурсивно пересоздать объекты и массивы всех родителей и неясно как это решается hyperapp. А если идти по пути нормализации, когда в тодошке храним не объекты а айдишники а сами объекты в плоском хеше объектов по их айдишнику то нужно добавить аналог mapStateToProps чтобы изменения одной тодошки не вызвало перерендер всего списка тодошек или вообще всего приложения
возможность подписаться только на обновления всего стора (утомительные селекторы + возможны неожиданные перерисовки)

В примере из статьи компоненты по прежнему подписываются практически на весь стор — например при обновлении одной единственной тодошки будет оповещен весь список тодошек
хотя изменения касаются только одного компонента и нет смысла оповещать все остальные. Дальше неясно какую организацию стора предполагает библиотека чтобы работали подписки. Если это вложенные структуры то как библиотека предлагает обновлять данные которые находятся глубоко внутри объекта? В примере в статье предполагается иммутабельное обновление стора то как в этом случае предлагается работать с такой структурой — главный объект состояния хранит объект юзера, в нем хранится массив объектов folder, в каждой папке хранится массив объектов project, в каждом проекте массив объектов task в каждом задаче массив объектов comment и каждый комментарий может хранить вложенные объекты других комментариев и эти комментарии могут неограниченно вкладываться друг в друга — как библиотека предлагает обновлять текст глубоко вложенного в этом случае комментария? А если все же предполагается нормализация стора когда вместо объектов в массиве храним айдишники а все объекты будут храниться в хеше где айдишнику соотвествует объект то как будут работать в этом случае подписки?
А вообще тему подписок только на часть состояния без каких либо проблем с обновлением глубоко вложенных объектов (и также без необходимости нормализировать стор) я подробно разобрал в этой статье а в этой статье я разобрал разные по эффективности алгоритмы оповещения подписчиков

Невозможность много раз отобразить в компоненте переданный ему контент ломает, на мой взгляд, всю концепцию композиции компонент — почему-то если мы в компонент передали какие-то данные через пропсы так их можно отрендерить сколько угодно раз, а как только передали контент (верстку или другие компоненты) через слоты — так теперь можно отрендерить слот только один раз и необходимо либо дублировать контент либо изобретать другие костыли (либо переходить на реакт ;)

Будет ли во vue.js добавлена возможность рендера одного и того же слота в компоненте дважды как это позволяет делать реакт?
Will vue.js be able to render the same slot (or named slot with the same name) twice as it's allowed in react ?


<div>
 ...content
 <slot>
 ...conent
 <slot>
 ...content
</div>

//[Vue warn]: Duplicate presence of slot "default" found in the same render tree - this will likely cause render errors.

//in react - all working fine
<div>
 ...content
 {this.props.children}
 ...conent
 {this.props.children}
 ...content
</div>
А кто-то может сказать почему такие баги с символами вообще происходят? Насколько я знаю символ состоит из глифов, глиф из сплайнов а те из кривых Безье и правила наложения глифов друг на друга четко описаны в форматах TrueType или OpenType. Где тут могут быть баги, неужели нельзя просто написать не глючный движок рендера набора кривых Безье?
Действительно, что-то я пропустил этот момент, спасибо за замечание
Во-первых, программист может захотеть прочитать зависимую ячейку до того как сработает таймер, который её «состарит» и она выдаст неактуальное значение,

Для этого последнего алгоритма «cпуска-подъем» который описывается в статье нет никакой разницы сработает ли вычисление после таймера или когда программист захочет прочитать зависимую ячейку до того как сработает таймер. При вызове метода .get() если значение не равно «actual» будет точно такой же процесс актуализации зависимостей как и при вызове таймера. И соотвественно вернется всегда только актуальные значение.
дальше запускается «состаривающий» проход, он правильно состаривает [c], тк. [a] и [b] изменились и пересчитать (позже, после этого прохода) всё же нужно (алгоритм же не знает, что получится исходное значение), но как ему понять нужно ли состарить [d] не вычисляя пока [c]?

Если мы имеете ввиду невозможность после двух присваиваний избежать вычисления вообще (потому что ячейка [c] при вычислении все равно не изменит свое значение) то да тут я согласен и даже уверен избежать вычисления [c] в принципе невозможно.
Если же вы спрашиваете про ячейку [d] то тут все просто — она после того как вычислится [c] увидет что ее значение не изменилось и сама не будет вычисляться. (точнее это не она увидит а ячейка [c] не установит ей флаг «dirty» потому что сама не изменила значение, которое потом проверится и «check» заменится на «actual», но это сути не меняет)
А если здесь всё же вычислять [c], то это уже по сути возвращение к первой схеме реализации РП, в которой этой проблемы изначально нет.

Это в какой такой первой схеме реализации РП этой проблемы изначально нет? Нет лишних вычислений только в третьем варианте который описывается в статье (я его называю как «спуск и подъем»)
Я решил разбирать сложности по очереди. Если лишние вычисления появляются уже при изменении только одной ячейки то что говорить про множественные изменения? Поэтому в статье и описывается три алгоритма уменьшения вычислений при изменении одной ячейки где только последний («спуск-подъем») обеспечивает наименьшее количество вычислений.

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

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

Ну контекст это по сути просто объект в который помечаются общие для какого-то поддерева компонентов данные. Если данные контекста не зависят от того в каком поддереве находится компонент например i18n и другие данные то мы можем просто вынести этот контекст во внешний объект в отдельном файле выполнить заимпортить этот объект и обращаться к нему напрямую. Если контекст у разных поддеревьях компонентов разный то всегда можно выразить эту сутуацию не в через компоненты а через данные.


По поводу внешнего хранилища. Тут ещё встаёт вопрос реализации. Скажем redux-react очень уязвим в вопросе количества подписчиков. Скажем если у вас приложение рендерит сотни или тысячи мелких компонент, то подписываясь в каждом из них на store, вы заставляете connect вычислять mapStateToProps на любой чих (коих может быть очень много). В лучшем случае это просто будет жрать батарейку, в худшем ещё и тормозить.

O, вы обратились по адресу — в статьях тут, тут и тут я разбирал эту проблему и ее решение в виде mobx

Ну да, с новым контекстом реакта доступ возможен только внутри render-метода. То что этот контекст не будет доступен в разных хуках это проблемы нового контекста. А вообще, если нужно связывать несколько полей, то зачем нужен контекст — не лучше ли использовать внешнее состояние? Я выше привел вариант hoc-ов когда вместо композиции мы используем наследование и избегаем компонентов-оберток в рантайме что позволит улучшить производительность и каких-то минусов по сравнению композиционными hoc-ами я не вижу
Кстати я тут описал общий способ реализации этих двух вариантов но для алгоритма которого я описал в статье (рекурсивный спуск и подъем) реализация будет состоять в добавлении всего нескольких строчек. В примере в статье я убрал для наглядности но в варианте который находится в репозитории уже добавлена реализация второго варианта с таймером. И никаких лишних вычислений у нас снова нет)
Это не те лишние вычисления которые связаны с алгоритмом. В статье я рассматривал ситуации избегания лишних вычислений только при изменении одной ячейки. Если же нужно чтобы несколько изменений одной или разных ячеек накапливались и выполнялся в итоге толко один набор перевычислений то это легко добавить. Тут есть два способа. Первый это написать некий враппер подобно декоратору action mobx внутри которого будем выполнять все операции обновления. При вызове этого враппера устанавливаем глобальных флаг и потом ячейка посмотрит на этот флаг будет вычисляться не сразу а добавится в некий временный массив. А в конце вызова этот враппер снимет глобальных флаг и вызовет обновление накопившихся ячеек. Но у этого варианта есть недостаток — нужно постоянно врапить асинхронные операции и так же он будет работать с async-await. А вот вторым вариантом является установка минимального таймера и отложенный пересчет ячеек после того как произойдет синхронная установка новых значений

Да просто использовать те же хокки но только не в виде


function hoc(TargetCmponent){
  return class extends React.Component {
   render(){
     return <TargetCmponent {...this.props}/>
   }
 }
}

а в виде


function hoc(target){
  const isTargetClass = target instanceof React.Component
  return class extends (isTargetClass ? target : React.Component) {
    render(){
        return isTargetClass ? super.render() : target(this.props) 
    }
  }
}

и все, больше никаких вложенностей и глубоких матрешек из компонентов-оберток — сколько бы хокков мы бы не навесили на компонент — да хоть тысячу — в рантайме все равно будет создан только один компонент что будет быстрее, так как вызов рендера не создаст тысячу объектов-элементов виртуального дума для этих врапперов, и реакту нужно будет выполнить diff одного компонента а не тысячи

Изобретаем HOC? Мешает то, что получаем ещё 1 уровень вложенности, который усложняет и debugging, и несущественно, но всё же влияет на производительность. У нас и так в сложном приложении повсюду возможны матрёшки из HOC, а так матрёшками нужно покрывать компоненты, которые используют context. Добавить к этому какие-нибудь styled-components, где матрёшкой обёрнут каждый тег со стилями, и получается, что древо компонент почти целиком состоит из композитных матрёшек. С точки зрения приложения — ок, работает же. С остальных точек зрения — спорно, очень спорно..

Используйте для HOC наследование вместо композиции и никаких новых уровней вложенности добавляться не будет
Ячейка label больше не будет зависеть от fullName а fullName так как от него никто не зависит и он не является «активным» обсервером то тоже отпишется от своих зависимостей и больше не будет вычисляться когда будет обновляться lastName
Тут подразумевалось что после того как браузер распарсит javascript у нас будет только вызов функции, то есть парсить в самом фрейморке уже не нужно

Мне кажется они в своем глиммере перемудрили с этим опкодом и уж тем более с бинарным кодом. Ради чего все это — для того чтобы уменьшить бандл? Или скорость парсинга? Мне кажется jsx версия <h1>Hello World</h1> которая скомпилируется в h('h1', null, 'Hello-world') не так уж и много занимает и парсить отдельно не нужно — скорость парсинга js достаточно быстрая в браузерах а браузеры и так ее пытаются постоянно улучшить. В общем экономия на спичках учитывая что сейчас модно использовать webpack со всяким code-splitting а не собирать все это в один большой бандл.
А если взглянуть что скрывается за этими опкодами и виртуальной машиной то там будет перестраиваемое дерево (или точнее связанный список) функций (шаблонных привязок) которые будут проверяться при перерендере шаблона, и в зависимости от значений отдельных привязок это дерево будет перестраиваться чтобы уменьшить количество проверок. Идея этой техники шаблонизации интересная потому что она явно быстрее чем virtual-dom реакта которому нужно проверять все статические части а не только выражения внутри шаблонных-привязок. Но вместо того чтобы просто реализовать эту технику шаблонизации ребята в глиммере решили намудрить и сделать не полноценные выражения (то есть нельзя в шаблоне <div>{{...}}</div> выполнить произвольное js-выражение как в реакте) и закодировать эти привязки как опкоды, то есть взять на себя роль движка js и выполнять выражения которые там доступны самостоятельно. А это естественно будет медленней потому что тот же v8 скомпилирует обращение к свойству "obj.someproperty" в один "mov [eax + 0x4343] ebx" а интерпретатору глиммера потребуется вытащить следующий опкод проверить тип и т.д — то есть выполнить в десятки если не в сотню раз больше операций.

Information

Rating
Does not participate
Registered
Activity