• Angular 5: Unit тесты
    0

    Есть два противоположных подхода:


    Жёсткие зависимости. Это когда ваш юнит не заработает, пока вы не предоставите ему все необходимые зависимости. Жёсткие зависимости логично выносить в конструктор, чтобы точно не забыть какую-либо из них. Чем больше у вас декомпозиция, тем больше каждому юниту требуется зависимостей и тем многословней происходит инстанцирование и тем больше кода занимается только лишь пробрасыванием зависимостей с верхних уровней к нижним. Чтобы побороть эту проблему используются IoC контейнеры, типа ангуляровских модулей, которые сами резолвят для вас эти зависимости на основе контекста. Всё, что вы говорите в шаблоне вашего компонента — это "хочу вот здесь такой-то компонент", при этом какие ему потребуются зависимости в общем случае вы не знаете. То есть объявление в конструкторе всех зависимостей компонента, становится бесполезным, так не даёт исчерпывающую информацию обо всех зависимостях всего вложенного в него дерева компонент, без которых он не заработает. Соответственно, когда вы настраиваете IoC, то должны либо в явном виде запровайдить все эти фактически неявные, но необходимые зависимости (жёсткий вариант), либо компонент должен давать подсказки для IoC контейнера, какую реализацию брать по умолчанию (мягкий вариант). И тут мы плавно переходим к другой крайности...


    Мягкие зависимости. Мы объявляем зависимости, тут же предоставляем реализацию по умолчанию и реализуем простой механизм переопределения этой зависимости. Чтобы воспользоваться юнитом ничего не нужно кроме как воспользоваться им. Не нужно настраивать какой-то внешний реестр зависимостей. И только если какое-либо его поведение вас не устраивает — вы можете подменить соответствующую зависимость. А чтобы эта подмена действовала не только на сам юнит, но и на все ниже по иерархии (иерархия может быть самой разнообразной и в том числе динамической), используется паттерн "окружающий контекст". Простейшая реализация этой логики выглядит так:


    Заголовок спойлера
    // global context alias for code consistency
    const $ = this
    
    class Thing {
    
        // local context for self and inner
        protected $ : typeof $
    
        constructor( context : typeof $ ) {
            this.$ = context
        }
    
    }
    
    function derivedContext( patch : Partial< typeof $ > ) {
        return Object.assign( Object.create( this ) , patch )
    }
    
    class Man extends Thing {
        hands = [
            new this.$.Hand( this.$ ) ,
            new this.$.Hand( this.$ ) ,
        ]
    }
    
    class Hand extends Thing {
        fingers = [
            new this.$.Finger( this.$ ) ,
            new this.$.Finger( this.$ ) ,
            new this.$.Finger( this.$ ) ,
            new this.$.Finger( this.$ ) ,
            new this.$.Finger( this.$ ) ,
        ]
    }
    
    class Finger extends Thing {}
    class TriggerFinger extends Thing {}
    
    // new Man with default hands and fingers
    const Jin = new this.$.Man( this.$ )
    
    // new Man with trigger fingers
    const John = new this.$.Man( this.$.derivedContext({
        Finger : TriggerFinger
    }) )
    
    // new Man that can't have trigger fingers
    const Jack = new this.$.Man( this.$.derivedContext({
        TriggerFinger : Finger 
    }) )

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

  • Angular 5: Unit тесты
    0

    Хороший/плохой — весьма скользкие понятия. Вы думаете что spyOn под капотом делает? Неявно заменяет метод, чем вполне может сломать ваш метод. Мой пример тоже супер-тестируемый. Разница лишь в объёме бойлерплейта и в понимании что происходит.

  • Angular 5: Unit тесты
    0

    Ну а пока вы мучаетесь, давайте я расскажу вам как происходит тестирование в $mol, чтобы поглумиться над вашей участью :-)


    Вам не нужно заучивать все 100500 методов jasmine-api. Вы просто используете один из 3 видов ассертов:


    $mol_assert_equal — все аргументы идентичны
    $mol_assert_unique — все аргументы разные
    $mol_assert_fail — исполнение валится с ошибкой


    Например, возьмём кота:


    Заголовок спойлера
    class Cat {
    
        lifecycle() {
            this.eat()
            this.crap()
        }
    
        eat() {}
        crap() {}
    }

    Нам нужно проверить, что при вызове lifecycle кот выполняет два действия ровно по одному разу. При этом важно не перепутать последовательность этих действий! Как это будет на Жасмине, Жести и тп штуках? Ну что-то типа:


    Заголовок спойлера
    describe( 'Сat' , () => {
    
        it( 'should eat', () => {
    
            const cat = new Cat('Lion')
    
            spyOn( cat , 'eat' ).and.callFake( ()=> undefined )
    
            cat.lifecycle()
    
            expect( cat.eat ).toHaveBeenCalledTimes( 1 )
        } )
    
        it( 'should crap', () => {
    
            const cat = new Cat('Lion')
    
            spyOn( cat , 'crap' ).and.callFake( ()=> undefined )
    
            cat.lifecycle()
    
            expect( cat.crap ).toHaveBeenCalledTimes( 1 )
        } )
    
        it( 'should eat then crap', () => {
    
            const cat = new Cat('Lion')
    
            spyOn( cat , 'eat' ).and.callFake( ()=> undefined )
            spyOn( cat , 'crap' ).and.callFake( ()=> undefined )
    
            cat.lifecycle()
    
            // so how to assert calling order?
        } )
    
    } )

    В $mol же это будет один простой и понятный тест, проверяющий ровно то, что требуется:


    Заголовок спойлера
    $mol_test({
        'Cat should eat then crap' () {
    
            const cat = new Cat
    
            let log = ''
            cat.eat = ()=> { log += 'eat;' }
            cat.crap = ()=> { log += 'crap;' }
    
            cat.lifecycle()
    
            $mol_assert_equal( log , 'eat;crap;' )
        }
    })

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


    Чтобы создать компонент не нужны никакие TestBed, detectChanges и прочие configureTestingModule — вы просто создаёте экземпляр и поехали тестировать:


    const app = new $mol_app_hello
    app.name( 'Jin' )
    
    $mol_assert_equal( app.greeting() , 'Hello, Jin!' )

    Тут мы проверили апи компонента, но можем опуститься и глубже по структуре компонент:


    const app = new $mol_app_hello
    app.Name().value( 'Jin' )
    
    $mol_assert_equal( app.Greeting().sub()[0] , 'Hello, Jin!' )

    А можем и ещё глубже, до DOM элементов:


    const app = new $mol_app_hello
    app.Name().dom_tree().value = 'Jin'
    
    $mol_assert_equal( app.Greeting().dom_tree().textContent , 'Hello, Jin!' )

    А как же зависимости? Да точно так же:


    Заголовок спойлера
    const app = new $mol_app_todomvc
    
    // Клонируем контекст
    app.$ = Object.create( app.$ )
    
    // Мочим локальное хранилище
    const storage = app.$.$mol_state_local = class< Value > extends $mol_state_local_mock< Value > {}
    
    // Заполняем хранилище
    storage.value( 'mol-todos' , '[1,2]' )
    
    // Проверяем, что данные из хранилища восстанавливаются верно
    $mol_assert_equal( app.task_ids().toString() , '1,2' )

    Всё, потратив всего 5 минут времени вы уже знаете как читать и писать любые $mol тесты. При этом получая в результате:


    • Быстрое написание тестов (меньше бойлерплейта, не нужно лазить по докам в поисках нужного метода)
    • Простое написание тестов (обычный JS/TS, единообразное строго типизированное апи)
    • Понятные стектрейсы (имя теста показывается как имя функции, стек не завален лишними вызовами)
    • Быстрые тесты (для сравнения, у меня 12 тривиальных тестов ангуляровских компонент идут 2 секунды, а 86 тестов $mol компонент пробегают за 100мс)
    • Исчерпывающие тесты (проверяем то, что надо, а не то, что позволяет тестовый фреймворк; за использование toHaveBeenCalled вместо toHaveBeenCalledTimes я бы руки отрывал, ибо пропускает целый класс довольно гадких ошибок)
  • Почему не стоит использовать LocalStorage
    +32
    cookies должны быть созданы сервером.

    Они могут быть созданы и клиентом.


    Недостатки

    Не работает в Сафари в порно режиме.


    Конечно, можно переводить все типы данных в строки, но это безобразное решение.

    Сериализация данных — вполне нормальное решение.


    если хакер сможет запустить JavaScript-код на вашем сайте, то

    Он сможет получить любую доступную пользователю приватную информацию, независимо от того используете ли вы localStorage или нет. И CSP конечно же не спасёт, ибо есть куча способов переслать себе данные через сам же атакуемый сайт.


    Наверняка ваш сайт содержит скрипты, которые загружаются с других серверов.

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


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

    Можно. Если вы не знаете как — это не значит, что это невозможно.

  • Удивительный Angular
    –1

    Одни передёргивания… вам не стыдно использовать такие убогие демагогические приёмы для защиты какого-то фреймворка?


    О каких приседаниях речь-то? Мне кажется, вы тут боретесь с собственноизобретенной проблемой.

    Конечно же ангуляровские модули — это моё изобретение.


    Уж выберите что-то одно — вы ходите писать html/css в том же файле, или отделять от логики? В чем там у вас проблема с «провязыванием» (1 строчка для темплейта, одна — для стилей) вообще не ясно.

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


    Это множественные роутеры, а не вложенные (когда несколько разных, независимых роутов на одной странице).

    Ага, это named router-outlet — одно из типично предлагаемых решений.


    Полезна, потому что показывает наличие забытых компонент.

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


    Оно и не должно быть консистентно, потому что с АОТ приложение работает не так как с джитом.

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


    Зачем вы полезную фичу объявляете багом?
    Багом её считаю не я, а автор баг-репорта и ещё 91 человек, что нагуглили ту же проблему и не поленились поставить лайк. Я считаю эту фичу кривым дизайном.
    Ну считайте, однако, мнение на факты не влияет.


    Огрызайтесь, пожалуйста, перед зеркалом.


    angular.io/guide/testing
    Там даже раздел про Inject() есть, разве не так?

    Уверен вы вполне в состоянии открыть ссылку и воспользоваться поиском по странице.


    Все верно, если они независимы — это отдельные модули по логике ангуляра. Что не так?

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


    Очередной костыль для какого-то частного случая. При чём тут он?
    При том, что он решает вашу проблему в 100% случаев.

    Не решает и для 1%.


    Не надо никакой груды операторов. Просто share() на конце любого observable со значительным количеством вычислений.

    Если это всё, что вы умеете делать для оптимизации, то вы пишите крайне неэффективный код. Эффективный код на Rx писать катастрофически сложно.


    Операторов, конечно, много, но по факту нужны map, flatMap, switchMap, combineLatest, withLatest, share. Ну do для отладки.

    debounce, distinctUntilChanged, subscribe, unsubscribe, next, throw, getValue, 'catch', defer,


    Что-то иное нужно крайне редко, раз на тысячи строк кода.

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


    И ангуляр забыли, там тоже стримов нет.

    https://github.com/angular/angular/search?q=rxjs


    RxJS так не работает.
    А как он, по-вашему, работает?

    Уверен вы прекрасно знаете как он работает. Остальным же рекомендую почитать: https://github.com/nin-jin/slides/tree/master/orp#%D0%9F%D0%B0%D1%80%D0%B0%D0%B4%D0%B8%D0%B3%D0%BC%D1%8B чтобы понять фундаментальную разницу.


    Среди того, что расписано я вижу плюсы html, а не минусы.

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


    Ambient context из $mol или React.
    Серьезно? Нечитабельный мол и помесь ужа с ежом из реакта — хорошая реализация?

    Серьёзно. Реализация контекста в $mol никак не связана с языком композиции кмпонент, который вы необоснованно хейтите. И что не так с его реализацией в реакте? Ну, кроме того, что реакт не умеет в реактивность.


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

    И при чём тут styles encapsulation, если мы говорили про shadow-dom, который без собственно dom не работает?


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

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


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

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


    Да, она не лучшая, но это лишь временное решение до повсеместного введения shadow dom.

    С учётом популярности React и непопулярности Polymer, shadow dom можно уже не ждать.


    Там слежение за изменениями не сделано никак, т.к. фреймворку о том, как и что изменяется, объясняет сам программист, руками.

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


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

    Разберитесь в предмете, пожалуйста, чотбы не говорить глупостей: https://habrahabr.ru/post/349022/


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

    На $mol их гораздо меньше в процентном соотношении. Всё потому, что писать качественные компоненты на $mol гораздо проще, чем на Ангуляре.

  • Удивительный Angular
    0

    serf


    Кодогенерация совсем не обязательна

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


    Включите себе AOT и fullTemplateTypeCheck в dev режиме и будет более строгая валидация.

    Очень странная логика. Разработчик пилит — у него ничего не падает. Запушил — получай стекстрейсами по фейсу. Правильно, не надо разработчику знать об ошибках. Даже варнинги слать не надо. Устроим ему приятный сюрприз. А когда он возмутится — будем его унижать, мол он бузит, не разобравшись в теме: надо же было догадаться пойти поискать флаг компилятора, влючающий проверку типов в шаблонах.


    Starche


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

    Два разных роута — два разных компонента. Ререндера не будет лишь при вложеном роутинге, но тогда со внешнего роута не получить доступа к параметрам вложенного. А они нужны. Но я выкрутился через один роут с queryParams и условным рендерингом компонента.


    Druu


    Вложенный router-outlet

    О, это чудеснейшая вещь, которая зашивает в урлах структуру отображения, из-за чего урлы у вас буду выглядеть не /tasks/1/, а /(left:tasks;right:tasks/1). Классный дизайн, ничего не скажешь. Любой более-менее серьёзный рефакторинг и у пользователя сломаются все закладки. А ведь всего-то надо было предоставить реактивный стор с параметрами урла и простую возможность формировать списки компонент для рендеринга, учитывая состояние этого и других сторов. И всё, больше ничего не надо было бы для реализации любой схемы навигации.


    Зачем вы полезную фичу объявляете багом?

    1. Она бесполезна. От слова "совсем".
    2. Поведение с АОТ и без него не консистентно.
    3. Багом её считаю не я, а автор баг-репорта и ещё 91 человек, что нагуглили ту же проблему и не поленились поставить лайк.
    4. Я считаю эту фичу кривым дизайном.

    Зачем вы плясали с бубном и пытались самостоятельно разбираться в том, что прямым текстом описано в любом туториале по тестированию ангуляра и ангуляровскому DI?

    Вы в конкретное место этого полотна меня ткните, где описано, что надо использовать "устаревший" @Inject(), чтобы тесты не падали: https://angular.io/guide/testing


    Зачем вам эти 4 файла?

    Как минимум: скрипт, шаблон, стили, модуль и тесты. Плюс то же самое для демонстрационного компонента. Всё это надо провязать друг с другом и с приложением. Писать html и css в строковых литералах — так себе удовольствие, если вы об этом. Даже IDEA криво поддерживает миксование языков.


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

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


    Не надо изворачиваться, просто share().

    Очередной костыль для какого-то частного случая. При чём тут он? В Rx десятки таких костылей. Так что головоломка не только в том, как правильно сконфигурировать потоки, но и в том, какой же из этих костылей нужно использовать. Даже шпаргалки есть, ибо разобраться в этой груде похожих операторов весьма не просто: https://xgrommx.github.io/rx-book/content/which_operator_do_i_use/instance_operators.html И то тут далеко не все операторы учтены.


    Как она может быть несовместима, если именно так и работает?

    RxJS так не работает. Так работает Ангуляр. В результате получается химера из ОРП и ФРП. Посмотрите, как сделан $mol или Vue — там нет никаких стримов. Только объекты, их свойства, функции вычисления их значений и всё, никаких десятков видов стримов с сотней операторов.


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

    Он говно не поэтому, а потому что синглтон.


    Даже если забыть про то, что html совршенно не годится для композиции компонент
    С каких это пор?

    С тех пор как появился. Тут я это уже расписывал: https://github.com/nin-jin/slides/tree/master/mol#%D0%A7%D1%82%D0%BE-%D0%BD%D0%B5-%D1%82%D0%B0%D0%BA-%D1%81-html


    Можете привести пример «неужасной» реализации?

    Ambient context из $mol или React.


    shadow dom и так то мертворождённая технология, ибо никому фактически не нужна, а проблем доставляет массу.
    Почему не нужна?

    Потому что болтами привязывает вас к DOM. Опыт Polymer показывает, что это тормоза на ровном месте и крайнее неудобство использования. А в тренде всякие VDOM, SSR, Canvas и Native.


    Разве придумали уже какой-то другой человеческий способ изоляции стилей?

    1. Изоляция стилей доставляет больше проблем, чем пользы. Банально попробуйте вменяемо реализовать разные цветовые схемы разных панелей, содержащих одни и те же компоненты, чтобы работало в IE11.
    2. Вы правда считаете реализацию этой псевдоизоляции в Ангуляре человеческой? С невменяемым поведением :host-context. С депрекейтнутым без альтернатив :ng-deep. С адской специфичностью селекторов. И криптомусором в доме.

    Опять же, в сравнению с чем? Реакт, вон, на каждое обновление вообще заново всю страницу перестраивает.

    Нашли на кого ровняться, да. Посмотрите как слежение за изменениями сделано в $mol и Vue — им не надо обновлять состояния всех компонент, чтобы понять, что ничего ререндерить не надо.


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

    Классный совет. Можно добавить ещё: никогда не обновляйте Ангуляр. А то у нас тут ребята потратили спринт на обновление до 5 версии, но так и не завелось. В итоге откатились до 4, ибо фичи сами себя не сделают, пока разработчики развлекаются с дебагом чужого говнокода. У меня к вам встречный совет — не используйте плохо написанные, криво спроектированные переусложнённые фреймворки без цельной экосистемы переиспользуемых компонент.

  • Изучаем и реализуем алгоритм работы правильного observer паттерна для react компонентов
    +2

    $mol_atom именно так и работает, за одним исключением.


    Если же значение равно "check", то ячейка попросит свои зависимые ячейки актуализироваться (вызовет метод actualize()) и после этого снова проверит свое значение и если оно равно "check" то мы меняем значение на "actual" и не вызываем перерасчет, если же оно "dirty" то мы соответственно должны вызвать перевычисление.

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

  • Удивительный Angular
    +4

    Итак, продолжим..


    Шаблоны, основанные на расширении HTML

    Даже если забыть про то, что html совршенно не годится для композиции компонент, реализация в Ангуляре самая ужасная. Вы можете прицепить структурную директиву к элементу через звёздочку… но к одному элементу не более одной. Вы можете забиндить выражение через двойные фигурные скобочки, а можете через квадратные вокруг имени атрибута. Мы очень последовательные ребята. Синтаксис настольо интуитивно понятен, что чтобы не путать квадратные и круглые скобочки даже придумали метафору "бананы в ящике", словно намекая на то, кто будет работать с этим фреймворком. Ну и конечно же куда без примитивной пародии на JS в атрибутах *ngFor и иже с ним. А как чудесен костыль с объявлением кастомных локальных переменных внутри цикла через *ngIf, чтобы не писать (task$|async).title и тому подобные штуки, которые безбожно тормозят в большом количестве.


    Кроссбраузерный Shadow DOM из коробки (либо его эмуляция)

    shadow dom и так то мертворождённая технология, ибо никому фактически не нужна, а проблем доставляет массу. Так ещё и эта изумительная эмуляция. Компонент практически не контролирует элемент в который рендерится. Вы даже не сможете банально директиву применить к этому элементу из компонента. В результате, если вашему компоненту нужна функциональность директивы, то извольте указывать её при каждом вызове компонента. И конечно же компонент не может указать каким хтмл-тэгом его рендерить. В результате, вы либо повторяете для каждой ссылки routerLinkActive="active" и [routerLinkActiveOptions]="{ __change_detection_hack__: [ id, mode ] }", либо каждая ссылка превращается в два элемента — элемент компонента и ссылка с нахлабучками.


    __change_detection_hack__ — это моё вчерашнее открытие. Без этого костыля роутер не всегда добавляет класс текущим ссылкам. Люди целые статьи пишут как починить базовое поведение стандартного роутера: https://www.bennadel.com/blog/3375-forcing-routerlinkactive-to-update-using-an-inputs-hack-in-angular-5-0-2.htm
    Сделать роутер настолько ущербным и настолько кривым — это нужно было постараться.


    Более современный фреймворк, чем AngularJS (на уровне React, Vue);

    Вот это великое достижение, да. На любое событие обновлять состояние вообще всех компонент на странице — это деградация даже по сравнению с AngularJS. Спасибо, что хоть вообще дали возможность выключить этот dirty cheking. Но, конечно, вынести это в общую настройку или хотя бы выключить по умолчанию — не наш метод. Пусть разработчики в каждом компоненте пишут: changeDetection: ChangeDetection.OnPush, если не хотят, чтобы их приложение лагало при скроллинге.


    Помните тот троллинг про Angular 2 (когда многие начинали переходить с AngularJS на Angular 2), когда наши приложения весили 1Мб и когда Webpack 2 падал с непонятными ошибками?

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


    Был скомпилирован написанный на Angular компонент, который вместе с ядром Angular в сумме (без дополнительных манипуляций сжатия, минификации и прочего) дает на выходе всего 44кб.

    Открываем консоль:


    hello-world.js — без минификации, да — 4.5kb
    elements.js — минифицирован — 15.6kb
    core.js — минифицирован — 125kb
    rx-lite — минифицирован — 17.7kb


    44.7кб получается лишь после gzip-ования.


    И это только для того, чтобы нарисовать 3 строчки текста в рамочке.


    Сейчас мы с вами поговорим о том, что нас ждет в Angular 6.

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


    Уф, выдохнул, спасибо, что выслушали.

  • Фундаментальная уязвимость HTML при встраивании скриптов
    +2

    Эта и многие другие фундаментальные проблемы html уже решены в xhtml. Но индустрия выбрала вариант стандартизировать говнокод.

  • Удивительный Angular
    +12
    но гордится больше всего тем, что почти 5 лет он проработал вместе со Стивом Джобсом. Даже здесь, разговаривая про Angular, мы можем вспомнить старину Джобса и

    … пытаемся выехать за счёт его известности. Ведь работа недалеко от известного человека магическим образом повышает уровень профессионализма и умения проектировать фреймворки.


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

    К сожалению, энтерпрайз тяготеет больше к дубовым переусложнённым вещам. В это среде очень популярно мнение, что если вещь сложная, то много чего умеет. А если простая, то там много чего не хватает. Мой опыт работы с этим чудом инженерной мысли говорит, что он очень не гибкий. Шаг в сторону от того, что написано в документации и ты напарывашься на "это невозможно" и "если тебе так не нравится как сделано в Ангуляре, то никто не заставляет его использовать". Ну, банальный пример: как сделать так, чтобы при переходе с /tasts/ на tasks/1/ роутер не перерендеривал весь список задач, а просто открывал рядом панельку с детализацией? Базовое поведение безбожно сломано кривыми абстракциями.


    Поддержка Google, Microsoft;

    Одних только не закрытых багов более 800: https://github.com/angular/angular/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen++bug
    Ещё 1000 других проблем: https://github.com/angular/angular/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+


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


    Первый же попавшийся пример: https://github.com/angular/angular/issues/13590 — человек включил AOT компиляцию (а без неё размер бандла становится огромным) и всё упало. Компилятор зачем-то требует, чтобы каждый компонент был импортирован хотя бы в один модуль.


    У меня было ещё веселее — приложение работает, а тесты все падают (в рантайме, конечно же), что не могут зарезолвить зависимости. Пляски с бубном и перелопачивание СтекаПереполнения подсказало воркэраунд — добавлять @Inject() для всех зависимостей. Вот тебе и устаревшая конструкция. При этом самостоятельно разобраться в том, почему такое происходит, не представляется возможным.


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


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


    Инструменты разработчика (CLI);

    Опять же, из-за переусложнённой архитектуры, требующей кучу приседаний, вам просто жизненно необходимы кодогенераторы. Ибо на каждый компонент нужно создать от 4 файлов и провязать их друг с другом и приложением. При этом стандартный кодогенератор генерит лишь что-то своё и не даёт толком подстроить бойлерплейт под проект. Например, нам для каждого общего компонента нужно создавать ещё и демонстрационную страничку, а также ангуляровский модуль. Ангуляровские модули — это вообще чудесная нашлёпка поверх нативных, из-за чего tree shaking перестаёт работать. Всё, что вы включили в модуль безусловно попадёт в бандл. Поэтому мы к каждому компоненту прикладываем отдельный модуль, чтобы можно было подключить только его, безо всех остальных компонент.


    А как этот CLI делает рекомпиляцию — это просто чудо. Например, он может просто забыть перекомпилировать код — и сиди думай, чего это то, что ты написал работает неправильно. Бывает сборка падает, но ты об этом не узнаешь пока не заглянешь в консоль — в браузере будет показываться просто устаревший код. Особенно при рефакторинге эта фича очень доставляет. Да, вы можете порекомендовать какой-то иной "стартер кит", где таких проблем нет. Но суть не в том, что нельзя никак выкрутиться (выкрутиться можно всегда, ну или привыкнуть и перестать замечать), а в том, что базовое поведение стандартных инструментов безбожно сломано.


    TypeScript из "коробки" (вы можете писать строго типизированный код);

    Ангуляр — один из немногих фреймворков, написанных на TS. Это действительно классно, когда IDE понимает, что как взаимосвязано в коде. Но и тут Ангуляровцы накосячили — шаблоны совсем не типизированы. Вы можете написать любую чушь в них и они мало того, что скомпилируются, но даже в рантайме не выдадут ошибок — у вас просто что-то не отрендерится. Да, в новой версии обещают переписать компилятор шаблонов. Но это лишний раз подчёркивает, что изначально эта базовая функциональность была сделана спустя рукава. И лишь спустя несколько лет латания дыр, они таки созрели что-то с этим сделать. И то, когда это ещё будет, а приложение выпускать надо уже вчера. Удивительный фреймворк будет этот Ангуляр версии к 10, ага.


    Реактивное программирование с RxJS;

    Это самая бестолковая форма реактивного программирования, превращающая код в головоломку вида "как бы так извертнуться, чтобы состояние всего мира не перевычислялось на каждый чих". Поэтому Rx в компонентах стараются не использовать, ибо она малость не совместима с простой и понятной схемой "поменяли что-то в объекте — компонент отложенно перерендерился". Хоть бы на MobX сделали, который сам автоматически конфигурирует потоки данных.


    Единственный фреймворк с Dependency Injection из "коробки";

    Не единственный. Но единственный с такой трендовой кривой реализацией. Компоненты нигде не инстанцируются напрямую через new (даже в тестах), поэтому инъекция через конструктор не даёт никаких профитов. А вот неудобств полно — каждый раз наследуясь от класса необходимо скопипастить объявления всех его зависимостей и прокинуть их ему. Резолвятся эти зависимости сразу при создании объекта, даже если никогда не будут им использованы. При этом dependency tree в лучших традициях запрятано куда-то в жопу и настолько переусложнено, что через отладчик хрен разберёшься, где что одно проиинъектиться и почему он не может что-то правильно заинъектить.


    Продоолженние следует...

  • Отменяемые Promises в EcmaScript6
    –1

    Ситуаций, где необходим неидемпотентный код крайне мало. Соответственно, и $mol_fiber_sync вставлять приходится крайне редко. И то в основном для оборачивания тяжёлых функций, чтобы они не блокировали поток на долго.

  • Отменяемые Promises в EcmaScript6
    0

    Нет, функия должна быть идемпотентной, но не обязательно чистой. Неидемпотентные вызовы просто заворачиваются в $mol_fiber_sync и они становятся идемпотентными.
    Дока с тестами тут: https://github.com/eigenmethod/mol/tree/master/fiber
    Вообще весь $mol построен на той же концепции, так что в продакшене вполне нормально с нею живётся.


    А ваш код должен выглядеть так:


    let requestCount = 0;
    
    const fetchInformation = () => {
      fetching = $mol_fiber_make(() => {
        $mol_fiber_sync( ()=> requestCount++ );
        log.innerText = requestCount;
        const text = fetch({ uri : 'some.csv' }).responseText;
      });
      fetching.start();
    }
  • Отменяемые Promises в EcmaScript6
    –3
    1. В вашем коде куча ошибок. Я, конечно, понимаю, что вы достиги дзена и можете интерпретировать код в голове. Но браузеры ещё не достигли вашего совершенства, чтобы выходить из бесконечного цикла за конечное время.


    2. Я тут переписал ваш код на файберах с отменяемыми запросами на любой стадии исполнения:

    Заголовок спойлера
    const log = document.getElementById( 'log' )
    
    let fetching = null
    
    const fetchInformation = ()=> {
    
      fetching = $mol_fiber_make( ()=> {
        log.innerText = 'Fetching...'
        const text = fetch({ uri : 'some.csv' }).responseText
        log.innerText = JSON.stringify( parseCSV( text ) )
      } )
    
      fetching.start()
    }
    
    const cancelFetch = ()=> {
      fetching.destructor()
      fetching = null
      log.innerText = 'Cancelled'
    }
    
    const fetch = ({ uri })=> {
    
      return $mol_fiber_async( back => {
    
        const xhr = new XMLHttpRequest()
    
        xhr.open( 'GET', uri ) // API endpoint URL with some big CSV database
    
        xhr.onload = back( () => {
    
          if( Math.floor( xhr.status / 100 ) !== 2 ) {
              throw new Error( xhr.statusText )
          }
    
          return xhr
        } )
    
        xhr.onerror = back( () => {
          throw new Error( xhr.statusText )
        } )
    
        xhr.send()
    
        return ()=> xhr.abort()
    
      } )
    
    }
    
    const parseCSV = $mol_fiber_func( text => {
    
      let lastDemileterIdx = 0
      let result = []
    
      do {
    
        const newIdx = $mol_fiber_sync( ()=> text.indexOf( '\n' , lastDemileterIdx ) )
    
        const row = $mol_fiber_sync( ()=> {
            log.innerText += '.'
            const end = newIdx > -1 ? newIdx : Infinity
            return parseCSVRow( text.substring( lastDemileterIdx , end ) )
        } )
    
        result.push( row )
        lastDemileterIdx = newIdx + 1
    
      }  while( lastDemileterIdx > 0 ) 
    
      return result
    } )
    
    /* Some function for row parsing which works very slow  */
    const parseCSVRow = text => {
    
      for( const from = Date.now() ; Date.now() < from + 5 ; ) {}
    
      return text.split( ',' )
    }

    http://plnkr.co/edit/vPKpKKwIhALm1lUEIrKu?p=preview

  • Эксперимент с бинарным кодом в Glimmer
    0

    https://lifeart.github.io/sierpinski-glimmer/ — сихронная версия подтормаживает
    https://lifeart.github.io/demo-async-dom/glimmer-port/index.html — асинхронная версия даёт плавную анимацию, но так же и артефакты при первичном рендеринге и не очень шуструю реакцию на действия пользователя.

  • Топ-10 ошибок из 1000+ JavaScript-проектов и рекомендации по их устранению
    0

    Нужно возвращать содержимое Origin, а не *, чтобы авторизация работала.

  • VDOM своими руками
    0

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

  • VDOM своими руками
    0

    Можно и без костылей.


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


    1. понятна его семантика
    2. понятно кто его владелец
    3. быстрый доступ из консоли
    4. не ломающиеся при каждом чихе тесты
    5. ну и никакие дифы считать не надо, элементы располагаются за линейное время

    mayorovp это совсем не трудоёмкая операция, если используются правильные инструменты.

  • VDOM своими руками
    –1

    При поверхностном взгляде кажется, что нужно просто сравнить два дерева и произвести минимальные изменения для трансформации одного в другое. И это действительно так, когда мы полностью контролируем состояние. К сожалению, как выше отметили, далеко не все состояния элементов нам подконтрольно. Поэтому мы не можем рендерить одни и те же данные то в одни узлы, то в другие. То есть узлы должны однозначно соответствовать исходным данным. В Реакте для этого есть костыль с указанием key для элементов полученным из массивов. Но эта проблема куда фундаментальней. key не поможет при переносе элемента из одного массива в другой, из одного контейнера в другой и тд. И самое печальное, что идентифицировать элементы невозможно автоматически — только программисту известно где перенос, а где удаление и добавление чего-то похожего. Поэтому в моих реализациях программист всегда явно задаёт уникальное имя каждому элементу исходя их данных, что он рендерит. Например: https://github.com/eigenmethod/mol/tree/master/dom/jsx

  • Соглашения по именованию CSS-сущностей и экономия времени
    0

    Шёл 2018 год, а столь простые вопросы по прежнему не находят ответа в "современных" решениях.

  • Создаем CSS кейлоггер
  • Office 365. Пример работы с Microsoft Graph API в Angular5 с помощью ADAL JS. ADAL JS vs MSAL JS
    0
    import { } from 'adal'; // без этого приложение будет собираться, но будут сообщения об ошибках

    Подозреваю должно быть так:


    import 'adal'; // без этого приложение будет собираться, но будут сообщения об ошибках
  • Соглашения по именованию CSS-сущностей и экономия времени
    0

    Расскажите, пожалуйста, как по особенному стилизовать элементы модуля А, когда он находится в модуле Б.

  • И так сойдёт… или как данные 14 миллионов россиян оказались у меня в руках
    0

    Гарантийное обслуживание никто не отменял.

  • JavaScript и ужасы мутаций
    0

    Любые сущности в программировании можно разделить на 2 типа:


    • контейнеры — мы в них можем что-то положить, и что-то вынуть. Контейнер может изменяться, но от этого он не превращается в другой контейнер. Мы можем его куда-то передать в качестве значения и ожидаем, что он будет так же мутабелен. Контейнеры реализуются через ссылочные типы: собственно ссылки, объекты, их поля, просто переменные. Два контейнера могут содержать одинаковые данные, но будут по прежнему разными контейнерами.
    • значения — структуры, суть которых определяется их содержимым. Два значения с одинаковым содержимым — равнозначны. Как правило изменения их не ожидаемы и даже опасны. Как правило их делают readonly. Передаются значения… по значению :-)

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

  • Javascript — решение асинхронной проблемы?
    0

    Думаю вам будет небезынтересно взглянуть на эту магию:
    https://github.com/eigenmethod/mol/tree/master/fiber


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

    Ну вот вы оценили задачу в 1 день. А программист делал её 2. Это программист плохой (и получит он в 2 раза меньше) или задача тяжёлая (и должна стоит в 2 раза дороже)?

  • Честный подход к управлению людьми, или Почему я никогда не делаю контрофферы
    +1

    Что мешает программисту содать 100500 мелких задач и не браться за крупные?
    Что мешает начальнику поставить 1 огромную задачу со словами "тут же только текст подвинуть"?
    Примеры намеренно утрированные. Суть в злоупотреблениях разной степени тяжести.

  • Про ошибки и исключения
    0

    Найтите 10 различий:


    public IActionResult Post(ChangeUserNameCommand command)
            {
                var res = command.Validate();
                if (res.IsFaulted) return res;
    
                return ChangeUserName(command)
                    .OnSuccess(SendEmail)
                    .Return<IActionResult>(Ok, x => BadRequest(x.Message));
            }

    public IResult Post(ChangeUserNameCommand command)
            {
                command.Validate();
    
                try {
                    return SendEmail(ChangeUserName(command))
                } catch( Exception x ) {
                   throw new BadRequest(x.Message);
                }
            }

    Давайте я начну:


    1. В первом коде вы узнаете об ошибке где-то далеко от места её возникновения. Во втором отладчик услужливо остановит исполнение там, где она произошла.
  • Честный подход к управлению людьми, или Почему я никогда не делаю контрофферы
    +13

    Иллюстрация теста на публичность:


  • Про ошибки и исключения
    0

    Сначала вводим тип-объединение, чтобы заставить вызывающий код проверять ошибки. А потом используем слово на букву М, чтобы их тупо игнорировать. Что-то тут не так..

  • Честный подход к управлению людьми, или Почему я никогда не делаю контрофферы
    +11

    Ну ещё бы. Свои ошибки признавать крайне неприятно. Приходится быть честным :-)


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


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


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


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

  • Соглашения по именованию CSS-сущностей и экономия времени
    0

    Не в любой, не обобщайте. У нас, например, используется такое именование: my_calculator_form_field_required. И с выделением ни в каком редакторе/вьювере нет проблем. И рефакторинг элементарен — просто заменяем подстроку по всем файлам. И не важно js это или css или даже json. Никакие js- префиксы не нужны.

  • Как вместить property в один байт?
    0

    Это называется lowering — автоматическое понижение высокоуровневых абстракций до низкоуровневых.

  • JavaScript и ужасы мутаций
    +1
    1. Object.freeze, Object.assign, ImmutableJS и прочие такие штуки серьёзно так замедляют работу приложения. Я бы рекомендовал воспользоваться TypeScript, который не позволит вам динамически изменить сигнатуру объекта или изменить readonly свойство. При этом ещё на стадии написания кода, а не в рантайме и соответственно без замедления исполнения.


    2. Объекты можно условно разделить на два типа: значения и контейнеры. Беда JS в том, что, например, один и тот же Array выступает и в роли контейнера (push, pop, ...) и в качестве значения (map, filter, ...).


    3. Никаких опасностей мутыций вы не продемонстрировали. Только описали азы языка и назвали их "опасными мутантами". Кстати, тот самый синий и волосатый монстр, о котором вы говорили, — весьма душевный человек и проницательный собеседник :-)
  • Разрабатываем React-компоненты многократного использования
    0

    В js с json все же работать проще. Хотя, конечно, убивает, что нельзя сказать "у вас ошибка на такой-то строке". Е4х, к сожалению, все.

  • Разрабатываем React-компоненты многократного использования
    –5

    Меня часто спрашивают, как же подобная задача решается в $mol, где все компоненты умные. Думаю вам в Единой Фронтальной Системе тоже будет не безынтересно это узнать.


    Итак, берём реализацию ToDoMVC. Всё приложение — это один компонент $mol_app_todomvc: https://github.com/eigenmethod/mol/tree/master/app/todomvc.


    Если заглянуть в код, то можно обнаружить, что работа с задачами ведётся через два свойства:



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


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


    Заголовок спойлера
    <= Todo_widget $mol_app_todomvc
        task_ids?val <=> task_ids?val /
        task!id?val <=> task!id?val *
            title \
            completed false

    А теперь, добавим нашу логику. Например, загрузим список задач из json файла:


    Заголовок спойлера
    tasks() : $mol_app_todomvc_task[] {
        return this.$.$mol_http.resource( 'tasks.json' ).json() 
    }
    
    task_ids() {
        return Object.keys( this.tasks() )
    }
    
    task( id : number ) {
        return this.tasks()[ id ]
    }

    Или что-нибудь по сложнее — будем и читать и писать в файл, через WebDav:


    Заголовок спойлера
    tasks( next? : $mol_app_todomvc_task[] ) {
        return this.$.$mol_webdav.item( 'tasks.json' ).json( next )
    }
    
    task_ids( next? : number[] ) {
        return Object.keys( this.tasks( next ? next.map( id => this.task( id ) ) : undefined ) )
    }
    
    task( id : number , next? : $mol_app_todomvc_task ) {
        return this.tasks( next )[ id ]
    }

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

  • Возможности JavaScript, о существовании которых я не знал
    –1

    2 — value()я не привёл так как приведённый код это далеко не всё её содержимое. В частности весь приведённый код завёрнут в try-catch для перехвата ошибок. Именно поэтому вызывать computedValue из других мест нельзя. Да и ситуаций, когда "кэширование явно не нужно" нету, ибо сам этот класс применяется, когда нужно кеширование. Вы делаете слишком далекоидущие выводы по обрывку кода.


    3 — тут нет хвостового вызова. Хэндлер может обратиться к другим реактивным переменным (и зачастую так и поступает) в которых вызывается эта же функция, но в другом контексте. Циклическая зависимость разумеется отсекается — код для этого располагается выше, но его я опять же не привёл, ибо он не имеет отношения к обсуждаемому вопросу.


    4 — не путайте отладку и тесты. Отладка вступает в дело, когда тесты упали и нужно понять почему. Кроме того юнит тесты бесполезны для коммуникационного модуля. А отладка — это далеко не только "раскручивание стека", но ещё и: точки останова, исполнение по шагам, просмотр значений переменных из разных скоупов (нужно скакать по стеку, чтобы посмотреть их содержимое).


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

  • Тестируем пользовательские сценарии вместе с «Гермионой». Лекция Яндекса
    +2

    Я, конечно, верю, что умный поиск — это большая и сложная задача. Но интерфейс поиска у вас крайне прост. Даже с учётом всех колдунщиков и аж целых 20 чекбоксов в настройках. Так что ваши "35 000 стилей, 160 000 строк шаблонов", говорят не о том, что у вас много функциональности, которую надо тестировать, а о том, что у вас очень много копипасты и почти отсутствует переиспользование кода. И за примером далеко ходить не надо. Открываем настройки поиска и видим стандартные отполированные выпадающие списки:


    Заголовок спойлера


    То, что текст не выровнен, а обводка вокруг выпадающего блока почти не видна, оставим на совести дизайнера. Открываем колдунщик по сериалам и видим следующую порнографию:


    Заголовок спойлера


    Ну ок, давайте посмотрим код:


    Заголовок спойлера
    <button class="button2 button2_width_max button2_size_m button2_theme_dark button2_view_classic select2__button i-bem button2_js_inited _popup2-destructor _popup2-destructor_js_inited" data-bem="{&quot;button2&quot;:{}}" type="button" autocomplete="off" tabindex="0" role="listbox" aria-labelledby="uniq1515975375759707652" aria-expanded="false">
        <span class="icon icon_size_m icon_type_arrow-up-down button2__icon button2__icon_side_right select2__arrow i-bem icon_js_inited">
            <div class="icon__arrows"></div>
        </span>
        <span class="button2__text" id="uniq1515975375759707652">любой жанр</span></button>

    Див внутри спана и выключенный автокомплит у кнопки… теперь я видел всё. Если вы руками прописываете все эти классы и атрибуты для каждой кнопочки, то не удивительно, что у вас 160000 строк шаблонов.


    Но особенно мне нравятся вот эта портянка для рисования треугольника:


    Заголовок спойлера
    .icon_type_arrow-up-down .icon__arrows:before {
        bottom: 50%;
        margin: 0 0 -7px -3px;
        border-width: 6px 3px 0;
        border-top-color: currentColor;
    }
    
    .icon_type_arrow-up-down .icon__arrows:after, .icon_type_arrow-up-down .icon__arrows:before {
        position: absolute;
        left: 50%;
        width: 0;
        height: 0;
        content: '';
        border: solid transparent;
    }

    И это вместо простейшей иконки:


    Заголовок спойлера
    <svg viewBox="0 0 24 24">
        <path d="M7 14l5-5 5 5z"/>
    </svg>

    Не удивительно, что вам нужно 40 фронтендеров всё это разгребать :-)


    Как это могло бы выглядеть при правильном проектировании:


    Заголовок спойлера
    <ya_button ya_search_serial_genre ya_select_trigger ya_button ya_view role="button" tabindex="0">
        Любой жанр
        <svg ya_search_serial_genre_trigger_icon ya_select_trigger_icon ya_icon ya_view viewBox="0 0 24 24">
            <path ya_search_serial_genre_trigger_icon_path ya_select_trigger_icon_path ya_icon_path ya_view d="M7 14l5-5 5 5z"/>
        </svg>
    </ya_button>

    И это даже не руками написанный код, а сгенерированный из такого вот исходника:


    Заголовок спойлера
    <= Trigger $ya_button sub /
        <= value_view \
        <= Icon $ya_icon path \M7 14l5-5 5 5z
  • Возможности JavaScript, о существовании которых я не знал
    0

    Это новая реализация вот этой библиотеки, с поддержкой квантификации вычислений: https://habrahabr.ru/post/317360/
    Сможете реализовать менее "мутно" — я вам спасибо скажу :-)

  • Возможности JavaScript, о существовании которых я не знал
    –2

    Какие мы заимели проблемы:


    1. Код стал в полтора раза больше.
    2. Функция разделена на 2 без практической ценности (computedValue опасно вызывать где-либо, кроме как в value).
    3. Увеличилось потребление стека (опять же косвенная рекурсия).
    4. Усложнились отладка.
    5. Усложнился профайлинг.

    Чего мы добились:


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

    Оно того стоило?