company_banner

Frontend: Разработка и поддержка (+голосование)



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

    Под катом будет рассказ, как мы стараемся организовать эти процессы, какие инструменты создаём для препарирования 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. А теперь небольшой опрос.

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

    Как вы верстаете?
    Live Coding / Hot Reload?
    GUI для разработки блоков?
    Mail.Ru Group 621,14
    Строим Интернет
    Поделиться публикацией
    Комментарии 52
    • +2
      Мне кажется что комментарии появляются не так быстро, т.к. все побежали пробовать ставить этот великолепный инспектор (я уж точно). :) Спасибо за пример подхода к разработке, очень и очень понравился!
      • +1
        Да, Рома (@lahmatiy) — молодец!

        Ещё у него есть убер визуализатор для basis.js, которые вроде как можно настроить для Rx/Beacon/Kefir, но тут лучше он уточнит ;]
          • +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. А вот визуализатор потоков данных пока нет планов выносить, по крайней мере до тех пор пока не будут реализованы основные запланированные фичи для него.

        • +2
          Live Coding / Hot Reload?

          Используем basis.js + basis.js inspector
          • НЛО прилетело и опубликовало эту надпись здесь
            • 0
              Это наш внутренний инструмент, который умеет препарировать структуру наших блоков, но если вы используйте Backbone или React, то есть более функциональный аналог от lahmatiy: https://github.com/lahmatiy/component-inspector
              • 0
                В статье же об этом написано
              • 0
                А не замеряли насколько использование такого подхода повышает эффективность? Затраты на внедрение покрываются?
                • +4
                  Хороший вопрос. Не замеряли, но можно сравнить, как было и как стало. Раньше dev-страница загружалась примерно за 4-7сек, плюс вам нужно было потратить ещё какое-то время, чтобы получить нужное состояние приложения. Теперь это один раз, вам больше не нужно постоянно жать после f5/cmd+r, это если говорить про непосредственное написание разработку. Поверьте, когда вы редактируете «по живому» приложению, это совершенно другой опыт. Сказали передвинуть блок, нет проблемы, вы двигаете и вместе с дизайнером/PM смотрите, как оно получается.

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

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

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

                  В целом, всё это не так сложно сделать, многое уже сделано, главное желание, ну а если вы используете какое-нибудь популярное решение, то скорей всего это уже есть ;]
                • –2
                  при старте приложение читает README.md, парсит список опций и их описания и, если вы используете неизвестную или недокументированную опцию, выдаёт ошибку.

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


                  Live Coding / Hot Reload?

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


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

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

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

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

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


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

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


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

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


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

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

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


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

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


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

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

                      • +1

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


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

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

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

                      • –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
                          Ну так используйте бандлы, какие проблемы? :-)

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


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

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


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

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


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

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

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

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


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

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


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

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


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

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

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

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


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

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


                              Обоснуйте.

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


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


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

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

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


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

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


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

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


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

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

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

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


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


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

                                  М?


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

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


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

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


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

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

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

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


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

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


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

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

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

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

                                      • 0

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


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

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

                                        • +1

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


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


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


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

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

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


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

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


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

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


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

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

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

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


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

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


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

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

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

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


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

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


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

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


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

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

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

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


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

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

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

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

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


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

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


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

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

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

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


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

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


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

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

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


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

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

                                                        • +1

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


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

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


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

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


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

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

                                                            • +1

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


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

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


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

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


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


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


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

                                                              • 0

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


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

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

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

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

                                                                  • 0

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

                                      • +2

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


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

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


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

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

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

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


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

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

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

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


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

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

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

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

                      • 0
                        С какого это перепуга 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).
                        • 0
                          Можно где по русски почитать в чём разница?
                          • 0
                            На русском не встречал. Насколько я знаю из беглого прочтения одной статьи, разница в том, что в отличии от vdom, который сначала обновляет виртуальное DOM-дерево(после этого ища разницу между виртуальным и реальным DOM-деревом и внося изменения в последнее), idom вносит изменения сразу непосредственно в реальное DOM-дерево по средствам «патчинга».
                          • 0
                            Т.е. idom это mvc?
                            • +1
                              Нет, но вы можете использовать idom при построении приложений с помощью подхода MVC.
                            • 0

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

                              • +1

                                Статья написана на основе реального опыта, так что новому разработчику нужно потратить ровно 5-10 минут на чтение README.md, больше от него ничего не требуется. А так как у нас разработчики частенько ротируются между проектами, проверенно не однократно. Поймите, все эти инструменты сделаны именно для того, чтобы не тратить время, если бы это было не так, зачем бы они тогда были нужны?

                              • 0
                                Константин, а планируется ли заопенсорсить Feast?
                                • 0

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

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

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