12/08/2018, 15:54

Một vài sự khác biệt: Angular 2 và React

Angular 2 được Google giới thiệu vào 9/2016, được nâng cấp để đối chọi với thư viện Javascript nổi tiếng của Facebook _ React. Với khả năng ổn định hơn, nhanh hơn và gọn nhẹ hơn bao giờ hết. Trong bài viết này, chúng ta sẽ đề cập đến ưu điểm của React JS và Angular, các khả năng mà hai công cụ này ...

Angular 2 được Google giới thiệu vào 9/2016, được nâng cấp để đối chọi với thư viện Javascript nổi tiếng của Facebook _ React. Với khả năng ổn định hơn, nhanh hơn và gọn nhẹ hơn bao giờ hết. Trong bài viết này, chúng ta sẽ đề cập đến ưu điểm của React JS và Angular, các khả năng mà hai công cụ này mang lại đáp ứng với các yêu cầu khác nhau.

Tổng quan về Angular 2 và React

  • Angular 2 là một web framework đầy đủ, trong khi React chỉ là một thư viện. Do vậy, để thực hiện so sánh 2 công cụ này, chúng ta tạm giả định React như một JS framework bởi vì dù sao React cũng luôn được sử dụng cùng với một vài thư viện JS để cung cấp một kiến trúc cho app dựa vào React. VD như các thư viện bao gồm Flux, MobX và Redux.

  • Angular 2 và React cùng là module hóa, cùng sử dụng kiến trúc module để phát triển các function như routing hay quản lý các dependencies trong một app SPA.

  • React cập nhật DOM nhanh hơn dựa vào DOM ảo. Và để khắc phục điểm yếu này, đánh bại được công nghệ DOM ảo của React, Google đã tiến hành thực hiện cơ chế phát hiện thay đổi trong Angular. Nhờ vậy, hiệu năng của cả Angular và ReactJS hiện giờ đủ tốt cho các app web hiện đại.

  • Cả React và Angular đều cùng sử dụng module đống gói là Webpack.

Để thuận tiện hơn, chúng ta có thể theo dõi bảng sau:

Javascript Framework Angular 2 React
Khái niệm Kiến trúc dựa theo component Kiến trúc dựa theo component
DOM thật DOM ảo
Render tại clien và server Render tại server
Cài đặt Tương đối dễ (Angular 1 khó hơn) Tương đối khó
Data binding Data binding 2 chiều Data binding 1 chiều
Quản lý dependency Sử dụng những công cụ được xây dựng sẵn cho quản lý sự phụ thuộc Yêu cầu ReactDI để quản lý sự phụ thuộc
Hỗ trợ ngôn ngữ TypeScript, CoffeeScript, Javascript JSX, JavaScript, TypeScript
Thoải mái khi sử dụng Ít thoải mái, phải tuân theo các quy ước Thoải mái hơn
Khả năng tiếp cận Khó khăn (yêu cầu dev có kiến thức nâng cao về JS) Dễ (chỉ cần dev có kiến thức trung bình về JS)

Trong bài viết này, ta sẽ đề cập đến các khía cạnh sau khi lựa chọn giữa Angular 2 và React cho một app SPA:

  • Cách thực hiện theo kiến trúc dựa vào component
  • Cách quản lý trạng thái của app
  • Chúng làm việc với những ngôn ngữ dựa theo JS gì?
  • Chúng cung cấp khả năng điều hướng gì?
  • Cách thực hiện quản lý sự phụ thuộc
  • Cách gắn dữ liệu giữa các template và các lớp Javascript
  • Cách tạo ra các template động

React và Angular 2 trong khái niệm kiến trúc dựa theo component

Tất cả code của một ứng dụng single-page có thể được gói gọn trong 3 files - index.html, index.css và index.js. Nhưng cách tiếp cận này không cho phép chúng ta mở rộng một SPA. Thay vào đó, chúng ta nên chia nhỏ một SPA thành các phần có nghĩa hay còn gọi là component - header, thanh điều hướng, bố cục chính, sidebar, ... Sử dụng các component độc lập là cách thích hợp để xây dựng các app SPA hiện đại. Mỗi component sẽ bao gồm markup độc lập, một stylesheet và các Javascript file. Vậy Angular 2 và React đã thực hiện theo kiến trúc dựa vào component như thế nào?

Dưới đây là một ví dụ cách mà ta tạo một component trong React:

import React from "react";

class NewComponent extends React.Component {
  constructor() {
    super();
    this.state = { 
      component: "New Component!" 
    };
  }
  
  render() {
    return(
      <div>
        <h1>This is My { this.state.component }</h1>
      </div>
    )
  };
};

Đầu tiên, ta phải import React từ thư viện core để component của ta có thể kế thừa từ React.Component. Trong hàm render, component phải trả về một vài markup JSX.

Một component Angular 2 sẽ khác so với component React:

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

@Component({
  selector: "ng2-app",
  template: `
    <h1 class="header-title" *ngIf="title === 'Application'">NG2 {{ title }}</h1>
    <h1 class="header-title" *ngIf="!title">NG2 App</h1>
  `,
  css: [`
    .class { 
      background-color: lightgreen;
      color: white; 
    }
  `]
})

export class AppComponent { 
    title = 'Application';
}

Với Angular, bạn có thể chèn template và style trực tiếp vào component - mặc dù điều này không được khuyên dùng nếu không phải template và stykle chỉ có vài dòng code. Trong thực tế, component của Angular 2 app không chứa layout và stylesheet. Bạn nên chia mỗi component thành 3 phần: xử lý logic, layout và styles. Do đó, component sẽ giống như:

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

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

export class AppComponent { 
  title = "Application";
}

Cách thực thi của các component sẽ cho ta thấy vài điểm khác biệt giữa Angular 2 và React. Layout của một React component luôn được đặt cùng file với component logic, trong khi các component của Angular 2 cho phép bạn tách logic từ layout.

Cả Angular 2 và React đều quản lý một component khá hiệu quả. Nhưng khi bạn tạo nhiều component cho app, thì một app nên được chia tách thành các đơn vị nhỏ có ý nghĩa chưa logic cụ thể.

Với cả Angular 2 và React, bạn muốn tạo một component chính để giữ tất cả các phần của app. Điều đó rất dễ dàng với cả 2 framework bằng cách sử dụng đặc tính import/ export của ES 6. React quản lý nhiều component theo cách:

import React from "react";

import Header from "./Header";
import Section from "./Section";
import Footer from "./Footer";

class NewComponent extends React.Component {
  //some business logic goes here
  render() {
    return(
      <div>
        <Header />
        <Section />
        <Footer />
      </div>
    )
  };
};

Bạn sẽ có thể export Header, Footer, Section component từ các file liên quan. Mỗi component con có thể chứa các component con khác. VD, một Header có thể có một Title, và một Section có thể chứa vài component Article.

Angular làm việc đó khá đơn giản tương tự như React. Một App component Angular có thể import một component con và sau đó được chèn vào layout:

import { Component } from "@angular/core";
import { LaptopComponent } from "./laptops/laptop.component";

@Component({
  selector: "ng-app",
  template: `
    <title>{{ title }}</title>
    <laptop></laptop>
  `
})

export class AppComponent { title="Application"; }

Với Angular, bạn sẽ phải thực hiện nhiều bước: tất cả các component nhỏ hơn phải được thêm vào file module. Cụ thể , bạn cần phải thêm component Laptop vào @NgModule bên cạnh các component khác

import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { LaptopsComponent } from './laptops/laptop.component';
import { LaptopPartsComponent } from './laptops/laptop-parts.component';

@NgModule({
  imports: [ ],
  declarations: [ 
    AppComponent, 
    LaptopsComponent, 
    LaptopPartsComponent ],
  bootstrap: [ AppComponent ]
})

export class AppModule {}

Đó là một vài sự khác nhau giữa React và Angular trong khái niệm về kiến trúc dựa theo component. Tổng kết, chúng ta có một vài thông tin sau:

  • Angular 2 và React cùng cho phép xây dựng ứng dụng từ các component độc lập riêng rẽ.
  • Cả hai framework khuyến khích chia nhỏ các component phức tạp thành các đơn vị nhỏ hơn.
  • Angular 2 khuyến khích tách các template phức tạp từ component logic, React thì ngươc lại, lưu các template và component logic trong cùng file.
  • Angular 2 sử dụng stylesheet dựa theo component và tách chúng trong các file khác; React không cung cấp cách thức để quản lý CSS stylesheets.

Quản lý trạng thái trong React và Angular 2

Quản lý trạng thái của một ứng dụng là nhiệm vụ đấy thử thách. Và nó trở thành một thảm họa thực sự khi một vài component có thể sử dụng chung một trạng thái đã được chia sẻ

Quản lý state là một vấn để mà cả React và Angular 2 phải giải quyết để có thể phát triển một ứng dụng client-side ổn định. Và thật thú vị, cả hai framework đều quản lý state theo một cách tương tự nhau, sử dụng một thư viện JS phổ biến là Redux

Vậy Redux thực sự là gì? Trong một app React đơn giản, bạn có thể kiểm soát trạng thái của app trong các component ở tầng layout. Nhưng giải pháp này sẽ có hiệu quả trong tương lai? Điều này cần phải xem xét. Nếu SPA của bạn phải xử lý các kịch bản luồng dữ liệu phức tạp hay các chuỗi sự kiện phức tạp, bạn cần một phương pháp quản lý tốt hơn, đó là điều Redux đem lại

Redux là một thư viện JavaScript chuẩn cung cấp một kiến trúc cho React. Kiểu kiến trúc đó là gì?

Redux sử dụng luồng dữ liệu một chiều, giúp quản lý trạng thái của ứng dụng rất thuận tiện

Khi bạn bắt đầu xây dựng một ứng dụng React với kiến trúc Redux, bạn cần cung cấp cho app một trạng thái ban đầu, được lưu trong một store. Với Redux, bạn không thay đổi trạng thái ban đầu của app khi trạng thái của app thay đổi . Thay vào đó, chúng ta tạo phiên bản mới trong cùng store. Theo cách này, chúng ta có thể theo theo dấu toàn bộ lịch sử của app React, và có cả cách để quay lại phiên bản đầu tiên. Với Redux, bạn có thể lưu lại mọi thay đổi nhỏ đã xảy ra trong một trạng thái của app. Điều đó thật tuyệt với trong việc debug.

Redux gói toàn bộ ứng dụng vào trong provider, đó là một component của kiến trúc Redux mà có thể lắng nghe store và các component được render lại sau mỗi lần thay đổi. Render lại component của app không phải lại một vấn đề với React nữa nhờ có DOM ảo

Ngoài provider và store, Redux còn có 2 kiểu component - smart và dumb component. Ý tưởng đằng sau Redux là để xây dựng các component mà có thể lấy và xử lý dữ liệu và các component có thể render sự thay đổi trong layout. Các component được gắn với các action là các đối tượng Javascript chứa dữ liệu được xử lý và render lại.

Cuối cùng, Redux cung cấp reducer. Reducer là các chức năng chấp nhận trạng thái hiện tại của một component với một action. Một reducer phải tạo ra một đối tượng trạng thái mới với dựa vào đối tượng action và cập nhật store. Redux có nhiều reducer, và mỗi reducer thay đổi chỉ một vài dữ liệu trong store.

Redux không chỉ là thư viện JavaScript cung cấp một kiến trúc cho app React. Bạn cũng có thể sử dụng MobX và Flux. Redux được khuyến khích cho các app lớn và phức tạp.

Với sự phổ biến của Redux, không có gì ngạc nhiên là bạn có thể sử dụng tốt nó trong các framework JavaScript khác, bao gồm cả Angular. Angular 2 có thể tiếp thu ưu điểm từ luồng dữ liệu vô hướng của Redux, được thực hiện bởi thư viện ngrx/store. Redux hoạt động trong Angular 2 tương tự như với React: sử dụng reducers, một store đơn, các component biểu diễn và các container .

Cả Angular 2 và React thường được sử dụng để xây dựng các app SPA nâng cao, ở mức độ doanh nghiêp với logic phức tạp. Bạn cần phải học cách để quản lý một trạng thái của app trong Redux khi sử dụng React hay Angular.

Lựa chọn giữa JSX và TypeScript của React và Angular 2

Tác giả của Angular 2 và React đã quyết định cung cấp một framework không đầy đủ. Để đáp ứng với nhưng yêu cầu phức tạp, các dev Angular và React phải học thêm một vài công nghệ bổ sung - TypeScript hay JSX. TypeScript là một tập hợp JavaScript và được khuyến khích cho phát triển Angular 2 app. JSX là một ngôn ngữ đặc biệt, được phát minh bởi Facebook và thay thể Javascript trong app React. TypeScript và JSX rất khác nhau trong syntax và cách làm việc.

Ngôn ngữ TypeScript phù hợp với Angular 2, nó khá giống JavaScript. Sức mạnh thực sự của TypeScript được thể hiện khi bạn muốn xác nhận chính xác kiểu dữ liệu trước khi thực thi. VD, trong một component Angular:

export class Laptop {
  id: number;
  title: string;
  price: number;
  cost: number;
  description: string;
  stock: number;
}

Mỗi thuộc tính của model laptop được gán với kiểu cụ thể. Trình biên dịch sẽ so sánh mỗi đối tượng laptop với model trên và thông báo nếu một component được gán sai kiểu giá trị. VD, một mảng các laptop trong LaptopsComponent:

import { Laptop } from './laptop';  //import the Laptop model

export class LaptopsComponent {
  laptops: Laptop[] = [{
    "id": 1,
    "title": "Samsony",
    "description": "The Best Laptop Ever",
    "price": 14999,
    "cost": 10000,
    "stock": "100500"  // Alert! This won’t compile! 'stock' must be a number
}, { }, { }];

Cú pháp Laptop[] trong ví dụ trên nói với TypeScript là xem xét biến laptop như một mảng các đối tượng của Laptop. Và nếu chứa các giá trị khác ngoài number, TypeScript sẽ cảnh báo chúng ta.

Điều quan trọng cần chú ý là bạn không cần phải bắt buộc sử dụng TypeScript; Angular 2 làm việc tốt với vanilla JavaScript. Và nếu là một Ruby on Rails dev, bạn sẽ thấy vui khi biết là nó có thể sử dụng CoffeScript với Angular 2.

JSX khác xa so với TypeScript về syntax.

class NewComponent extends React.Component {
  render() {
    return(
      <div className="wrapper">
        <h1 className="title">This is My { this.state.component }</h1>
        <p className="description">This component is a React component.</p>
      </div>
    )
  };
};

Đằng sau đó, code trong hàm return được dịch thành:

React.createElement("div", { className: 'wrapper' },
  React.createElement("h1", { className: 'title' }, `This is My ${ this.state.component }`)
)

Hàm createElement sẽ tạo ra một đối tượng JavaScript với các thuộc tính như div và class với các giá trị tương ứng.

JSX cũng cho phép bạn gắn JavaScript trực tiếp vào trong template: chỉ cẩn đóng gói code JS vào trong '{}' và JSX sẽ tính toán giá trị tự động.

Mặc dù JSX khá giống HTML, nhưng thực sự nó không hoạt động như HTML. Code trong hàm render sẽ luôn được biên dịch thành một đối tượng JS với một tập các thuộc tính.

Facebook khuyến khích nên sử dụng JSX, nhưng bạn cũng không cần phải bắt buộc dùng nó. Bạn có thể sử dụng vanilla JS nếu thích. VD, tọa một template, bạn có thể gọi hàm createElement với tập các tham số - tên của HTML element, nội dung HTM, .. thay vì viết bằng JSX. Mặc dù vậy, trong thế giới React, bạn luôn có một lời khuyên là sử dụng JSX.

JSX rất dễ đọc, dễ dùng và viết JSX đơn giản hơn viết hàm JS trực tiếp.

Điều hướng với Angular 2 và React

Một app SPA mà không điều hướng đến các trang khác thì không thực sự là một ứng dụng một trang. Với công cụ điều hướng phù hợp, app có thể chuyển đến mọi trang, sử dụng các route con, tận dụng khả năng của lazy loading, và quản lý các truy cập cho từng trang, ngăn chặn ngay lập tức user truy cập vào một trang chưa được authenticate.

Bởi vì routing là một tính năng cơ bản cho một ứng dụng SPA nâng cao, Angular 2 và React phải đưa ra một vài cách để quản lý route. Nhưng thực sự là không có framework nào có sẵn khả năng xử lý route trong thư viện core, mà bạn phải cái đặt một vài thư viện bổ sung là Angular Router và React Router. Nhưng route này hoạt động đơn giản - chúng chỉ là tọa một đường dẫn cụ thể như một đầu vào và tải component liên kết với đường dẫn đó.

Tạo các route trong một app React đơn giản hơn so với Angular 2. Để bắt đầu sử dụng route trong app React, bạn cần cài đặt react-router bằng NPM, sau đó import thư viện route vào trong main.js, client.js hay các file JavaScript khác.

import React from 'react';
import ReactDOM from 'react-dom';
import { Router, Route, IndexRoute, browserHistory } from 'react-router';

import MainLayout from './main-layout';
import ContactUs from './contact-us';
import Users from './users';
import NotFound from './not-found';

const app = document.querySelector('#react-app');

ReactDOM.render(
  <Router history={ browserHistory }>
    <Route path="/" component={ Layout }>
      <IndexRoute component={ About }></IndexRoute>
      <Route path="/contact-us" component={ ContactUs }></Route>
      <Route path="/users:user" component={ Users }></Route>
      <Route path="*" component={ NotFound }></Route>
    </Route>
  </Router>,
app);

Đó là một cách sử dụng đơn giản của route trong app React. Thay vì render trực tiếp như vậy, bạn có thể sử dụng Router, sẽ đóng gói tất cả route , bao gồm cả index route. Sau đó, bạn chỉ cần chỉ ra đường dẫn và một component tương ứng cho mỗi route và thực hiện các link điều hướng trong main page để user có thể chuyển qua lại giữa các page.

Với Angular 2, route khá khác so với React. Chúng ta có thể cấu hình route lúc đầu và chỉ ra layoute cơ sở mà route sẽ được tải. Layout này nằm trong index.html file, chúng ta cần thêm tag 'base' và thẻ head để Angular Router biết chỗ tải route.

Trong React, bạn có thể cấu hình route trong cùng file, nhưng Angular thích hợp với cách chia theo module hơn.

//separate file app.routing.ts
import { Routes, RouterModule } from '@angular/router';
import { MainComponent } from './main/home.component';
import { UsersComponent } from './users/users.component';
import { AboutUserComponent } from './users/about-user.component';
import { ContactUsComponent } from './contact-us/contact-us.component';
import { NotFoundComponent } from './not-found/not-found.component';

const appRoutes: Routes = [
  { path: ', component: MainComponent  },
  { path: 'users', component: UsersComponent },
  { path: 'users/:user', component: AboutUserComponent },
  { path: 'contact-us', component: ContactUsComponent },
  { path: '**', component: NotFoundComponent }
];

Cả hai framework đều yêu cầu module cho route. Angular yêu cầu sử dụng một file tách riêng cho route và cài đặt trước khi bạn thêm vào một module trong app. React làm việc tốt khi bạn chỉ ra route trong file main JS, vì vậy có thể không cần tạo các file bổ sung cho route.

Dependency Injection trong Angular 2 và React

Dependency injection (DI) là một khái niệm để phân biệt Angular 2 và React; DI là một phần trong Angular framework, trong khi trong React thì không định nghĩa khái niệm DI.

Trong một app Angular, dependency injection cho phép chúng ta loại bỏ sự trùng lặp của các lớp giống nhau với các dữ liệu giống nhau trong các component khác nhau. Nếu bạn có 2 component: một component là cha, và component khác là con. Chúng luôn chia sẽ dữ liệu, như một model và một mảng đối tượng. Nếu như không có dependency injection, bạn sẽ phải copy và paste model và mảng từ component cha sang các component con của nó. Nhưng Angular 2 giúp bạn tránh điều đó bằng cách sử dụng module @Injectable và một vài lớp dịch vụ đặc biệt:

import { Injectable } from '@angular/core';
import { Team } from '../models/team';

const teams: Team[] = [
  {
    id: 524497,
    name: 'Renault Sport F1',
    username: 'RenaultSportF1',
    image: 'https://pbs.twimg.com/profile_images/833997138417819651/LMzylD_R_400x400.jpg'
  }, 
  {
    id: 123153,
    name: 'McLaren F1',
    username: '@McLarenF1',
    image: 'https://pbs.twimg.com/profile_images/831538604409233408/w4jg5JtR_400x400.jpg'
  }, 
  {
    id: 125448,
    name: 'Scuderia Ferrari',
    username: '@ScuderiaFerrari',
    image: 'https://pbs.twimg.com/profile_images/712562570897580032/amoHCwrI_400x400.jpg'
  }
];

@Injectable()

export class TeamService {
  //the function that returns all teams
  getTeams() {
    return teams;
  }
}

Service này phải được đăng ký trong NgModule trong mảng provider. Bạn có thể loại bỏ code lặp từ các component cha và con bằng cách sử dụng service inject

Angular tự động tạo ra một dependency injector để bạn có thể cho vào tất cả các đối tượng service chứa cùng thông tin giống nhau được dùng trong các component

Vậy bạn có thể chia sẽ các dữ liệu cho các component React như thế nào? React yêu cầu một thư viện đặc biệt, gọi là ReactDI để quản lý sự phụ thuộc. Với thư viện này, nó có thể đăng ký tất các lớp dịch vụ và gửi chúng vào một vài component. Với các component, bạn có thể gọi một dịch vụ cần thiết sử dụng hàm di() và truyền hàm vào service.

Nếu bạn muốn sử dụng Angular, bnaj phải thỏa thuận với dependency injection. Với React, bạn có thể tránh hoàn toàn dependency injection. Tuy nhiên, bạn có thể muốn sử dụng dependency injection trong React. Các thư viện JavaScript như ReactDI và Inversify JS làm cho React có thể sử dụng dependency injection.

0