Присоединяйтесь к новому бесплатному курсу по Angular: "Список пользователей"
Курс проходит в мессенджере (Telegram, Viber). В конце курса - сессия живого разбора кода. Регистрация по ссылке.

1 Май 2017

React vs Angular 2 — сравнить несравнимое?

Как известно, React сильно набрал популярность в 2016 году. Многие говорят, что 2017 год будет годом Angular 2 – полностью обновленного и очень популярного фреймворка от Google. Я считаю, что эти две технологии имеют сходства, поэтому я решил сравнить их.

Разработчики Angular 2 советуют использовать синтаксис ES6, поэтому я буду сравнивать Angular 2 с самой последней версией React (комбинированный с Babel для синтаксиса ES6 и JSX синтаксис для шаблонов). В своем сравнении я попытаюсь показать основные сходства и отличия, а Вам уже решать какая библиотека лучше для Вас.

А можно ли сравнивать React и Angular 2?

Отличный вопрос! Многие могут отметить: » ReactJS это всего лишь библиотека, а Angular 2 полноценный фреймворк! Их невозможно сравнивать…». Я с этим полностью согласен. Но давайте не будем уж столь принципиальны и все-таки начнем наше исследование.
Выберем один из двух способов сравнения: мы можем сравнить все элементы React и Angular 2 или же целый фреймворк Angular 2 и ReactJS как библиотеку – при создании приложения с помощью React вы будете также пользоваться вспомогательными ресурсами – react-router, Redux, Immutable и другие. Я выберу второй вариант и постараюсь следовать ему сегодня.

Компонентная модель

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

Вот как это выглядит у ReactJS:


import React from 'react';
import 'ExampleComponent.css';

export default class ExampleComponent extends React.Component {
constructor(props) {
super(props);
this.state = {message: 'Hello from component!'};
}

render() {
return(
<div className="container">
<p>{this.state.message}</p>
</div>
);
}
}


Теперь давайте посмотрим как выглядит компонент на Angular2:

import { Component } from 'angular2/core';

@Component({
selector: 'component-placeholder',
templateUrl: 'app/example.template.html',
styleUrls: [ app/example.css ],
})

export class ExampleContent {
public message = 'Hello from component!';
}

Как видно в обоих примерах используются JavaScript классы (мы использовали ES6 – о нем я расскажу позже).
Здесь главное различие в том, что в React мы расширяем базовый объект Component, а в Angular 2 мы используем decorator @Component. В React вам нужно просто создать компонент который вы будете использовать в дальнейшем как обычный HTML элемент (это все преимущество синтаксиса JSX – на этом я также остановлюсь немного позже). В Angular 2 все наоборот – вам необходимо создать placeholder в HTML разметке и потом сообщить компоненту где его вставить (используя свойство селектора).
Еще одно различие – в Angular 2 мы можем расположить шаблоны в отдельных файлах как и в предыдущей версии фреймворка (так же можно описать template в виде строки). А в React мы объявляем template компонента в скрипте используя синтаксис JSX.
Давайте на этом остановимся более подробно. Некоторые разработчики считают, что разделение шаблона и JavaScript кода лучше, потому что таким образом мы разделяем концепции. Лично у меня есть опыт в Angular, и когда я столкнулся с React мне показались компоненты React слишком замусоренные – в них смешивался HTML шаблон и Javascript код. Теперь у меня на счет этого появляются сомнения. Я думаю мне нужно написать несколько приложений на Angular 2 и потом решить, что все-таки лучше для меня.

Шаблоны и привязка данных

Отлично, теперь мы немного знаем о компонентах в каждой из технологий, самый раз обратить внимание на их шаблоны. Как говорилось ранее, в React мы описываем всю логику приложения непосредственно в методах и классах. Такую возможность дает нам синтаксис JSX. Но его использовать не обязательно, все можно писать и на чистом JavaScript.
Синтаксис JSX позволяет собирать компоненты приложения и HTML компоненты по частям.

Также JSX является частью «виртуального DOM», одной из наиболее интересных концепций React. Когда React начинает рендерить компоненты в браузере, он сразу создает их в виртуальной память библиотеки. Каждый раз когда React перестраивает компоненты он сравнивает виртуальный DOM с реальным DOM элементом и меняет только то что изменилось.
В Angular 2 все наоборот – шаблоны HTML представлены в отдельном файле. Рендеринг шаблонов происходит в реальном времени и каждое изменение UI происходит с помощью манипуляций в DOM элементе.
Говоря об изменениях в DOM не нужно забывать о привязке данных (data binding). В Angular 2 также есть двусторонний data-binding который вы помните с предыдущей версии фреймворка. Это значит, что шаблоны должны выглядеть примерно, так как:


<h1>Two way binding:</h1>
<div>{{message}}</div>
<label for="new-mesage">Type: message</label>
<input type="text" [(ngModel)]="message" id="new-message" />

<h1>Lists:</h1>
<ul>
<li ngFor="#item of nameList">
{{item.name}}
</li>
</ul>

<h1>Directives:</h1>
<custom-directive>Loading...</custom-directive>


Посмотрим на строку номер 2. Message – это свойство самого компонента. Его значение привязано к элементу div с помощью двойных фигурных скобок, таким образом, достигается односторонняя привязка данных – в этом месте мы можем только получить значение свойства. В строке 4 такая же привязка свойства к input элементу, но с помощью двустороннего data-binding (благодаря аттрибуту [ngModel]) – набирая текст внутри input поля, значение свойства в самом компоненте также меняется. В этом и есть сила двустороннего data-binding.
Ok, как же это выглядит в React? Во-первых там нет two-way data binding – там отображается реальное значение элемента. Давайте разберем как происходит рендеринг у React:

render() {
return (

{this.state.message}
);
}

Как видите, в строке 4 для привязки данных мы используем одинарные фигурные скобки. Я также создал свойство message в объекте state. Это важно, потому что при каждом изменении значения свойства (каждый раз с помощью метода SetState) React повторно вызывает рендеринг компонента. Это меняет виртуальный DOM в памяти, который потом сравнивается с реальным DOM элементом и если есть различия, то они будут внесены в реальный DOM.
Звучит очень просто. Но так оно и есть – все очень просто! Единственный недостаток во всем этом – ничего не происходит само собой, нам приходится писать больше кода по сравнению с двусторонней привязкой данных, потому что в React все нужно изменять вручную.

ECMAScript 6

И ReactJS и Angular 2 поддерживают синтаксис ES6 – компоненты в обеих технологиях – это классы. Но есть и отличия – для React мы обычно используем Babel для транспиляции кода с ES6 в ES5.
Для Angular 2 нам советуют использовать TypeScript, который является частью синтаксиса ES6 и добавляет статическую типизацию в язык. Вы, конечно же можете спорить будет ли это правильным решением – отсутствие статической типизации в JavaScript это главное его достоинство, так почему же не использовать это преимущество?
Теоретически вы можете использовать Angular 2 с чистым скриптом, но я думаю, что вы не станете этого делать, по той причине что большинство примеров в сети написаны на TypeScript.

Давайте посмотрим на примеры кода React – много примеров написаны на чистом ES5, но много и на ES6. Есть те, кто используют JSX, но и те кто им не пользуются. Это может сбить с толку новичков…

Routing

Это роутинг есть частью Angular 2, но в мире React эту функцию выполняют внешние библиотеки. Самой популярной является react-router, поэтому я буду говорить о ней.
Давайте начнем. Вот как объявляется маршрут (route) при использовании данной библиотеки:

ReactDOM.render(
<Router history={browserHistory}>
<Route path="/" component={App}>
<Route path="about" component={About}/>
<Route path="users" component{Users}>
<Route path="/user/:userId" component{User}/></Route>
<Route path="*" component={NoMatch}/></Route>
</Router>,
document.getElementById('app')
);


Обычно код добавляется вначале React application. Как вы видите маршруты (route) настраиваются с помощью компонента Route. Каждый маршрут объединяет URL отображаемой страницы с соответствующим ему компонентом. Нужно понимать каждый переданный компонент в route, как отдельную страницу приложения – он добавляется к элементу с помощью id=’app’ (посмотрите второй параметр render метода).
А как это выглядит в приложении на Angular2? Посмотрите на пример роутинг конфигурации:

@Component({ ... });
@RouteConfig([
{ path: '/', name: 'Home', component: Home },
{ path: '/users', name: 'Users', component: About },
{ path: '/user:id', name: 'User', component: User }
])
export class App {
...

И снова декораторы… Мы просто добавляем RouteConfig декоратор в начале приложения. Здесь мы можем объявлять направление и предавать информацию, которую необходимо отображать на мониторе. Ну и где же появится этот компонент? В предыдущей версии Angular у нас был атрибут data-ng-view. Теперь мы можем сами создавать этот компонент:

<router-outlet></router-outlet>

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

Архитектура

React – это библиотека поэтому у него нет собственной архитектуры. Но создатели советуют использовать Flux архитектуру. Он предоставляет однонаправленный поток данных. Действие, содержащее тип, а иногда и новые данные, отправляется диспатчеру. Диспатчер запускает callback функции которые существуют в памяти. Всякий раз, когда вызывается функция он информирует об этом view. View может изменить свое состояние в соответствии с действием и перезапуститься. View может также спровоцировать следующие действия, например когда пользователь с ней взаимодействует и поток стартует по новому.
В Angular был внедрен паттерн MVC. Но в новой версии архитектура основана на компонентах. На самом деле все очень просто – компоненты и разметка обмениваются информацией использую двустороннюю привязку данных, а также компоненты используют сервисы для взаимодействия с такими процессами как доступ к данным, залогинивание, настройка и другие. Все что предоставляет данные или информацию может быть сервисами. В Angular 2 также есть механизм, который облегчает работу девелопера – это внедрение зависимостей (dependency injection).
Как вы видите фреймворк не такой уж и полноценный – они предоставили нам компоненты и сервисы и в принципе это все! К счастью мы можем использовать другую архитектуру, если нам это необходимо. Например, можно взять Flux или Redux. Поэтому можно все настроить под себя.

Выводы

Ну и ну… наконец-то. Я давно уже не писал такую долгую статью… но я надеюсь это стоило моих усилий. Я уверен, что есть еще много составных этих двух технологий, которые можно сравнивать, но я старался сосредоточить свое внимание на наиболее важных моментах. Я старался не вносить какое-либо субъективное мнение в данную статью, потому как уверен что выбор библиотеки или фреймворка очень спорный вопрос в наши дни… Поэтому, каждый раз когда Вам предстоит сделать выбор, будьте прагматичны, имейте ввиду все Ваши нужды и предпочтения и делайте свой выбор! Обе технологии достойны и скорей всего обе будут соответствовать Вашим запросам.