Pull to refresh

Comments 52

О, вменяемый гайд по вебпаку, ура.


  1. Файл .babelrc

Я бы отметил, что сам по себе .babelrc поддерживает разные окружения. Удобно в dev использовать только то, что не поддерживает твой браузер, а в прод деплоить чистый ES5 для IE, Safari и прочих любителей клея, и без соурс-мапов.

Есть хороший скринкаст от Ильи Кантора: http://learn.javascript.ru/screencast/webpack на Русском.
Подтверждаю, процентов 90-95 всех вопросов как рукой снимет.

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

Нет допущения что скринкаст может быть очень удачным? Я лично присоединяюсь ко всем кто советует. Множество практических примерров. Переход от простого к сложному. Разбор множество деталей и неочевидностей. Помоему достаточно аргументов.

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

Большинство проектов используют так много функций, что у них, как правило, есть 2 больших файла конфигурации Webpack.

Есть такой модуль: webpack-merge — он по уму склеивает разные конфиги, так что большие конфиги бьются на маленькие, а из основного можно подтягивать нужные чанки в зависимости от переменных окружения и делать разные сборки (dev, prod, test например)

entry  – объект

если ендпоинтов несколько, можно массивом сделать не только entry но и сам конфиг webpack. То есть если в webpack.config.js сделать вот так:
module.exports = [mainConfig, helpersConfig, handlersConfig]; — где элементы массива — полноценные конфиги webpack- а, то он отработает по очереди, как если бы его три раза вызвали с разными конфигами.

как если бы его три раза вызвали с разными конфигами.

а почему «как если бы» если это так и есть

Вызывая три раза с разными конфигами, вы не расшарите между ними контекст, вызывая три конфига в одном массиве — все три конфига захватывают один контекст (конфиг webpack — это просто js модуль, мы же помним?)

Я бы лучше постарался все впихнуть в один конфиг, с несколькими конфигами не всегда будет удобно работать.
К пункту 4:
file-loader позволяет передать свой собственный publicPath через параметры, отличный от того, что в output. Таким образом можно все картинки в production-режиме закинуть на отдельный сервер.
Более того, в качестве publicPath можно даже функцию передать, чтобы динамически url генерить.
Отсюда совет — читайте исходники лоадеров, в них много всего интересного.
Новички не знают, но указывая entry таким образом (путь как имя entry) можно иметь разные output для разных entry, иногда бывает очень полезно:

entry: {
    '/modules/someModule': './src/modules/someModule')
}


На выходе получите бандл помещенный в /modules/someModule относительно output.path.

Как уже указывали выше, если работаешь с Webpack плотно, делая не совсем типичные вещи, придется читать исходиники, документация не полная.
Еще «resolve.alias» очень полезная вещь.
Есть, кстати, отличный туториал по вебпаку на русском https://learn.javascript.ru/screencast/webpack
А как обстоят дела с обработкой изображений в webpack? Например нужно собрать все мелкие картинки(svg,png,jpg...) и склеить их в спрайт (один или несколько в зависимости от размера) и при этом корректно подменить в в css? Это возможно?

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

Если у кого есть хороший опыт в этом вопросе поделитесь пожалуйста
Может быть, вы сможете ответить на мой вопрос. Есть три модуля TypeScript. Модуль A.ts:

import {B} from './B';

export class A {
 toB(): B {
  return new B();
 }
}


И модуль B.ts:

import {A} from './A';

export class B extends A {
}


И главный модуль:

export * from './A';
export * from './B';


А потом мне надо собрать главный модуль как бандл. Сборка осуществляется через WebPack и ts-loader. Очевидно, в бандле объявление класса A должно идти раньше объявления класса B, иначе возникнет ошибка при попытке наследования. Но из-за циклической зависимости между модулями A и B подключение происходит в обратном порядке. А если подобных модулей добавить еще больше, порядок запуска кода предсказать становится очень сложно. Как решить эту проблему, не объединяя весь код в один модуль? Можно ли заставить WebPack подключать зацикленные модули в том порядке, который мне нужен?
Насколько я знаю то webpack при зборке вынесет class A и B в верх бандла, а потом их будет использовать. Но какая то странная зависимость. Так делать не стоит.
Нет, он прогонит код B.ts и бандл упадет с ошибкой еще на этапе загрузки. А если в главном модуле поменять их местами, все будет работать. Никакая это не странная зависимость, такое встречается сплошь и рядом. Особенно в TypeScript, где целый модуль надо импортировать лишь для того, чтобы указать имя стороннего класса в семантике какого-нибудь метода. Это не только проблема WebPack: RequireJS тоже этим страдает. В то время как Java и .NET подобной проблемой не страдали никогда. Обидно, что при всем своем развитии в последние годы средства работы с JavaScript все еще такие неудобные.
Вообще класс родитель не должен ничего знать о наследниках

Соответственно у вас в коде реальная жесть.
Либо уберите extends
Либо перенесите toB() в класс B
Вообще класс родитель не должен ничего знать о наследниках

Почему? Какому принципу ООП это противоречит?

Почему жесть? Я говорю же, что в Java и .NET это работает. Даже никаких warning'ов не всплывает. И даже в JavaScript без всяких бандлеров это отлично работает, если просто файлы в правильном порядке подключить. Либо это косяк WebPack, либо решение есть, но мы просто не знаем о нем. Никак не иначе.

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

Возможно, это и решение, если мне не нужна дефолтная реализация. А она иногда нужна =)
Хотя даже при наследовании интерфейсов будет циклическая зависимость между модулями, где классы объявлены.
Почему? Какому принципу ООП это противоречит?

инкапсуляция и KISS.


либо решение есть, но мы просто не знаем о нем. Никак не иначе.

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

Хотите реальный пример?

class Object {
 String toString();
}
class String extends Object {}


Теперь давайте дружно назовем разработчиков языков программирования Java, JS, и всего .NET-совместимого обезьянами.

Инкапсуляция и KISS — это не принципы, а концепции. И они здесь ни при чем. Учите матчасть.
Теперь давайте дружно назовем разработчиков языков программирования Java, JS, и всего .NET-совместимого обезьянами.

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


В вашем примере как раз и проявляется кривизна архитектуры. Потому как toString должен сериализовать объект в строку — СКАЛЯР. А потом строку делают объектом и вылезают костыли. Что характерно — свою задачу, на которой Вы столкнулись с этой засадой Вы почему то постеснялись озвучить, стыдливо прикрывшись разработчиками языков, которые видимо должны были вызвать благоговейный трепет и безусловную капитуляцию. И расскажите мне, пожалуйста, что именно я должен был увидеть на вики по приведенной Вами ссылке что дало бы мне просветление и осознание ограниченности моего кругозора?

Я не собираюсь с вами препираться. Если не знаете ответ на поставленный вопрос о WebPack, я с вами разговаривать не буду. А вы можете продолжать есть этот кактус, оправдываясь, что если он чего-то не поддерживает, значит «архитектура кривая».
The OOP Principles

All object-oriented programming languages provide mechanisms that help you implement the object-oriented model. They are encapsulation, inheritance, and polymorphism. Let’s take a look at these concepts.

Кстати, на сайте о java

Так что действительно учите-ка матчасть
Под принципами ООП в первую очередь понимают SOLID. Где здесь нарушена инкапсуляция?
Если рассматривать со стороны SOLID, то приведеный пример нарушает I
Как интерфейс, содержащий один метод, может нарушить ISP? Будь их два, можно было бы поспорить.
Интерфейс не может содержать реализацию.

Object и String в java это классы, а не интерфейсы.
Более того в примерах вашего кода тоже классы.

Что касается нарушения, то сериализация в строку произвольного класса в корневом это и есть general-purpose.

Дальше объяснять?
Я умею отличать класс от интерфейса. Проблема остается в обоих случаях:

interface IA {
 toB(): IB;
}
interface IB extends IA {}
class A implements IA {
 toB(): IB {
  return new B(); // здесь зависимость A от B
 }
}
class B extends A implements IB {} // здесь зависимость B от A


Модули классов A и B все равно могут идти в неправильном порядке.

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

Я не понял, вы считаете, что toString является плохим архитектурным решением, нарушающим ISP?
Начну с конца.

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

И, да, Object.toString в java это именно нарушение.
Что явилось причиной такого компромисса это не ко мне.

Вы же даже противоречия в подходе не видите.
Чёткий догмат ООП это SOLID, Java это идеальное ООП — значит бездумно лепим как там.

Теперь возвращаясь к вашему коду
Класс A может содержать метод toB(), но не как интерфейс полученный от родителя.

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

То есть возможен и будет правильным вариант
interface ICommon {}
interface IA extends ICommon {}
interface IB extends ICommon {
toA(): IA
}

Более точный рецепт вы сможете получить дав описание конкретной задачи
Ок, если в Java/JS/.NET это нарушение ISP, так и быть, приведу свой пример.

Есть иерархия классов представлений одной и той же модели — записи в блоге. Запись может отображаться в виде элемента общей ленты, в виде элемента ленты своих записей, в виде попапа на Google-карте, в виде содержимого диалога и никто не знает, какие еще новые представления появятся в будущем. Все представления выглядят по-разному, но у них есть много общего — общая часть вынесена в базовый абстрактный класс. В частности, общее у всех то, что если кликнуть по кнопке «Показать в диалоге», то открывается новый диалог с этой статьей. Здесь и возникает циклическая зависимость. Я реализовал в базовом классе обработку клика по кнопке с показом диалога. Это отлично работает, если явно указать порядок подключения файлов. TypeScript не показывает ошибок компиляции, даже предупреждений. Проблема возникает при попытке собрать это WebPack'ом. Приходится копировать эту логику во все подклассы. Не предлагайте решения с шиной событий или прочими хитрыми штуками: не хватало еще ради использования WebPack перезжать на другой фреймворк.

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

Впрочем, я тут пилю свой велосипед, который имеет следующие особенности:


  1. Он просто работает. Не надо ничего конфигурировать, ставить плагины и тп. Всё, что нужно — следовать нескольким достаточно простым соглашениям по расположению кода и именованию идентификаторов.
  2. Не требует ручного импорта зависимостей — зависимости определяются по факту использования. Тут же выясняется приоритет зависимости, что позволяет даже циклические зависимости выстраивать в правильном порядке.
  3. Уже поддерживаются: JS и TS вперемешку; poscss+cssnext для CSS; сборка тестов в отдельный бандл; разные бандлы под разные окружения; сорсмапы с исходниками внутри.
  4. Есть дев сервер, собирающий бандлы по требованию и отслеживающий изменения файлов. то есть перезагружая страницу вы получаете гарантированно свежий бандл с учётом изменений.
  5. Намернно нет различия между продом и девом, что исключает редкие, но болезненные неприятные сюрпризы.

Например, ваш пример будет выглядеть как-то так:


my/aaa/aaa.ts


class $my_aaa {
    toBBB() {
        return new $my_bbb();
    }
}

my/bbb/bbb.ts


class $my_bbb extends $my_aaa {
}

Чтобы сбилдить бадл достаточно набрать: npm start my
В директории ./my/-/ сгенерируются все необходимые файлы.


Был бы рад услышать конструктивную критику :-)

Да, к WebPack я прикопался лишь потому, что он совмещает CommonJS с клиентом. На самом деле, как мне кажется, проблема в самом CommonJS. Но и AMD не лучше, а других общепринятых форматов объявления JS-модулей я не знаю.

Я не совсем понял, как ваш сборщик работает. Он бандлит напрямую TypeScript, без промежуточной компиляции в CommonJS/AMD? Одна папка = один бандл? Есть документация какая-нибудь, кроме README.md? Есть поддержка SourceMaps? Я тоже свой велосипед использую, но честно-честно пытаюсь найти что-нибудь для себя подходящее, что все используют. Пока плохо получается :D

Он действует просто:


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

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

Запилил, наконец, отслеживание зависимостей в рамках одного модуля. Теперь можно не раскидывать зависимые друг от друга файлы по разным директориям. Главное — давать им правильные имена:


./my/aaa.ts
./my/bbb.ts

Либо:


./my/my_aaa.ts
./my/my_bbb.ts

В этом случае, сначала в бандл войдёт модуль bbb, а потом уже aaa, так как aaa от bbb зависит с приоритетом -2, а bbb от aaa с приоритетом 0.

То есть наоборот, сначала aaa, потом bbb. :-)

давать им правильные имена:

Можете развернуть мысль подробнее? (интерес не праздный) Каковы правила именования?

Они должны матчиться на пространства имён. Например, в коде используется класс $my.view.card (js/ts) или $my_view_card (js/ts) или .my-view-card (css) или [my-view-card] (css) или --my-view-card (css). Его определение должно лежать в модуле ./my/view/card или ./my/view или ./my. Модуль грузится целиком (все стили, скрипты, шаблоны подключаются).


В рамках одного модуля зависимости определяются по префиксу имени. Например, в модуле ./my/view имя файла может быть view_card.ts или view-card.ts или card.ts или card_important.ts какой-нибудь.

Ага, понял. Спасибо за ответ. А регулярки — постоянное решение? В сторону тайпскриптного компилятора не смотрели?(строить ast и искать по взрослому)

Пока да, они работают просто, надёжно, быстро и универсально (кроме TS, есть ещё и JS, и CSS и прочие языки). У тайпскриптового компилятора документации практически нет, так что у меня пока даже инкрементальную компиляцию прикрутить не удалось, из-за чего приходится ждать по несколько секунд перекомпиляции после изменения файла.


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


git clone https://github.com/nin-jin/pms.git ./pms && cd pms
npm start
У тайпскриптового компилятора документации практически нет, так что у меня пока даже инкрементальную компиляцию прикрутить не удалось

Это да. Я сейчас заморочился на метапрограммирование (конкретно на данном этапе — генерация клиента и сервера на основе swagger спеки, typescript естественно, то что предлагают разработчики стандарта — не выдерживает никакой критики (именно по typescript-у, за остальные молчу), но не только, там много всего. Так вот там похоже что похожая задача встанет, потому и присматриваюсь к Вашему проекту. Но хочется решить ее (если потребуется) так что бы правила не лезли в код и не диктовали, так как там похоже править надо будет не шаблоны а уже сгенеренный код (в частности смотреть какие теги использовались на клиенте и какие sql вызовы — на бэке)

Я похожую задачу решал чуть по другому. Было описание бизнес домена в простом виде типа:


$my_album $my_model
    - альбом с фотографиями
    title string
    description string
    person link $my_person
    image link-list $my_image

$my_image $my_model
    - мета информация о фотографии
    linkSmall string
    linkBig string
    width integer
    height integer
    person link $my_person
    album link-set $my_album
    service link-set $my_service

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

Да, у меня похожая задача. Но я swagger не генерю, он задан (ручками) — и на его основе генерится клиент и каркас сервера (используем плюсы статичной типизации при компиляции + валидатор в рантайме). Тут вроде как workflow утрясся, больших изменений не предполагаю. А вот с базой и вьюхами на клиенте пока думаю.


А Вы на базу через ORM лезли или ручками? (я пробую всю логику перенести на базу а в коде оставить только вызовы хранимок. Ну как пробую — делаю уже, а пробую из этого реализовать плюсы)


и генерировались базовые классы

То есть Вы по сути свою ORM генерили?

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


Я использовал графовую субд — её для таких вещей использовать одно удовольствие. https://habrahabr.ru/post/267079/

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

зборка > сборка, збой > сбой и т.д.
Sign up to leave a comment.