-Поиск по дневнику

Поиск сообщений в rss_rss_hh_full

 -Подписка по e-mail

 

 -Постоянные читатели

 -Статистика

Статистика LiveInternet.ru: показано количество хитов и посетителей
Создан: 17.03.2011
Записей:
Комментариев:
Написано: 1

Habrahabr








Добавить любой RSS - источник (включая журнал LiveJournal) в свою ленту друзей вы можете на странице синдикации.

Исходная информация - http://habrahabr.ru/rss/.
Данный дневник сформирован из открытого RSS-источника по адресу http://feeds.feedburner.com/xtmb/hh-full, и дополняется в соответствии с дополнением данного источника. Он может не соответствовать содержимому оригинальной страницы. Трансляция создана автоматически по запросу читателей этой RSS ленты.
По всем вопросам о работе данного сервиса обращаться со страницы контактной информации.

[Обновить трансляцию]

[Из песочницы] Как я перестал любить Angular

Воскресенье, 10 Сентября 2017 г. 13:17 + в цитатник
MooooM сегодня в 13:17 Разработка

Как я перестал любить Angular

Вступление


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


На дворе 2017ый год и для каждого нового продукта/проекта встает вопрос выбора фреймворка для разработки. Долгое время я был уверен, что новый Angular 2/4 (далее просто Angular) станет главным трендом enterprise разработки еще на несколько лет вперед и даже не сомневался что буду работать только с ним.


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


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


AngularJS



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


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


Angular



И вот наконец Angular был переписан с нуля дабы стать основной для многих будущих веб-приложений.


Конечно путь к этому был долог и полон Breaking Changes,
но на сегодняшний день Angular 4 стабилен и позиционируется как полностью production-ready.


Одна из наиболее крутых вещей, которую дал нам новый Angular — популяризация TypeScript.
Лично я был с ним знаком и работал еще до того, как он стал основным для моего любимого фреймворка,
но многие узнали о нем именно благодаря Angular.


TypeScript



Не буду подробно останавливаться на TypeScript, т.к. это тема для отдельной статьи,
да и написано о нем уже больше чем нужно. Но для enterprise разработки TypeScript дает огромное количество преимуществ. Начиная с самой статической типизации и областями видимости и заканчивая поддержкой ES7/8 даже для IE9.


Главное преимущество работы с TypeScript — богатый инструментарий и прекрасная поддержка IDE. По нашему опыту, юнит тестов с TS приходится писать существенно меньше.


Vue



Если вы читаете данную статью, то с вероятностью 95% вы уже знаете что это такое.


Но для тех 5% кто еще не знает — Vue.js это крайне легковесный (но очень богатый по функционалу) фреймворк, вобравший в себя многое хорошее, как из AngularJS, так и из React.


Фактически больше он похож все же на React, но шаблоны практически идентичны AngularJS (HTML + Mustache).


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


Предыстория


Было — большой проект на AngularJS


Последний мой проект, который совсем недавно вышел в production, мы писали на AngularJS 1.5-1.6.


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


Вот пример нашего компонента из данного проекта:


import {Component} from "shared-front/app/decorators";
import FileService, {ContentType, IFile} from "../file.service";
import AlertService from "shared/alert.service";

@Component({
    template: require("./file.component.html"),
    bindings: {
        item: "<",
    },
})
export default class FileComponent {
    public static $inject = ["fileService"];
    public item: IFile;

    constructor(private fileService: FileService, private alertService: AlertService) {
    }

    public isVideo() {
        return this.item.contentKeyType === ContentType.VIDEO;
    }

    public downloadFile() {
        this.fileService.download(this.getFileDownloadUrl()).then(() => {
            this.alertService.success();
        });
    }

    private getFileDownloadUrl() {
        return `url-for-download${this.item.text}`;
    }
}

На мой взгляд выглядит очень даже приятно, не слишком многословно, даже если вы не фанат TS.
К тому же все это замечательно тестируется как Unit-тестами, так и Е2Е.


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


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


Стало — средний проект на Angular


Так мы и поступили, рационально выбрав Angular 2 (позже 4) для нашего нового проекта несколько месяцев назад.


Выбор казался довольно очевидным, учитывая, что вся наша команда имеет большой опыт работы с первой версией. К тому же, лично я ранее работал с alpha-RC версиями и тогда проблемы фреймворка списывались на 0.х номер версии.


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


А вот пример компонента из проекта на Angular:


import {Component} from '@angular/core';

import FileService, {ContentType, IFile} from "../file.service";
import AlertService from "shared/alert.service";

@Component({
  selector: 'app-file',
  templateUrl: './file.component.html',
  styleUrls: ['./file.component.scss']
})
export class FileComponent {

    Input() item: IFile;

    constructor(private fileService: FileService, private alertService: AlertService) {
    }

    public isVideo() {
        return this.item.contentKeyType === ContentType.VIDEO;
    }

    public downloadFile() {
        this.fileService.download(this.getFileDownloadUrl()).subscribe(() => {
            this.alertService.success();
        });
    }

    private getFileDownloadUrl() {
        return `url-for-download${this.item.text}`;
    }
}

Возможно чуть чуть более многословно, но гораздо чище.


Плюсы


Angular CLI — единственное реальное преимущество перед AngularJS



Первое, что вы установите при разработке нового Angular 4 приложения это Angular CLI


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


Это то, чего так сильно не хватало в AngularJS, и все решали проблему отсутствия данной тулзы по-своему. Множество различных сидов (стартеров), сотни разных подходов к одному и тому же, в общем анархия. Теперь с этим покончено.


Конечно CLI тоже имеет ряд недостатков в части настроек и конфигурации "под себя", но все же он на голову выше аналогичных утилит для React (create-react-app) или Vue (vue-cli). Хотя второй, благодаря своей гибкости, становится лучше с каждым днем.


Минусы или "За что я перестал любить Angular"


Изначально я не хотел писать очередную хейтерскую статью вроде
Angular 2 is terrible (нашелся даже перевод).


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


В целом не совсем разделяю взгяд автора на RxJS, т.к. библиотека невероятно мощная.


An Ajax request is singular, and running methods like Observable.prototype.map when there will only ever be one value in the pipe makes no semantic sense. Promises on the other hand represent a value that has yet to be fulfilled, which is exactly what a HTTP request gives you. I spent hours forcing Observables to behave before giving up using Observable.prototype.toPromise to transform the Observable back to a Promise and simply using Promise.all, which works much better than anything Rx.js offers.

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


Но суровая правда в том, что Object.observe нативно мы все же не увидим:


After much discussion with the parties involved, I plan to withdraw the Object.observe proposal from TC39 (where it currently sits at stage 2 in the ES spec process), and hope to remove support from V8 by the end of the year (the feature is used on 0.0169% of Chrome pageviews, according to chromestatus.com).

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


Также не соглашусь в целом по поводу TypeScript'а, так как это все же замечательный язык, но об этом ниже.


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


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


TypeScript в Angular


Пожалуй самое болезненное разочарование для меня — это то, во что превратили работу с TypeScript'ом в Angular.


Ниже несколько примеров наиболее неудачных решений.


Ужасные API


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


Примеры:


HttpParams

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


Например код вида:


let params = new HttpParams();
params.set('param', param);
params.set('anotherParam', anotherParam);
...
this.http.get('test', {params: params});

Не будет добавлять параметры к запросу. Казалось бы почему?
Ведь никаких ошибок, ни TypeScript ни сам Angular не отображает.


Только открыв сам класс в TypeScript можно найти комментарий


This class is immuatable — all mutation operations return a new instance.

Конечно, это совершенно неочевидно.


В вот и вся документация про них:


http
  .post('/api/items/add', body, {
    params: new HttpParams().set('id', '3'),
  })
  .subscribe();

RxJS operator import

Начнем с того, что документация по Angular вообще не имеет толкового разбора и описания Observable и того, как с ними работать.


Нет даже толковых ссылок на документацию по RxJS. И это при том, что Rx является ключевой частью фреймворка, а само создание Observable уже отличается:


// rx.js
Rx.Observable.create();
vs
// Angular
new Observable()

Ну да и черт с ним, здесь я хотел рассказать о Rx + TypeScript + Angular.


Допустим вы хотите использовать некий RxJS оператор, вроде do:


observable.do(event => {...})

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


Вот только, во время выполнения возникнет такая ошибка:


ERROR TypeError: observable.do is not a function

Потому что вы очевидно (потратили кучу времени на поиск проблемы) забыли заимпортировать сам оператор:


import 'rxjs/add/operator/do';

Почему это ломается в рантайме, если у нас есть TypeScript? Не знаю. Но это так.


Router API

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


Events

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


this.router.events.subscribe(event => {
  if(event instanceof NavigationStart) {
    ...
  }
}


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


this.router.navigate(['/some']);
...
this.router.navigate(['/other']);

Почему это плохо?


Потому что команды в данном случае имеют сигнатуру any[].
Для незнакомых с TypeScript — это фактически отключение его фич.


Это при том, что роутинг — наиболее слабо связанная часть в Angular.


Например мы в нашем AngularJS приложении наоборот старались типизировать роуты,
чтобы вместо простых строк они были хотя бы enum.


Это позволяет находить те или иные места, где используется данный роут без глобального поиска по строке 'some'.


Но нет, в Angular это преимущество TypeScript не используется никак.


Lazy Load

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


{
  path: 'admin',
  loadChildren: 'app/admin/admin.module#AdminModule',
},

Forms API

Для начала — в Angular есть два типа форм: обычные
и реактивные.


Само собой, работать с ними нужно по-разному.


Однако лично меня раздражает именно API reactive forms:


// Зачем нужен первый пустой параметр?
// Почему name это массив c валидатором??
this.heroForm = this.fb.group({
  name: ['', Validators.required ],
});

или из документации


// Почему пустое поле это имя??
this.heroForm = this.fb.group({
  name: '', // <--- the FormControl called "name"
});

и так далее


this.complexForm = fb.group({   
  // Почему понадобился compose ?
  // Неужели нельзя без null ??
  'lastName': [null, Validators.compose([Validators.required, Validators.minLength(5), Validators.maxLength(10)])],
  'gender' : [null, Validators.required],
})

А еще — нельзя просто использовать атрибуты типа [disabled] с реактивными формами...


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


__metadata


К сожалению использование горячо любимого мною TypeScript'а в Angular слишком сильно завязано на декораторы.


Декораторы — прекрасная вещь, но к сожалению в рантайме нет самой нужной их части, а именно __metadata.


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


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


Впрочем, мы в нашем AngularJS приложении использовали такие декораторы, например @Component:


export const Component = (options: ng.IComponentOptions = {}) => controller => angular.extend(options, {controller});

Он фактически просто оборачивает наши TypeScript классы в компоненты AngularJS и делает их контроллерами.


Но в Angular, несмотря на экспериментальность фичи, это стало частью ядра фреймворка,
что делает необходимым использование полифила reflect-metadata в совершенно любом случае. Очень спорное решение.


Абстракции


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


Самый яркий пример подобных проблем — это Dependency Injection в Angular.


Сама по себе концепция замечательная, особенно для unit тестирования.
Но практика показывает, что большой нужды делать из фронтенда нечто Java-подобное нет.
Да, в нашем AngularJS приложении мы очень активно это использовали, но поработав с тестированием Vue компонентов, я серьезно начал сомневаться в пользе DI.


В Angular для большинства обычных зависимостей, вроде сервисов, DI будет выглядеть очень просто, с получением через конструктор:


constructor(heroService: HeroService) {
  this.heroes = heroService.getHeroes();
}

Но так работает только для TypeScript классов, и если вы хотите добавить константу, необходимо будет использовать @Inject:


constructor(@Inject(APP_CONFIG) config: AppConfig) {
  this.title = config.title;
}

Ах да, сервисы которые вы будете инжектить должны быть проанотированы как @Injectable().


Но не все, а только те, у которых есть свои зависимости, если их нет — можно этот декоратор не указывать.


Consider adding @Injectable() to every service class, even those that don't have dependencies and, therefore, do not technically require it.
Here's why:

Future proofing: No need to remember @Injectable() when you add a dependency later.

Consistency: All services follow the same rules, and you don't have to wonder why a decorator is missing.

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


Еще прекрасная цитата из официальной документации по поводу скобочек:


Always write @Injectable(), not just @Injectable. The application will fail mysteriously if you forget the parentheses.

Короче говоря, создается впечатление, что TypeScript в Angular явно используется не по назначению.


Хотя еще раз подчеркну, что сам по себе язык обычно очень помогает в разработке.


Синтаксис шаблонов


Синтаксис шаблонов — основная претензия к Angular. И по вполне объективным причинам.


Пример не только множества разных директив, но еще и разных вариантов их использования:


style using ngStyle
Hello Wordl!
CSS class using property syntax, this text is blue
object of classes
array of classes
string of classes

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


Обещали, что будет достаточно только [] и ().


Binding Example
Properties
Events
Two-way

К сожалению в реальности директив едва ли не больше чем в AngularJS.



И да, простое правило запоминания синтаксиса two-way binding про банан в коробке
из официальной документации:


Visualize a banana in a box to remember that the parentheses go inside the brackets.

Документация


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


Контрпример — доки Vue.
Мало того, что написаны подробно и доходчиво, так еще и на 6 языках,
в т.ч. русском.


View encapsulation


Angular позволяет использовать так называемый View encapsulation.


Суть сводится к эмуляции Shadow DOM или использовании нативной его поддержки.


Сам по себе Shadow DOM — прекрасная вещь и действительно потенциально позволяет использовать даже разные CSS фреймворки для разных копмонентов без проблем.
Однако нативная поддержка на сегодняшний день совершенно печальна.


По умолчанию включена эмуляция Shadow DOM.


Вот пример простого CSS для компонента:


.first {
  background-color: red;
}
.first .second {
  background-color: green;
}
.first .second .third {
  background-color: blue;
}

Angular преобразует это в:


.first[_ngcontent-c1] {
  background-color: red;
}
.first[_ngcontent-c1]   .second[_ngcontent-c1] {
  background-color: green;
}
.first[_ngcontent-c1]   .second[_ngcontent-c1]   .third[_ngcontent-c1] {
  background-color: blue;
}

Совершенно не ясно зачем делать именно так.


Например Vue делает то же самое, но гораздо чище:


.first[data-v-50646cd8] {
  background-color: red;
}
.first .second[data-v-50646cd8] {
  background-color: green;
}
.first .second .third[data-v-50646cd8] {
  background-color: blue;
}

Не говоря уже о том, что в Vue это не дефолтное поведение и включается добавлением простого scoped к стилю.


Так же хотелось бы отметить, что Vue (vue-cli webpack) подобным же образом позволяет указывать SASS/SCSS, тогда как для Angular CLI нужны команды типа ng set defaults.styleExt scss.
Не очень понятно зачем все это, если внутри такой же webpack.


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


В частности мы использовали один из наиболее популярных UI фреймворков — PrimeNG,
а он иногда использует подобные селекторы:


body .ui-tree .ui-treenode .ui-treenode-content .ui-tree-toggler {
    font-size: 1.1em;
}

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


Что в итоге приводит к необходимости писать нечто подобное:


body :host >>> .ui-tree .ui-treenode .ui-treenode-content .ui-tree-toggler {
  font-size: 2em;
}

Иногда и вовсе приходилось вспомнить великий и ужасный !important.


Безусловно все это связано конкретно с PrimeNG и не является как таковой проблемой фреймворка, но это именно та проблема, которая скорее всего возникнет и у вас при реальной работе с Angular.


К слову о стабильности


В примере выше мы использовали >>> — как и /deep/ это алиас для так называемого shadow-piercing селектора.


Он позволяет как бы "игнорировать" Shadow DOM и для некоторых сторонних компонентов порой просто незаменим.


В одном из относительно свежих релизов Angular создатели фреймворка решили,
в соответствии со стандартом, задепрекейтить /deep/ и >>>.


Никаких ошибок или ворнингов их использование не принесло, они просто перестали работать.
Как выяснилось позже, теперь работает только ::ng-deep — аналог shadow-piercing селектора в Angular вселенной.


Обновление это было отнюдь не мажорной версии (4.2.6 -> 4.3.0), просто в один прекрасный момент наша верстка во многих местах поползла (спасибо и NPM за версии с шапочкой ^).


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


К тому же скоро и ::ng-deep перестанет работать.
Как в таком случае править стили кривых сторонних компонентов, вроде тех же PrimeNG, ума не приложу.


Наш личный вывод: дефолтная настройка — эмуляция Shadow DOM порождает больше проблем чем решает.


Свой HTML парсер


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


Нет смысла холиварить на тему ухода Angular от стандартов, но по мнению многих это довольно странная идея, ведь в том же AngularJS обычного HTML (регистронезависимого) вполне хватало.


С AngularJS нередко бывало такое: добавили вы некий а тест не написали.
Прошло некоторое время и модуль который содержит логику данного компонента был удален/отрефакторен/итд.


Так или иначе — теперь ваш компонент не отображается.


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


Правда теперь любой сторонний компонент требует либо полного отключения этой проверки,
либо включения CUSTOM_ELEMENTS_SCHEMA которая пропускает любые тэги с -


Сорцы можете оценить самостоятельно


...
const TAG_DEFINITIONS: {[key: string]: HtmlTagDefinition} = {
  'base': new HtmlTagDefinition({isVoid: true}),
  'meta': new HtmlTagDefinition({isVoid: true}),
  'area': new HtmlTagDefinition({isVoid: true}),
  'embed': new HtmlTagDefinition({isVoid: true}),
  'link': new HtmlTagDefinition({isVoid: true}),
  'img': new HtmlTagDefinition({isVoid: true}),
  'input': new HtmlTagDefinition({isVoid: true}),
  'param': new HtmlTagDefinition({isVoid: true}),
  'hr': new HtmlTagDefinition({isVoid: true}),
  'br': new HtmlTagDefinition({isVoid: true}),
  'source': new HtmlTagDefinition({isVoid: true}),
  'track': new HtmlTagDefinition({isVoid: true}),
  'wbr': new HtmlTagDefinition({isVoid: true}),
  'p': new HtmlTagDefinition({
    closedByChildren: [
      'address', 'article', 'aside', 'blockquote', 'div', 'dl',      'fieldset', 'footer', 'form',
      'h1',      'h2',      'h3',    'h4',         'h5',  'h6',      'header',   'hgroup', 'hr',
      'main',    'nav',     'ol',    'p',          'pre', 'section', 'table',    'ul'
    ],
    closedByParent: true
  }),
...
  'td': new HtmlTagDefinition({closedByChildren: ['td', 'th'], closedByParent: true}),
  'th': new HtmlTagDefinition({closedByChildren: ['td', 'th'], closedByParent: true}),
  'col': new HtmlTagDefinition({requiredParents: ['colgroup'], isVoid: true}),
  'svg': new HtmlTagDefinition({implicitNamespacePrefix: 'svg'}),
  'math': new HtmlTagDefinition({implicitNamespacePrefix: 'math'}),
  'li': new HtmlTagDefinition({closedByChildren: ['li'], closedByParent: true}),
  'dt': new HtmlTagDefinition({closedByChildren: ['dt', 'dd']}),
  'dd': new HtmlTagDefinition({closedByChildren: ['dt', 'dd'], closedByParent: true}),
  'rb': new HtmlTagDefinition({closedByChildren: ['rb', 'rt', 'rtc'
  ...

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


Однако это решается прекомпиляцией шаблонов благодаря другому, AOT компилятору.
Нужно всего лишь собирать с флагом --aot, но и здесь не без ложки дегтя, это плохо работает с ng serve
и еще больше тормозит и без того небыструю сборку. Видимо поэтому он и не включен по умолчанию (а стоило бы).


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


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


Обратите внимание на элегантные предлагаемые решения проблемы:


don't use default exports :)

Just place both export types and it works

Или нечто подобное описанному здесь (AOT не всегда разбирает замыкания)


Код подобного вида вызывает очень странные ошибки компилятора AOT:


@NgModule({
  providers: [
    {provide: SomeSymbol, useFactor: (i) => i.get('someSymbol'), deps: ['$injector']}
  ]
})
export class MyModule {}

Приходится подстраиваться под компилятор и переписывать код в более примитивном виде:


export factoryForSomeSymbol = (i) => i.get('someSymbol');

@NgModule({
  providers: [
    {provide: SomeSymbol, useFactor: factoryForSomeSymbol, deps: ['$injector']}
  ]
})
export class MyModule {}

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


Zone.js


Одна из концептуально очень крутых вещей которые появились в Angular это Zone.js.
Он позволяет отслеживать контекст выполнения для асинхронных задач, но новичков отпугивают огромной длины стактрейсы. Например:


core.es5.js:1020 ERROR Error: Uncaught (in promise): Error: No clusteredNodeId supplied to updateClusteredNode.
Error: No clusteredNodeId supplied to updateClusteredNode.
    at ClusterEngine.updateClusteredNode (vis.js:47364)
    at VisGraphDataService.webpackJsonp.../../../../../src/app/services/vis-graph-data.service.ts.VisGraphDataService.updateNetwork (vis-graph-data.service.ts:84)
    at vis-graph-display.service.ts:63
    at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invoke (zone.js:391)
    at Object.onInvoke (core.es5.js:3890)
    at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invoke (zone.js:390)
    at Zone.webpackJsonp.../../../../zone.js/dist/zone.js.Zone.run (zone.js:141)
    at zone.js:818
    at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:424)
    at Object.onInvokeTask (core.es5.js:3881)
    at ClusterEngine.updateClusteredNode (vis.js:47364)
    at VisGraphDataService.webpackJsonp.../../../../../src/app/services/vis-graph-data.service.ts.VisGraphDataService.updateNetwork (vis-graph-data.service.ts:84)
    at vis-graph-display.service.ts:63
    at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invoke (zone.js:391)
    at Object.onInvoke (core.es5.js:3890)
    at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invoke (zone.js:390)
    at Zone.webpackJsonp.../../../../zone.js/dist/zone.js.Zone.run (zone.js:141)
    at zone.js:818
    at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:424)
    at Object.onInvokeTask (core.es5.js:3881)
    at resolvePromise (zone.js:770)
    at zone.js:696
    at zone.js:712
    at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invoke (zone.js:391)
    at Object.onInvoke (core.es5.js:3890)
    at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invoke (zone.js:390)
    at Zone.webpackJsonp.../../../../zone.js/dist/zone.js.Zone.run (zone.js:141)
    at zone.js:818
    at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invokeTask (zone.js:424)
    at Object.onInvokeTask (core.es5.js:3881)

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


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


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


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



UI frameworks


Еще один пункт, который формально не относится к самому фреймворку — это довольно скудное количество готовых наборов UI компонентов. Думаю, что написание большинства компонентов с нуля — непозволительная роскошь для многих веб-разработчиков. Зачастую проще, быстрее и разумнее взять готовый UI framework вместо написания собственных гридов и деревьев.


Да, я понимаю, что выбирать фреймворк по наличиую UI компонентов в корне неверно,
но при реальной разработке это необходимо.


Вот список основных UI фреймворков для Angular: https://angular.io/resources (раздел UI components).


Рассмотрим наиболее популярные бесплатные варианты.


Angular Material 2



Безусловно, наибольшие надежды я возлагал на Angular Material 2 ввиду того, что разрабатывается он командой Angular и наверняка будет соответствовать всем гайдлайнам.


К сожалению, несмотря на его возраст, набор компонентов крайне мал.


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


И вот совсем недавно он-таки появился. Но функционал все же пока довольно базовый.


Я считаю, что Angular Material 2 подойдет лишь небольшим или, в лучшем случае, средним проектам, т.к. до сих пор нет, например, деревьев. Часто очень нужны компоненты вроде multiple-select, коих тоже нет.


Отдельно стоит сказать про очень скупую документацию и малое количество примеров.


Перспективы развития тоже довольно печальные.


Feature Status
tree In-progress
stepper In-progress, planned Q3 2017
sticky-header In-progress, planned Q3 2017
virtual-repeat Not started, planned Q4 2017
fab speed-dial Not started, not planned
fab toolbar Not started, not planned
bottom-sheet Not started, not planned
bottom-nav Not started, not planned

Bootstrap



По тем же причинам, что и выше не буду останавливаться на Bootstrap фреймворках типа
ng2-bootstrap (получше) и ngx-bootstrap.
Они очень даже неплохи, но простейшие вещи можно сделать и обычным CSS, а сложных компонентов тут нет (хотя наверняка многим будет достаточно modal, datepicker и typeahead).


Prime Faces



Это на сегодняшний день наиболее популярый фреймворк содержащий множество сложных компонентов. В том числе гриды и деревья (и даже Tree Table!).


Изначально я вообще довольно скептически относился к PrimeFaces т.к. у меня был давний опыт работы с JSF, и много неприятных воспоминаний. Да и выглядят PrimeNG визуально так же (не очень современно). Но на момент начала нашего проекта достойных альтернатив не было, и все же хочется сказать спасибо разработчикам за то, что в короткие сроки был написан дейтсивтельно широчайший инструментарий.


Однако проблем с этим набором компонентов у нас возникало очень много.


Часто документация совершенно не помогает.


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


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


Clarity



Луч света в темном царстве — это относительно молодая (меньше года от роду) библиотека Clarity от vmware.


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


Фреймворк не просто предоставляет набор UI компонентов, но и CSS гайдлайны.
Эдакий свой bootstrap. Благодаря этому достигается консистентный и крайне приятный/минималистичный вид компонентов.


Гриды очень функциональные и стабильные, а сорцы говорят сами за себя
(о боже, комментарии и юнит тесты, а так можно было?).


Однако пока что очень слабые формы,
нет datepicker'а и select2-подобного компонента.
Работа над ними идет в данный момент:
DatePicker,
Select 2.0
(как всегда дизайн на высоте, и хотя с разработкой не торопятся я могу быть уверен, что делают на совесть).


Пожалуй, "Clarity Design System" — единственная причина почему я еще верю в жизнь Angular
(и вообще единственный фреймворк который не стыдно использовать для enterprise разработки).
Как никак VMware серьезнейший мейнтейнер и есть надежда на светлое будущее.


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


Но он лишь один


Да, я считаю что для Angular на сегодняшний день есть лишь один достойный UI фреймворк.
О чем это говорит?


Полноценно разрабатывать такие фреймворки для Angular могут лишь серьезнейшие компании вроде той же VMware. Нужен ли вам такой суровый enterprise? Каждый решает сам.


А теперь давайте посмотрим, что происходит с одним из свежих конкурентов.


Vue UI frameworks



Для сравнения мощные уже существующие фреймворки для Vue.js с теми же гридами:


Element (~15k stars), Vue Material
(существенно младше Angular Material 2 но уже содержит в разы больше),
Vuetify (снова Material и снова множество компонентов),
Quasar,
также надо отметить популярные чисто китайские фреймворки типа
iView и Muse-UI
(iView выглядит очень приятно, но документация хромает).


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


Какой вывод мы сделали?


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


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


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


Поэтому для нашего нового проекта мы выбрали Vue.js.


Достаточно просто развернуть базовый webpack шаблон для vue-cli и оценить скорость работы библиотеки.


Несмотря на то, что лично я всегда был сторонником фреймворков all-in-one,
Vue без особых проблем делает почти все то же, что и Angular.


Ну и конечно, множество тех же UI framework'ов также играет свою роль.


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


Но ребята из Microsoft уже вот вот вмержает ее.
А потом она появится и в webpack шаблоне.


Почему мы не выбрали React? После AngularJS наша команда гораздо проще вошла в Vue,
ведь все эти v-if, v-model и v-for уже были очень знакомы.


Лично мне во многом, но не во всем, нравится Aurelia но уж больно она малопопулярна,
а в сравнении с взрывным ростом популярности Vue она кажется совсем неизвестной.


Надеюсь, что через год-два Angular под давлением community все-таки избавится от всего лишнего,
исправит основные проблемы и станет наконец тем enterprise framework'ом, которым должен был.
Но сегодня я рекомендую вам посмотреть в сторону других, более легковесных и элегантных решений.
И поверьте, спустя 4 года работы с Angular, вот так бросить его было очень нелегко.
Но достаточно один раз попробовать Vue...

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/337578/


Метки:  

ICO для моей игры: ошибки и инсайты

Воскресенье, 10 Сентября 2017 г. 12:43 + в цитатник
Alienter сегодня в 12:43 Разное

ICO для моей игры: ошибки и инсайты

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

    Немного освежим фактологию:
    • Игра разрабатывается уже 10 месяцев
    • Да, разработка началась с круглого нуля одним человеком на энтузиазме
    • На данный момент сформировалась постоянная команда из 4 человек (3 кодера, из которых один является и геймдизайнером, плюс 3D-моделлер) + имеется ощутимая помощь от сообщества (еще некоторое количество человек, оказывавших и оказывающих существенную помощь кодом, моделями, ценными идеями)
    • Игра приобрела концепцию и свою изюминку, а вместе с этим — и новое название: Privateers.Life (замахнувшись от вполне обычной выживалки на полноценный симулятор жизни в 17 веке)

    Главная страница сайта игры Privateers.Life

    Начнем с того, что идея ICO для игры витала в воздухе еще полгода назад. Хотя тогда я не знал, что она обретет воплощение именно в виде ICO, но мысль сделать игровую валюту осязаемой была уже тогда. А по мере развития игры, приближения к играбельной альфа-версии, а следовательно — перехода на, так сказать, новый уровень (мультиплеер), возникло понимание, что ICO в нашем случае это самый оптимальный вариант и с его помощью решаются сразу несколько вопросов.
    1. Айсио позволяет получить экономически обоснованную игровую валюту (оборот койна не только внутри игры, но и на биржах, позволит придать валюте ценность, а значит в действие вступят законы рынка, что позволит избежать искусственных перекосов в ценообразовании на игровые предметы). Со стороны разработчика, может быть, и было бы выгоднее такие перекосы допускать (получая больше необоснованной прибыли с продажи переоцененных вещей за ничем не обеспеченные фантики, чем грешат многие игры), но это палка о двух концах — можно либо отпугнуть аудиторию, заигравшись с этими ценовыми перекосами, либо получить неприятные долговременные последствия, нарушающие игровой баланс в целом.
    2. В результате Первичного Предложения Монет разработчик получает финансирование на продолжение работ над продуктом (в нашем случае — игрой).
    3. Наличие у разработчика определенного количества монет после ICO позволяет получать финансирование на текущие нужды, без необходимости обращаться к заемному капиталу (просто продав некоторое количество монет по текущему рыночному курсу)


    Так как проект изначально строился на энтузиазме, сначала моем, а со временем — и присоединившихся ко мне людей, у нас не было большого рекламного бюджета, чтобы громко заявить о себе (хотя, лично я считаю, что у нас больше оснований громко о себе заявлять, поскольку, в отличие от множества других ICO, мы, кроме концепции, имеем определенную подтвержденную историю разработки (канал на YouTube с более чем 1000 часов видео разработки в прямом эфире) и почти готовую альфа-версию игры), следовательно цели собрать миллионы долларов просто потому, что «мы такие красивые и перспективные» у нас не было. Первостепенная цель ICO — получить после него определенное количество монет с ценой, определяемой не нами, а рынком. И этой цели мы уже, как бы то ни было, почти достигли (продано около 300000 монет LDM, что, с учетом делимости токена (18 знаков после точки), более чем достаточно для запуска игровой экономики, осталось дождаться завершения ICO, зафиксировать итоговое выпущенное количество монет и добавить токен LDM (от лат. Ludum — Игровой) на биржи).

    Однако, как в свое время и разработка игры, проведение ICO для меня было новым опытом, о котором я и хочу немного порассуждать, рассказать о совершенных ошибках/недочетах и спросить мнения знатоков (которые на Хабре однозначно есть, судя по широте освещения здесь темы ICO), что нами было сделано неправильно, что можно было бы исправить или улучшить.

    Очень важный для меня инсайт произошел на мероприятии, проводимом в питерском #BlockchainHouse, где собрались организаторы и инвесторы в ICO, а так же представители инфраструктурных отраслевых проектов (того же ICOrating): мне предложили описать свой проект в паре предложений, а у меня это не получилось. Теперь мне понятно, что одной из главных моих ошибок было то, что я сам не представлял концепции игры, которую я создаю.

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

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


    Да, в нашей игре мы предоставим возможность любому игроку построить свой бизнес. Причем — для этого не обязательно будет покупать игровую валюту (токены LDM), их можно будет заработать, например, выполнением контрактов от разработчика на создание предметов для игрового магазина (это могут быть как крупные объекты, вроде торговых или военных кораблей, так и компоненты, требующие намного меньших затрат времени, но позволяющие относительно быстро получить первый пусть и небольшой доход). Вы, как игрок, сможете открыть, например, бордель (какая пиратская колония без борделя?), работу которого вам нужно будет наладить (обеспечить снабжение расходными материалами, зарплату персонала, соцпакет ;), то есть кормить-лечить-защищать), вы однозначно будете нести расходы, а вот ваша прибыль будет зависеть от вашего подхода к своему предприятию (бордель на необитаемом острове будет, мягко говоря, убыточным, а вот в крупном городе — может приносить десятки и сотни процентов прибыли, в сравнении с которыми биржевой рост курса игровой валюты может померкнуть). Более расширено эти аспекты рассмотрены в Белой книге.

    Значительным недочетом с нашей стороны было чрезмерное полагание на эффективность топика на bitcointalk. Да, так повелось, это можно даже считать своеобразным стандартом — наличие топика на Bitcointalk.org у проекта, выходящего на ICO, обязательно. Но его (и даже нескольких языковых версий в других разделах форума) недостаточно для охвата сколько-нибудь серьезной по размерам аудитории. А на профильные ICO-трекеры мы начали добавлять информацию вообще только перед самым началом продажи токенов, а то и уже после фактического начала ICO.

    Немалое значение для успеха ICO имеет система бонусов (в сфере игровых ICO бонусы довольно существенны и могут составлять 50-100% за приобретаемые на определенных условиях монеты, а на баунти-кампанию может быть выделено 10-15% от всего объема выпускаемых токенов, интересно было проанализировать в этой связи опыт ICO околоигровых монет Nexium (игра Beyond the Void) и Rustbits (визуальная новелла SpacePirates). Подводный камень в том, что большие бонусы это гарантия слива купленных или полученных за Баунти токенов сразу же на первой бирже (часто ей становится EtherDelta, выход на которую — дело техники), следовательно с большим бонусом не стоит продавать большие объемы монет, так как даже продажа по цене ICO может быть чрезвычайно выгодной тем, кто получил монеты с 100%-ным бонусом, а «баунтисты» вообще особо не замечены в каком-то особом пиетете к проектам, в которых участвуют и могут стать именно тем спусковым крючком, вызывающим сильное проседание курса после ICO. Что касается нашего проекта, то нам, похоже, удалось найти ту золотую середину, когда инвесторы заходят с долгосрочными планами (даже с бонусом), а баунтисты не смогут оказать особого влияния на курс (в силу того, что на Баунти-программы нами выделено всего 5% монет). Во всяком случае, при личном общении обладатели сколь-нибдуь существенного количества токенов LDM настаивают на неспекулятивном интересе к нашему проекту.

    Напоследок я хотел бы перечислить несколько моментов, на которые обязательно стоит обратить внимание тем, кто собирается проводить ICO своего продукта или компании, или, как минимум, стоит иметь в виду:
    • Есть два глобальных варианта производства токенов: премайн (когда вся сумма создается заранее и потом распределяется, а непроданная часть сжигается) и производство токенов в момент покупки (соответствующая сумма токенов создается в момент поступления ETH на кошелек контракта и отправляется на адрес инвестора, в этом случае сжигать нечего, так как «непроданных» токенов не будет — они просто не будут созданы — но от вопросов вида «а что с непроданными токенами?» это вас все равно не избавит)
    • Не премините воспользоваться удобным ENS-сервисом от MyEtherWallet.com и присвоить номеру кошелька вашего контракта короткое название, например — созвучное с именем домена. Мы о такой возможности узнали, когда в наших топиках появился мошенник,
      подсуетившийся раньше нас (и присвоивший похожее на официальное название своему кошельку). В нашем случае это не принесло ущерба, потому что мы все связанные с деньгами процессы автоматизировали (токены создаются только в момент поступления ETH на кошелек смарт-контракта, указанный на сайте), телеграмм-чата где можно спамить кошельками у нас нет, а в Дискорде (аналог Slack) у нас сидят по большей части люди, следящие за разработкой игры, а не инвесторы-спекулянты. Но лучше это иметь ввиду и сделать самостоятельно, чем потом разбираться с возможными последствиями.
    • Как и с токенами, с бонусами так же есть пара основных вариантов: бонусы за приобретаемую сумму (или в рамках определенного продаваемого объема) и в течение определенного времени, на мой взгляд, бонусы за сумму больше подходят проектам из реального сектора (да, такие тоже уже проводят ICO, и мне уже поступают вопросы от некоторых действующих офлайн-бизнесов с многолетней историей развития, раздумывающих о проведении Первичного Предложения Монет), а бонусы за время покупки — онлайн-проектам. И лучше бонусами не злоупотреблять — пока ICO не завершено и цена монеты не определена рынком, ваши койны не стоят ничего, а следовательно, более менее значимый объем рекламы за них вы не получите (скорее всего). А вот локальный слив получатели тех или иных бонусов после выхода уже на первую биржу вашему токену устроить при желании смогут.
    Original source: habrahabr.ru (comments, light).

    https://habrahabr.ru/post/337544/


    Метки:  

    ICO для моей игры: ошибки и инсайты

    Воскресенье, 10 Сентября 2017 г. 12:43 + в цитатник
    Alienter сегодня в 12:43 Разное

    ICO для моей игры: ошибки и инсайты

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

      Немного освежим фактологию:
      • Игра разрабатывается уже 10 месяцев
      • Да, разработка началась с круглого нуля одним человеком на энтузиазме
      • На данный момент сформировалась постоянная команда из 4 человек (3 кодера, из которых один является и геймдизайнером, плюс 3D-моделлер) + имеется ощутимая помощь от сообщества (еще некоторое количество человек, оказывавших и оказывающих существенную помощь кодом, моделями, ценными идеями)
      • Игра приобрела концепцию и свою изюминку, а вместе с этим — и новое название: Privateers.Life (замахнувшись от вполне обычной выживалки на полноценный симулятор жизни в 17 веке)

      Главная страница сайта игры Privateers.Life

      Начнем с того, что идея ICO для игры витала в воздухе еще полгода назад. Хотя тогда я не знал, что она обретет воплощение именно в виде ICO, но мысль сделать игровую валюту осязаемой была уже тогда. А по мере развития игры, приближения к играбельной альфа-версии, а следовательно — перехода на, так сказать, новый уровень (мультиплеер), возникло понимание, что ICO в нашем случае это самый оптимальный вариант и с его помощью решаются сразу несколько вопросов.
      1. Айсио позволяет получить экономически обоснованную игровую валюту (оборот койна не только внутри игры, но и на биржах, позволит придать валюте ценность, а значит в действие вступят законы рынка, что позволит избежать искусственных перекосов в ценообразовании на игровые предметы). Со стороны разработчика, может быть, и было бы выгоднее такие перекосы допускать (получая больше необоснованной прибыли с продажи переоцененных вещей за ничем не обеспеченные фантики, чем грешат многие игры), но это палка о двух концах — можно либо отпугнуть аудиторию, заигравшись с этими ценовыми перекосами, либо получить неприятные долговременные последствия, нарушающие игровой баланс в целом.
      2. В результате Первичного Предложения Монет разработчик получает финансирование на продолжение работ над продуктом (в нашем случае — игрой).
      3. Наличие у разработчика определенного количества монет после ICO позволяет получать финансирование на текущие нужды, без необходимости обращаться к заемному капиталу (просто продав некоторое количество монет по текущему рыночному курсу)


      Так как проект изначально строился на энтузиазме, сначала моем, а со временем — и присоединившихся ко мне людей, у нас не было большого рекламного бюджета, чтобы громко заявить о себе (хотя, лично я считаю, что у нас больше оснований громко о себе заявлять, поскольку, в отличие от множества других ICO, мы, кроме концепции, имеем определенную подтвержденную историю разработки (канал на YouTube с более чем 1000 часов видео разработки в прямом эфире) и почти готовую альфа-версию игры), следовательно цели собрать миллионы долларов просто потому, что «мы такие красивые и перспективные» у нас не было. Первостепенная цель ICO — получить после него определенное количество монет с ценой, определяемой не нами, а рынком. И этой цели мы уже, как бы то ни было, почти достигли (продано около 300000 монет LDM, что, с учетом делимости токена (18 знаков после точки), более чем достаточно для запуска игровой экономики, осталось дождаться завершения ICO, зафиксировать итоговое выпущенное количество монет и добавить токен LDM (от лат. Ludum — Игровой) на биржи).

      Однако, как в свое время и разработка игры, проведение ICO для меня было новым опытом, о котором я и хочу немного порассуждать, рассказать о совершенных ошибках/недочетах и спросить мнения знатоков (которые на Хабре однозначно есть, судя по широте освещения здесь темы ICO), что нами было сделано неправильно, что можно было бы исправить или улучшить.

      Очень важный для меня инсайт произошел на мероприятии, проводимом в питерском #BlockchainHouse, где собрались организаторы и инвесторы в ICO, а так же представители инфраструктурных отраслевых проектов (того же ICOrating): мне предложили описать свой проект в паре предложений, а у меня это не получилось. Теперь мне понятно, что одной из главных моих ошибок было то, что я сам не представлял концепции игры, которую я создаю.

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

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


      Да, в нашей игре мы предоставим возможность любому игроку построить свой бизнес. Причем — для этого не обязательно будет покупать игровую валюту (токены LDM), их можно будет заработать, например, выполнением контрактов от разработчика на создание предметов для игрового магазина (это могут быть как крупные объекты, вроде торговых или военных кораблей, так и компоненты, требующие намного меньших затрат времени, но позволяющие относительно быстро получить первый пусть и небольшой доход). Вы, как игрок, сможете открыть, например, бордель (какая пиратская колония без борделя?), работу которого вам нужно будет наладить (обеспечить снабжение расходными материалами, зарплату персонала, соцпакет ;), то есть кормить-лечить-защищать), вы однозначно будете нести расходы, а вот ваша прибыль будет зависеть от вашего подхода к своему предприятию (бордель на необитаемом острове будет, мягко говоря, убыточным, а вот в крупном городе — может приносить десятки и сотни процентов прибыли, в сравнении с которыми биржевой рост курса игровой валюты может померкнуть). Более расширено эти аспекты рассмотрены в Белой книге.

      Значительным недочетом с нашей стороны было чрезмерное полагание на эффективность топика на bitcointalk. Да, так повелось, это можно даже считать своеобразным стандартом — наличие топика на Bitcointalk.org у проекта, выходящего на ICO, обязательно. Но его (и даже нескольких языковых версий в других разделах форума) недостаточно для охвата сколько-нибудь серьезной по размерам аудитории. А на профильные ICO-трекеры мы начали добавлять информацию вообще только перед самым началом продажи токенов, а то и уже после фактического начала ICO.

      Немалое значение для успеха ICO имеет система бонусов (в сфере игровых ICO бонусы довольно существенны и могут составлять 50-100% за приобретаемые на определенных условиях монеты, а на баунти-кампанию может быть выделено 10-15% от всего объема выпускаемых токенов, интересно было проанализировать в этой связи опыт ICO околоигровых монет Nexium (игра Beyond the Void) и Rustbits (визуальная новелла SpacePirates). Подводный камень в том, что большие бонусы это гарантия слива купленных или полученных за Баунти токенов сразу же на первой бирже (часто ей становится EtherDelta, выход на которую — дело техники), следовательно с большим бонусом не стоит продавать большие объемы монет, так как даже продажа по цене ICO может быть чрезвычайно выгодной тем, кто получил монеты с 100%-ным бонусом, а «баунтисты» вообще особо не замечены в каком-то особом пиетете к проектам, в которых участвуют и могут стать именно тем спусковым крючком, вызывающим сильное проседание курса после ICO. Что касается нашего проекта, то нам, похоже, удалось найти ту золотую середину, когда инвесторы заходят с долгосрочными планами (даже с бонусом), а баунтисты не смогут оказать особого влияния на курс (в силу того, что на Баунти-программы нами выделено всего 5% монет). Во всяком случае, при личном общении обладатели сколь-нибдуь существенного количества токенов LDM настаивают на неспекулятивном интересе к нашему проекту.

      Напоследок я хотел бы перечислить несколько моментов, на которые обязательно стоит обратить внимание тем, кто собирается проводить ICO своего продукта или компании, или, как минимум, стоит иметь в виду:
      • Есть два глобальных варианта производства токенов: премайн (когда вся сумма создается заранее и потом распределяется, а непроданная часть сжигается) и производство токенов в момент покупки (соответствующая сумма токенов создается в момент поступления ETH на кошелек контракта и отправляется на адрес инвестора, в этом случае сжигать нечего, так как «непроданных» токенов не будет — они просто не будут созданы — но от вопросов вида «а что с непроданными токенами?» это вас все равно не избавит)
      • Не премините воспользоваться удобным ENS-сервисом от MyEtherWallet.com и присвоить номеру кошелька вашего контракта короткое название, например — созвучное с именем домена. Мы о такой возможности узнали, когда в наших топиках появился мошенник,
        подсуетившийся раньше нас (и присвоивший похожее на официальное название своему кошельку). В нашем случае это не принесло ущерба, потому что мы все связанные с деньгами процессы автоматизировали (токены создаются только в момент поступления ETH на кошелек смарт-контракта, указанный на сайте), телеграмм-чата где можно спамить кошельками у нас нет, а в Дискорде (аналог Slack) у нас сидят по большей части люди, следящие за разработкой игры, а не инвесторы-спекулянты. Но лучше это иметь ввиду и сделать самостоятельно, чем потом разбираться с возможными последствиями.
      • Как и с токенами, с бонусами так же есть пара основных вариантов: бонусы за приобретаемую сумму (или в рамках определенного продаваемого объема) и в течение определенного времени, на мой взгляд, бонусы за сумму больше подходят проектам из реального сектора (да, такие тоже уже проводят ICO, и мне уже поступают вопросы от некоторых действующих офлайн-бизнесов с многолетней историей развития, раздумывающих о проведении Первичного Предложения Монет), а бонусы за время покупки — онлайн-проектам. И лучше бонусами не злоупотреблять — пока ICO не завершено и цена монеты не определена рынком, ваши койны не стоят ничего, а следовательно, более менее значимый объем рекламы за них вы не получите (скорее всего). А вот локальный слив получатели тех или иных бонусов после выхода уже на первую биржу вашему токену устроить при желании смогут.
      Original source: habrahabr.ru (comments, light).

      https://habrahabr.ru/post/337544/


      Метки:  

      [Из песочницы] Что «под капотом» у LinkedList?

      Суббота, 09 Сентября 2017 г. 20:33 + в цитатник
      AsteriskIT сегодня в 20:33 Разработка

      Что «под капотом» у LinkedList?

      Добрый день, хабрачитатели!

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

      Этот же автор описывает принципы работы LinkedList, однако часть данных устарела еще с выходом Java 7, поэтому попытка детально разобраться, что происходит внутри этой коллекции, по рисункам tarzan82 уже сложно. Да и в других источниках я не встретила понятных картинок, потому и возникла идея написать эту статью.

      Итак, LinkedList — класс, реализующий два интерфейса — List и Deque. Это обеспечивает возможность создания двунаправленной очереди из любых (в том числе и null) элементов. Каждый объект, помещенный в связанный список, является узлом (нодом). Каждый узел содержит элемент, ссылку на предыдущий и следующий узел. Фактически связанный список состоит из последовательности узлов, каждый из которых предназначен для хранения объекта определенного при создании типа.

      Разберемся, что же происходит, когда мы пишем уже простые и привычные строки кода.

      1. Создание связанного списка


      LinkedList numbers = new LinkedList<>();

      Данный код создает объект класса LinkedList и сохраняет его в ссылке numbers. Созданный объект предназначен для хранения целых чисел (Integer). Пока этот объект пуст.

      Класс LinkedList содержит три поля:

      // модификатор transient указывает на то, что данное свойство класса нельзя
      // сериализировать 
      transient int size = 0;
      transient Node first;
      transient Node last;



      2. Добавление объекта в конец связанного списка


      numbers.add(8);

      Данный код добавляет число 8 в конец ранее созданного списка. Под «капотом» этот метод вызывает ряд других методов, обеспечивающих создание объекта типа Integer, создание нового узла, установку объекта класса Integer в поле item этого узла, добавление узла в конец списка и установку ссылок на соседние узлы.

      Для установки ссылок на предыдущий и следующий элементы LinkedList использует объекты своего вложенного класса Node:

      private static class Node {
          E item;
          Node next;
          Node prev;
      
          Node(Node prev, E element, Node next) {
              this.item = element;
              this.next = next;
              this.prev = prev;
          }
      }

      При каждом добавлении объекта в список создается один новый узел, а также изменяются значения полей связанного списка (size, first, last).

      В случае с добавлением первого элемента создается узел, у которого предыдущий и следующий элементы отсутствуют, т.е. являются null, размер коллекции увеличивается на 1, а созданный узел устанавливается как первый и последний элемент коллекции.

      Добавим еще один элемент в нашу коллекцию:

      numbers.add(5);

      Сначала создается узел для нового элемента (число 5) и устанавливается ссылка на существующий элемент (узел с числом 8) коллекции как на предыдущий, а следующим элементом у созданного узла остается null. Также этот новый узел сохраняется в переменную связанного списка last:

      Как можно увидеть на рис. 4, первый элемент коллекции (под индексом 0) пока ссылается на null как на следующий элемент. Теперь эта ссылка заменяется и первый элемент начинает ссылаться на второй элемент коллекции (под индексом 1), а также увеличивается размер коллекции:

      3. Добавление объекта в середину связанного списка


      numbers.add(1, 13);

      LinkedList позволяет добавить элемент в середину списка. Для этого используется метод add(index, element), где index — это место в списке, куда будет вставлен элемент element.

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

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

      Если же index не равен size списка, то осуществляется вставка перед элементом, который до этой вставки имеет заданный индекс, т.е. в данном случае перед узлом со значением 5.

      Для начала с помощью метода node(index) определяется узел, находящийся в данный момент под индексом, под который нам необходимо вставить новый узел. Поиск данного узла осуществляется с помощью простого цикла for по половине списка (в зависимости от значения индекса — либо с начала до элемента, либо с конца до элемента). Далее создается узел для нового элемента (число 13), ссылка на предыдущий элемент устанавливается на узел, в котором элементом является число 8, а ссылка на следующий элемент устанавливается на узел, в котором элементом является число 5. Ссылки ранее существующих узлов пока не изменены:

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

      4. Удаление объекта из списка


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

      Рассмотрим удаление элемента из связанного списка по его значению. Удалим элемент со значением 5 из нижепредставленного списка:

      numbers.remove(Integer.valueOf(5));

      Обратите внимание, что принимаемым значением в методе remove(object) является именно объект, если же мы попытаемся удалить элемент со значением 5 следующей строкой
      numbers.remove(5);

      то получим IndexOutOfBoundsException, т.к. компиллятор воспримет число 5 как индекс и вызовет метод remove(index).

      Итак, что же происходит при вызове метода remove(object)? Сначала искомый объект сравнивается по порядку со всеми элементами, сохраненными в узлах списка, начиная с нулевого узла. Когда найден узел, элемент которого равен искомому объекту, первым делом элемент сохраняется в отдельной переменной. Потом переопределяются ссылки соседних узлов так, чтобы они указывали друг на друга:

      Затем обнуляется значение узла, который содержит удаляемый объект, а также уменьшается размер коллекции:

      Теперь вернемся к тому моменту, что элемент из удаляемого узла мы сохраняли в памяти. Зачем мы это делали, спросите вы, если эти данные мы нигде дальше не использовали. Дело в том, что рассматриваемый нами метод в результате своей работу не возвращает удаленный элемент, потому данные, возврещенные вызванным в рамках работы метода unlink(node), вызванного методом remove(object), просто не понадобились. А вот когда мы используем метод remove(index), также вызывающий метод unlink(node), то значение данного элемента последовательно возвращается сначала методом unlink(node), а затем и методом remove(index). Похожая ситуация наблюдается и в остальных методах, возвращающих значение удаленного элемента, только внутри вызываются другие методы, отсоединяющие ссылку: в методах poll(), pollFirst(), remove() и removeFirst() это метод unlinkFirst(node), а в методах pollLast() и removeLast() — метод unlinkLast(node).

      Итак, что следует помнить о LinkedList, решая, использовать ли данную коллекцию:

      • не синхронизирована;
      • позволяет хранить любые объекты, в том числе null и повторяющиеся;
      • за константное время O(1) выполняются операции вставки и удаления первого и последнего элемента, операции вставки и удаления элемента из середины списка (не учитывая время поиска позиции вставки элемента);
      • за линейное время O(n) выполняются операции поиска элемента по индексу и по значению.

      Ссылки


      Исходный код LinkedList (JDK 8)
      Документация LinkedList
      Original source: habrahabr.ru (comments, light).

      https://habrahabr.ru/post/337558/


      Метки:  

      Learnopengl. Урок 2.4 — Текстурные карты

      Суббота, 09 Сентября 2017 г. 18:07 + в цитатник
      eanmos сегодня в 18:07 Разработка

      Learnopengl. Урок 2.4 — Текстурные карты

      • Tutorial

      image


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





      Текстурные карты


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


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


      Диффузные карты


      Все, что нам нужно — это способ установить диффузный цвет для каждого фрагмента объекта. Что может повлиять на значение цвета, основываясь на позиции фрагмента?


      Припоминаете? Это — текстуры, которые мы интенсивно обсуждали в одном из предыдущих уроков. Карты освещения – всего лишь другое название того же принципа: используя изображение, нанесенное на поверхность объекта, мы можем делать выборки цвета для каждого фрагмента. В сценах с освещением это обычно называется диффузной картой (как правило ее так называют 3D художники), так как текстурное изображение представляет все диффузные цвета объекта.


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



      Использование диффузных карт в шейдерах очень похоже на использование текстур в одном из предыдущих уроков. Однако теперь мы заменим ранее определенный вектор vec3 диффузного цвета диффузной картой sampler2D.


      Имейте в виду, что sampler2D это, так называемый, непрозрачный тип данных. Это значит, что мы не можем создать экземпляр такого типа, мы можем только определить его как uniform. Если мы попытаемся использовать этот тип не как uniform (например как параметр функции), то GLSL выведет странные ошибки. Это правило также распространяется и на любую структуру, которая содержит непрозрачный тип.



      Мы также удалили вектор ambient, так как он в большинстве случаев совпадает с диффузным цветом, так что нам не нужно хранить его отдельно:


      struct Material {
          sampler2D diffuse;
          vec3      specular;
          float     shininess;
      }; 
      ...
      in vec2 TexCoords;
      

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



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


      vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));  
      

      Также не забудьте установить фоновый цвет материала таким же, как и диффузный:


      vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));
      

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


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


      #version 330 core
      layout (location = 0) in vec3 aPos;
      layout (location = 1) in vec3 aNormal;
      layout (location = 2) in vec2 aTexCoords;
      ...
      out vec2 TexCoords;
      
      void main()
      {
          ...
          TexCoords = aTexCoords;
      }  
      

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


      lightingShader.setInt("material.diffuse", 0);
      ...
      glActiveTexture(GL_TEXTURE0);
      glBindTexture(GL_TEXTURE_2D, diffuseMap);
      

      Используя диффузную карту, мы снова получили огромный прирост детализации и теперь, с добавленным освещением, наш контейнер действительно начал блистать (в буквальном смысле). Вероятно, теперь он выглядит вот так:



      Вы можете найти полный исходный код приложения здесь.


      Бликовые карты


      Вероятно, вы заметили, что блик выглядит немного странно, ведь наш объект — это контейнер, который большей частью состоит из дерева. А, как мы знаем, дерево не дает такого зеркального блеска. Мы можем исправить это, установив вектор specular в структуре Material равным vec3(0.0), но это значит, что железная рамка контейнера тоже перестанет давать блики, а мы знаем, что металл должен, хотя бы немного блестеть. И снова мы хотели бы контролировать, какие части объекта должны блестеть и с какой силой. Эта проблема очень похожа на обсуждение диффузных карт. Совпадение? Не думаю.


      Мы снова можем воспользоваться текстурной картой, только теперь для зеркальных бликов. Это значит, что нам нужно создать черно-белую (или цветную, если хотите) текстуру, которая определит силу блеска каждой части объекта. Вот пример бликовой карты:



      Интенсивность блеска определяется яркостью каждого пикселя изображения. Каждый пиксель такой карты может быть представлен как цветовой вектор, где черный цвет — это vec3(0.0), а серый — vec3(0.5), например. Затем, во фрагментном шейдере мы отбираем соответствующее цветовое значение и умножаем его на интенсивность бликового цвета. Соответственно, чем "белее" пиксель, тем больше получается результат умножения, а, следовательно, и яркость блика на фрагменте объекта.


      Так как контейнер большей частью состоит из дерева, а дерево — это материал, который не дает бликов, то вся "деревянная" часть текстуры закрашена черным. Черные части вообще не блестят. Поверхность стальной рамки контейнера имеет переменную силу зеркального блеска: сама сталь дает довольно интенсивные блики, в то время как трещины и потертости – нет.


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



      Используя инструменты, такие как Photoshop или Gimp, довольно просто превратить диффузную текстуру в бликовую. Достаточно просто вырезать некоторые части, сделать изображение черно-белым и увеличить яркость/контрастность.


      Сэмплинг бликовых карт


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


      lightingShader.setInt("material.specular", 1);
      ...
      glActiveTexture(GL_TEXTURE1);
      glBindTexture(GL_TEXTURE_2D, specularMap); 
      

      Затем обновим свойства материала во фрагментном шейдере, чтобы отражающий компонент принимался в качестве sampler2D, а не vec3:


      struct Material {
          sampler2D diffuse;
          sampler2D specular;
          float     shininess;
      };  
      

      И, наконец, нам нужно провести выборку карты бликов, чтобы получить соответствующую интенсивность блика для каждого фрагмента объекта:


      vec3 ambient  = light.ambient  * vec3(texture(material.diffuse, TexCoords));
      vec3 diffuse  = light.diffuse  * diff * vec3(texture(material.diffuse, TexCoords));  
      vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords));
      FragColor = vec4(ambient + diffuse + specular, 1.0);   
      

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


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



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



      Вы можете найти полный исходный код приложения здесь.


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


      Упражнения


      • Попробуйте поиграться с фоновым, диффузным и бликовым векторами источника света и посмотрите, как они влияют на внешний вид объекта.
      • Попробуйте инвертировать цвета бликовой карты во фрагментном шейдере, т.е. дерево должно блестеть, а железная рамка — нет (заметьте, что трещины на рамке все еще дают зеркальное отражение, хотя и с меньшей интенсивностью): решение.
      • Попробуйте создать карту бликов из диффузной карты, которая использует не черно-белые цвета, и вы увидите, что результат не очень реалистичный. Вы можете использовать эту цветную карту бликов, если не можете сделать ее сами. Результат.
      • Вы также можете попробовать добавить к нашему кубу карту эмиссии, которая хранит значение свечения каждого фрагмента объекта. Значения эмиссии — это цвета, которые объект может излучать, как если бы он содержал источник света. Таким образом, объект может светиться независимо от условий освещения. Обычно карты свечения можно увидеть на тех игровых объектах, которые светятся (например глаза робота или полосы света на контейнере). Добавьте эту текстуру (от creativesam) на наш контейнер в качестве карты свечений, так, чтобы буквы излучали свет. Решение, результат.
      Original source: habrahabr.ru (comments, light).

      https://habrahabr.ru/post/337550/


      Метки:  

      Настройка среды разработки Webpack 3 + Angular 4: от сложного к простому

      Суббота, 09 Сентября 2017 г. 16:01 + в цитатник
      maxim1006 сегодня в 16:01 Разработка

      Настройка среды разработки Webpack 3 + Angular 4: от сложного к простому

      • Tutorial
      Всем привет!

      Современное front-end-приложение на Angular должно включать в себя следующие характеристики:

      • Возможность использования типизированного JS — Typescript
      • Обеспечение удобства и производительности разработки с помощью HMR (hot module replacement);
      • Модульность приложений и возможность отложенной загрузки модулей (Lazy Loading);
      • AoT — режим (ahead-of-time), повышающий производительность приложения.

      Существует много вариантов сборки, решающих эти задачи (angular cli, A2 seed и т. д.). Обычно они имеют сложную структуру, плохо настраиваются/расширяются и представляют собой монолит, который невозможно изменить.

      В статье я расскажу, как совместить Angular 2+ с webpack и разобраться со всеми этапами сборки/разработки.

      Вы удивитесь, как это просто.

      Финальное приложение.

      Постараюсь по максимуму осветить тонкие моменты. Итак, поехали.

      1) Создаем проект


      Создаем папку с проектом, чтобы никто не догадался, назовем ее angular-project.
      (Использую Webstorm, однако можете проделывать то же самое в вашем редакторе)



      2) Окружение


      Устанавливаем node.js (npm в комплекте по умолчанию).

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

      package.json
          {
          "name": "angular-project",
          "version": "1.0.0",
          "description": "angular scaffolding",
          "author": "maxim1006",
          "license": "MIT",
          "dependencies": {
              //блок необходимых для Angular модулей
              "@angular/animations": "^4.3.6",
              "@angular/common": "^4.3.6",
              "@angular/compiler": "^4.3.6",
              "@angular/compiler-cli": "^4.3.6",
              "@angular/core": "^4.3.6",
              "@angular/forms": "^4.3.6",
              "@angular/http": "^4.3.6",
              "@angular/platform-browser": "^4.3.6",
              "@angular/platform-browser-dynamic": "^4.3.6",
              "@angular/router": "^4.3.6",
              //модули для hmr
              "@angularclass/hmr": "^2.1.1",
              "@angularclass/hmr-loader": "^3.0.2",
              //polyfills для es5
              "core-js": "^2.5.0",
              //модуль для работы декораторов в браузере 
              "reflect-metadata": "^0.1.8",
               //модуль для работы с реактивным программированием
              "rxjs": "^5.4.3",
               //типизация и доп. возможности для js
              "typescript": "2.3.4",
              //зоны в js, очень интересно, обязательно почитайте
              "zone.js": "^0.8.17"
          },
          "devDependencies": {
              //для сборки AoT (Ahead-of-Time Compilation) angular
              "@ngtools/webpack": "^1.6.2",
              //поддержка типизации, чтобы не ругался typescript
              "@types/es6-shim": "^0.31.35",
              "@types/jasmine": "^2.5.54",
              "@types/node": "^7.0.43",
              //routing в приложении
              "angular-router-loader": "^0.6.0",
              //так как на выходе получится бандл со встроенными темплейтами
              "angular2-template-loader": "^0.6.2",
              //чтобы не писать префиксы в css
              "autoprefixer": "^6.3.7",
              //для оптимизации работы typescript в webpack
              "awesome-typescript-loader": "^3.2.3",
              //если вдруг надо скопировать папку/файл
              "copy-webpack-plugin": "^4.0.1",
              //для работы с css
              "css-loader": "^0.28.5",
              "css-to-string-loader": "^0.1.2",
              //es6 polyfills
              "es6-shim": "^0.35.1",
              //для мобильной разработки 
              "hammerjs": "^2.0.8",
               //чтобы webpack работал с html
              "html-webpack-plugin": "^2.29.0",
              //препроцессор для более удобной работы со стилями
              "less": "^2.7.2",
              "less-loader": "^4.0.3",
              //по завершению сборки сможем вызвать коллбек
              "on-build-webpack": "^0.1.0",
              //вставляет результат работы webpack на страничку
              "raw-loader": "^0.5.1",
              //для работы со стилями
              "postcss-loader": "^1.3.3",
              "style-loader": "^0.17.0",
              //линтер
              "tslint": "^5.7.0",
              //если надо что-нибудь удалить
              "rimraf": "^2.6.1",
              //чтобы вставить картинки в css в виде base64
              "url-loader": "^0.5.8",
              //webpack
              "webpack": "^3.5.5",
              //и его встроенный express сервер
              "webpack-dev-server": "^2.7.1"
          },
      
      //когда введем в терминале эти команды с помощью npm run __command__ (например npm run serve)выполняться соответствующие команды)
          "scripts": {
      //Запускаем сервер. При каждом сохранении в вашем редакторе при работе с файлами проекта страничка будет перезагружаться, и вы будете видеть результат. Расскажем подробнее о команде. Для начала запускаем веб-сервер с данными настройками. Если мы хотим видеть в консоли, что с ним происходит (что бандлится и т. д.), используем (флаг --profile); если хотим, чтобы при сохранении в редакторе webpack автоматически обновлял результат, используем (--watch); ну а если хотим видеть проценты компиляции, можем опционально использовать (флаг –-progress).
              "serve": "webpack-dev-server --config ./webpack.config.js --profile --watch --progress",
              //то же, что и serve, но без перезагрузки страницы 
              "hmr": "webpack-dev-server --config ./webpack.config.js --profile --watch --progress",
              //создаем prod папочку с нашим проектом
              "prod": "npm run aot",
              //посмотреть как наш проект выглядит в prod, мало ли что
              "prodServer": "webpack-dev-server --config ./webpack.config.js --open",
              //очищаем ./dist на всякий случай
              "clean": "rimraf ./dist",
              //нужно, чтобы в webpack.js понять, что мы делаем aot. Делать это необязательно, но для наглядности нужно.
              "aot": "webpack",
              //тесты для приложения
              "test": "karma start"
          }
      }
      



      3) Установка модулей


      Через терминал заходим в папку, где лежит package.json, и вводим команду npm i.


      4) Установка глобальных модулей


      Так как мы используем команды rimraf, webpack и webpack-dev-server в терминале, то придется объяснить их вашему ПК с помощью команды npm i rimraf webpack webpack-dev-server -g

      После этих манипуляций наш проект пополнился папкой node_modules.




      5) README.md


      Создаем README.md, куда кроме ссылки на эту статью можно добавить особенности разработки вашего проекта.


      6) Линтер


      Создаем tslint.json, тут не буду останавливаться, так как нет серебряной пули.

      tslint.json
      {
        "rules": {
          "no-unused-variable": true,
          "curly": true,
          "no-console": [
            true,
            "log",
            "error",
            "debug",
            "info"
          ],
          "no-debugger": true,
          "no-duplicate-variable": true,
          "no-eval": true,
          "no-invalid-this": true,
          "no-shadowed-variable": true,
          "no-unsafe-finally": true,
          "no-var-keyword": true,
          "triple-equals": [
            true,
            "allow-null-check",
            "allow-undefined-check"
          ],
          "semicolon": [
            true,
            "always",
            "ignore-interfaces"
          ],
          "variable-name": [
            true,
            "ban-keywords",
            "check-format",
            "allow-leading-underscore"
          ]
        }
      }



      7) PostCss


      Создадим postcss.config.js, чтобы не писать префиксы к стилям

      postcss.config.js
      module.exports = {
          plugins: [
              require('autoprefixer')({
                  browsers: [
                      'last 2 versions'
                  ],
                  cascade: true
              })
          ]
      };
      



      Дальше пойдут немного более сложные манипуляции, пожалуйста, сфокусируйтесь.


      8) Настройка Typescript tsconfig.json


      Так как разработка A2+, на мой взгляд, невозможна без typescript, его надо настроить. Настройки обычные, однако если будут вопросы, спрашивайте в комментариях.

      tsconfig.json
      {
      //Настраиваем компилятор typescript
          "compilerOptions": {
              "target": "es5",
              "module": "es2015",
              "declaration": false,
              "moduleResolution": "node",
              "sourceMap": true,
              "emitDecoratorMetadata": true,
              "experimentalDecorators": true,
              "removeComments": false,
              "noImplicitAny": false,
              "suppressImplicitAnyIndexErrors": true,
              "skipLibCheck": true,
              "lib": ["es6", "dom"], 
              "outDir": "./dist/",
              "typeRoots": [
                  "./node_modules/@types/"
              ]
          },
          "compileOnSave": false,
          "buildOnSave": false,
      //наше приложение будет лежать в папке ./src
          "include": [
              "./src/**/*"
          ],
      //запрещаем typescript обращать внимание на:
          "exclude": [
              "node_modules/*",
              "dist/*",
              "dist-serve/*",
              "node/*",
              "**/*.spec.ts"
          ],
      //настраиваем loader для webpack
          "awesomeTypescriptLoaderOptions": {
              "forkChecker": true,
              "useWebpackText": true,
              "useCache": true
          },
      //нужно для AoT
          "angularCompilerOptions": {
              "genDir": ".",
              "skipMetadataEmit" : true
          }
      }
      



      9) Настройка Webpack


      Самое сложное – дать понять webpack, что мы от него хотим. Для этого создаем webpack.conf.js, без паники, постараюсь все объяснить

      webpack.conf.js
      "use strict";
      
      //это node модули и webpack плагины, которые понадобятся нам в разработке
      const path = require('path');
      const fs = require('fs');
      const webpack = require('webpack');
      const WebpackOnBuildPlugin = require('on-build-webpack');
      const CopyWebpackPlugin = require('copy-webpack-plugin');
      const AotPlugin = require('@ngtools/webpack').AotPlugin;
      
      
      //помните, в package.json были команды serve, hmr, prod и т. д.? так вот, текущую команду (например, если вы введете npm run serve, то команда будет называться ‘serve’) можно получить и обработать вот так:
      const ENV = process.env.npm_lifecycle_event ? process.env.npm_lifecycle_event : '';
      const isStatic = ENV === 'serve';
      const isHmr = ENV === 'hmr';
      const isProd = ENV === 'prod';
      const isTest = ENV === 'test';
      const isAot = ENV.includes('aot');
      const isProdServer = ENV.includes('prodServer');
      //в зависимости от команды, мы будем объяснять webpack что делать
      
      
      //обычно из webpack.conf.js экспортируется функция, возвращающая объект с настройками
      module.exports = function makeWebpackConfig() {
      
          console.log(`You are in ${ENV} mode`); //напомнить что мы запустили
      
          let config = {}; //главный объект с настройками
      
      //если вдруг кто-то выполнит команду npm run prodServer, не выполнив предварительно npm run prod, кидаем напоминалку
          if (isProdServer) {
              if (!fs.existsSync('./dist')) {
                  throw "Can't find ./dist, please use 'npm run prod' to get it.";
              }
          }
      
      //подключаем sourcemaps
          if (isHmr || isStatic) {
              config.devtool = 'inline-source-map';
          } else {
              config.devtool = 'source-map';
          }
      
      //обозначаем главный файл, который будет создавать webpack. Этот файл доступен в index.html по пути “./ng-app.js”
          config.entry = {
              'ng-app': './src/app/ng-main.ts'
          };
      
      //специально для AoT режима нужно создать другой файл с другим наполнением, так надо…
          if (isAot) {
              config.entry['ng-app'] = './src/app/ng-main-aot.ts';
          }
      
      // Имя файла, который создаст webpack будет 'ng-app’, так как задали filename: '[name].js', также когда запустим prod сборку, результирующий бандл попадет в папку './dist', это указали с помощью path: root('./dist') 
          config.output = isTest ? {} : {
              path: root('./dist'), //root – всего лишь функция, для создания правильных путей относительно папки, в которой находится webpack.config.js
              filename: '[name].js' 
          };
      
      //в свойстве entry при настройке webpack обязательно нужно задать какой-нибудь файл, иначе возникнет ошибка, но в режиме prodServer нам нужно лишь посмотреть на нашу prod сборку. По этой причине и создаем поддельный файл, чтобы сервер ни на что, кроме статики, не отвлекался. Можно в корень проекта, рядом с webpack.conf.js, положить пустой файл webpack-prod-server.js, чтобы в логи сервера не попадала ошибка, что этого файла нет, хотя и без него сервер будет работать.
          if (isProdServer) {
              config.entry = {
                  'server': './webpack-prod-server.js'
              };
              config.output = {};
          }
      
      //указываем расширения файлов, с которыми webpack будет работать
          config.resolve = {
              extensions: ['.ts', '.js', '.json', '.html', '.less', '.svg']
          };
      
      //определяем так называемые loaders: если будут вопросы по ним, отвечу в комментариях. Если коротко, тут готовый пример для превращения ts в js, html вставляем в js бандл, less компилируем в css и вставляем в js бандл, картинки до 10 кб в base64 и вставляем в js бандл.
          config.module = {
              rules: [
                  {
                      test: /\.ts$/,
                      use: isAot ? [{loader: '@ngtools/webpack'}] : [
                          {
                              loader: 'awesome-typescript-loader?'
                          },
                          {
                              loader: 'angular2-template-loader'
                          },
                          {
                              loader: 'angular-router-loader'
                          }
                      ].concat(isHmr ? '@angularclass/hmr-loader?pretty=' + !isProd + '&prod=' + isProd : []),
                      exclude: [/\.(spec|e2e|d)\.ts$/]
                  },
                  {
                      test: /\.html$/, loader: 'raw-loader',
                      exclude: [/node_modules\/(?!(ng2-.+))/, root('src/index.html')]
                  },
                  {
                      test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
                      loader: "url-loader?name=[name].[ext]&limit=10000&useRelativePath=true"
                  },
                  {
                      test: /\.less$/,
                      use: [
                          {loader: "css-to-string-loader"},
                          {loader: "css-loader"},
                          {loader: "postcss-loader"},
                          {loader: "less-loader"}
                      ]
                  }
              ]
          };
      
      
      
      //если работаем не в режиме тестирования, то подключаем webpack плагины
          if (!isTest) {
              config.plugins = [
      //не останавливать webpack warcher при ошибках
                  new webpack.NoEmitOnErrorsPlugin(),
      //передать текущий режим в наши .ts файлы, как их получить в .ts файлах увидите чуть позже
                  new webpack.DefinePlugin({
                      'process.env': {
                          'STATIC': isStatic,
                          'HMR': isHmr,
                          'PROD': isProd,
                          'AOT': isAot
                      }
                  }),
      //сделать что-то по окончании сборки
                  new WebpackOnBuildPlugin((stats) => {
                      console.log('build is done');
                  })
              ]
      //если работаем в режиме hmr, то подключить плагин для hmr
                  .concat(isHmr ? new webpack.HotModuleReplacementPlugin() : []);
          }
      
      
      
      //если вы вызовете команду ‘npm run prod’, то запустите процесс создания prod сборки с AoT
          if (isAot) {
              config.plugins = [
      //нужно для AoT режима
                  new AotPlugin({
                      tsConfigPath: './tsconfig.json',
                      entryModule: root('src/app/app.module.ts#AppModule')
                  }),
      //Оптимизируем полученный бандл
                  new webpack.optimize.UglifyJsPlugin({
                      compress: {
                          warnings: false,
                          screw_ie8: true,
                          conditionals: true,
                          unused: true,
                          comparisons: true,
                          sequences: true,
                          dead_code: true,
                          evaluate: true,
                          if_return: true,
                          join_vars: true
                      },
                      output: {
                          comments: false
                      },
                      sourceMap: true
                  }),
      //Копируем нужные нам файлы в ./dist папку (js бандл туда положит сам webpack, а мы перенесем то, что нам понадобится дополнительно)                    
       new CopyWebpackPlugin([
                      {from: 'index.html', context: './src'},
                      {from: 'assets/themes/base/fonts/**/*', context: './src'},
                      {from: 'assets/themes/base/images/other-images/**/*', context: './src'},
                  ]),
                  new WebpackOnBuildPlugin((stats) => {
                      console.log('build in aot is done');
                  })
              ];
          }
      
      
      //Ну и наконец настроим наш webpack-dev-server
          config.devServer = {
      
              contentBase: isProdServer ? "./dist" : "./src",//корневая папка сервера, в prod режиме в ./dist, в режиме разработки в ./src
              headers: {
                  "Access-Control-Allow-Origin": "*",
                  "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS",
                  "Access-Control-Allow-Headers": "X-Requested-With, content-type, Authorization"
              }, //стандартные заголовки для rest запросов
              historyApiFallback: true, //включаем HTML5 history api, очень удобно 1ой строкой
              compress: true,//включаем gzip
              quiet: false, //ничего лишнего нам выводить в логи не нужно
              inline: isHmr || isStatic || isProdServer, //inline mode
              hot: isHmr, //включаем hmr, если в hmr режиме
              stats: "minimal",
              port: 9000,
      //модное окно смерти при ошибке от Webpack
              overlay: {
                  errors: true
              },
      //Опции для webpack warcher 
              watchOptions: {
                  aggregateTimeout: 50,
                  ignored: /node_modules/
              }
          };
      
          return config;
      };
      
      
      //делаем правильный путь от текущей директории
      function root(__path = '.') {
          return path.join(__dirname, __path);
      }
      



      10) Структура src


      Сейчас наш проект выглядит так, кроме папки src



      Создаем структуру в папке src:



      Пара комментариев: в папке app будет лежать наше angular приложение, в папке assets вспомогательные файлы, index.html просто кладем в src. В assets поддержим темизацию и разобьем папки для удобной работы со шрифтами, картинками, стилями.

      В нашей компании мы используем БЭМ методологию, немного переработанную и более оптимальную, на наш взгляд. base.less – агрегирующий .less файл для base темы:
      base.less
      // Common
      @import "themes/base/styles/common/normalize";
      @import "themes/base/styles/common/colors";
      @import "themes/base/styles/common/common";
      @import "themes/base/styles/common/fonts";
      @import "themes/base/styles/common/vars";
      
      // Blocks
      // (please, add new blocks in alphabetical order)
      @import "themes/base/styles/blocks/app-component";
      



      Заметим, что, на наш взгляд, следует разносить функциональную и стилевую части приложения: это решает ряд проблем как сборки, так и поддержки проекта. Если использовать БЭМ и парадигму один блок – один less файл, то проблем у подхода не обнаруживается. Однако есть куча альтернатив. Более подробно покопаться в assets можно в приложении, к этому посту. Вопросы задавайте в комментариях к статье.


      11) index.hml


      index.html – стал безумно прост в A2+ приложениях

      index.html
      
      
      
       //нужно для A2+ routing
      
      
          
          
      
      
      
      Loading...
      
      
      
      
      
      



      12) Angular app


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

      Создадим структуру в папке app:



      На первый взгляд – ребус.

      Однако если вы прошли хотя бы Angular 2+ Tutorial, то все это вам уже знакомо. Для остальных же краткие комментарии: все приложение разбито на модули, фреймворк даже предоставляет такую сущность – module. Есть главный модуль – app.module.ts, есть дополнительные модули, расширяющие функционал приложения. Большая часть приложений будет иметь home, lazy и shared модули. Названия модулей, разумеется, опциональны, однако при соблюдении правил наименования у вас не возникнет проблем с расширяемостью приложения.

      Про сам фреймворк говорить много не будем, есть отличная документация. Лучше сосредоточимся на тонких моментах:

      ng-main.ts


      С него все начинается

      ng-main.ts
      import './ng-polyfills'; //чтобы работало в ie 9+
      import …
      
      //в настройках webpack мы прокидывали переменные, тут их ловим
      if (process.env.STATIC) {
          //console.log("******************You are in Dev mode******************");
          platformBrowserDynamic().bootstrapModule(AppModule).then(():any => {});
      } else if (process.env.HMR) {
      //нужно для hmr в Angular
         //console.log("******************You are in HMR mode******************");
          bootloader(main);
      }
      
      export function main() {
          return platformBrowserDynamic()
              .bootstrapModule(AppModule)
      }
      



      ng-main-aot.ts для AoT


      Для AoT (Ahead-of-Time Compilation) режима создаем другой главный файл ng-main-aot.ts, так нужно…

      ng-main-aot.ts
      import …
      
      console.log("******************You are in prod mode******************");
      
      enableProdMode();
      
      platformBrowser()
          .bootstrapModuleFactory(AppModuleNgFactory)
          .catch(error=>console.log(error));
      



      HMR, стили, hammerjs


      HMR, стили нашего приложения (на всякий случай оставил пример подключения картинок) и настройки hammerjs для мобильной разработки подключаем в app.module.ts таким образом:

      app.module.ts
      require("style-loader!../assets/base.less"); //так подключаем стили через webpack
      
      import …
      
      //настраиваем  hammer.js
      export class MyHammerConfig extends HammerGestureConfig  {
          overrides = {
              'swipe': {velocity: 0.4, threshold: 20}
          }
      }
      
      @NgModule({
          declarations: [
              AppComponent,
          ],
          imports: [
              BrowserModule,
              HomeModule,
              NgRoutingModule
          ],
          providers: [
          ],
          bootstrap: [
              AppComponent
          ]
      })
      
      export class AppModule {
          constructor(public appRef: ApplicationRef) {}
          hmrOnInit(store) {
              if (!store || !store.state) return;
      
              if ('restoreInputValues' in store) {
                  store.restoreInputValues();
              }
      
              this.appRef.tick();
              delete store.state;
              delete store.restoreInputValues;
          }
          hmrOnDestroy(store) {
              let cmpLocation = this.appRef.components.map(cmp => cmp.location.nativeElement);
              store.disposeOldHosts = createNewHosts(cmpLocation);
              store.state = {data: 'yolo'};
              store.restoreInputValues  = createInputTransfer();
              removeNgStyles();
          }
          hmrAfterDestroy(store) {
              store.disposeOldHosts();
              delete store.disposeOldHosts;
          }
      }
      



      Lazy loading


      Lazy loading модулей подключаем в ng-routing.module.ts

      ng-routing.module.ts
      import …
      
      const routes: Routes = [
          {path: '', redirectTo: '/home', pathMatch: 'full'},
          {path: 'home', component: HomeComponent},
          //так подключаем lazy модули, отдельные .js файлы webpack для них создаст сам   
          {path: 'lazy', loadChildren: './modules/lazy/lazy.module#LazyModule'},
          {path: '**', component: PageNotFoundComponent},
      ];
      
      @NgModule({
          imports: [
              RouterModule.forRoot(routes)
          ],
          exports: [
              RouterModule
          ]
      })
      export class NgRoutingModule { }
      



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

      lazy.module.ts
      import …
      
      const routes: Routes = [
          {path: '', component: LazyComponent},
      ];
      
      @NgModule({
          imports: [SharedModule, RouterModule.forChild(routes)],
          exports: [LazyComponent],
          declarations: [LazyComponent]
      })
      export class LazyModule {}
      



      Хм… ну вот в принципе и все. Покопаться в app папке можно в приложении к данному посту.

      Для разработки с перезагрузкой странички на каждое изменение кода в редакторе, пишем в терминале, находясь папке с package.json: npm run serve
      То же, но без перезагрузки странички: npm run hmr
      Делаем prod сборку с AoT: npm run prod
      Запускаем статический сервер, чтобы посмотреть prod: npm run prodServer
      Почистить ./dist папку: npm run clean

      Всего несколько шагов и у нас работают: webpack сборка с Angular 4, AoT, HMR, Lazy loading. Все, включая шаблоны и стили, аккуратно кладется в бандл и оптимизируется.
      Разумеется, эту конфигурацию можно расширять, улучшать, менять, однако на мой взгляд, ее вполне достаточно, чтобы смело начать разрабатывать с Angular 2+.

      P.S.

      Небольшая реклама АoT: отличный boost к производительности вашего SPA приложения на Angular.



      Спасибо за внимание.
      Original source: habrahabr.ru (comments, light).

      https://habrahabr.ru/post/337540/


      Метки:  

      «Молодые хотят покупать на бирже акции знакомых стартапов »: тренды финансов по мнению основателя сервиса Robinhood

      Суббота, 09 Сентября 2017 г. 15:46 + в цитатник


      Приложение для покупки акций Robinhood позволяет всем желающим торговать на бирже с помощью простого приложения (мы писали о нем здесь). Сейчас финтех-стартап оценивается примерно в $1,3 млрд и насчитывает около 2 млн пользователей. Один из соучредителей компании — Байджу Бхатт — дал интервью Business Insider, в котором рассказал о современном подходе к сбережениям и главной финансовой проблеме человечества. Мы публикуем главные мысли из этого разговора. Читать дальше ->

      https://habrahabr.ru/post/337538/


      Метки:  

      «Молодые хотят покупать на бирже акции знакомых стартапов »: тренды финансов по мнению основателя сервиса Robinhood

      Суббота, 09 Сентября 2017 г. 15:46 + в цитатник


      Приложение для покупки акций Robinhood позволяет всем желающим торговать на бирже с помощью простого приложения (мы писали о нем здесь). Сейчас финтех-стартап оценивается примерно в $1,3 млрд и насчитывает около 2 млн пользователей. Один из соучредителей компании — Байджу Бхатт — дал интервью Business Insider, в котором рассказал о современном подходе к сбережениям и главной финансовой проблеме человечества. Мы публикуем главные мысли из этого разговора. Читать дальше ->

      https://habrahabr.ru/post/337538/


      Метки:  

      CaptureManager SDK

      Суббота, 09 Сентября 2017 г. 15:20 + в цитатник
      Xirexel сегодня в 15:20 Разработка

      CaptureManager SDK

        Эта статья представляет мой «Hobby» проект — CaptureManager для настольной платформы Windows. Этот проект является простым набором функционала (SDK) для включения поддержки широкого набора видео и аудио источников в разрабатываемое приложение.

        CaptureManager построен на основе Microsoft Media Foundation — нового поколения медиа технологий, заменившем объявленное устаревшем DirectShow. Microsoft Media Foundation была впервые включена в Windows Vista и получила поддержку видео и аудио источников начиная с Windows 7. Достоинством Microsoft Media Foundation является новая модель конвейера обработки медиа данных, оптимальная для многопроцессорных систем, и продолжающееся её развитие, и поддержка со стороны Microsoft.
        В проекте CaptureManager я хотел решить ряд проблем, с которыми столкнулся при написании приложений с использованием Microsoft Media Foundation:
        1. Реализация СОМ функционала. Как-бы странно это не звучало, но с технологией Microsoft Media Foundation корпорация Microsoft отступила от своей-же модели приложений – от СОМ. Конечно, все интерфейсы классов в Microsoft Media Foundation всё также являются производными от IUnknown и связаны с GUID. Но сами классы создаются через прямые «С» вызовы функций из статически линкованных системных библиотек. Это отличается от реализации DirectShow, которая требует вызова CoCreateInstance и обращения через СОМ абстракцию. На мой взгляд, данное решение корпорация Microsoft является недостатком – во-первых, затрудняется интеграция Microsoft Media Foundation в проекты, написанные не на С/С++, к примеру С# проекты, которые, кстати, на Windows взаимодействуют с СОМ объектами практически бесшовно, генерируя требуемые определения интерфейсов из TLB. Во-вторых, повышается риск потери совместимости приложения со следующей версией Windows при миграции функции из одной статически линкованной библиотеки в другую – с Microsoft Media Foundation это уже произошло однажды: Library Changes in Windows 7 — «Starting in Windows 7, certain Media Foundation functions are exported from different DLL files than previous versions.».
        2. На мой взгляд Microsoft Media Foundation черезмерно перегружена функциями и интерфейсами – было-бы неплохо спрятать большинство из них за дополнительным уровнем абстракции для оптимизации задачи захвата и записи видео и аудио данных.
        3. Значительным недостатком, на мой взгляд, является ограничения в поддержке записи видео и аудио в Microsoft Media Foundation. Microsoft Media Foundation предоставляет два механизма работы с медиа данными: через граф-топологию и SourceReader-SinkWriter. Первый предполагает сборку требуемой конфигурации из узлов-преобразователей и позволяет гибко настроить требуемую конфигурацию. Второй предлагает получение порций медиа данных от SourceReader и отправки их в SinkWriter в контексте разрабатываемого приложения. Граф-топология весьма удобна, на мой взгляд, и позволяет легко генерировать требуемую конфигурацию записи по требованию пользователя. Однако, данное решение от Microsoft не позволяет решить задачу записи – дело в том, что объект для создания рабочей сессии записи на базе топологии с интерфейсом IMFMediaSession из функции MFCreateMediaSession оптимизирован для воспроизведения медиа данных, и не выполняет ряд требуемых операций – к примеру, при окончании записи в файл, требуется выполнить расчёт метрик – подсчитать средний скорость потоков и рассчитать длительность воспроизведения – но IMFMediaSession из функции MFCreateMediaSession этого не делает – для задачи воспроизведения операция расчёта метрик бессмысленна. Также существует проблема с таймингом — IMFMediaSession из функции MFCreateMediaSession рассматривает начало воспроизведения с нулевого времени – это логично при воспроизведении медиа файла. Однако, видео и аудио источники, такие как веб-камеры или микрофоны используют текущее системное время – согласно документации Microsoft Media Foundation они должны инициализироваться нулевым значением времени, но они не выполняют данное требование.

        Я полагаю, и думаю многие согласятся, что указанные выше проблемы существенны и было-бы желательно их разрешить. Это и послужило причиной начала проекта CaptureManager (а также задача захвата видео с двух веб-камер и записи этого видео в один медиа файл).
        Вкратце, что из себя представляет CaptureManager:
        1. Полноценный СОМ In-Process Server – или как иногда называют – ActiveX. Он включает TLB и может быть интегрирован в проекты на С++, С#, Python наравне с DirectShow.
        2. CaptureManager связан с библиотеками Microsoft Media Foundation, но использует «отложенное связывание» — библиотеки Microsoft Media Foundation загружаются в коде CaptureManager и связываются с соответствующими функциями во время выполнения приложения. При невозможности найти функцию в библиотеках, она замещается на функцию-пробку, возвращающую код ошибки – E_NOTIMPL. Таким образом, CaptureManager позволяет уменьшить риск аварийного прекращения работы целевого приложения в ситуации миграции функций из одной библиотеки Microsoft Media Foundation в другую.
        3. CaptureManager имеет упрощённый набор интерфейсов. Важной особенностью является генерация данных, описывающих медиа источники, кодеки и медиа контейнеры в формате XML документа – обрабатывать XML документ намного легче, чем многочисленные Variant и PropVariant, особенно на высокоуровневых API, как WPF.
        4. CaptureManager включает в себя ряд видео и аудио источников, отсутствующих в исходном Microsoft Media Foundation: Screen Capture – для захвата изображений с дисплея (или нескольких дисплеев), AudioLoop Capture – для захвата звукового потока с аудио выхода, DirectShow-Crossbar Capture – для захвата видео с карт видео захвата.
        5. CaptureManager включает в себя «аккумулятор кадров», позволяющий получить серию крайних кадров.
        6. CaptureManager включает собственную реализацию IMFMediaSession интерфейса, оптимизированную для задачи записи – т.е. реализован полный отказ от вызова MFCreateMediaSession функции.
        7. CaptureManager включает функционал для изменения параметров видео процессора веб-камеры и параметров камеры (фокус, экспозиция, и т.п.).

        Функционал CaptureManager представлен в демонстрационных программах, доступных на GitHub — CaptureManager-SDK-Demos:
        • CPPDemos:
          1. EVRWebCapViewerViaCOMServer – простое C++ приложение для демонстрации функционала просмотра видео источников через CaptureManager рендерер.
          2. OpenGLWebCamViewerViaCOMServer – простое C++ приложение для демонстрации функционала просмотра видео источников через OpenGL рендерер.
          3. TextInjectorDemo – простое C++ приложение для демонстрации функционала смешивания теста с видео потоком с камеры.
            image
          4. WaterMarkInjectorDemo – простое C++ приложение для демонстрации функционала смешивания изображений с видео потоком с камеры.
            image
          5. EVRVieweingAndRecording – простое C++ приложение для демонстрации функционала записи с видео и аудио источников в один медиа файл.
            image
          6. NativeMediaFoundationPlayer – простое C++ приложение для демонстрации воспроизведения множества видео файлов в общем рендерере.
            image

        • CSharpDemos:
          1. WPFMultiSourceRecorder – простое C# приложение для демонстрации функционала записи с одного, двух и более видео и аудио источников в один общий медиа файл.
            image
          2. WPFMediaFoundationPlayer – простое C# приложение для демонстрации воспроизведения множества видео файлов в общем рендерере.
            image
          3. WPFVideoAndAudioRecorder – простое C# приложение для демонстрации функционала записи с видео и аудио источников в один медиа файл.
            image
          4. WPFIPCameraMJPEGMultiSourceViewer – простое C# приложение для демонстрации функционала захвата видео с нескольких Интернет камер и воспроизведения их в общем рендерере.
            image
          5. WPFMultiSourceViewer – простое C# приложение для демонстрации функционала захвата видео с нескольких нескольких и воспроизведения их в общем рендерере.
            image
          6. WPFViewerEVRDisplay – простое C# приложение для демонстрации функционала интеграции CaptureManager рендерера в WPF приложение.
            image
          7. WPFIPCameraMJPEGViewer – простое C# приложение для демонстрации функционала захвата видео с Интернет камеры.
            image
          8. WPFImageViewer – простое C# приложение для демонстрации функционала захвата изображения из файла.
            image
          9. WindowsFormsDemo – простое C# приложение для демонстрации функционала просмотра и записи видео источников.
            image
          10. WPFWebCamSerialShots – простое C# приложение для демонстрации функционала «аккумулятор кадров».
          11. WPFWebCamShot – простое C# приложение для демонстрации функционала захвата кадров с видео источника.
          12. WPFRecorder – простое C# приложение для демонстрации функционала просмотра и записи видео источников.
            image
          13. WPFWebViewerEVR – простое C# приложение для демонстрации функционала просмотра видео источников через CaptureManager рендерер.
            image
          14. WPFWebViewerCallback – простое C# приложение для демонстрации функционала захвата кадров с видео источника через копирование из потока CaptureManager.
            image
          15. WPFWebViewerCall – простое C# приложение для демонстрации функционала захвата кадров с видео источника через прямой вызов методов CaptureManager.
            image
          16. WPFSourceInfoViewer – простое C# приложение для демонстрации функционала получения информации о доступных видео и аудио источников.
            image

        • PythonDemos:
          1. CaptureManagerSDKPythonDemo – простое Python приложение для демонстрации функционала просмотра и записи видео источников.
            image

        • QtMinGWDemos:
          1. CaptureManagerSDKQtMinGWDemo – простое С++ приложение на Qt для демонстрации функционала просмотра и записи видео источников.
            image

        • UnityDemos:
          1. UnityWebCamViewer — простое приложение для демонстрации функционала работы с видео источником в Unity3D.
            image


        Больше информации о проекте можно найти сайте CaptureManager SDK. На NuGet существует C# оболочка CaptureManager.
        Original source: habrahabr.ru (comments, light).

        https://habrahabr.ru/post/337190/


        Метки:  

        CaptureManager SDK

        Суббота, 09 Сентября 2017 г. 15:20 + в цитатник
        Xirexel сегодня в 15:20 Разработка

        CaptureManager SDK

          Эта статья представляет мой «Hobby» проект — CaptureManager для настольной платформы Windows. Этот проект является простым набором функционала (SDK) для включения поддержки широкого набора видео и аудио источников в разрабатываемое приложение.

          CaptureManager построен на основе Microsoft Media Foundation — нового поколения медиа технологий, заменившем объявленное устаревшем DirectShow. Microsoft Media Foundation была впервые включена в Windows Vista и получила поддержку видео и аудио источников начиная с Windows 7. Достоинством Microsoft Media Foundation является новая модель конвейера обработки медиа данных, оптимальная для многопроцессорных систем, и продолжающееся её развитие, и поддержка со стороны Microsoft.
          В проекте CaptureManager я хотел решить ряд проблем, с которыми столкнулся при написании приложений с использованием Microsoft Media Foundation:
          1. Реализация СОМ функционала. Как-бы странно это не звучало, но с технологией Microsoft Media Foundation корпорация Microsoft отступила от своей-же модели приложений – от СОМ. Конечно, все интерфейсы классов в Microsoft Media Foundation всё также являются производными от IUnknown и связаны с GUID. Но сами классы создаются через прямые «С» вызовы функций из статически линкованных системных библиотек. Это отличается от реализации DirectShow, которая требует вызова CoCreateInstance и обращения через СОМ абстракцию. На мой взгляд, данное решение корпорация Microsoft является недостатком – во-первых, затрудняется интеграция Microsoft Media Foundation в проекты, написанные не на С/С++, к примеру С# проекты, которые, кстати, на Windows взаимодействуют с СОМ объектами практически бесшовно, генерируя требуемые определения интерфейсов из TLB. Во-вторых, повышается риск потери совместимости приложения со следующей версией Windows при миграции функции из одной статически линкованной библиотеки в другую – с Microsoft Media Foundation это уже произошло однажды: Library Changes in Windows 7 — «Starting in Windows 7, certain Media Foundation functions are exported from different DLL files than previous versions.».
          2. На мой взгляд Microsoft Media Foundation черезмерно перегружена функциями и интерфейсами – было-бы неплохо спрятать большинство из них за дополнительным уровнем абстракции для оптимизации задачи захвата и записи видео и аудио данных.
          3. Значительным недостатком, на мой взгляд, является ограничения в поддержке записи видео и аудио в Microsoft Media Foundation. Microsoft Media Foundation предоставляет два механизма работы с медиа данными: через граф-топологию и SourceReader-SinkWriter. Первый предполагает сборку требуемой конфигурации из узлов-преобразователей и позволяет гибко настроить требуемую конфигурацию. Второй предлагает получение порций медиа данных от SourceReader и отправки их в SinkWriter в контексте разрабатываемого приложения. Граф-топология весьма удобна, на мой взгляд, и позволяет легко генерировать требуемую конфигурацию записи по требованию пользователя. Однако, данное решение от Microsoft не позволяет решить задачу записи – дело в том, что объект для создания рабочей сессии записи на базе топологии с интерфейсом IMFMediaSession из функции MFCreateMediaSession оптимизирован для воспроизведения медиа данных, и не выполняет ряд требуемых операций – к примеру, при окончании записи в файл, требуется выполнить расчёт метрик – подсчитать средний скорость потоков и рассчитать длительность воспроизведения – но IMFMediaSession из функции MFCreateMediaSession этого не делает – для задачи воспроизведения операция расчёта метрик бессмысленна. Также существует проблема с таймингом — IMFMediaSession из функции MFCreateMediaSession рассматривает начало воспроизведения с нулевого времени – это логично при воспроизведении медиа файла. Однако, видео и аудио источники, такие как веб-камеры или микрофоны используют текущее системное время – согласно документации Microsoft Media Foundation они должны инициализироваться нулевым значением времени, но они не выполняют данное требование.

          Я полагаю, и думаю многие согласятся, что указанные выше проблемы существенны и было-бы желательно их разрешить. Это и послужило причиной начала проекта CaptureManager (а также задача захвата видео с двух веб-камер и записи этого видео в один медиа файл).
          Вкратце, что из себя представляет CaptureManager:
          1. Полноценный СОМ In-Process Server – или как иногда называют – ActiveX. Он включает TLB и может быть интегрирован в проекты на С++, С#, Python наравне с DirectShow.
          2. CaptureManager связан с библиотеками Microsoft Media Foundation, но использует «отложенное связывание» — библиотеки Microsoft Media Foundation загружаются в коде CaptureManager и связываются с соответствующими функциями во время выполнения приложения. При невозможности найти функцию в библиотеках, она замещается на функцию-пробку, возвращающую код ошибки – E_NOTIMPL. Таким образом, CaptureManager позволяет уменьшить риск аварийного прекращения работы целевого приложения в ситуации миграции функций из одной библиотеки Microsoft Media Foundation в другую.
          3. CaptureManager имеет упрощённый набор интерфейсов. Важной особенностью является генерация данных, описывающих медиа источники, кодеки и медиа контейнеры в формате XML документа – обрабатывать XML документ намного легче, чем многочисленные Variant и PropVariant, особенно на высокоуровневых API, как WPF.
          4. CaptureManager включает в себя ряд видео и аудио источников, отсутствующих в исходном Microsoft Media Foundation: Screen Capture – для захвата изображений с дисплея (или нескольких дисплеев), AudioLoop Capture – для захвата звукового потока с аудио выхода, DirectShow-Crossbar Capture – для захвата видео с карт видео захвата.
          5. CaptureManager включает в себя «аккумулятор кадров», позволяющий получить серию крайних кадров.
          6. CaptureManager включает собственную реализацию IMFMediaSession интерфейса, оптимизированную для задачи записи – т.е. реализован полный отказ от вызова MFCreateMediaSession функции.
          7. CaptureManager включает функционал для изменения параметров видео процессора веб-камеры и параметров камеры (фокус, экспозиция, и т.п.).

          Функционал CaptureManager представлен в демонстрационных программах, доступных на GitHub — CaptureManager-SDK-Demos:
          • CPPDemos:
            1. EVRWebCapViewerViaCOMServer – простое C++ приложение для демонстрации функционала просмотра видео источников через CaptureManager рендерер.
            2. OpenGLWebCamViewerViaCOMServer – простое C++ приложение для демонстрации функционала просмотра видео источников через OpenGL рендерер.
            3. TextInjectorDemo – простое C++ приложение для демонстрации функционала смешивания теста с видео потоком с камеры.
              image
            4. WaterMarkInjectorDemo – простое C++ приложение для демонстрации функционала смешивания изображений с видео потоком с камеры.
              image
            5. EVRVieweingAndRecording – простое C++ приложение для демонстрации функционала записи с видео и аудио источников в один медиа файл.
              image
            6. NativeMediaFoundationPlayer – простое C++ приложение для демонстрации воспроизведения множества видео файлов в общем рендерере.
              image

          • CSharpDemos:
            1. WPFMultiSourceRecorder – простое C# приложение для демонстрации функционала записи с одного, двух и более видео и аудио источников в один общий медиа файл.
              image
            2. WPFMediaFoundationPlayer – простое C# приложение для демонстрации воспроизведения множества видео файлов в общем рендерере.
              image
            3. WPFVideoAndAudioRecorder – простое C# приложение для демонстрации функционала записи с видео и аудио источников в один медиа файл.
              image
            4. WPFIPCameraMJPEGMultiSourceViewer – простое C# приложение для демонстрации функционала захвата видео с нескольких Интернет камер и воспроизведения их в общем рендерере.
              image
            5. WPFMultiSourceViewer – простое C# приложение для демонстрации функционала захвата видео с нескольких нескольких и воспроизведения их в общем рендерере.
              image
            6. WPFViewerEVRDisplay – простое C# приложение для демонстрации функционала интеграции CaptureManager рендерера в WPF приложение.
              image
            7. WPFIPCameraMJPEGViewer – простое C# приложение для демонстрации функционала захвата видео с Интернет камеры.
              image
            8. WPFImageViewer – простое C# приложение для демонстрации функционала захвата изображения из файла.
              image
            9. WindowsFormsDemo – простое C# приложение для демонстрации функционала просмотра и записи видео источников.
              image
            10. WPFWebCamSerialShots – простое C# приложение для демонстрации функционала «аккумулятор кадров».
            11. WPFWebCamShot – простое C# приложение для демонстрации функционала захвата кадров с видео источника.
            12. WPFRecorder – простое C# приложение для демонстрации функционала просмотра и записи видео источников.
              image
            13. WPFWebViewerEVR – простое C# приложение для демонстрации функционала просмотра видео источников через CaptureManager рендерер.
              image
            14. WPFWebViewerCallback – простое C# приложение для демонстрации функционала захвата кадров с видео источника через копирование из потока CaptureManager.
              image
            15. WPFWebViewerCall – простое C# приложение для демонстрации функционала захвата кадров с видео источника через прямой вызов методов CaptureManager.
              image
            16. WPFSourceInfoViewer – простое C# приложение для демонстрации функционала получения информации о доступных видео и аудио источников.
              image

          • PythonDemos:
            1. CaptureManagerSDKPythonDemo – простое Python приложение для демонстрации функционала просмотра и записи видео источников.
              image

          • QtMinGWDemos:
            1. CaptureManagerSDKQtMinGWDemo – простое С++ приложение на Qt для демонстрации функционала просмотра и записи видео источников.
              image

          • UnityDemos:
            1. UnityWebCamViewer — простое приложение для демонстрации функционала работы с видео источником в Unity3D.
              image


          Больше информации о проекте можно найти сайте CaptureManager SDK. На NuGet существует C# оболочка CaptureManager.
          Original source: habrahabr.ru (comments, light).

          https://habrahabr.ru/post/337190/


          Метки:  

          [Из песочницы] Исправляем 7 распространенных ошибок обработки исключений в Java

          Суббота, 09 Сентября 2017 г. 14:21 + в цитатник
          Привет, Хабр! Представляю вашему вниманию перевод статьи Fixing 7 Common Java Exception Handling Mistakes автора Thorben Janssen.

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

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

          https://habrahabr.ru/post/337536/


          Метки:  

          [Из песочницы] Исправляем 7 распространенных ошибок обработки исключений в Java

          Суббота, 09 Сентября 2017 г. 14:21 + в цитатник
          Привет, Хабр! Представляю вашему вниманию перевод статьи Fixing 7 Common Java Exception Handling Mistakes автора Thorben Janssen.

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

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

          https://habrahabr.ru/post/337536/


          Метки:  

          Учим webworkers хорошим манерам

          Суббота, 09 Сентября 2017 г. 14:18 + в цитатник
          Рано или поздно у каждого кто работал с webworkers возникает ситуация когда код превращается в кашу вроде этого:

          main.js
          const worker = new Worker('test.worker.js');
          worker.onmessage = (data) => {
            if(data.eventName === 'someFuncResult')
              someFunc();
            else if(data.eventName === 'someFunc2Result')
              someFunc2();
          };
          
          worker.postMessage({eventName: 'someFunc'});
          

          test.worker.js
          self.addEventListener('message', (data) => {
            if(data.eventName === 'someFunc') {
              doSomeFunc();
              self.postMessage('someFuncResult');
            }
            
           if(data.eventName === 'someFunc2') {
              doSomeFunc();
              self.postMessage('someFunc2Result');
            }
          })
          


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

          Цели:
          — Читаемость кода
          — Конкурентные запросы
          — Ассинхронные функции
          — Прозрачная обработка ошибок
          — Возможность отправки промежуточных результатов выполнения процедуры

          Читать дальше ->

          https://habrahabr.ru/post/337492/


          Метки:  

          Учим webworkers хорошим манерам

          Суббота, 09 Сентября 2017 г. 14:18 + в цитатник
          Рано или поздно у каждого кто работал с webworkers возникает ситуация когда код превращается в кашу вроде этого:

          main.js
          const worker = new Worker('test.worker.js');
          worker.onmessage = (data) => {
            if(data.eventName === 'someFuncResult')
              someFunc();
            else if(data.eventName === 'someFunc2Result')
              someFunc2();
          };
          
          worker.postMessage({eventName: 'someFunc'});
          

          test.worker.js
          self.addEventListener('message', (data) => {
            if(data.eventName === 'someFunc') {
              doSomeFunc();
              self.postMessage('someFuncResult');
            }
            
           if(data.eventName === 'someFunc2') {
              doSomeFunc();
              self.postMessage('someFunc2Result');
            }
          })
          


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

          Цели:
          — Читаемость кода
          — Конкурентные запросы
          — Ассинхронные функции
          — Прозрачная обработка ошибок
          — Возможность отправки промежуточных результатов выполнения процедуры

          Читать дальше ->

          https://habrahabr.ru/post/337492/


          Метки:  

          10 лет с Колибри: как это было

          Суббота, 09 Сентября 2017 г. 13:12 + в цитатник
          Обзор в честь моего 10-летия участия в проекте создания операционной системы Kolibri.

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

          image

          Читать дальше ->

          https://habrahabr.ru/post/337532/


          Метки:  

          10 лет с Колибри: как это было

          Суббота, 09 Сентября 2017 г. 13:12 + в цитатник
          Обзор в честь моего 10-летия участия в проекте создания операционной системы Kolibri.

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

          image

          Читать дальше ->

          https://habrahabr.ru/post/337532/


          Метки:  

          Использование Akka в Spring-приложении

          Суббота, 09 Сентября 2017 г. 12:32 + в цитатник
          Фреймворк Akka позволяет реализовать многопоточность в Java-приложении, используя концепцию акторов, взаимодействующих посредством посылки друг другу сообщений. Создав несколько копий акторов одного и того же типа, мы можем таким образом распределить нагрузку в приложении между несколькими потоками.

          В данной статье приводится пример использования Akka в Spring-приложении, что представляет некоторую сложность, поскольку в силу ее особенностей, акторов нельзя создавать посредством простого вызова new.
          Читать дальше ->

          https://habrahabr.ru/post/337524/


          Метки:  

          Использование Akka в Spring-приложении

          Суббота, 09 Сентября 2017 г. 12:32 + в цитатник
          Фреймворк Akka позволяет реализовать многопоточность в Java-приложении, используя концепцию акторов, взаимодействующих посредством посылки друг другу сообщений. Создав несколько копий акторов одного и того же типа, мы можем таким образом распределить нагрузку в приложении между несколькими потоками.

          В данной статье приводится пример использования Akka в Spring-приложении, что представляет некоторую сложность, поскольку в силу ее особенностей, акторов нельзя создавать посредством простого вызова new.
          Читать дальше ->

          https://habrahabr.ru/post/337524/


          Метки:  

          [Из песочницы] Параллелизм против многопоточности против асинхронного программирования: разъяснение

          Суббота, 09 Сентября 2017 г. 11:57 + в цитатник
          Хочу представить вашему вниманию перевод статьи Concurrency vs Multi-threading vs Asynchronous Programming: Explained.

          В последние время, я выступал на мероприятиях и отвечал на вопрос аудитории между моими выступлениями о Асинхронном программировании, я обнаружил что некоторые люди путали многопоточное и асинхронное программирование, а некоторые говорили, что это одно и тоже. Итак, я решил разъяснить эти термины и добавить еще одно понятие Параллелизм. Здесь есть две концепции и обе они совершенно разные, первая синхронное и асинхронное программирование и вторая – однопоточные и многопоточные приложения. Каждая программная модель (синхронная или асинхронная) может работать в однопоточной и многопоточной среде. Давайте обсудим их подробно.
          Читать дальше →

          https://habrahabr.ru/post/337528/


          Метки:  

          [Из песочницы] Параллелизм против многопоточности против асинхронного программирования: разъяснение

          Суббота, 09 Сентября 2017 г. 11:57 + в цитатник
          Хочу представить вашему вниманию перевод статьи Concurrency vs Multi-threading vs Asynchronous Programming: Explained.

          В последние время, я выступал на мероприятиях и отвечал на вопрос аудитории между моими выступлениями о Асинхронном программировании, я обнаружил что некоторые люди путали многопоточное и асинхронное программирование, а некоторые говорили, что это одно и тоже. Итак, я решил разъяснить эти термины и добавить еще одно понятие Параллелизм. Здесь есть две концепции и обе они совершенно разные, первая синхронное и асинхронное программирование и вторая – однопоточные и многопоточные приложения. Каждая программная модель (синхронная или асинхронная) может работать в однопоточной и многопоточной среде. Давайте обсудим их подробно.
          Читать дальше →

          https://habrahabr.ru/post/337528/


          Метки:  

          Поиск сообщений в rss_rss_hh_full
          Страницы: 1824 ... 1523 1522 [1521] 1520 1519 ..
          .. 1 Календарь