Angular 5: Unit тести

Angular 5: Unit тести

За допомогою unit тестів ми можемо переконатися, що окремі частини програми працюють саме так, як ми від них очікуємо.

Це в деякій мірі рятує від поломок існуючий код, допомагає прояснити – як він буде працювати в тих чи інших випадках. І, врешті-решт, дозволяє подивитися на код, так скажімо, з боку, щоб побачити його слабкі сторони.

Навіть існує думка, що складно тестований код – претендент на переписування.

Мета даної статті – допомогти в написанні unit тестів для Angular 5+ додатки. Нехай це буде захоплюючий процес, а не головний біль.

Ізольовані або Angular Test Bed?

Що стосується unit тестування Angular додатки, то можна виділити два види тестів:

  • Ізольовані – ті, які не залежать від Angular. Вони простіше в написанні, їх легше читати і підтримувати, так як вони виключають всі залежності. Такий підхід хороший для сервісів та пайпов.
  • Angular Test Bed – тести, в яких за допомогою тестової утиліти TestBed здійснюється настройка і ініціалізація середовища для тестування. Утиліта містить методи, які полегшують процес тестування. Наприклад, ми можемо перевірити, створився чи компонент, як він взаємодіє з шаблоном, з іншими компонентами і з залежностями.

Ізольовані

При ізольованому підході ми тестуємо сервіс як звичайнісінький клас з методами.

Спочатку створюємо екземпляр класу, а потім перевіряємо, як він працює в різних ситуаціях.

Перш ніж переходити до прикладів, необхідно позначити, що я пишу і запускаю тести за допомогою jest, так як сподобалася його швидкість. Якщо ви віддаєте перевагу karma + jasmine, то приклади для вас також будуть актуальні, оскільки відмінностей в синтаксисі зовсім небагато.

Jasmine / jest відмінності

jasmine.createSpy ( 'name') -> jest.fn () 
and.returnValue () -> mockReturnValue () 
spyOn (...). and.callFake (() => {}) -> jest. spyOn (...). mockImplementation (() => {})

Розглянемо приклад сервісу для модального вікна. У нього всього лише два методи, які повинні розсилати певне значення для змінної popupDialog. І зовсім немає залежностей.

import { Injectable } from '@angular/core';
import { ReplaySubject } from 'rxjs/ReplaySubject';

@Injectable()
export class PopupService {

  private popupDialog = new ReplaySubject<{popupEvent: string, component?, options?: {}}>();

  public popupDialog$ = this.popupDialog.asObservable();

  open(component, options?: {}) {
    this.popupDialog.next({popupEvent: 'open', component: component, options: options});
  }

  close() {
    this.popupDialog.next({popupEvent: 'close'});
  }

}

При написанні тестів не потрібно забувати про порядок виконання коду. Наприклад, дії, які необхідно виконати перед кожним тестом, ми поміщаємо в beforeEach. Так, створений в коді нижче екземпляр сервісу нам знадобиться для кожної перевірки.

import { PopupService } from './popup.service';
import { SignInComponent } from '../components/signin/signin.component';

describe('PopupService', () => {
  let service: PopupService;
  // создаем экземпляр PopupService
  beforeEach(() => { service = new PopupService(); });
  // done нужно, чтобы тест не завершился до получения данных
  it('subscribe for opening works', (done: DoneFn) => {
    // вызываем метод open
    service.open(SignInComponent, [{title: 'Попап заголовок', message: 'Успешно'}]);
    // при изменении значения popupDialog$ должен сработать subscribe
    service.popupDialog$.subscribe((data) => {
      expect(data.popupEvent).toBe('open');
      done();
    });

  });
  it('subscribe for closing works', (done: DoneFn) => {
    service.close();
    service.popupDialog$.subscribe((data) => {
      expect(data.popupEvent).toBe('close');
      done();
    });
  });
});

Angular Test Bed тести

Простий компонент

А тепер подивимося на всю потужність утиліти TestBed. Як приклад для початку візьмемо найпростіший компонент:

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

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'app';
}

Файл шаблону:

<h1>
  Welcome to {{ title }}!
</h1>

Файл тестів розберемо по шматочках. Для початку ставимо TestBed конфігурацію:

 beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        AppComponent
      ],
    }).compileComponents();
  }));

compileComponents – метод, який робить винесені в окремі файли стилі і шаблон вбудованими.

Цей процес є асинхронним, так як компілятор Angular повинен отримати дані з файлової системи.

Іноді compileComponents не потрібен

Якщо ви використовуєте WebPack, то цей виклик і метод async вам не потрібен. Справа в тому, що WebPack автоматично перед запуском тестів вбудовує зовнішні стилі і шаблон.

Відповідно, і при прописуванні стилів і шаблону всередині файлу компонента компілювати самостійно не треба.

Для тестів необхідно, щоб компоненти скомпілювати до того, як через метод createComponent () будуть створені їх екземпляри.

Тому тіло першого BeforeEach ми помістили в asynс метод, завдяки чому його вміст виконується в спеціальній асинхронної середовищі. І поки не буде виконаний метод compileComponents (), наступний BeforeEach не запуститься:

beforeEach(() => {
    fixture = TestBed.createComponent(AppComponent);
    comp = fixture.componentInstance;
  });

Завдяки винесенню в beforeEach всіх загальних даних, подальший код виходить значно чистіше.

Для початку перевіримо створення екземпляра компонента і його властивість:

it('should create the comp',  => {
  expect(comp).toBeTruthy();
});
it(`should have as title 'app'`, () => {
   expect(comp.title).toEqual('app');
});

Далі ми хочемо перевірити, що змінна компонента title вставляється в DOM. При цьому ми очікуємо, що їй присвоєно значення ‘app’. А це привласнення відбувається при ініціалізації компонента.

Запустивши за допомогою detectChanges CD цикл, ми инициализируем компонент.
До цього виклику зв’язок DOM і даних компонента не відбудеться, а отже тести не пройдуть.

it('should render title in a h1 tag', () => {
    fixture.detectChanges();
    const compiled = fixture.debugElement.nativeElement;
    expect(compiled.querySelector('h1').textContent)
      .toContain('Welcome to app!');
  });

Повний код тесту компонента

import { TestBed, async, ComponentFixture } from '@angular/core/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {

  let comp: AppComponent;
  let fixture: ComponentFixture;
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        AppComponent
      ],
    }).compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(AppComponent);
    comp = fixture.componentInstance;
  });

  it('should create the comp', () => {
    expect(comp).toBeTruthy();
  });
  it(`should have as title 'app'`, () => {
    expect(comp.title).toEqual('app');
  });
  it('should render title in a h1 tag', () => {
    fixture.detectChanges();
    const compiled = fixture.debugElement.nativeElement;
    expect(compiled.querySelector('h1').textContent)
      .toContain('Welcome to app!');
  });
});

Компонент з залежностями

Давайте усложним наш компонент, запровадивши в нього сервіс:

export class AppComponent {
  constructor(private popup: PopupService) { }
  title = 'app';
}

Начебто поки не особливо ускладнили, але тести вже не пройдуть. Навіть якщо ви не забули додати сервіс в providers AppModule.

Тому що в TestBed ці зміни теж потрібно відобразити:

TestBed.configureTestingModule({
      declarations: [
        AppComponent
      ],
      providers: [PopupService]
    });

Ми можемо вказати сам сервіс, але зазвичай краще замінити його на клас або об’єкт, який описує саме те, що нам необхідно для тестів.

Чому?

А ви уявіть сервіс з купою залежностей і вам все доведеться при тестуванні прописати. Не кажучи вже про те, що ми тестуємо в даному випадку саме компонент. Взагалі, тестувати щось одне – це як раз про unit тести.

Отже, прописуємо стаб наступним чином:

const popupServiceStub = {
    open: () => {}
};

Методи задаємо тільки ті, які тестуємо.

Якщо хочемо описати стаб як клас

class popupServiceStub {
    open() {}
}
providers: [{provide: PopupService, useClass: popupServiceStub } ]

У TestBed конфігурацію додаємо providers:

providers: [{provide: PopupService, useValue: popupServiceStub } ]

Не варто плутати PopupService і PopupServiceStab. Це різні об’єкти: перший – клон другого.

Відмінно, але ми ж сервіс впроваджували не просто так, а для використання:

ngOnInit() {
    this.popup.open(SignInComponent);
}

Тепер варто переконатися, що метод дійсно викликається. Для цього спочатку отримаємо екземпляр сервісу.

Так як в даному випадку сервіс заданий в providers кореневого модуля, то ми можемо зробити так:

popup = TestBed.get(PopupService);

А як ще?

Якби мова йшла про сервіс, який прописаний в providers компонента, то довелося б отримувати його так:

popup = fixture.debugElement.injector.get(PopupService);

Нарешті сама перевірка:

it('should called open', () => {
    const openSpy = jest.spyOn(popup, 'open');
    fixture.detectChanges();
    expect(openSpy).toHaveBeenCalled();
  });

Наші дії:

  1. Встановлюємо шпигуна на метод open об’єкта popup.
  2. Запускаємо CD цикл, в ході якого виконається ngOnInit з перевіряється методом
  3. Переконуємося, що він був викликаний.

Зауважте, що перевіряємо ми саме виклик методу сервісу, а не те, що він повертає або інші речі, що стосуються самого сервісу. Їх, щоб зберегти розум, варто тестувати в сервісі.

Сервіс з http

Зовсім недавно (в Angular 4) файл тестів сервісу з запитами міг виглядати воістину страхітливо.

Згадати, як це було

beforeEach(() => TestBed.configureTestingModule({
    imports: [HttpModule],
    providers: [
      MockBackend,
      BaseRequestOptions,
      {
        provide: Http,
        useFactory: (backend, defaultOptions) => new Http(backend, defaultOptions),
        deps: [MockBackend, BaseRequestOptions]
      },
      UserService
    ]
  }));

Втім, і зараз в інтернеті повно статей з цими прикладами.

А між тим розробники Angular не сиділи склавши руки, і ми тепер можемо писати тести набагато простіше. Просто скориставшись HttpClientTestingModule і HttpTestingController.

Розглянемо сервіс:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { ReplaySubject } from 'rxjs/ReplaySubject';

import { Game } from '../models/gameModel';
import { StatisticsService } from './statistics.service';

@Injectable()
export class GameService {
  gameData: Array;
  dataChange:  ReplaySubject;
  gamesUrl = 'https://any.com/games';

  constructor(private http: HttpClient, private statisticsService: StatisticsService) {
    this.dataChange  = new ReplaySubject();
  }

  getGames() {
    this.makeResponse()
      .subscribe((games: Array) => {
        this.handleGameData(games);
      });
  }

  makeResponse(): Observable {
    return this.http.get(this.gamesUrl);
  }
  handleGameData(games) {
    this.gameData = games;
    this.doNext(games);
    this.statisticsService.send();
  }

  doNext(value) {
    this.dataChange.next(value);
  }

}

Для початку описуємо всіх наших глобальних героїв:

let http: HttpTestingController;
let service: GameService;
let statisticsService: StatisticsService;
const statisticsServiceStub = {
    send: () => {}
};

Тут з цікавого – стаб statisticsService. Ми за аналогією з компонентом Стабія залежності, так як тісто зараз тільки конкретний сервіс.

Як бачите, я просто прописала саме те, що знадобиться в цьому тесті. Просто уявіть, що в StatisticsService насправді величезна кількість методів і залежностей, а використовуємо в даному сервісі ми тільки один метод.

Далі оголосимо дані, які будемо підкидати у відповідь на запит:

 const expectedData = [
    {id: '1', name: 'FirstGame', locale: 'ru', type: '2'},
    {id: '2', name: 'SecondGame', locale: 'ru', type: '3'},
    {id: '3', name: 'LastGame',  locale: 'en', type: '1'},
  ];

У TestBed необхідно імпортувати HttpClientTestingModule і прописати всі сервіси:

TestBed.configureTestingModule({
      imports: [
        HttpClientTestingModule,
      ],
      providers: [GameService, { provide: StatisticsService, useValue: statisticsServiceStub }]
    });

Наступний крок – отримання примірників всіх сервісів, які нам знадобляться:

service = TestBed.get(GameService);
    statisticsService = TestBed.get(StatisticsService);
    http = TestBed.get(HttpTestingController);

Не завадить відразу ж прописати в afterEach перевірку на те, що немає відкладених запитів:

afterEach(() => {
    http.verify();
});

І переходимо до самих тестів. Найпростіше, що ми можемо перевірити – створився чи сервіс. Якщо ви забудете в TestBed вказати будь-яку залежність, то цей тест не пройде:

it('should be created', () => {
    expect(service).toBeTruthy();
  });

Далі вже цікавіше – перевіряємо, що по очікуваному запитом отримаємо певні дані, які самі ж і підкидає:

it('should have made one request to GET data from expected URL', () => {

    service.makeResponse().subscribe((data) => {
      expect(data).toEqual(expectedData);
    });

    const req = http.expectOne(service.gamesUrl);
    expect(req.request.method).toEqual('GET');
    req.flush(expectedData);
  });

Не завадить перевірити ще й як працює ReplaySubject, тобто чи будуть відловлювати у передплатників отримані гри:

it('getGames should emits gameData', () => {

    service.getGames();

    service.dataChange.subscribe((data) => {
      expect(data).toEqual(expectedData);
    });
    
    const req = http.expectOne(service.gamesUrl);
    req.flush(expectedData);

  });

І нарешті останній приклад – перевірка, що statisticsService метод send буде викликаний:

 it('statistics should be sent', () => {
    const statisticsSpy = jest.spyOn(statisticsService, 'send');
    service.handleGameData(expectedData);
     expect(statisticsSpy).toHaveBeenCalled();
  });

Повний код тестів

import { TestBed } from '@angular/core/testing';

import { GameService } from './game.service';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { StatisticsService } from './statistics.service';

import 'rxjs/add/observable/of';

describe('GameService', () => {
  let http: HttpTestingController;
  let service: GameService;
  let statisticsService: StatisticsService;
  const statisticsServiceStub = {
    send: () => {}
  };

  const expectedData = [
    {id: '1', name: 'FirstGame', locale: 'ru', type: '2'},
    {id: '2', name: 'SecondGame', locale: 'ru', type: '3'},
    {id: '3', name: 'LastGame',  locale: 'en', type: '1'},
  ];


  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [
        HttpClientTestingModule,
      ],
      providers: [GameService, { provide: StatisticsService, useValue: statisticsServiceStub }]
    });

    service = TestBed.get(GameService);
    statisticsService = TestBed.get(StatisticsService);
    http = TestBed.get(HttpTestingController);

  });

  afterEach(() => {
    http.verify();
  });

  it('should be created', () => {
    expect(service).toBeTruthy();
  });

  it('should have made one request to GET data from expected URL', () => {

    service.makeResponse().subscribe((data) => {
      expect(data).toEqual(expectedData);
    });

    const req = http.expectOne(service.gamesUrl);
    expect(req.request.method).toEqual('GET');
    req.flush(expectedData);
  });


  it('getGames should emits gameData', () => {

    service.getGames();

    service.dataChange.subscribe((data) => {
      expect(data).toEqual(expectedData);
    });
    
    const req = http.expectOne(service.gamesUrl);
    req.flush(expectedData);

  });

  it('statistics should be sent', () => {
    const statisticsSpy = jest.spyOn(statisticsService, 'send');
    service.handleGameData(expectedData);
    expect(statisticsSpy).toHaveBeenCalled();
  });

});

Як полегшити тестування?

  1. Вибирайте той тип тестів, який підходить в даній ситуації і не забувайте про суть unit тестів
  2. Переконайтеся, що знаєте все можливості вашої IDE в плані допомоги при тестуванні
  3. При генерації сутностей за допомогою Angular-cli автоматично генерується і файл тестів
  4. Якщо в компоненті безліч таких залежностей, як директиви і дочірні компоненти, то можна відключити перевірку їх визначення. Для цього в TestBed конфігурації прописуємо NO_ERRORS_SCHEMA:
TestBed.configureTestingModule({
    declarations: [ AppComponent ],
    schemas:      [ NO_ERRORS_SCHEMA ]
  })

Переклад статті “Angular 5: Unit тесты

Підручник Webpack 4

Webpack 4

Команда розробників Webpack цей раз добряче попрацювала над новим поколінням популярного збирача модулів (Бандлера) – webpack 4.

Репозиторій з використовуваним кодом тут.

Webpack 4 як збирач модулів з нульовою конфігурацією

Ніхто не сперечається: у нього є потужні плюси, велика кількість можливостей і налаштувань, проте головним болем є файл конфігурації.

Написання конфіга не складає проблеми для середніх і великих проектів, без цього їм важко існувати. Проте, невеликі проекти це може дратувати, особливо якщо хочеться створити додаток-іграшку.

Шон і команда webpack поліпшили життя всім нам: webpack 4 більше не вимагає файлу конфігурації за замовчуванням!

Що ж, протестуємо.

Створіть нову директорію і перейдіть туди:

mkdir webpack-4-quickstart && cd $_

Ініціалізуйте package.json:

npm init -y

Тепер пускаємо в бій webpack 4 (Версія зараз знаходиться в стадії beta, тому потрібно додати next):

npm i webpack@next --save-dev

Додамо webpack-cli, що живе своїм життям в окремому пакеті:

npm i webpack-cli --save-dev

Відкриваємо package.json і прописуємо скрипт збірки:

"scripts": { 
   "build": "webpack" 
}

Збережіть файл, закрийте. запустіть:

npm run build

Що ж трапилося?

ERROR in Entry module not found: Error: Can't resolve './src' in '~/webpack-4-quickstart'

Webpack 4 шукає вхідні точку прикладання ./src! Якщо не знаєте, чому так вийшло, то опишу коротко: вхідні точка – це файл, з якого webpack починає збірку. У ранніх версіях потрібно було оголосити її безпосередньо в webpack.config.js.

Але починаючи з 4-ої версії вам не потрібно вказувати вхідні точку. Вона буде взята з ./src/index.js за замовчуванням!

Перевіримо. Створіть ./src/index.js:

console.log('Test');

Знову запустіть збірку:

npm run build

Ви отримаєте файл ~/webpack-4-quickstart/dist/main.js .

Невже нам не потрібно ставити і точку виходу теж? Саме! Ні точку входу, ні виходу. Тим більше, не потрібен файл конфігурації .

Я знаю, що для більшості це не дивно: сила webpack в поділі коду. Але повірте: нуль конфігурації прискорює розробку в рази.

Режими production і development

Режими production і development

Дуже часто можна зустріти поділу конфіга на кілька файлів.

Типовий проект може мати:

  • Конфігурацію для розробки (development), з webpack-dev-server і іншими іграшками розробників.
  • Конфігурація для продакшена з UglifyJSPlugin, картами сайту і іншим.

Поки великі проекти продовжують використовувати поділ конфігов, ми з webpack 4 зробимо все одним рядком.

Як так? Зустрічайте режими production і development.

Якщо ви звернете увагу на висновок npm run build, то побачите красиву помилку:

Опція 'mode' (режим) була задана. Увімкніть режим в 'development' або 'production', щоб застосувати настройки за замовчуванням.
Опція ‘mode’ (режим) була задана. Увімкніть режим в ‘development’ або ‘production’, щоб застосувати настройки за замовчуванням.

Що це означає? Будемо розбиратися. Відкрийте package.json і допишіть об’єкт скриптів, як показано нижче:

"scripts": {
    "dev": "webpack --mode development",
    "build": "webpack --mode production"
  }

І тепер спробуємо:

npm run dev

Погляньмо на ./dist/main.js. Що ви бачите? Так, я знаю, нудний бандл … не зменшений. А якщо:

npm run build

Що тепер? Файл зборки був зменшений. Так! Режим 'production' використовує всі види оптимізації самостійно. І там не тільки мінімізація.

З іншого боку режим розробника (development mode) оптимізує швидкість програми і нічого більше.

Таким чином, з 4-х webpack’ом ви можете змінювати всю збірку одним рядком. Просто додайте прапорець --mode і отримаєте результат абсолютно безболісно.

Переклад статті “Webpack 4 tutorial: All You Need to Know, from 0 Conf to Production Mode

Typescript. Властивості доступні тільки для читання

У версії Typescript 2.0 був доданий модифікатор readonly. Властивостями поміченим модифікатором readonly значення може бути присвоєно тільки в момент ініціалізації, або в конструкторі того ж класу. Будь-які інші присвоювання значення заборонені.

Давайте подивимося на приклад. Тут представлений простий тип Point, описуваний двома властивостями, доступними тільки для читання:

type Point = {
  readonly x: number;
  readonly y: number;
};

Тепер ми можемо створити об’єкт являє собою початок координат, і ініціалізувати x і y багатозначно 0:

const origin: Point = { x: 0, y: 0 };

Однак, так як властивості x і y позначені readonly, ми не можемо змінити їх значення згодом:

// Error: Left-hand side of assignment expression
// cannot be a constant or read-only property
origin.x = 100;

Більш реалістичний приклад

Наведений вище приклад може здатися надуманим, давайте розглянемо наступну функцію:

function moveX(point: Point, offset: number): Point {
  point.x += offset;
  return point;
}

Функція moveX не повинна змінювати властивість xпереданого об’єкта point. Компілятор TypeScript обов’язково почне лаятися, якщо ви спробуєте це зробити, так як властивість позначено модифікатором readonly. Замість цього moveX повинна повертати новий об’єкт зі зміненими значеннями:

function moveX(p: Point, offset: number): Point {
  return {
    x: p.x + offset,
    y: p.y
  };
}

Тепер компілятор буде щасливий, більше немає спроб привласнити значення властивостям поміченим readonly. Ми створили новий об’єкт, який ініціалізується за оновленими значеннями.

Властивості класу, доступні тільки для читання

Ви також можете застосовувати модифікатор readonlyдо властивостей описаних в класі. Тут представлений клас Circle з readonly полем radius і властивістю area, яке непрямим чином реалізує доступність тільки для читання, тому що не має сетера:

class Circle {
    readonly radius: number;

    constructor(radius: number) {
        this.radius = radius;
    }

    get area() {
        return Math.PI * this.radius ** 2;
    }
}

Зверніть увагою, що area обчислюється за допомогою оператора піднесення до степеня. І radius, і area доступні ззовні класу для читання, тому що жоден з них не позначений як private, але не для запису:

const unitCircle = new Circle(1);
unitCircle.radius;  // 1
unitCircle.area;    // 3.141592653589793

// Error: Left-hand side of assignment expression
// cannot be a constant or read-only property
unitCircle.radius = 42;

// Error: Left-hand side of assignment expression
// cannot be a constant or read-only property
unitCircle.area = 42;

Доступні тільки для читання поля інтерфейсів

Поля інтерфейсів також можуть позначені, як доступні тільки для читання. Наприклад, тип ReadOnlyArray<T> запобігає запис значень в описані властивості:

interface ReadonlyArray<T> {
  readonly length: number;
  // ...
  readonly [n: number]: T;
}

Наступне присвоювання буде невалідним:

const primesBelow10: ReadonlyArray<number> = [2, 3, 5, 7];

// Error: Left-hand side of assignment expression
// cannot be a constant or read-only property
primesBelow10[4] = 11;

Readonly vs іммутабельність

Модифікатор readonly – це частина системи типів TypeScript. Він використовується тільки компілятором для перевірки незаконних присвоєнні значень. Як тільки TypeScript код компілюється в JavaScript таке поняття, як readonl піде геть.

Так як модифікатор readonly – це тільки артефакт при компіляції, він не є захистом від присвоювання значень під час виконання коду. Проте це ще одна особливість TypeScript, яка допоможе вам писати правильний код, залишивши компілятору роботу по перевірці ненавмисних присвоєнь значень.

Переклад статті “TypeScript 2.0: Read-Only Properties

Angular оновився до версії 5.1

Вийшло невелике оновлення JS-фреймворка Angular 5.1. У список поліпшень входять стабільні версії Angular Material і CDK, підтримка Service Worker API в CLI і підтримка TypeScript 2.5.

Після виходу мажорного релізу Angular 5.0 минулого місяця команда оголосила про випуск коригуючого поновлення Angular 5.1.0, в якому з’явилося кілька дрібних фич і виправлені деякі помилки. Також був випущений Angular CLI 1.6 і представлена перша стабільна версія Angular Material.

Що нового?

  • стабільні версії Angular Material і CDK;
  • підтримка Service Worker API в CLI;
  • поліпшена підтримка Universal і AppShell в CLI;
  • доопрацьовані повідомлення про помилки декоратора;
  • підтримка TypeScript 2.5.

Повний список функцій і виправлень можна подивитися на сторінках Angular, Material, і CLI.

Стабільні версії Angular Material і CDK

Angular Material заснований на візуальному мовою Material Design від Google. Angular Material пропонує 30 компонентів для користувача інтерфейсу для створення додатків. Angular CDK являє собою набір компонентів для створення власних елементів, без необхідності вирішувати заново спільні завдання. Ці компоненти вже використовуються у виробництві поряд додатків Google, включаючи Google Analytics Suite , Google Cloud Platform Developer Console , і Google Shopping Express .

Починаючи з поточної версії, Angular Material буде дотримуватися тієї ж філософії SemVer, що і Angular. Основні версії Angular Material і Angular CDK виходитимуть одночасно з рештою платформою. Патч-релізи будуть випускатися з тижневими інтервалами, а другорядні випуски стануть доступні у міру готовності функцій.

Документація і посібник для початківців доступні на офіційному сайті. Також стежити за ходом роботи команди можна на сторінці проекту на GitHub.

Підтримка Service Worker в CLI 1.6

У Angular 5.0.0 була включена підтримка Service Worker, адаптованої для Angular-додатків. Angular CLI 1.6 підтримує функції, які залежать нову функцію. За твердженням команди розробників, використання @angular/service-worker може підвищити продуктивність завантаження додатків в браузерах, які підтримують даний API.

Детальніше про початок роботи c Angular Service Worker можна ознайомитися на сайті.

Покращена підтримка Universal і App Shell в CLI 1.6

З виходом Angular CLI 1.6 доопрацьована можливість впровадження Universal в існуючі проекти через Schematics. Також додана підтримка App Shell.

Angular Universal

Наступна команда дозволяє додати підтримку Universal в існуюче CLI-додаток:

ng generate universal

Це створить модуль Universal в поточному додатку і автоматично сконфигурирует файл angular-cli.json.

Створити Universal додаток:

ng build --app=

Підтримка App Shell

З додаванням підтримки архітектури App Shell, з’явилася можливість створити оболонку додатки, яка створить статичний «перший екран» під час завантаження програми.

Спочатку необхідно імпортувати RouterModule в NgModule додатки, а <router-outlet></router-outlet> в шаблон компонента програми. App Shell використовує маршрутизатор для рендера додатки.

Потім запускається команда:

ng generate app-shell [ --universal-app ] [ --route ]

Далі необхідно зібрати додаток використовуючи ng build і маршрут з вашого застосування має активізуватися в index.html файл.

Доопрацьовані повідомлення про помилки декораторів

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

Наприклад, виклик функції для створення шаблону не тримається

@Component({
  template: genTemplate()
})
export class MyComponent {}

Раніше це викликало б наступну помилку:

Error encountered resolving symbol values statically. Calling function ‘genTemplate’, function calls are not supported. Consider replacing the function or lambda with a reference to an exported function, resolving symbol MyComponent in components.ts, resolving symbol MyComponent in components.ts

Повідомлення про помилку було доопрацьовано і тепер пояснює джерело і причину проблеми:

component.ts(9,16): Error during template compile of 'MyComponent'.  Function calls are not supported in decorators but 'genTemplate' was called.

Підтримка TypeScript 2.5

Додана підтримка TypeScript 2.5. Цей випуск TypeScript включає кілька корисних розширених функцій. Оновити TypeScript можна, запровадивши таку команду:

yarn add typescript@'~2.5.3'or npm install typescript@'~2.5.3'.

Але дана дія не є обов’язковим, оскільки TypeScript 2.4 продовжує підтримуватися Angular 5.xx Підтримку TypeScript 2.6 команда планує додати в наступному оновленні.

Переклад статті “Angular 5.1 & More Now Available

Всі основні браузери тепер підтримують WebAssembly

Всі розробники веб-браузерів всього лише протягом 2 років змогли впровадити в свої продукти підтримку WebAssembly.

Робота над WebAssembly почалася в 2015 році, коли розробники основних веб-браузерів об’єднали зусилля для створення бінарного формату (байт-коду) для мережі Інтернет.

Переваги WebAssembly

Перевага байт-коду полягає в тому, що він в кілька разів менше в розмірі, ніж код, з якого він був скомпільований. Це означає, що бінарний формат може бути використаний для економії трафіку і збільшення швидкості завантаження сторінки сучасних сайтів і веб-додатків.

Інший плюс використання байт-коду полягає в легкості, з якою сучасні веб-браузери можуть його прочитати. Тим самим забезпечуються краще структурування формату даних і швидкий парсинг коду.

На додаток до всього вищесказаного, розробники зможуть писати код на С, С ++ або Rust і компілювати його в байт-код безпосередньо без необхідності проміжного перекладу в JavaScript-код.

Популярність

З моменту анонса єдиного бінарного формату WebAssembly став шалено популярний в індустрії онлайн-ігор. З його допомогою стало можливим створення більш просунутих ігрових движків, які могли конкурувати зі своїми настільними аналогами.

Саме тому розробники Mozilla оголосили WebAssembly фактором, що змінює правила гри в світі інтернету. Позитивні риси нового стандарту і потенційний прибуток, яку він міг принести, стали каталізатором всієї роботи.

Всі учасники в грі

Незважаючи на деякий скепсис щодо нового стандарту і його життєздатності, в жовтні 2016 року Google, Microsoft і Mozilla вже представили превью-версії своїх браузерів з підтримкою байт-коду. А в березні 2017 року роботу з впровадження стандарту була закінчена. Таким чином, браузери почали використовувати WebAssembly за замовчуванням, щоб протестувати його в реальних умовах.

Протягом літа 2017 року Firefox і Chrome стали першими великими браузерами, які отримали підтримку WebAssembly в своїх стабільних версіях. В Opera і Vivaldi також була впроваджена функціональність, як тільки вона була додана в стабільні версії Chromium. Одними з останніх, хто ввів WebAssembly в стабільні гілки браузерів Safari 11.0 і Edge, стали відповідно Apple і Microsoft.

В даний час стандарт має гучний успіх – він уже впроваджений у багатьох іграх Facebook завдяки потужним ігровим движкам, випущеним такими компаніями, як Unity і Epic. Однак WebAssembly вже зробив собі ім’я і в світі зловредів. Як стверджують експерти, Майнер криптовалюта, такі як Coinhive і CryptoLoot, не були б можливі без впровадження нового стандарту в усі браузери.

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

Переклад статті “All Major Browsers Now Support WebAssembly

Реліз бібліотеки TensorFlow 1.4

У TensorFlow 1.4 розробники додали кілька нових функцій, а також підтримку Dataset API і Estimator API.

Реліз бібліотеки TensorFlow 1.4

7 листопада компанія Google анонсувала вихід нової версії TensorFlow, яка включає в себе кілька нових функцій і підтримку нижчезгаданих API.

Keras

У TensorFlow 1.4 Keras переїхала з tf.contrib.kerasв ядро пакета tf.keras. Keras – це популярна бібліотека для машинного навчання, написана на Python. Вона включає в себе високорівневі API для спрощення реалізації ідей розробників. Бібліотека сумісна з функціональними можливостями TensorFlow, включаючи API Estimator. Розробники можуть створювати Estimator’и, використовуючи будь-яку модель Keras, за допомогою функції tf.keras.estimator.model_to_estimator.

Datasets

Google повідомила, що Dataset API також був доданий в ядро пакета tf.datatf.contrib.data). Цей API привносить підтримку генераторів Python. Розробники рекомендують використовувати його при створенні вхідних конвеєрів для моделей TensorFlow, оскільки він дозволяє використовувати більше можливостей, працює краще і простий у використанні. Google зосередиться на розвитку Dataset, а не інших аналогічних API.

Розподілені навчання і оцінка Estimator

У TensorFlow 1.4 була додана функція tf.estimator.train_and_evaluate, яка спрощує навчання, оцінку і експорт моделей Estimator. Вона дозволяє виконувати процеси навчання і оцінки як локально, так і розподілений.

Установка TensorFlow 1.4

Нову версію можна встановити за допомогою pip.

# Note: the following command will overwrite any existing TensorFlow
# installation.
$ pip install --ignore-installed --upgrade tensorflow
# Use pip for Python 2.7
# Use pip3 instead of pip for Python 3.x

Також розробники оновили документацію на сайті проекту.

Переклад статті “Announcing TensorFlow r1.4

Зустрічайте нову версію Angular – Angular 5

Ми раді оголосити версію 5.0.0 Angular, pentagonal-donut. Це основний реліз, що містить нові функції та виправлення помилок. Цей реліз продовжує концентруватися на тому, щоб зробити Angular меншим, швидшим і простішим у використанні.

Angular 5

Ось розбивка деяких найбільших змін у v5. Для повного списку перегляньте історію змін.

Oптимізатор збірки

Станом на 5.0.0, виробничі збірки, створені за допомогою CLI, за замовчуванням застосують оптимізатор збірки.

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

Оптимізатор збірки має дві основні завдання. По-перше, ми можемо відзначити частину вашої програми, оскільки pure це покращує струс сорочки, що забезпечується існуючими інструментами, видалення додаткових частин вашої програми, які не потрібні.

Друга справа, що робить оптимізатор збірки, полягає в тому, щоб видалити Angular декоратори з коду виконання програми. Декоратори використовуються компілятором, і не потрібні під час виконання, і їх можна видалити. Кожна з цих завдань зменшує розмір ваших пакетів JavaScript і збільшує швидкість завантаження вашої програми для ваших користувачів.

Angular Universal State Transfer API та підтримка DOM

Тепер ви можете легше розділити стан програми між сторонніми та клієнтськими версіями вашої програми.

Angular Universal – це проект, спрямований на те, щоб допомогти розробникам виконувати серверну обробку (SSR) кутових програм. Після того, як ви зробите свої Angular програми на сервері, а потім завантажите їх у горі сгенерированного HTML, ви можете додати підтримку скребків і сканерів, які не підтримують JavaScript, і ви можете збільшити сприйману продуктивність вашої програми.

У 5.0.0 команда додала ServerTransferStateModuleі відповідну BrowserTransferStateModule. Цей модуль дозволяє генерувати інформацію як частину вашого рендеринга на платформі-сервері, а потім перенести його на сторону клієнта, щоб ця інформація не потребувала регенерації. Це корисно для випадків, наприклад, коли ваша програма отримує дані через HTTP. Передаючи стан з сервера, це означає, що розробникам не потрібно робити другий HTTP-запит, як тільки програма передасть її клієнту. Документація для State Transfer має відбутися протягом наступних кількох тижнів.

Іншою зміною від команди Angular Universal є додавання Domino на платформер-сервер. Domino означає, що ми підтримуємо більше маніпуляцій DOM з коробки в контексті серверної сторони, покращуючи нашу підтримку сторонніх JS та компонентних бібліотек, які не відомі на стороні сервера.

Поліпшення компілятора

Ми вдосконалили Angular компілятор, щоб підтримувати поступову компіляцію. Це забезпечує швидке перебудову, особливо для виробничих збірок та збірок з AOT. Ми також додали функції для наших декораторів та дозволили поставити менші пакети, видаливши пробіл.

Перетворення TypeScript

Під капотом Angular компілятор тепер працює як перетворення TypeScript, що робить поступові перебудови значно швидше. TypeScript перетворення були новою функцією, представленою як частина TypeScript 2.3, що дозволяє нам зачепитись до стандартного контуру компіляції TypeScript.

Ви можете скористатись цим, запустивши ng serve прапорець AOT.

ng serve --aot

Ми рекомендуємо кожному спробувати це. Це стане типовим для майбутнього випуску CLI. Існують деякі відомі проблеми зі швидкістю з проектами з більш ніж тисячею компонентів. Ми хочемо бути впевненими, що проекти будь-якого розміру зазнають цих покращень.

Під час виконання інкрементного побудови AOT з https://angular.io, новий конпілятор компілятора зберігає 95% часу збирання (від більш ніж 40 секунд до менше 2 секунд на наших машинах розробки).

Нашою метою було зробити компіляцію AOT достатньо швидко, щоб розробники могли використовувати її для розробки, усунувши ті відмінності, які розробники іноді стикаються, коли вперше намагаються перейти до виробництва. Команда набрала свої 2-секундні інкрементні AOT перебудовувані цільові показники, і за замовчуванням вона буде перетворювати AOT у майбутній випуск CLI.

У рамках цього переходу на перетворення ми більше не потребуємо genDir і outDir змінилися: тепер ми завжди випромінюємо створені файли для пакетів в node_modules.

preserveWhitespaces

Історично вклади, нові рядки та пробіли у ваших шаблонах були точно відтворені та включені в вашу збірку компілятором. Тепер ви можете вибрати, чи зберегти пробіл, що йде від ваших компонентів та вашої програми.

Ви можете вказати це як частину декоратора кожного компонента, де на даний момент він за замовчуванням true.

@Component({
  templateUrl: 'about.component.html',
  preserveWhitespaces: false
}
export class AboutComponent {}

Або ви можете вказати його широке застосування у своєму tsconfig.json, де воно також за замовчуванням true.

{
  "extends": "../tsconfig.json",
  "compilerOptions": {
    "outDir": "../out-tsc/app",
    "baseUrl": "./",
    "module": "es2015",
    "types": []
  },
  "angularCompilerOptions": {
    "preserveWhitespaces": false
  },
  "exclude": [
    "test.ts",
    "**/*.spec.ts"
  ]
}

Загалом, специфікації на рівні компоненту перевищують загальні специфікації. Надалі команда сподівається на дефолт, щоб за замовчуванням false економити місце для розробників. Не турбуйтеся про свої <pre> теги, вони розумно обробляються.

Дізнайтеся більше про preserveWhitespaces на нашому сайті docs.

Покращена підтримка декоратора

Тепер ми підтримуємо expression lowering в декоратор для лямбда і вартості useValue, useFactory і data в об’єкті літералах. Це дозволяє використовувати значення, які можна обчислити лише під час виконання в декораторах для виражених полів.

Тепер ви можете використовувати лямбда замість названої функції, тобто ви можете виконати код, не впливаючи на ваш d.ts або ваш загальнодоступний API.

Component({
  provider: [{provide: SOME_TOKEN, useFactory: () => null}]
})
export class MyClass {}

Ми також знизимо вирази в рамках useValue.

Component({
  provider: [{provide: SOME_TOKEN, useValue: SomeEnum.OK}]
})
export class MyClass {}

Internationalized Number, Date та Currency Pipes

Ми створили нові Number, Date та Currency Pipes, які підвищують стандартизацію через браузери та усувають потребу в i18n polyfills.

У Angular ми покладалися на браузер, щоб надавати форматування Number, Date та Currency Pipes за допомогою API i18n браузера. Це призвело до того, що більшість розробників потребували заповнення polyfills, що означало, що користувачі бачили в різних веб-переглядачах суперечливі результати, і ми отримали коментарі, які звичайні формати (наприклад, Currency Pipes) не відповідали очікуванням розробника.

У 5.0.0 ми оновили pipes, щоб використовувати нашу власну реалізацію, спираючись на CLDR, щоб забезпечити велику підтримку та конфігурацію мов для будь-яких локалей, які ви хочете підтримати. Ми підготували документ, який порівнює поведінку pipes між v4 та v5.

Якщо ви не готові до нових pipes, ви можете імпортувати, DeprecatedI18NPipesModule щоб отримати доступ до старої поведінки.

Дізнайтеся більше про зміни в наших pipes i18n в журналі змін.

Замініть ReflectiveInjector за допомогою StaticInjector

Для того, щоб видалити ще більше поліфліків, ми замінили ReflectiveInjector на StaticInjector. Цей інжектор більше не вимагає Reflect polyfill, зменшуючи розмір програми для більшості розробників.

Раніше

ReflectiveInjector.resolveAndCreate(providers);

Після

Injector.create(providers);

Покращення швидкості Zone

За умовчанням ми зробили Zone швидше, і ми дозволили повністю обходити Zone для програм, орієнтованих на продуктивність.

Щоб обійти Zone, завантажте вашу програму з “noop” як ваш ngZone.

platformBrowserDynamic().bootstrapModule(AppModule, {ngZone: 'noop'}).then( ref => {} );

Для повного прикладу, перегляньте приклад проекту ng-component-state.

exportAs

Ми додали підтримку для декількох імен для ваших компонентів / директив. Це може бути дуже корисним для того, щоб допомогти користувачам мігрувати без порушення змін. Виводячи директиву з декількома іменами, ви можете створювати нові імена в Angular microsyntax, не порушуючи існуючий код. Це було використано як частина проекту Angular Material при міграції префікса, а також може допомогти іншим авторам компонентів.

Приклад

@Component({
  moduleId: module.id,
  selector: 'a[mat-button], a[mat-raised-button], a[mat-icon-button], a[mat-fab], a[mat-mini-fab]',
  exportAs: 'matButton, matAnchor',
  .
  .
  .
}

HttpClient

У версії 4.3 ми поставили HttpClient @angular/common як менший, простий та потужний спосіб зробити веб-запити на Angular. Новий HttpClient отримав велику похвалу від розробників, тому ми зараз рекомендуємо HttpClient для всіх програм і припиняємо попередню @angular/http library.

Для поновлення HttpClient, вам необхідно замінити HttpModule з HttpClientModule від @angular/common/http кожного з ваших модулів, вводять послугу HttpClient і видаліть всі map(res => res.json()) виклики, які більше не потрібно.

CLI v1.5

Починаючи з v1.5 Angular CLI, ми додали підтримку для Angular v5.0.0 і за замовчуванням генеруємо v5 проекти.

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

Ми також оновлюємо спосіб, яким ми використовуємо .tsconfig файли для більш точного дотримання стандартів TypeScript. Раніше, коли ми виявили лінький завантажений маршрут, і ви вручну вказали список files або include у вашому списку tsconfig.json, ми автоматично додамо ці маршрути, але тепер ми дотримуємось специфікації TypeScript і далі виконуємо це. За замовчуванням CLI налаштовує TypeScript без files або include розділів, тому більшість розробників не будуть залежати від цього.

Angular Forms додає updateOn Blur / Submit

Тепер ви можете запускати перевірку та оцінку оновлень на blur або на submit, а не на кожній вхідній події.

Форми є дуже важливою частиною багатьох застосувань, і якщо у вас є якісь перевірки на стороні сервера або будь-які більш важкі процеси, що спрацьовують при перевірці або змінах значення, які ви хочете виконувати рідше, тепер ви можете контролювати зміну валідації та значення час на контрольному рівні або вкажіть його для всієї форми.

Крім того, тепер ви можете вказати asyncValidators безпосередньо в об’єкті параметрів, а не вказувати його як третій параметр.

Шаблонні форми

до

<input name="firstName" ngModel>

після

<input name="firstName" ngModel [ngModelOptions]="{updateOn: 'blur'}">

або

<form [ngFormOptions]="{updateOn: 'submit'}">

Реактивні форми

до

new FormGroup(value);
new FormControl(value, [], [myValidator])

після

new FormGroup(value, {updateOn: 'blur'}));
new FormControl(value, {updateOn: 'blur', asyncValidators: [myValidator]})

RxJS 5.5

Ми оновили наше використання RxJS до версії 5.5.2 або пізнішої версії. Цей нещодавній випуск RxJS повністю надає розробникам можливість уникнути побічних ефектів від попереднього механізму імпорту з новим способом використання RxJS під назвою “lettable operators“. Ці нові оператори усувають побічні ефекти та проблеми розбиття коду / дерева, які існували за допомогою попереднього методу імплементації операторів.

Замість

import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/filter';
names = allUserData
.map(user => user.name)
.filter(name => name);

Тепер ти можеш

import { Observable } from 'rxjs/Observable';
import { map, filter } from 'rxjs/operators';
names = allUserData.pipe(
  map(user => user.name),
  filter(name => name),
);

Крім того, RxJS тепер розподіляє версію за допомогою модулів ECMAScript. Новий кутовий CLI буде запускати цю версію за замовчуванням, значно заощаджуючи розмір розшарування. Але якщо ви не використовуєте Angular CLI, ви все одно повинні вказувати на новий розповсюдження. Документацію можна знайти в розділі “Build and Treeshaking” документації операторів, що надають путівник.

Нові події життєвого циклу маршрутизаторів

Ми додали маршрутизаторам нові етапи життєвого циклу, що дозволяє розробникам відстежувати цикл маршрутизатора від початку роботи охоронців до завершення активації. Ці події можуть бути використані для таких речей, як показує обертання на певному outlet маршрутизатора, коли child оновлюється, або для вимірювання ефективності guards та / або resolvers.

Нові події (в послідовності) є GuardsCheckStart, ChildActivationStart, ActivationStart, GuardsCheckEnd, ResolveStart, ResolveEnd, ActivationEnd, ChildActivationEnd. Приклад використання цих подій для запуску / зупинки поворотника може виглядати наступним чином:

class MyComponent {
  constructor(public router: Router, spinner: Spinner) {
    router.events.subscribe(e => {
      if (e instanceof ChildActivationStart) {
        spinner.start(e.route);
      } else if (e instanceof ChildActivationEnd) {
        spinner.end(e.route);
      }
    });
  }
}

Як я можу оновити?

Ми створили керівництво по оновленню Angular, щоб допомогти вам проаналізувати цей процес і дізнатись про будь-які зміни, які ви хочете зробити у своєму коді перед початком процесу оновлення, інструкціями щодо оновлення додатка та інформацією про підготовку майбутніх версій Angular.

Ми видалили багато застарілих інтерфейсів API (як OpaqueToken) і випустили кілька нових застарілих API. Цей посібник допоможе вам проаналізувати зміни, які вам доведеться внести до вашої програми.

Відомі проблеми

https://github.com/angular/angular/issues/19840

Переклад статті “Version 5.0.0 of Angular Now Available

React з використанням ряду особливостей ES6+

За цей рік, в процесі реорганізації Instagram Web, ми насолодилися використанням ряду особливостей ES6+, при написанні нашх React компонентів. Дозвольте мені зупинитися на тих моментах, коли нові можливості мови можуть вплинути на те як ви пишете React додатки, і зроблять цей процес легше і веселіше, ніж коли-небудь.

Класи

До цих пір найбільш помітним з видимих змін в тому, як ми пишемо наші React компоненти, використовуючи ES6 + є те, що ми вирішили використовувати синтаксис визначення класу. Замість того щоб використовувати метод React.createClass для визначення компонента, ми можемо визначити справжній ES6 клас, який розширює клас React.Component:

class Photo extends React.Component {
  render() {
    return <img alt={this.props.caption} src={this.props.src} />;
  }
}

Ви відразу ж ви помітите невелике розходження – вам стає доступним більш лаконічний синтаксис визначення класу:

// The ES5 way
var Photo = React.createClass({
  handleDoubleTap: function(e) { … },
  render: function() { … },
});
// The ES6+ way
class Photo extends React.Component {
  handleDoubleTap(e) { … }
  render() { … }
}

Варто зазначити, що ми відкинули дві дужки і завершальну крапку з комою, а для кожного оголошеного методу ми опускаємо двокрапка, ключове слово function і кому.

Всі методи життєвого циклу компонента, крім одного можуть бути визначені, як можна було б очікувати, з використанням нового синтаксису визначення класів. Конструктор класу в даний час виступає в ролі раніше використовуваного методу componentWillMount:

// The ES5 way
var EmbedModal = React.createClass({
  componentWillMount: function() { … },
});
// The ES6+ way
class EmbedModal extends React.Component {
  constructor(props) {
    super(props);
    // Operations usually carried out in componentWillMount go here
  }
}

Ініціалізатор властивостей

У світі класів ES6 +, типи властивостей і значення за замовчуванням можуть існувати як статичні властивості цього класу. Ці змінні, а також початковий стан компонента, можуть бути визначені з використанням ES7 ініціалізаторів властивостей:

// The ES5 way
var Video = React.createClass({
  getDefaultProps: function() {
    return {
      autoPlay: false,
      maxLoops: 10,
    };
  },
  getInitialState: function() {
    return {
      loopsRemaining: this.props.maxLoops,
    };
  },
  propTypes: {
    autoPlay: React.PropTypes.bool.isRequired,
    maxLoops: React.PropTypes.number.isRequired,
    posterFrameSrc: React.PropTypes.string.isRequired,
    videoSrc: React.PropTypes.string.isRequired,
  },
});
// The ES6+ way
class Video extends React.Component {
  static defaultProps = {
    autoPlay: false,
    maxLoops: 10,
  }
  static propTypes = {
    autoPlay: React.PropTypes.bool.isRequired,
    maxLoops: React.PropTypes.number.isRequired,
    posterFrameSrc: React.PropTypes.string.isRequired,
    videoSrc: React.PropTypes.string.isRequired,
  }
  state = {
    loopsRemaining: this.props.maxLoops,
  }
}

Ініціалізатор властивостей ES7 працюють усередині конструктора класу, де this відноситься до поточного екземпляру класу перед його створенням. Завдяки цьому, початковий стан компонента може залежати від this.props. Примітно, що ми більше не повинні визначати значення props за замовчуванням і початковий стан об’єкта в термінах getter функції.

Arrow функції

Метод React.createClass використовується для виконання деяких додаткових робіт по прив’язці до методів екземпляра компонента, щоб переконатися, що всередині них, ключове слово this буде ставитися до примірника компонента.

// Autobinding, brought to you by React.createClass
var PostInfo = React.createClass({
  handleOptionsButtonClick: function(e) {
    // Here, 'this' refers to the component instance.
    this.setState({showOptionsModal: true});
  },
});

Так як ми не пов’язані використанням методу React.createClass, при визначенні компонентів синтаксисом класів ES6+, здавалося б, що нам потрібно вручну прив’язати методи екземпляра, туди де ми хочемо їх використовувати:

// Manually bind, wherever you need to
class PostInfo extends React.Component {
  constructor(props) {
    super(props);
    // Manually bind this method to the component instance...
    this.handleOptionsButtonClick = this.handleOptionsButtonClick.bind(this);
  }
  handleOptionsButtonClick(e) {
    // ...to ensure that 'this' refers to the component instance here.
    this.setState({showOptionsModal: true});
  }
}

На щастя, шляхом об’єднання двох можливостей ES6+ – arrow функції і ініціалізатор властивостей – відмова від прив’язки до примірника компонента стає дуже легким:

class PostInfo extends React.Component {
  handleOptionsButtonClick = (e) => {
    this.setState({showOptionsModal: true});
  }
}

Тіло ES6 arrow функцій використовує той же лексичне this як і код, який її оточує. Це дозволяє нам досягти бажаного результату через те, що ES7 ініціалізатор властивостей знаходяться в області видимості. Загляньте під капот, щоб зрозуміти чому це працює.

Динамічні імена властивостей і шаблонів рядки

Одним з удосконалень литералов об’єктів включає в себе можливість призначати похідні імена властивостями. Спочатку ми могли б зробити щось подібне для установки деякої частини стану компонента:

var Form = React.createClass({
  onChange: function(inputName, e) {
    var stateToSet = {};
    stateToSet[inputName + 'Value'] = e.target.value;
    this.setState(stateToSet);
  },
});

Тепер у нас є можливість створювати об’єкти, в яких імена властивостей визначаються виразом JavaScript під час виконання. Тут ми використовуємо шаблонні рядки, для того щоб визначити, яке властивість встановити в стані компонента:

class Form extends React.Component {
  onChange(inputName, e) {
    this.setState({
      [`${inputName}Value`]: e.target.value,
    });
  }
}

Деструктуризація і поширення атрибутів

Часто при створенні компонентів, ми могли б передати більшість властивостей батьківського компонента до дочірньому компоненту, але не всі з них. У поєднанні ES6 + деструктурірованіе і поширення атрибутів JSX, це стає можливим без танців з бубном:

class AutoloadingPostsGrid extends React.Component {
  render() {
    var {
      className,
      ...others,  // contains all properties of this.props except for className
    } = this.props;
    return (
      <div className={className}>
        <PostsGrid {...others} />
        <button onClick={this.handleLoadMoreClick}>Load more</button>
      </div>
    );
  }
}

Так само ми можемо поєднувати JSX поширення атрибутів з регулярними атрибутами, користуючись простим правилом пріоритету для реалізації перевизначення значень і установки значень атрибута за замовчуванням. Цей елемент отримає перевизначення значення атрибута className, навіть якщо властивість className існує в this.props:

<div {...this.props} className="override">
  …
</div>

Атрибут className цього елемента за замовчуванням має значення «base», якщо не існує властивості className в this.props щоб перевизначити його:

<div className="base" {...this.props}>
  …
</div>

Переклад статті “React on ES6+

Mozilla, Microsoft, Google, Samsung і W3C створили єдиний портал документації для веб-розробників

Microsoft, Google, W3C, Samsung і Mozilla об’єдналися для створення єдиного порталу кросбраузерної документації. Метою даної ініціативи є спрощення роботи розробників.

mdn (більше…)

Минуле, теперішнє та майбутнє Angular CLI

Angular CLI являє собою інтерфейс командного рядка для створення Angular додатків, і протягом останнього року він став одним з найбільш важливих інструментів в арсеналі Angular розробника.

Angular CLI

CLI несе відповідальність за автоматизацію вирішення багатьох проблем та головних болів, які стають розробниками в 2017 році. Починаючи з конфігурації, яка працює з коробки, випікання в найкращих практиках, які були відкриті спільнотою з часом, і допомагаючи вам створювати більше підтримуваний код, CLI виставляє деякі з найпотужніших можливостей Angular таким чином, що простіше для початку, і простіше швидко рухатися, коли порівнюєте все, що складається з собою.

Якщо ви не користуєтеся CLI сьогодні, це легко почати. Якщо ви встановили node та npm, ви можете запустити ці чотири команди, щоб швидко вставати та працювати з Angular та CLI.

npm install —g @angular/cli
ng new my-angular-project
cd my-angular-project
ng serve

Швидко рухється

Angular CLI зробив значний прогрес протягом останніх декількох місяців, ви можете переглянути всеосяжні вказівки про цей прогрес на сторінці випуску , але це може бути корисним, якщо переглянути, що сталося за останні 4 місяці.

1.2 – червень 2017р

– мінімальний прапор

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

  • Вбудовані стилі – стилі зберігаються безпосередньо в файлах компонентів
  • Вбудовані шаблони. Шаблони зберігаються безпосередньо в файлах компонентів
  • Немає тестів – немає підрозділів або закінчення тестів, сформованих проектом

Названі bundles

Коли ви використовуєте Lazy Loading with Angular, ми створимо набір файлів, які завантажуватиметься Angular Router тільки тоді, коли це буде потрібно для певного маршруту, який вимагає користувач.

При створенні виробничих робіт це, в кінцевому підсумку, називається цифровим і послідовним з процесу побудови.

0.46af283b5453c1d30d30.chunk.js 
1.2b2a029972b8935a384a.chunk.js

Тепер із зазначеними розшаруваннями ми будемо використовувати назву модуля як підказку в рамках процесу побудови, щоб допомогти у налагодженні непробних збірок.

admin.module.chunk.js 
home.module.chunk.js

Дедупликація ліцензій та виділення

У Angular та багатьох сторонніх проектах часто є сотні окремих файлів з ліцензіями. Історично наш CLI допоміг розробникам залишатися сумісними, об’єднавши ці ліцензії як частину процесу комплектування.

Тепер, щоб максимально скоротити зв’язки, ми 3rdpartylicenses.txt вилучаємо ці ліцензії в окремий файл і видаляємо всі дублікати ліцензій.

1.3 – серпень 2017 р

Покращення розміру пакета

З кожним випуском ми намагаємось покращити якість коду, який створює CLI з виробничими збірками. З цим випуском ми використовуємо найновіші можливості Webpack 3, включаючи можливості підняття обсягу.

Ми також включили бета-версію нового інструменту, над яким ми працюємо, щоб допомогти вам видалити код, на який ви не залежите, називається нашим оптимізатором збірки. Ви можете дати цей прапор спробувати з:

ng build --prod --build-optimizer

З нашої власної роботи на angular.io, ми побачили зменшення розміру 52% у виробництві нашої продукції.

Angular Universal

Візуалізація на стороні сервера – це чудовий спосіб збільшити сприйнятну продуктивність вашої програми, а також зробити роботу програми кращою для сканерів, як Twitter або Facebook, які вискакують ваш веб-сайт, коли користувачі намагаються поділитися своїм вмістом.

Angular Universal був випущений з v4 Angular і включає в себе всі основні API, необхідні для відтворення вашої Angular програми на сервері. Універсальна підтримка в CLI полегшує це, дозволяючи вам створити другу програму CLI через .angular-cli.json конфігурацію, яка обертає вашу основну програму та дає вам можливість генерувати server пакет.

Початкова підтримка ранніх бета-версій v5

Angular робить основні випуски приблизно кожні 6 місяців. Оскільки все, що робить команда, – це Open Source, нові вузлові випуски, як правило, не мають багато функцій рано в процесі бета-версії, але вони можуть бути цікавими для випробувань, і команда сподівається отримати відгук від ранніх користувачів.

Спробувати та перевірити майбутні релізи Angular, сподіваюся, ви можете спробувати і повідомити нам, що ви думаєте. Версія 5.0.0 для Angular вимагатиме пізнішої версії CLI.

1.4 – вересень 2017 р

Схеми

Одним із головних проектів команди CLI є створення схеми. Схематизація дозволяє запускати довільний код для виконання перетворень дерева як спосіб автоматичного написання коду та створення файлів. Ці можливості дозволяють створювати свої власні шаблони для ng new або ng generate специфічні для вашого стилю або компанії, побудувати на шаблонах ми пропонуємо для вас, або ви можете зробити більш складні речі, такі як генерація коду на підставі форми і відповідей віддаленого API.

Схеми

Майбутнє CLI

Команда, що працює над CLI, має багато планів щодо полегшення життя розробників. Ось декілька ідей, котрі сподіваються (але не обіцяють) включити в майбутні випуски:

  • Попередній режим створення компіляції за замовчуванням – команда Angular робить багато роботи, щоб зробити компіляцію швидше, і як тільки компілятор зможе досягти аналогічного рівня продуктивності до того, що ми бачимо сьогодні, ми перевернемо перемикач і завжди будемо будувати з AOT включено. Це дозволить усунути проблеми, з якими розробники стикаються з різницями між цими двома режимами.
  • ng update - Angular робить патч, незначні та основні випуски на дуже регулярній основі. Інтерфейс ІР може мати повноваження, щоб допомогти вам у курсі. Команда Angular працює над інструментами для матеріалу та інтернаціоналізацією, щоб допомогти автоматизувати процес оновлення, піклуючись про внесення змін для вас, і CLI буде чудовим місцем для обробки цих інструментів.
  • Підтримка бібліотек – сьогодні CLI може створювати UMD, оптимізовані для браузера, а також комбінації CommonJS, оптимізовані для сервера. Що робити, якщо CLI може допомогти вам створити зв’язок, який може бути використаний іншими вузловими програмами?
  • За замовчуванням оптимізатор Build – Оптимізатор Build є дійсно перспективним інструментом, який може допомогти розробникам завантажувати в браузер різко менші розшарування. Після того, як цей інструмент стане твердим і підтверджено спільнотою, за замовчуванням ми ввімкнемо його.
  • Service Worker за замовчуванням. Ми вважаємо, що допомога розробникам у створенні чудових веб-програм часто включає доставку вашої програми як прогресивного веб-додатка (PWA) з працівником служби підтримки. Ми вважаємо, що ці інструменти можна включити за умовчанням, не порушуючи очікувань розробників щодо загальних завдань, таких як видалення або оновлення, і сподіваємося це зробити в майбутньому.

Переклад статті “The Past, Present, and Future of the Angular CLI