Давайте представим, что вас перевели на новый проект. Или вы сменили работу и о проекте максимум только слышали. Вот вы садитесь за рабочее место, к вам приходит менеджер, жмёт руку и… прямо сходу открывает страницу проекта, тыкает пальцем в монитор и просит вставить «информер о предстоящем событии Х». На этом вы расстаётесь… Что делать? С чего начать? Как создать «информер»? Где найти нужный шаблон? И море других вопросов.

Под катом будет рассказ, как мы стараемся организовать эти процессы, какие инструменты создаём для препарирования SPA. Кроме этого, мы поговорим о технических подробностях реализации Live Coding / Hot Reload и чуток о VirtualDom и React с Angular.

Приступим. Итак, вот вы и остались один на один с проектом, тимлид сообщил, где найти репозиторий, а дальше — читай README.md, и всё.

README.md


Это отправная точка при погружении в проект, она встречает вас базовой информацией:

  • Установка — тут описаны все шаги, как запустить проект, чтобы даже «дизайнер» мог это сделать;
  • Запуск — примеры базовых команд для запуска проекта;
  • Опции запуска — список всех возможных параметров и их описание;
  • «Первые шаги» — собственно, это и есть нужный раздел:
    • быстрый поиск UI-блока — описание инструмента для препарирования приложения;
    • «Что? Где? Когда?» — краткое описание структуры проекта;
    • создание UI-блока — минимальная информация, как создать UI-блок;
    • «Логика приложения, или где искать обработку событий?»;
  • Примеры/скринкасты — экспериментальный раздел с примерами.

Пример, как это выглядит в интерфейсе gitlab



На всё про всё уйдёт примерно пять минут. Самое главное, что вы узнаете из README: для решения задачи нужно:

  1. Установить NodeJS/npm.
  2. Клонировать репозиторий проекта.
  3. Запустить npm install и npm start.
  4. Открыть проект в браузере и нажать на «пипетку» в правом нижнем углу. ;]

Но давайте по порядку.

Установка


Мы уже очень давно используем пакетную разработку, поэтому многие части (grunt- и gulp-таски, утилиты, UI-компоненты и т. п.) разрабатываются как отдельные npm- или jam-пакеты. Такой подход даёт возможность максимально переиспользовать код между проектами, обеспечивает версионность (по semver) и, кроме этого, позволяет собрать инфраструктуру для каждого пакета именно под задачу. И главное, никакого легаси, пакет самостоятелен и, кто знает, может со временем превратиться в хороший opensource.

Кроме этого, не забывайте применять npm-хуки, например postinstall. Мы его используем для установки таких git-хуков, как:

  • pre-commit — проверка стиля кодирования (ESLint);
  • pre-push — запуск тестов;
  • post-merge — запуск npm install и jam install.

Последний хук может показаться странным, но, когда вы работаете с кучей пакетов, которые динамично обновляются, без него никак. Набрав git pull, разработчик должен получить актуальную версию проекта, чего можно достичь, только принудительно запустив npm install.

Если проект зависит от npm или другого стороннего менеджера пакетов, позаботьтесь о локальном registry, чтобы не зависеть от внешнего мира и его проблем (left-pad, Роскомнадзор и т. п.).

Запуск


npm start — всё, что нужно знать, и неважно, что у вас под капотом: gulp, grunt, webpack… Выше я уже писал, что в README.md есть описание параметров запуска: при старте приложение читает README.md, парсит список опций и их описания и, если вы используете неизвестную или недокументированную опцию, выдаёт ошибку. Вот таким нехитрым способом решается проблема документации: нет описания — нет опции.

Пример запуска:

npm start -- --xhr --auth=oauth --build

> project-name@0.1.0 start /git/project-name/
> node ./ "--xhr" "--auth=oauth" "--build"

 - Ветка: master (Sun Aug 29 2016 10:28:06 GMT+0300 (MSK))
 - Дополнительные опции
    - xhr: true (загрузка статики через `XMLHttpRequest`)
    - auth: oauth (авторизация через `proxy`, `oauth`, `account`)
    - build: true (использовать сборку)
 - Сборка проекта
 - Запуск сервера на 3000
 - Сервер запущен: localhost:3000

Первые шаги


Вернемся к задаче. Итак, README.md прочитан, проект установлен и запущен, переходим к пункту «быстрый поиск блока, или „пипетка“ — наше всё».

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

Пример использования
Пипетка
image

Инспектор
image

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

Теперь кликаем на название файла, и… открывается IDE, а курсор установлен в нужную строчку. Рядом есть «глаз», если нажать на него — откроется GUI/просмотрщик с выбранным блоком.



Всё, основные точки входа нашли, теперь приступим к добавлению «информера».

Создание UI-блока


Есть два пути создания блока (оба описаны в README):

  • через консоль;
  • используя GUI.

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

GUI


Это веб-интерфейс для просмотра, а главное, разработки UI-блоков проекта. Что он умеет:

  • просмотр списка всех блоков;
  • простой поиск по названию и ключевым словам;
  • вывод всех вариантов использования для конкретного блока;
  • создание нового блока;
  • переименование блока.

image

Первым делом нужно узнать, нет ли в проекте подобных информеров. Воспользовавшись поиском, находим подобный блок, опять используем «пипетку» для изучения его структуры и нажимаем «+», вводим название нового блока, кликаем «ОК», после чего GUI открывает просмотр созданного блока. Снова используем пипетку и открываем IDE для редактирования css/шаблона/js.



Итак, что же произошло? После нажатия кнопки «ОК» GUI создаёт папку с типовым блоком, который в нашей архитектуре состоит минимум из четырёх файлов:

  • block-name.html — шаблон блока

    <div></div>


  • block-name.scss — стили

    .block-name {
       padding: 10px;
       background: red;
    }
    


  • block-name.js — описание поведения

    import feast from 'feast';
    import template from 'feast-tpl!./block-name';
    import styleSheet from 'feast-css!./block-name';
    
    /**
     * @class UIBlockName
     * @extends feast.Block
     */
    export default feast.Block.extend({
       name: 'block-name',
       template,
       styleSheet
    });
    


  • block-name.spec.js — спецификация, на основе которой строятся примеры использования

    export default {
      'base': {
        attrs: {}
      }
    };
    



При редактировании любого из этих файлов все изменения применяются без перезагрузки страницы. Это не просто модная забава, а огромная экономия времени. Блоки могут иметь логику, а Hot Reload позволяет не терять текущее состояние, что происходит при F5 / cmd + r. Ещё при редактировании шаблона подключённые блоки автоматически обновляются. Иными словами, GUI немного программируют за вас. ;]



Вот так, почти ничего не зная о проекте, можно добавить новый блок. Вам не требуется читать километры документации, чтобы выполнить обычную задачу. Но это не значит, что «километры» не нужны: ещё как нужны — для углубления знаний и жизни проекта без его основных мейнтейнеров. Например, для работы с API и бизнес-логикой у нас есть внутренняя JSSDK, документация которой генерируется на основе JSDoc3.

Мини-итог


Изучать документацию и кодовую базу проекта нужно и правильно, но уже на стадии основательного погружения, поначалу же достаточно описать сценарии выполнения типовых задач. Такие инструкции должны быть легкими и интуитивно понятными. Автоматизируйте всё, что можно автоматизировать. Как видите, в нашем случае это не просто создание блока: автоматизация начинается с установки проекта, хуков, обновления пакетов и т. д. Вход в проект должен быть лёгок и весел ;]

Техническая часть


Начну немного издалека. В начале 2012 года мы создали свой шаблонизатор Fest. Он преобразовывал XML в js-функцию, которую можно использовать на клиенте и сервере. Функция принимала объект параметров и выдавала строку: классический js-шаблонизатор. Только, в отличие от собратьев, функция на тот момент была супероптимизирована, мы могли запускать её на чистом V8, добившись производительности Си-шаблонизатора, который применяли раньше.

[XML -> JSFUNC -> STRING -> DOM]

За это время на базе Fest мы разработали внутреннюю библиотеку блоков, которая используется сразу на нескольких проектах (Почта, Облако и др.). То есть кнопки, инпуты, формы, списки и т. д. у нас общие. Собственно, это и были первые шаги по структурированию верстки и компонентов.

Время шло, и всё острее вставал вопрос «Как нам жить дальше?», ведь Fest возвращает только строку, обновить состояние можно двумя способами: либо «перерисовать всё», либо «точечно воздействовать на DOM из JS».

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

  • Перерисовать всё — переинициализация событий, повторное получение ссылок на нужные DOM-фрагменты, «мигание» картинок и т. п.
  • Точечное воздействие — размывает и дублирует логику, усложняя разработку.

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

Экспериментов было много. Пробовали ввести data-binding, очень похожий на ангуляровский, но, в отличие от него, Fest всё так же выдавал строку, а data-binding накладывался уже после вставки в DOM. Это позволило сохранить изначальную скорость и работу через V8. Увы, на больших списках у нас остались те же проблемы с аля-$digest, что и у ангуляра, хоть наша реализация и была немного быстрей (в рамках наших задач).

Со временем на рынок вышел React и подарил нам VirtualDom. Проведя бенчмарки, я немного впал в уныние: базовый «список писем» получился примерно в три раза медленней, чем у нас (и это с урезанной реализацией). К тому же мы хотели не переписать свой код, а только заменить принцип обновления шаблона. Но нет худа без добра: React дал толчок всему js-сообществу, и в скором времени, как грибы, начали расти альтернативные реализации vdom: Incremental DOM, morphdom, Deku, mithril, Bobril и многие другие.

Дело оставалось за малым: провести бенчмарки на наших задачах, выбрать подходящий и написать для наших шаблонов транспилер.

[XHTML -> JSFUNC -> VDOM? -> DOM]

Но главной целью было получить максимально комфортную разработку блоков, а именно:

  • Интерфейс создания, просмотра и тестирования блоков.
  • Live coding (CSS/HTML/JS).
  • Автоматизация создания/редактирования блоков.
  • Инструменты для инспектирования компонентов.
  • Визуализация связей между компонентами.

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

Разработка


Live Coding


Думаю, не ошибусь, если скажу: все знают, что такое Webpack и BrowserSync. О них написано много, так что заострять внимание на них не буду, а покажу альтернативный путь: как быть, когда вам не подходят коробочные решения. Только не думайте, что я призываю вас изобретать велосипед: отнюдь, это просто более низкоуровневый вариант, про который многие забывают и тратят уйму времени на «прикручивание» того же Webpack.

Если это так, то node-watch + socket.io — всё, что вам нужно. Два готовых инструмента, которые вы легко можете интегрировать в свой проект.

const fs = require('fs');
const http = require('http');
const watch = require('node-watch');
const socket = require('socket.io');
cosnt PORT = 1234;

const app = http.createServer((req, res) => {
   res.writeHead(200, {'Content-Type': ‘html/text’});
   res.end();
});

const io = socket(app);

app.listen(PORT, () => {
    watch('path/to', {recursive: true}, (file) => {
        fs.readFile(file, (err, content) => {
           const ext = file.split('.').pop();
           io.emit(`file-changed:${ext}`, {file, content});
       });
    });
});


<script src=”//cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.5/socket.io”></script>
<script>
   const io = io(location.protocol + '//' + location.host)
   socket.on('file-changed:html, function (data) {
      // data.file, data.content
   });
</script>

Вот и всё, теперь на клиенте можно получать изменения.

В реальности у нас всё примерно так и выглядит, основное отличие от приведённого листинга — это препроцессинг JS и CSS при отдаче на клиент. Да, именно так; в отличие от Webpack, у нас в dev-среде не используются банды, файлы преобразуются по требованию.

Горячее обновление блоков


Чтобы вдохнуть новую жизнь в fest, понадобилось выбрать библиотеку для работы с vdom и написать транспилер для xhtml/xml, учесть проблемы реализации и решить их.

Какие проблемы? Например, чтобы добавить новый функционал (обработку конструкции/тега), приходилось вносить изменения в библиотеку и поднимать версию. Кроме этого, шаблоны можно было скомпилировать только на сервере.

Так и появился Feast. ;]

Еще он преобразует xml/xhtml в JSFUNC, но эта функция возвращает не строку, а JSON, который дальше передается citojs (это очень шустрая и простенькая библиотека для работы с vdom), а citojs уже строит или обновляет vdom.

Кроме этого, теперь компиляция шаблонов происходит прямо на клиенте, поэтому шаблоны отдаются «как есть» и на клиенте преобразуются сначала в AST, а потом, согласно правилам трансформации, в JSFUNC.

Например, вот так выглядят правила для преобразования тега `fn:for`
// <fn:for data="attrs.items" as="key" value="item">...</fn:for>
'fn:for': {
	scope: true,
	required: ['data'],
	expressions: ['data'],
	prepare: (node, {as, key, data}) => ({
		as: attrs.as || '$value',
		key: attrs.key || '$index',
		data
	}),
	toCode: () => ['EACH($data, @@.children, function (&as, &key) {', '});']);
}

Это позволило решить сразу несколько проблем:

  • больше не нужен сервер для компиляции;
  • размер даже избыточного html меньше, чем скомпилированного JS;
  • правила трансформации можно дополнять на лету;
  • максимум метаинформации.

Поэтому при получении нового html на клиенте он заново транслируется в JS-функцию и вызывается перерендер всех блоков, созданных на основе этого шаблона:
socket.on('file-changed:html', (data) => {
  const updatedFile = data.file;
  feast.Block.all.some(Block => {
     if (updatedFile === Block.prototype.template.file) {
           const template = feast.parse(data.content, updatedFile);
           Block.setTemplate(template);
           Block.getInstances().forEach(block => block.render());
           return true;       
     }
  });
});

Для CSS примерно такая же логика, главным изменением было внедрение CSS-модульности, чтобы раз и навсегда распрощаться с main.css и доставлять css вместе с кодом компонента, а также защитить селекторы от пересечения и возможности обфускации.

CSS Modules


Как бы громко это ни звучало, но сам процесс достаточно простой и уже был известен (например), но мало распространен из-за отсутствия удобных инструментов. Всё изменилось с появлением postcss и webpack. Прежде чем перейти к нашей реализации, взглянем, как это работает у других, например у React и Angular2.

React + webpack


import React from 'react';
import styles from './button.css';

export default class Button extends React.Component {
    render () {
        return <button className={styles.btn}>
            <span className={styles.icon}><Icon name={this.props.icon}/></span>
            <span className={styles.text}>{this.props.value}</span>
        </button>;
    }
}

React + webpack + react-css-modules


import React from 'react';
import CSSModules from 'react-css-modules';
import styles from './button.css';

class Button extends React.Component {
    render () {
        return <button styleName='btn'>
            <span styleName='icon'><Icon name={this.props.icon}/></span>
            <span styleName='text'>{this.props.value}</span>
        </button>;
    }
}

export default CSSModules(Button, styles);

@CSSModules(styles)
export default class Button extends React.Component {
   // ...
}

Angular2


В отличие от React, Ангуляр поддерживает подобие модульности из коробки. По умолчанию он ко всем селекторам добавляет специфичности в виде уникального атрибута, но, если выставить определённый «флажок», будет использовать shadow dom.

@Component({
  selector: `my-app`,
  template: `<div class="app">{{text}}</div>`,
  styles: [`.app { ... }`] // .app[_ngcontent-mjn-1] { }
});
export class App {
   // …
}

Наш вариант — что-то среднее, для него не нужно специально подготавливать шаблон, достаточно просто подгрузить css и добавить его в описание блока:

import feast from 'feast';
import template from 'feast-tpl!./button.html';
import styleSheet from 'feast-css!./button.css';

export default feast.Block.extend({
  name: 'button',
  template,
  styleSheet,
});

Кроме этого, есть ещё экспериментальная ветка не просто с заменой классов, а с полноценным inline стилей. Это может пригодиться для работы на слабых устройствах (телевизоры и пр.).

Собственно, сама ветка выглядит так:

const file = "path/to/file.css";

fetch(file)
   .then(res => res.text())
   .then(cssText => toCSSModule(file, cssText))
   .then(updateCSSModuleAndRerenderBlocks)
;

function toModule(file, cssText) {
  const exports = {};

  cssText = cssText.replace(R_CSS_SELECTOR, (_, name) => {
    exports[name] = simpleHash(url + name);
    return '.' + exports[name];
  });

  return {file, cssText, exports};
}

Как видите, абсолютно никакой магии, всё очень банально: получаем css как текст, находим все селекторы, при помощи простого алгоритма считаем hash и сохраняем в объект экспорта [оригинальное название] => [новое].

Ну и самое интересное: JS, что с ним?

JS / Hot Reload


Рассмотрим пример. Допустим, у нас есть класс Foo:

class Foo {
  constructor(value) {
    this.value = value;
  }
  log() {
    console.log(`Foo: ${this.value}`, this instanceof Foo);
  }
}

Дальше где-то в коде:

var foo = new Foo(123);
foo.log(); // "Foo: 123", true

После чего мы решаем обновить реализацию на NewFoo:

class NewFoo {
  constructor(value) {
    this.value = value;
  }
  log() {
    console.log(`NewFoo: ${this.value}`, this instanceof NewFoo);
  }
});

Да так, чтобы уже созданные экземпляры продолжили работать корректно.

foo.log(); // "NewFoo: 123", true
foo instanceof Foo; // true

Чтобы проделать такой фокус, не нужен препроцессинг, достаточно чистого JS:

function replaceClass(OldClass, NewClass) {
  const newProto = NewClass.prototype;
  OldClass.prototype.__proto__ = newProto;
   // Обновляем статические методы
  Object.keys(NewClass).forEach(name => {
    OldClass[name] = NewClass[name];
  });
  // Переносим методы прототипа
  Object.getOwnPropertyNames(newProto).forEach(name => {
    OldClass.prototype[name] = newProto[name];
  });
}

Да, вот и вся функция, десять строк — и JS Hot Reload готов. Почти. Я специально не стал перегружать эту функцию, а показал только суть. По-хорошему нужно ещё пометить старые методы, которых больше нет, как неудалённые.

Но тут есть проблема :]

replaceClass(Foo, class NewFoo {  /* ... */});
foo.constructor === Foo; // false (!!!)

Решить её можно несколькими способами:

  1. Всё же использовать Webpack, он оборачивает создание класса в специальный враппер, который возвращает и обновляет создаваемый класс.
  2. Применять обвязку для создания классов, например createClass('MyClassName', {...});.
  3. Ещё можно обратиться к Proxy, но тут тоже понадобится препроцессинг

В итоге наша схема выглядит так:

socket.on('file-changed:js', (data) => {
  const updatedFile = data.file;
  new Function(‘define’, data.content)(hotDefine);
});

hotDefine занимается всей магией: вместо запрашиваемого объекта (например, feast) возвращает не оригинальный, а специальный FeastHotUpdater, который и обновляет реализацию.

Инструменты для анализа кода


Как я показал в примере, на данный момент основной инструмент, который позволяет инспектировать элементы прямо из браузера, — это «пипетка». Одна из приятных фич — открытие нужного файла в IDE. Для этого используется замечательная библиотека Романа Дворнова lahmatiy/open-in-editor:

const openInEditor = require('open-in-editor');
const editor = openInEditor.configure(
   {editor: ‘phpstorm’},
   (err) => console.error('Something went wrong: ' + err)
);

editor.open('path/to/file.js:3:10')
  .catch(err => {
    console.error('[open-in-editor] Ooops:', err);
  });

Еще у Романа есть подобный компонент для инспекции React и Backbone, который умеет намного больше моего, да и выглядит суперски. ;]
Пример работы component-inspector от Романа
image

Те, кто хорошо знаком с React, Ember, Angular, Backbone, прекрасно знают и о таких решениях, как React Developer Tools, Ember Inspect, Batarand, Backbone Debugger и др. Всё это расширения DevTools для препарирования положения.

Сначала у меня в планах был именно экстеншен, благо API Хрома располагает к этому + есть примеры, а все выше перечисленные расширения лежат на github, так что вы всегда можете посмотреть реализацию.

Но увы, нельзя поставить расширение пользователю, а нам очень часто приходится изучать проблемы на машинах не только коллег. Поэтому пока я сконцентрировался на инструментах, через которые можно получить максимум информации в браузере без его перезагрузки. Тут и раскрывается вся прелесть компиляции шаблонов на клиенте: вам не нужны две сборки (боевая и dev), сборка всегда одна, при дебаге вы всегда получите всю возможную метаинформацию о компоненте.

Что ещё?


Логирование


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

image

Покрытие кода


По большей части это только эксперимент, но его вполне можно использовать для проверки качества ручных тестов. Берём istanbul, прогоняем через него код и раскатываем на тестовые машины, дальше раз в N секунд скидываем в лог покрытие. Вот таким нехитрым способом можно увидеть, насколько хорошо написаны у вас сценарии для тестеров, покрывают ли они функционал.

Пример отображения
image
image

Анализ структуры приложения


Чем дальше, тем больше приложение растёт, ветвится, и однажды его структура становится непонятной. Так выглядела первая попытка ;]

Первая попытка визуализации структуры приложения
image

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

Очеловеченный результат
image
image

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

Timeline


Громко будет сказано, но ближайший аналог, только очень простой, — это DevTools Timeline. На сегодня он умеет отображать процессы, происходящие внутри приложения (рендер, события, изменения моделей и т. п.). Это позволяет быстро понять, что именно и в какой последовательности изменилось, сколько времени это заняло. Кроме того timeline уже не раз помог выявить аномальное поведение (лишний перерендер или подозрительные изменения моделей).

Пример запуска приложения в dev-режиме
image
image

Заключение


Не важно, какой фреймворк вы используете, под капотом всё равно будет код, написанный вами, со своей специфичной логикой и стилем, – вот о чем нужно помнить. Поэтому документируйте, описывайте и автоматизируйте эту «специфику». Не бойтесь создавать инструменты под себя, даже маленький bash-скрипт может существенно упростить вашу жизнь. Кроме этого, обязательно ищите готовые инструменты, даже если вам кажется, что таких нет. Чем популярнее инструмент, который вы используете, тем больше его сообщество. У таких решений, как React, Vue, Ember, Angular, – хорошая поддержка Live Coding, расширения для Dev Tools и многое другое. Например, для React недавно вышла уже вторая версия react-storybook.

P.S. А теперь небольшой опрос.
Как вы верстаете?

Проголосовало 302 человека. Воздержался 121 человек.

Live Coding / Hot Reload?

Проголосовало 325 человек. Воздержалось 120 человек.

GUI для разработки блоков?

Проголосовал 241 человек. Воздержался 151 человек.

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

Поделиться с друзьями
-->

Комментарии (48)


  1. bustEXZ
    06.09.2016 14:22
    +2

    Мне кажется что комментарии появляются не так быстро, т.к. все побежали пробовать ставить этот великолепный инспектор (я уж точно). :) Спасибо за пример подхода к разработке, очень и очень понравился!


    1. RubaXa
      06.09.2016 14:24
      +1

      Да, Рома (@lahmatiy) — молодец!

      Ещё у него есть убер визуализатор для basis.js, которые вроде как можно настроить для Rx/Beacon/Kefir, но тут лучше он уточнит ;]



      1. lahmatiy
        06.09.2016 17:51
        +4

        Спасибо, Костя :)
        Для других библиотек нужно встраивать поддержку. Пробовал с knoсkout'ом подружить https://github.com/lahmatiy/knockout-data-flow
        Думаю в ближайшие пару месяцев станет возможно сделать этот визуализатор более независимым, как потребитель json. То есть вы или ваша библиотека должны сгенерировать описание графа в JSON (постороение специфично для библиотеки/фреймворки), а он его построит. Собственно он уже так работает, но пока в составе последнего basis.js. А делалось это в рамках новых remote tools https://www.youtube.com/watch?v=j6q3gsWOpgM
        Скоро по такому же принципу будет работать Component Inspector. А вот визуализатор потоков данных пока нет планов выносить, по крайней мере до тех пор пока не будут реализованы основные запланированные фичи для него.


  1. smelukov
    06.09.2016 15:49
    +2

    Live Coding / Hot Reload?

    Используем basis.js + basis.js inspector


  1. AlgenGold
    06.09.2016 16:22
    -1

    че это за пипетка?


    1. RubaXa
      06.09.2016 16:26

      Это наш внутренний инструмент, который умеет препарировать структуру наших блоков, но если вы используйте Backbone или React, то есть более функциональный аналог от lahmatiy: https://github.com/lahmatiy/component-inspector


    1. bustEXZ
      06.09.2016 16:26

      В статье же об этом написано


  1. button
    06.09.2016 19:12

    А не замеряли насколько использование такого подхода повышает эффективность? Затраты на внедрение покрываются?


    1. RubaXa
      06.09.2016 19:46
      +4

      Хороший вопрос. Не замеряли, но можно сравнить, как было и как стало. Раньше dev-страница загружалась примерно за 4-7сек, плюс вам нужно было потратить ещё какое-то время, чтобы получить нужное состояние приложения. Теперь это один раз, вам больше не нужно постоянно жать после f5/cmd+r, это если говорить про непосредственное написание разработку. Поверьте, когда вы редактируете «по живому» приложению, это совершенно другой опыт. Сказали передвинуть блок, нет проблемы, вы двигаете и вместе с дизайнером/PM смотрите, как оно получается.

      Так же, просмотрщик компонентов/блоков позволяет не плодить всё новые и новые «кнопочки», при получении задачи, вы просто компонуете уже имеющиеся блоки и получаете результат. Зачастую вам даже не нужен дизайнер. Кроме этого, когда вы редактируете блок в GUI, вы видите все варианты использования и как на них влияют ваши изменения.

      Инспектор, позволяет разобраться в приложении на лету. Вы видите проблемное место и инспектируете его: на основе каких данных он отображен, какие блоки используются и в один клик попадаете в нужны шаблон, который вы можете опять же на лету отредактировать и увидеть изменения.

      И так далее. На такие вещи сложно повесить метрику. Вот у нас блок из четыре файлов, можно было бы просто написать в README пример и человек копи-пастом бы создавал файлы. Но даже если бы один, всё равно это время, прочитать, скопировать, узнать куда путь до папки с блоками и создать там файл. А можно всего этого не писать и создать тулзу, которая сделает это. Можно пойти дальше, и с делать GUI, которые буду создавать все необходимые файлы и по завершению открывать IDE с нужным файлов. Этим шагом вы не просто сделаете удобный инструмент, но и не навязчиво покажете разрабу, где же лежат эти шаблоны ;]

      В целом, всё это не так сложно сделать, многое уже сделано, главное желание, ну а если вы используете какое-нибудь популярное решение, то скорей всего это уже есть ;]


  1. vintage
    06.09.2016 22:16
    -2

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

    Readme Driven Development? Довольно странное и сложное решение. Не лучше ли вытягивать документацию из кода, а не вытягивать код из документации?


    Live Coding / Hot Reload?

    Нет, потому, что:


    1. Во время активного редактирования файлов комп тормозит, гудит и жарит яичницу, так как каждый браузер, в котором открыта страница, в фоне что-то там "перезагружает".
    2. Стоит сохранить сломанный код и приложение напрочь ломается — приходится его перезагружать, чтобы не глючило.
    3. Состояние, полученное такими вот "горячими патчами" не идентично, получаемому "холодным стартом". Код может работать здесь и сейчас, но сломаться после перезагрузки.
    4. Перезагрузка одного лишь отредактированного файла не дружит со статической типизацией и переиспользованием CSS (примеси, константы и тп).

    Раньше dev-страница загружалась примерно за 4-7сек, плюс вам нужно было потратить ещё какое-то время, чтобы получить нужное состояние приложения.

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


    1. RubaXa
      06.09.2016 22:56
      +2

      Readme Driven Development? Довольно странное и сложное решение. Не лучше ли вытягивать документацию из кода, а не вытягивать код из документации?

      Для этого нужно узнать, а как запустить? Или у вас под рукой только доступ к gitlab/github, что делать? Дублировать доку?


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

      В собранном состоянии, холодный старт приложения меньше ~800ms, а вот в dev не используются бандлы и холодный старт не такой быстрый, да, тут есть ещё над чем работать.


      и не теряют состояние при перезагрузке — тогда вам не потребуются никакие "горячие замены".

      Хм, т.е. нужно, чтобы после f5, даже раскрытый dropdown сохранял своё состояние? Допустим, но проблема с f5 останется. Каждый раз при редактировании, вам придется тратить на alt+tab / cmd+tab, нажать f5 и ждать. После загрузки страницы, браузер ещё должен промотать её в нужное место, это будет заметно, резкий и неприятный скачек, графика должна так же прогрузится. И если говорить про состояния, то вам придется хранить ещё все scrollTop для overflow: scroll/hidden и как-то сохранить какой элемент был в фокусе и восстановить его…


      Может расскажите, как вы видите решение этой задачи, а то я чет написал и не верю, что можно добиться такой восстанавливаемости на чём-то хоть чуть-чуть сложнее todo-app :]


      1. lahmatiy
        06.09.2016 23:48
        +1

        Для этого нужно узнать, а как запустить? Или у вас под рукой только доступ к gitlab/github, что делать? Дублировать доку?

        Я вот тоже думаю, что должно быть наоборот command -h > README.md. Но эта дискуссия навела на мысль, что в command -h можно добавлять ссылку на подробное описание команд/флагов...


        в dev не используются бандлы и холодный старт не такой быстрый

        В dev-сервер'е basis.js для решения проблемы делается динамический бандл. По сути это просто JSON файл, где ключ – это имя файла, значение – его контент. Сервер внедряет этот скрипт (который сохраняет объект в некоторую глобальную переменную) в каждую отдаваемую html-страницу. Когда модульная система делает запрос к серверу за файлом (через xhr), она добавляет спец-заголовок. По этому заголовку сервер понимает, что это ресурс приложения и добавляет в бандл + начинает watch'ить этот файл, и при изменениях обновляет контент в бандле. При следующем обращении к бандлу, если в нем были изменения делается 'window.globalVar = ' + JSON.stringify(bundle). Модульная система инициализирует свой кэш бандлом (если есть). И если запрашиваемый ресурс есть в кэше, то берет оттуда или делает запрос к серверу. Так же есть коннект к серверу через ws, через который прилетают обновления по ресурсам, которые обновляют кэш модульной системы (если ресурс еще не инициализировался) или уже используемый, если его можно апдейтить. Первая загрузка страницы после старта сервера получается так же медленно (но не так медленно, как если собирать все приложение, так как запрашивается только необходимое), но последующие загрузки происходят почти так же быстро, как если бы это было собранно. Еще к серверу могут подключится клиенты, которые уже используют определенные файлы – тогда клиент присылает список файлов, а сервер их прогревает и наполняет бандл ресурсов, но это уже лирика…
        Когда начинал писать думал, что все гораздо проще – просто уже давно работает, и об этом не задумываешься :)


        не верю, что можно добиться такой восстанавливаемости

        Я вот тоже не верю. И более того, даже если добиться этого на "сферических конях", то получится настолько сложное решение (и сложно поддерживаемое), что лучше его будет не использовать.


        1. RubaXa
          07.09.2016 08:07
          +1

          Курица или яйцо, да :] С документацией давно экспериментирую, в другом проекте, она генерируется на основе jsdoc в MD, но примеры находятся именно в markdown, т.е. они сначала вытягиваются из него, и компонуются с описаниями функция. Просто в писать примеры jsdoc совсем вырви глаз получается и не удобно жутко. Конечно можно полностью в код запихать, особенно с учетом появления template string, но всё же это не markdown и писать доку в коде, то ещё удовольствие.


          Бандлы у нас есть, обираются r.js и это долго, но планы по оптимизации давно есть и ждут своего часа.


          1. lahmatiy
            07.09.2016 15:52

            Бандлы у нас есть, обираются r.js и это долго

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


      1. vintage
        06.09.2016 23:58
        -2

        Для этого нужно узнать, а как запустить? Или у вас под рукой только доступ к gitlab/github, что делать? Дублировать доку?

        Например, для работы с API и бизнес-логикой у нас есть внутренняя JSSDK, документация которой генерируется на основе JSDoc3.



        В собранном состоянии, холодный старт приложения меньше ~800ms, а вот в dev не используются бандлы и холодный старт не такой быстрый, да,

        Ну так используйте бандлы, какие проблемы? :-)


        Хм, т.е. нужно, чтобы после f5, даже раскрытый dropdown сохранял своё состояние?

        Да, и состояние фокусировки, выделения, введённое значение в инпуте.


        Каждый раз при редактировании, вам придется тратить на alt+tab / cmd+tab, нажать f5 и ждать.

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


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

        Не будет там никаких скачков. Например: http://nin-jin.github.io/chart/


        Может расскажите, как вы видите решение этой задачи, а то я чет написал и не верю, что можно добиться такой восстанавливаемости на чём-то хоть чуть-чуть сложнее todo-app :]

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


        1. RubaXa
          07.09.2016 08:22
          +1

          Ну так используйте бандлы, какие проблемы? :-)

          r.js медленно их собирает, но мы работает над этим.


          каждый браузер пытается перекомпилировать шаблоны

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


          Не будет там никаких скачков. Например: http://nin-jin.github.io/chart/

          Ну простая же страничка, добавьте графику, список побольше, походы в api и всё уже не так радужно.


          Просто при реализации компонент делаем им поведение по умолчанию — хранить рантайм состояние в sessionStorage

          Я так то же могу написать, но от этого реальностью мои слова не станет. Во-вторых, представим, что чудо произошло и всё получилось, но эта функциональность нужна только в dev-режиме, потом что в бою dropdown не должен после f5 остаться раскрытым, значит придется дополнительно программировать/конфигурировать эту логику.


          1. vintage
            07.09.2016 09:29

            r.js медленно их собирает, но мы работает над этим.

            И я даже знаю почему — чтобы склеить все файлы в один ему приходится каждый файл транспилировать в другой формат модулей. Если вы сразу будете писать имя модуля в самом модуле, то r.js можно будет с лёгкостью заменить на какой-нибудь concat-with-sourcemaps, который соединяет файлы практически мгновенно.


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

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


            Ну простая же страничка, добавьте графику, список побольше, походы в api и всё уже не так радужно.

            Ничего принципиально не изменится, на самом деле. Я бы показал демку, но она сейчас сломана. :-) Чуть позже починю. Но суть простая — перемещаем скроллинг сразу после того как отрендерили контент.


            в бою dropdown не должен после f5 остаться раскрытым

            Обоснуйте. На самом деле это интересная фича — вы можете обновлять пользователю приложение, пока он с ним работает и он ничего даже не заметит :-)


            1. RubaXa
              07.09.2016 10:59
              +2

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

              Ноу, просто r.js использует есприму для получения конфигов и разбора define, но это не главное, в целом он написан и работает крайне не оптимально.


              А потом он применяется к списку на 10000 элементов и привет тормоза.

              Это ваши домыслы. Обновить 10К элементов быстрей, чем обновить всю страницу и создать те же 10К, а так же остальные 50К элементов, но кроме этого, ещё и сервер нагрузить, который тоже нужно написать конечно быстрым, это я уже понял ;]


              Обоснуйте.

              Обосновать что? Что dropdown после f5 должен быть закрыт? Ну не знаю, наверно чтобы просто не выглядеть глюком.


              Окей, возьмем dropdown и будем хранить его состояние в sessionStorage, как вы и предложили, при f5 нужно восстанавливать его состояние в dev-окружении, но так же при горячем апдейте в бою. Окей, возможно мы больше не считаем глюком его раскрытое состояние при F5, пусть будет так. Но что делать с этим состоянием, если я перешел на другую страницу, sessionStorage не обнулится, или его сбрасывать при изменении маршрута? Возможно, возможно есть ещё какие-то факторы, которые я просто не могу учесть.


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


              1. vintage
                07.09.2016 11:25
                -1

                Ноу, просто r.js использует есприму для получения конфигов и разбора define, но это не главное, в целом он написан и работает крайне не оптимально.

                Я разве не то же самое сказал? :-)


                Обновить 10К элементов быстрей, чем обновить всю страницу

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


                Обосновать что? Что dropdown после f5 должен быть закрыт? Ну не знаю, наверно чтобы просто не выглядеть глюком.

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


                Но что делать с этим состоянием, если я перешел на другую страницу, sessionStorage не обнулится, или его сбрасывать при изменении маршрута?

                Да, очевидно состояние дропдауна лучше хранить не в sessionStorage, а в history state.


                1. RubaXa
                  07.09.2016 11:38
                  +2

                  Не обновлять страницу — куда быстрее.

                  Ха-ха, «нет», как будем выяснять, чьё НЕТ сильней? :]


                  Давайте так, создать один элемент Х, запуск приложения и создание остальных элементов (которые мы не рассматриваем) пусть будет Y, обновление одного элемента U, притом U <= X (обновляются только измененные участки). Итого получаем:


                  • F5, всегда X*10K + Y
                  • HOT, первый запуск: X*10K + Y, обновления: U*10K

                  М?


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

                  Да, именно на ваш.


                  Например, открыл я дропдаун, а там нет нужного пункта меню, так как нет прав. Открыл отдельную вкладку, дал права (или админу написал), вернулся на текущую и обновил — опа, появился нужный пункт.

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


                  Да, очевидно состояние дропдауна лучше хранить не в sessionStorage, а в history state.

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


                  1. vintage
                    07.09.2016 15:03

                    F5, всегда X10K + Y
                    HOT, первый запуск: X
                    10K + Y, обновления: U*10K

                    Подкину ещё дровишек — ленивый рендеринг, позволяет мгновенно открыть страницу любой сложности. например: http://eigenmethod.github.io/mol/app/supplies/


                    Да, именно на ваш.

                    Как будем выяснять чей взгляд правильный? :-)


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

                    Не менее странный кейс — когда я открыл дропдаун, прицелился на нужный пункт, а он улетел куда-то, потому, что список в реальном времени обновился. С рилтаймовыми обновлениями тоже нужно быть осторожными.


                    1. RubaXa
                      07.09.2016 15:05
                      +1

                      Подкину ещё дровишек

                      А это тут причем? Мы тут говорим про обновление 10К элементов, как не крути, как не «ленись», но обновить их быстрей, чем создать заново.


                      1. vintage
                        07.09.2016 15:12

                        При том, что я уже не раз повторял:


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

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


                        1. RubaXa
                          07.09.2016 15:22
                          +1

                          Ну не верю, вы уж простите.


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


                          Как ленивая загрузка решить проблему 10К элементов? Никак. Значит у вас будут эти 10К как-то по частям добавляться и потом лениво инициализироваться. Ну так, у себя в приложении я тоже не дурак, и делаю умные списки, которые отображают только видимую часть, но это не из-за того, что HOT обновления медленные, дело в мобилках и количестве DOM-элементов.


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


                          1. vintage
                            07.09.2016 16:11

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

                            Мгноверно происходит разве что манкипатчинг объектов. Далее половина приложения должна обновить своё состояние в соответствии с новой реализацией методов. Одно неосторожное движение и каждый браузер уходит в бесконечный цикл, отжирая по одному ядру процессора. Подобные приколы хотрелоада просто вымораживают.


                            Как ленивая загрузка решить проблему 10К элементов?

                            Ленивый рендеринг, а не ленивая загрузка.


                            Ну так, у себя в приложении я тоже не дурак, и делаю умные списки, которые отображают только видимую часть

                            Ага, всё ручками. "Умное дерево" тоже будете каждый раз вручную реализовывать?


                            дальше можно про инлайн данных, кэширование через localStorage, потом умное кэширование через ServiceWorker и так далее…

                            Давайте про эту ерунду не будем :-)


                            1. RubaXa
                              07.09.2016 16:24
                              +1

                              Одно неосторожное движение и каждый браузер уходит в бесконечный цикл

                              Одно неосторожнее движение и после F5 вы уходите в бесконечный цикл. Откуда вы все эти доводы берете, это личный опыт или домысел?


                              Ленивый рендеринг, а не ленивая загрузка.

                              А что такое ленивый рендеринг? Какой смысл вы вкладываете в эти слова?


                              Ага, всё ручками. "Умное дерево" тоже будете каждый раз вручную реализовывать?

                              Что значит каждый раз? Я не понимать. А как не каждый раз? Написать базовый функционал, которые расширять под проект, ну ок. Или что?


                              1. vintage
                                07.09.2016 17:05

                                Одно неосторожнее движение и после F5 вы уходите в бесконечный цикл. Откуда вы все эти доводы берете, это личный опыт или домысел?

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


                                А что такое ленивый рендеринг? Какой смысл вы вкладываете в эти слова?

                                Очевидно, рендериг лишь того, что возможно попадает в видимую область.


                                Что значит каждый раз?

                                В каждом месте, где выводятся большие объёмы данных, чтобы не тормозило.


                                А как не каждый раз?

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


                                1. RubaXa
                                  07.09.2016 17:24

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

                                  Сочувствую, но мы пока не столкнулись с подобной проблемой. Возможно наш «велосипед» безопасен, да и сам код пишем прямо ;] Конечно, бывает и падает (при обновлении JS), но не зависает. С шаблонами и CSS никогда не было проблемы, даже не могу представь, откуда они могут быть.


                                  Очевидно, рендериг лишь того, что возможно попадает в видимую область.

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

                                  Не согласен, частная реализация всегда лучше общей. Кроме этого, все эти разговоры про умный рендер сводятся к какому-нибудь простецкому списку и монитору с крошечным разрешением, выводя максимум 20-30 строк.


                                  1. vintage
                                    07.09.2016 23:24

                                    Сочувствую, но мы пока не столкнулись с подобной проблемой. Возможно наш «велосипед» безопасен, да и сам код пишем прямо ;]

                                    Я бы с удовольствием почитал, как вы решили описанные мной проблемы.


                                    Не согласен, частная реализация всегда лучше общей.

                                    Поэтому вы пилите свой фреймворк.


                                    Кроме этого, все эти разговоры про умный рендер сводятся к какому-нибудь простецкому списку и монитору с крошечным разрешением, выводя максимум 20-30 строк.

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


                                    1. RubaXa
                                      07.09.2016 23:33

                                      Поэтому вы пилите свой фреймворк.

                                      Нооу, ну зачем вы так меня :[ Никаких фреймворков, боже упаси, хотя очень много своего кода, но тут специфика самого проекта.


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

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


                                      Как же хочется, чтобы вот это кончилось, чтобы браузер сам всё это решал, чтобы не было этих vdom и умных «списков» и т.п. Не нужен мне ServiceWorker, обойдусь :]


                                      1. vintage
                                        08.09.2016 08:05

                                        Никаких фреймворков, боже упаси, хотя очень много своего кода, но тут специфика самого проекта.

                                        У вас фреймворкофобия? :-)


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

                                        Чуть выше я уже привёл "сказочный" пример. Можете так же глянуть более наглядный пример: http://nin-jin.github.io/todomvc/examples/mol/ — подобавляйте задач и обратите внимание на пропадание футера с определённого момента.


                                        1. RubaXa
                                          08.09.2016 08:38
                                          +1

                                          Ну вот два ярких примера, о чем я говорю, на экран влезает максимум 20-30 строчек, в ваших примерах ещё меньше.



                                          1. vintage
                                            08.09.2016 09:08

                                            Ну вот два ярких примера, о чем я говорю, на экран влезает максимум 20-30 строчек, в ваших примерах ещё меньше.

                                            Совершенно не важно сколько влезает на экран. Важно, что время открытия страницы не пропорционально числу строк.


                                            притормаживает, скролл неплавный, таймлайн тоже показывается просадку fps

                                            Я работаю над этим. Скроллинг подтормаживает только если его дёргать мышью за скроллбар. Если использовать колесо или палец, то всё плавно. Думаю троттлинг должен помочь.


                                            ну это очередной todos-mvc, слишком просто и к реальности не имеет отношения.

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


                                            1. RubaXa
                                              08.09.2016 10:32
                                              +1

                                              На маке нет скрола, притормаживает на трекпаде (не сильно, но заметно)


                                              Совершенно не важно сколько влезает на экран. Важно, что время открытия страницы не пропорционально числу строк.

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


                                              обратите внимание на пропадание футера с определённого момента.

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


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


                                              А можете вкратце описать, как именно вы понимаете, что ноду нужно не нужно вставлять?


                                              Как я понял, вам умный рендер сейчас работает только на отсекание нижней части или умеет и верхушку? Хотя не важно, низ тоже интересен, как именно у вас происходит решение, что ноду, нужно вставить? Каждый раз запрашиваете позицию у parent.lastChild? Или умнее?


                                              1. vintage
                                                08.09.2016 12:08

                                                Запрашивать позицию элемента дорого и не надёжно (при ресайзе блока всё поедет, а отслеживать размер блока — дорого). У меня там реализована простейшая логика:


                                                1. За базовую высоту вьюпорта берётся размер окна.
                                                2. Компонент "скроллер" увеличивает размер вьюпорта для дочерних компонент на смещение скролла.
                                                3. Для любой компоненты может быть задана минимальная высота — формулой или константой.
                                                4. Компонент "листер" задаёт себе минимальную высоту как сумму минимальных высот вложенных компонент. Кроме того, рендерит внутри себя лишь те компоненты, суммарная минимальная высота которых превышает высоту вьюпорта.

                                                Таким образом получается отсечение снизу с некоторой избыточностью. Отсечение сверху уже сложнее реализовать.


                                                1. RubaXa
                                                  08.09.2016 12:18
                                                  +1

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

                                                  А-а-а, ну так неинтересно ;]


                                                  1. vintage
                                                    10.09.2016 11:39

                                                    А как интересно? Это сравнительно низкая плата за ленивый рендеринг.


                    1. RubaXa
                      07.09.2016 15:11
                      +2

                      Чёрт, хабр, что ты делаешь… продолжу


                      Как будем выяснять чей взгляд правильный? :-)

                      Никак, вы можете делать в своё приложении как хотите. Просто окружающий мир ведет себя именно так, «дродауны» не открыты после restart'а системы. Какие-то части всё же сохраняют состояния, но не повально, это поведение продумано заранее.


                      Не менее странный кейс — когда я открыл дропдаун, прицелился на нужный пункт,

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


                      1. vintage
                        07.09.2016 15:16

                        Просто окружающий мир ведет себя именно так, «дродауны» не открыты после restart'а системы.

                        Реальный мир как раз так себя и ведёт — если я открыл книгу, вышел погулять, а потом вернулся, то книга останется открытой.


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

                        Мы говорим о решениях и их поведениях в различных кейсах.


                        1. RubaXa
                          07.09.2016 15:31
                          +2

                          Реальный мир как раз так себя и ведёт — если я открыл книгу, вышел погулять, а потом вернулся, то книга останется открытой.

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


                          Мы говорим о решениях и их поведениях в различных кейсах.

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


                          1. babylon
                            07.09.2016 22:59

                            Можно ли сделать рендер или ререндер странички на основе JSONNET?
                            В идеале всё — и CSS и DOM и config в json подобном формате. Причем каждый узел может иметь «умные указатели» со своим набором событий и хэндлеров которые могли бы посылаться в eventbus и в ней обрабатываться…


                            1. RubaXa
                              07.09.2016 23:15
                              +1

                              Не понял, это вопрос или предложение? Судя по этой ветке комментов, можно сделать что угодно ;]


  1. Voronar
    08.09.2016 23:25

    С какого это перепуга Incremental DOM стал альтернативной реализацией Virtual DOM?
    Читаем первоисточник:

    Incremental DOM is a library for building up DOM trees and updating them in-place when data changes. It differs from the established virtual DOM approach in that no intermediate tree is created (the existing tree is mutated in-place).


  1. babylon
    09.09.2016 21:11

    Можно где по русски почитать в чём разница?


    1. Voronar
      09.09.2016 23:57

      На русском не встречал. Насколько я знаю из беглого прочтения одной статьи, разница в том, что в отличии от vdom, который сначала обновляет виртуальное DOM-дерево(после этого ища разницу между виртуальным и реальным DOM-деревом и внося изменения в последнее), idom вносит изменения сразу непосредственно в реальное DOM-дерево по средствам «патчинга».


  1. babylon
    10.09.2016 10:01

    Т.е. idom это mvc?


    1. Voronar
      10.09.2016 11:30
      +1

      Нет, но вы можете использовать idom при построении приложений с помощью подхода MVC.