Angular vs. React — это популярная дискуссия среди разработчиков JavaScript-приложений, и чаще всего дискуссия оказывается смещенной в сторону одной или другой технологии. Angular и React, разработанные Google и Facebook соответственно, являются двумя популярными технологиями, используемыми для создания интерактивных одностраничных приложений.
Всестороннее сравнение между Angular и React неизбежно, потому что есть определенные места, в которых они значительно перекрываются с точки зрения того, что они предлагают, то есть создание внешнего интерфейса вашего приложения и других мест, где их функциональность остается неполной, если только это не помогло третьим сторонам. тусовочная библиотека. Принятие одной технологии над другой — это вопрос того, лучше ли Angular или React решит вашу проблему и немного интуиции. В этом уроке мы сравним и сопоставим семь ключевых особенностей Angular и React.
Я — горячий сторонник подхода «сначала код» (говорят, код говорит громче, чем слова). Помня об этом, я добавил, где это возможно, примеры кода Angular и React, чтобы вы могли опираться на свою интуицию и решать, что работает для вас, а что нет. Давайте начнем.
Framework против библиотеки
Angular — это фреймворк, а React — это библиотека.
Так что это значит? Реагирование само по себе не позволит вам создать веб-приложение, потому что оно разработано для создания представлений (отсюда и «V» в MVC). Что может сделать React, так это создать основанные на компонентах представления, для которых данные могут передаваться дочерним представлениям. Чтобы заполнить этот пробел, Facebook разработал Flux , архитектурный паттерн, дополняющий React. Архитектура Flux в сочетании с React обеспечивает следующий сценарий:
- Пользователь нажимает на элемент React.
- Действие запущено. Это действие отправляется в магазин через библиотеку диспетчера.
- Магазин отслеживает состояние приложения и методы извлечения данных. Любое обновление состояния отражается в представлениях, и это помогает поддерживать представления в соответствии с состоянием приложения.
Не имеет смысла? Эта цифра должна разобраться за вас.
Angular — это фреймворк для создания клиентских приложений.
AngularJS был прочно построен на основе шаблона MVC, который разделил приложение на три разных уровня. Сочетание модели, представления и контроллера плюс дополнительная сложность, связанная с освоением директив, фабрик, сервисов и других компонентов для создания одностраничного приложения, заставили разработчиков в Google перейти к архитектуре на основе компонентов.
Но когда ваше приложение начинает расти, важно иметь прочную структуру, которая защищает бизнес-логику вашего приложения от компонентов. Будучи каркасом, Angular позволяет вам обеспечивать структурную организацию, перемещая бизнес-правила в модель предметной области (используя комбинацию классов моделей и служб) и внедряя модель в ваши компоненты посредством внедрения зависимостей.
Ниже приведен пример кода, который иллюстрирует, как бизнес-логика инкапсулируется внутри модели User и службы User, а также вне нашего компонента.
1
2
3
4
5
6
7
8
9
|
/* Path: /app/models/User.ts */
export class User {
id: number;
username: string;
password: string;
firstName: string;
lastName: string;
}
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
/* /app/services/user.service.ts */
import { Injectable } from ‘@angular/core’;
import { Http } from ‘@angular/http’;
import { User } from ‘../models/User’;
@Injectable()
export class UserService {
constructor(private http: Http) { }
getAll() {
// API to return all users
}
create(user: User) {
//API call to create user
}
update(user: User) {
//API call to update user
}
delete(id: number) {
//API call to delete user
}
}
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
/* Path: /app/page/page.component.ts */
import { Component } from ‘@angular/core’;
import { User } from ‘../models/User’;
import { UserService } from ‘../services/user.service’;
@Component({
templateUrl: ‘page.component.html’
})
export class PageComponent {
currentUser: User;
users: User[] = [];
constructor(private userService: UserService) {
//Dependency is Injected inside the constructor’s arguments
deleteUser(id: number) {
this.userService.delete(id).subscribe(() => { #Do Something});
}
private loadAllUsers() {
this.userService.getAll().subscribe(users => { #Do something else });
}
}
|
01
02
03
04
05
06
07
08
09
10
11
12
13
|
<!—Path: /app/home/page.component.html —>
<div class=»title»>
<h2>All users:</h2>
<ul>
<li *ngFor=»let user of users»>
{{user.username}} ({{user.firstName}} {{user.lastName}})
— <a (click)=»deleteUser(user.id)»>Delete</a>
</li>
</ul>
</div>
|
Компонентный подход
Компоненты являются самым основным строительным блоком пользовательского интерфейса в приложении Angular. Приложение Angular — это дерево компонентов Angular.
Какие компоненты? В Angular компоненты — это классы TypeScript, над @Component
отмечен декоратор @Component
. Более того, внутри этих декораторов мы можем определить, что Angular называет метаданными, которые включают в себя шаблон, стили, селекторы и так далее.
Иерархия компонентов в Angular разработана таким образом, что вы можете связать структуру и функциональность в рамках одной сущности. Вот высокоуровневый архитектурный обзор компонентов и как это связано со всем остальным в Angular.
Обмен данными между компонентами возможен путем вложения компонентов, как показано ниже.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
/* UserParentComponent.ts */
import { Component } from ‘@angular/core’;
// The <user-child> selector is nested inside <user-parent>.
@Component({
selector: ‘user-parent’,
template: `
<h2>There are {{users.length}} registered users {{status}} now</h2>
<user-child *ngFor=»let user of users»
[user]=»user»
[status]=»status»>
</user-child>
`
})
export class UserParentComponent {
users: { id: number, name: string }[] = [
{ «id»: 0, «name»: «Chris» },
{ «id»: 1, «name»: «Dwayne» },
{ «id»: 2, «name»: «Eve» }
];
status: string = «online»;
}
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
/* UserChildComponent.ts */
import { Component, Input } from ‘@angular/core’;
// Input properties are adorned with @decorators
// user & status are input properties
@Component({
selector: ‘user-child’,
template: `
<h2>{{user.name}}</h3>
<p> id : {{user.id}} </p>
<p> Status: {{status}} </p>
`
})
export class UserChildComponent {
@Input() user: { id: number, name: string };
@Input() status: string;
}
|
Концепция компонентов глубоко укоренилась в React, как и в Angular. Вызовы Facebook Реагируйте на библиотеку компонентов, которая позволяет создавать интерактивные пользовательские интерфейсы. Однако, в отличие от Angular, компоненты React являются просто функциями JavaScript с произвольным числом входов и выходов . В приведенном ниже коде показан компонент, определенный с помощью функции JavaScript и класса ES6.
01
02
03
04
05
06
07
08
09
10
11
12
13
|
// Writing components using JavaScript functions
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
// Writing components using ES6 Class
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
|
Отныне мы будем придерживаться стандартов ES6 для составления компонентов, потому что именно это рекомендует Facebook. Каждый компонент React принимает произвольное количество входных данных, которые хранятся внутри объекта с именем props
.
Он также имеет метод render
, и, как следует из названия, этот метод определяет, что будет отображаться при вызове компонента. Каждый компонент поддерживает внутреннее состояние (через this.state
), и каждый раз, когда состояние изменяется, функция рендеринга этого компонента вызывается снова.
Особенности языка: TypeScript против ES6
Угловые приложения написаны на TypeScript, который является надмножеством ECMA2015 и использует транспортер для компиляции вашего строго типизированного файла .ts в простой файл .js. TypeScript предлагает языковые расширения, предназначенные для облегчения написания на JavaScript, а также связывает информацию о типах с сущностями JavaScript, чтобы обеспечить проверку типов и улучшить рабочий процесс разработки.
Некоторые из ключевых функций TypeScript включают необязательную статическую типизацию и поддержку интерфейсов, классов и декораторов. (Декораторы — это функции с префиксом «@», за которыми сразу следует класс, параметр или свойство.)
Давайте погрузимся в React, не так ли? В этом примере кода очевидна одна из важных особенностей языка React.
01
02
03
04
05
06
07
08
09
10
11
|
function Tweet(props) {
return(
<div className=»tweet»>
<img src=»http://twitter.com/some-avatar.png» className=»tweet__avatar» />
<div className=»tweet__body»>
<p>This is a tweet.</p>
</div>
</div>
);
}
|
Разве это не здорово? React позволяет встраивать теги XML / HTML в файл JavaScript, и это делается с помощью JSX, который предлагает возможность расширения синтаксиса для JavaScript. Мы должны использовать такой компилятор, как Babel, который компилирует наш JSX-код в JavaScript, понятный браузерам. Приведенный выше код сводится к следующему:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
«use strict»;
function Tweet(props) {
return React.createElement(
«div»,
{ className: «tweet» },
React.createElement(«img», { src: «http://twitter.com/some-avatar.png», className: «tweet__avatar» }),
React.createElement(
«div»,
{ className: «tweet__body» },
React.createElement(
«p»,
null,
«This is a tweet.»
)
)
);
}
|
Хотя использование JSX рекомендуется, вы можете придерживаться React.createElement()
если вы против идеи встраивания HTML-тегов в JavaScript.
Кроме того, вы можете использовать либо стандарты ES6, либо традиционную форму JavaScript при работе с React. Хотя ES6 является относительно новым, он добавляет множество функций, таких как классы, функции стрелок, литералы шаблонов, деструктурирование и использование let и const. Единственный недостаток, о котором я могу думать, это то, что он добавляет немного стандартного кода, такого как необходимость вызывать super()
каждый раз, когда вы вызываете constructor()
, и что он не связывает автоматически методы обработки событий с this
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
class User extends React.Component {
constructor(props) {
//bloat code alert
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.handleInputChange = this.handleInputChange.bind(this);
}
handleSubmit(user) {
//Method to handle submit
}
handleInputChange(user) {
//Method to handle input
}
render() {
return (
<div>
<UserRegistration
onSubmitSuccess={this.handleSubmit} onInputChange={this.handleInputChange} />
</div>
);
}
}
|
Проверка типов в Angular против PropTypes в React
Проверка статического типа выполняется во время компиляции. Компилятор предупреждает вас о возможных несовпадениях типов и обнаруживает определенные ошибки, которые в противном случае остались бы незамеченными. Кроме того, определение контракта для переменной, свойства или параметров функции может привести к более удобочитаемому и поддерживаемому коду.
Объявления переменных и функций делаются более выразительными, объявляя их типы данных. Вы можете прочитать больше о различных примитивных типах данных в документации TypeScript .
01
02
03
04
05
06
07
08
09
10
|
let isLoggedIn: boolean = false;
let id: number = 10;
let name: string = «Davis»;
let list: number[] = [1, 2, 3];
enum Color {Red, Green, Blue};
let c: Color = Color.Red;
let bucket: any = 4;
bucket = «I can be a string»;
bucket = false;
|
Определение сигнатуры API с использованием интерфейса делает код менее двусмысленным и более легким для понимания. Интерфейс служит в качестве краткого руководства по началу работы, которое поможет вам сразу приступить к работе с кодом и сэкономит время, затрачиваемое на чтение документации или фактическую реализацию библиотеки.
01
02
03
04
05
06
07
08
09
10
11
12
13
|
interface ButtonSettings {
text: string;
size?: { width: number;
color?: string;
}
function createButton(settings: ButtonSettings) { … }
createButton({ text: ‘Submit’ });
createButton({ text: ‘Submit’, size: { width: 70, height: 30 }});
createButton({ text: ‘Submit’, color: 43);
createButton({ text: ‘Submit’, size: { width: 70 });
createButton({ color: ‘Blue’});
|
Ключевое слово type
в TypeScript может использоваться для создания псевдонима для типа. Затем вы можете создавать новые типы, которые являются объединением или пересечением этих примитивных типов.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
//Union Types
type Age = number |
function getAge (age: Age): string {
return `You are ${age}!`;
}
let ofSusan: Age =21;
let ofTyler: Age = ‘thirty one’;
getAge(ofSusan);
getAge(ofTyler);
//Intersection Types
interface Name{
name(firstName: string, lastName: string): string;
}
interface Age {
age(current: number): number;
}
// assign intersection definition to alias User
type User = Name & Age;
function createUser (testUser: User) {
testUser.name(«David»,»John»);
testUser.age(99);
testUser.address();
|
React имеет ограниченную поддержку для проверки типов, потому что базовый ES6 не поддерживает его. Тем не менее, вы можете реализовать проверку типов с помощью библиотеки prop-types
разработанной командой React. Проверка типа props
компонента, чтобы проверить, является ли это строкой, может быть выполнена, как показано ниже.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
import PropTypes from ‘prop-types’;
//importing prop-types library
class Greeting extends React.Component {
render() {
return (
<h1>Hello, {this.props.name}</h1>
<P> My age is, {this.props.age} </h2>
);
}
}
Greeting.propTypes = {
name: PropTypes.string;
age: PropTypes.number;
};
|
Но prop-types
не ограничиваются строками, числами и логическими значениями. Вы можете сделать намного больше, как описано в документации библиотеки prop-types. Однако, если вы серьезно относитесь к статической проверке типов, вам следует использовать что-то вроде Flow , который является статической библиотекой проверки типов для JavaScript.
Леса: Угловой CLI против создания-реагировать-приложение
Начать проект с нуля на первый взгляд может показаться забавным. Однако процесс настройки структуры каталогов, написания стандартного кода для компонентов и загрузки приложения является бесполезным и непродуктивным упражнением. Ваша стратегия должна заключаться в том, чтобы как можно быстрее справиться с этим и сосредоточиться на фактической разработке приложения. Благодаря Google и Facebook у вас есть инструменты для удобного создания и создания приложений.
Настроить Angular-CLI для angular и создать-реакцию-приложение для React просто, используя npm.
1
2
3
4
5
6
7
|
// Angular CLI
$ npm install -g @angular/cli
// create-react-app
$ npm install -g create-react-app
|
Чтобы создать новое приложение Angular, вы должны использовать следующую команду:
1
2
|
$ ng new PROJECT-NAME
$ ng serve
|
Но это не так. Команда ng generate
позволяет вам генерировать компоненты, маршруты, каналы, директивы и сервисы.
1
2
3
4
5
6
7
8
|
$ ng generate component Page
installing component
create src\app\page\page.component.css
create src\app\page\page.component.html
create src\app\page\page.component.spec.ts
create src\app\page\page.component.ts
update src\app\app.module.ts
|
Angular CLI может сделать гораздо больше, например, создать сборку приложения Angular, команды для запуска модульных тестов и сквозного тестирования. Вы можете прочитать больше об этом на GitHub .
С другой стороны, create-react-app
является официально поддерживаемым способом создания приложения React без каких-либо файлов конфигурации.
$ npm install -g create-react-app
Это должно создать функциональное приложение React со всеми зависимостями Babel и webpack. Вы можете запустить приложение в своем браузере, используя npm start
.
Вы можете найти сценарии, доступные для приложения реагирования, в файле package.json .
1
2
3
4
5
6
7
|
«scripts»: {
«start»: «react-scripts start»,
«build»: «react-scripts build»,
«test»: «react-scripts test —env=jsdom»,
«eject»: «react-scripts eject»
}
}
|
Привязка данных: двусторонняя привязка против однонаправленной привязки
Привязка данных — это функция, которая позволяет синхронизировать данные между состоянием приложения (моделью) и представлением. В односторонней процедуре привязки данных любое изменение в состоянии приложения автоматически обновляет представление. Напротив, двусторонняя привязка данных связывает вместе свойства и события в рамках одной сущности, то есть любая модификация модели обновляет представление и наоборот.
В React свойства передаются от родительских компонентов к дочерним, что известно как однонаправленный или нисходящий поток данных. Состояние компонента является инкапсулированным и недоступным для других компонентов, если оно не передается дочернему компоненту в качестве реквизита, т.е. состояние компонента становится реквизитом дочернего компонента.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
class UserChild extends React.Component {
render() {
let userData = this.props.users.map( (user) => {
return (<p> <strong>{user.id} </strong> : {user.name} </p>);
});
return (
<div>
<h2> Hello.
{userData}
</div>
);
}
}
class UserParent extends React.Component {
constructor() {
super();
//State gets defined here
this.state = {
status: «Online»
}
}
render() {
return (
<div>
<UserChild users={this.props.users} status={this.state.status} />
</div>
);
}
}
var USERS = [
{ «id»: 0, «name»: «Chris» },
{ «id»: 1, «name»: «Dwayne» },
{ «id»: 2, «name»: «Eve» }
];
ReactDOM.render(
<UserParent users={USERS} />,
document.getElementById(‘container’)
);
|
Но что, если вам нужно распространять данные через дерево компонентов? Это делается с помощью дочерних событий и родительских обратных вызовов. Документация React содержит хороший пример, который касается такого сценария.
Методы привязки данных, доступные в Angular, являются одними из нескольких функций, которые делают его интересным. Angular имеет встроенную поддержку для интерполяции, односторонней привязки, двусторонней привязки и привязки событий.
Интерполяция — это самый простой способ связать свойство вашего компонента внутри текста между вашими тегами HTML и назначениями атрибутов.
<p>Welcome back {{currentUser.name}}!</p>
Привязка свойств аналогична интерполяции в том смысле, что вы можете привязать свойства элементов вашего представления к свойствам компонента. Привязка свойств способствует взаимодействию компонентов и идентична передаче реквизитов в React.
<img [src]="userImgUrl">
<user-child [user]="currentUser"></user-child>
Привязки событий позволяют поток данных в противоположном направлении, то есть от элемента к компоненту. Здесь click
является целевым событием, а справа у нас есть метод onSave()
который вызывается при возникновении события.
<button (click)="onSave()">Save</button>
Но наиболее важной особенностью является двустороннее связывание с использованием [(ngModel)]
. Это объединяет привязку свойства и привязку события в одну директиву и особенно полезно с формами и полями ввода.
1
2
3
4
|
<div>
<label>name: </label>
<input [(ngModel)]=»hero.name» placeholder=»name»>
</div>
|
Рендеринг на стороне сервера
Рендеринг на стороне сервера является традиционной техникой рендеринга. Здесь сервер возвращает весь HTML-файл по запросу, а браузеру остается просто показать его пользователю. Рендеринг на стороне клиента, с другой стороны, возвращает простой HTML-документ, таблицу стилей и файл JavaScript.
JavaScript делает последующие запросы на визуализацию остальной части сайта с помощью браузера. React, Angular и все другие современные библиотеки интерфейса JavaScript являются хорошими примерами рендеринга на стороне клиента. Это очевидно, если вы просматриваете источник вашего приложения Angular / React.
Но у рендеринга на стороне клиента есть недостатки: он плохо работает для SEO и возвращает неполный HTML-контент, когда вы делитесь своей ссылкой на сайтах социальных сетей. В Angular есть решение под названием Angular Universal, которое позаботится о том, чтобы сделать вашу поисковую систему дружественной для поисковых систем и социальных сетей. Это библиотека, созданная командой Angular, и ее использование определенно приветствуется.
В Universal используется технология предварительного рендеринга, при которой весь сайт сначала отображается с сервера, а через пару секунд пользователь переключается на рендеринг на стороне клиента. Поскольку все это происходит под капотом, пользователь не замечает ничего другого.
Если вы используете React с Redux, в документации по Redux есть хорошее руководство по настройке рендеринга сервера. Вы также можете настроить React для рендеринга с сервера, используя компоненты BrowserRouter
и StaticRouter
доступные в библиотекеact react-router
. Вы можете прочитать больше об этом в этой средней статье . Но если вы заинтересованы в производительности и оптимизации, вы можете попробовать next.js , библиотеку для SSR в React.
Завершение
Сравнение полноценной, многофункциональной инфраструктуры с надежной библиотекой пользовательского интерфейса может показаться несправедливым. Однако они представляют собой передовые технологии JavaScript, используемые для создания интерактивных одностраничных приложений, и в этой связи эта статья должна помочь вам выбрать одно из них.
Что вы думаете о Angular vs. React? Делитесь ими в комментариях ниже.