+5

React với AngularJS - có gì khác nhau

2015_06_19_f8fbbe68a5.png

Mục đích của bài này là đi qua việc xây dựng một vài tính năng đơn giản sử dụng Angular và React thông qua đó giúp bạn hiểu cần phải làm gì để có thể làm một ứng dụng. Angular là người khổng lồ trong cuộc chiến này, vì nó xuất hiện trước React, nhưng React có ưu điểm về hiệu năng hiển thị (rendering performance). Bài này chúng ta sẽ không đi sâu vào hai framework này.

Hy vọng rằng, sau khi đọc xong bài này, bạn sẽ hiểu rõ hơn framework nào thì phù hợp hơn cho project kế tiếp của mình. Tối thiểu, các bạn sẽ hiểu được cách những framework UI JavaScript tổng quan hoạt động như thế nào.

Tôi sẽ đi qua việc tạo những ví dụ code mẫu với từng framework. Sau đó, tôi sẽ đi tiếp tới những control UI phức tạp hơn, từng bước xây dựng app mẫu của chúng ta.

Hello, world!

Angular JS

Angular đã tồn tại khá lâu, nó xuất hiện vào 2009. Từ đó, nó đã đi một chặng đường dài, và bây giờ nó đã tới phiên bản 1.3.8, với những xuất bản bán-thường xuyên (semi-regular releases). Gần đây, có tin đồn về Angular 2.0, nói rằng phiên bản này sẽ có nhiều thay đổi lớn, nhưng đó là một chặng đường dài, và được dự trù xuất bản vào năm 2016. Phiên bản Angular hiện tại dùng nhiều những thuộc tính (attributes) và những thành phần (elements) HTML mới (custom) để cung cấp tính năng trên nhiều thành phần DOM khác nhau. Những phần này gọi là chỉ thị (directives) và chúng rất đa năng/đa dụng.

Xem code

Code khởi tạo của Angular thì khá dài, nó yêu cầu một application và một controller, và những định nghĩa cho những directives mà chúng ta tạo ra. Đoạn code ở trên cho thấy cách khởi tạo 1 Angular app, rồi định nghĩa một controller và cuối cùng tạo ra directive hello world. Sau đây, là từng phần, của những gì chúng ta làm với JavaScript.

  • Đầu tiên, chúng ta khởi tạo app Angular bằng việc gọi angular.module và cho nó một cái tên. Tham số thứ hai của hàm này là danh sách những phụ thuộc mà module của chúng ta dựa trên chúng. Ở đây, module không có phụ thuộc, nên chúng ta truyền vào 1 mảng rỗng.

  • Kế tiếp, chúng ta khởi tạo controller Angular testController. Nó không làm gì cả vì hiện tại directive của chúng ta đang đảm nhiệm mọi gánh nặng.

  • Cuối cùng, là định nghĩa directive. Tất cả những gì nó làm là xuất ra 1 div chứa đoạn text hello world. Một app Angular yêu cầu nhiều directives ng-app và ng-controller được thiết lập cho nhiều thành phần khác nhau. Đây là giải thích từng phần.

  • Thẻ html có directive ng-app áp vào nó, nói lên rằng trang này đại diện cho 1 app Angular có tên “testApp”.

  • Thẻ body được áp directive ng-controller, nói với Angular rằng tầm vực (scope) của controller chính là nằm trong thẻ body này.

  • Cuối cùng, chúng ta hiển thị directive hello-world như là đoạn mã duy nhất trong phần body. React JS

Nếu bạn chưa nghe về React JS, thì nó là một framework hiển thị view chú ý đến hiệu năng (performance-minded) được tạo ra bởi Facebook. Rất nhiều đối thủ nặng ký về framework MVVM (Model-View-ViewModel) mất một thời gian lớn để hiển thị những lượng data lớn, như trong trường hợp những danh sách (list) và tương tự. Nhưng React đó không còn là vấn đề, vì nó chỉ hiển thị những gì thay đổi.

Ví dụ, nếu user đang xem một danh sách có 100 items được hiển thị bởi React, và người này thay đổi item thứ 3 từ trên xuống theo cách nào đó, thì chỉ item đó được hiển thị lại, và 99 items còn lại vẫn giữ nguyên (không cần hiển thị lại). React cũng dùng cái gọi là “DOM ảo” (“virtual DOM”) để tăng hiệu năng bằng cách xuất ra một hiển thị ảo, sau đó kiểm tra sự khác biệt giữa hiển thị ảo và những gì có trên DOM và tạo một bản vá (a patch).

React dùng những file JSX (không bắt buộc) để tạo views, có nghĩa là JavaScript và HTML cùng tồn tại trong một file. Nó có một chút thay đổi về mô hình và cần làm quen một chút. JSX không bắt buộc phải dùng nhưng dùng nó thì dễ hơn phải chỉ rõ ra từng component nào cần hiển thị, do đó, tôi đề xuất dùng nó. Bạn có thể tìm hiểu thêm React trong bài Xây dựng ứng dụng TODO với Reactjs và Flux. Chú ý: những lớp React có thể được tạo bằng 1 trong 2 cách: bằng JavaScript thông thường hoặc bằng JSX, ngôn ngữ kết hợp HTML và JavaScript trong một định dạng file. Tôi sẽ dùng JSX trong những phần JavaScript trong những ví dụ bên dưới. Bạn có thể tìm hiểu kỹ hơn JSX ở đây.

Xem code

React yêu cầu tạo một class để định nghĩa những HTML cần hiển thị. Đó là những gì chúng ta cần tạo ra trong phần JavaScript.

Chúng ta gọi React.createClass để tạo class React. Class này sau đó sẽ được append vào một phần tử HTML để user tương tác. Hàm createClass nhận lấy một đối tượng mà chứa hàm render, đó là tất cả những gì có trong class đơn giản của chúng ta. Hàm render trả về một chuỗi JSX đơn giản để hiển thị chữ hello world. Dòng cuối của mã JSX được dùng để hiển thị lớp hello world của chúng ta vào trong document.body. Phần HTML của React thì không có gì đáng để chú ý cả, và nó sẽ không thay đổi trong xuyên suốt bài, nên bạn có thể không cần để ý đến nó nữa.

Chèn Dữ liệu

Angular JS

Cách Angular dùng để chèn dữ liệu vào directives là thông qua scopes. scope trong Angular là một đối tượng chứa dữ liệu cho nhiều controls, như directives và controllers. Trên tất cả các scope, có một đối tượng scope root (được chèn với $rootScope), nó hoạt động như là scope trên cùng, được truy xuất bởi tất cả những thành Angular khác.

Một directive có thể có một trong một vài kiểu scope. Thứ nhất, không có scope, nghĩa là directive dùng scope của cha nó (thường là scope của controller). Thứ hai, scope độc lập (isolated scope), nghĩa là directive có scope của riêng nó, hoàn toàn tách biệt với scope của controller. Thường thì, scope của controller được dùng để chèn dữ liệu vào scope độc lập của directive. Theo kinh nghiệm của tôi, scope độc lập được dùng nhiều nhất. Cái này từ từ các bạn sẽ hiểu hoặc đọc 7 bước từ nghiệp dư đến chuyên gia - Phần 2 - Scope. Những thuộc tính của scope có 3 dạng:

Ràng buộc 2 chiều: Có nghĩa là mọi thay đổi trên trên scope cha hoặc scope con sẽ được phản ánh trên scope còn lại (“tuy 2 mà 1”). Điều này được biểu diễn bằng dấu bằng (=) khi ta tạo scope. Ràng buộc 1 chiều: Dữ liệu chỉ chuyển từ con lên cha thông qua 1 hàm. Biểu diễn bằng dấu và (&). Ràng buộc văn bản/chuỗi ký tự. Đơn giản là một chuỗi ký tự, và không có thông tin về ràng buộc. Biểu diễn bằng dấu (@). Xem code

Ở đây, chúng ta dùng ràng buộc văn bản để chuyển tên của tôi vào ví dụ hello world.

Thành phần hello-world trong phần HTML pane chứa 1 thuộc tính name, tương ứng với giá trị được định nghĩa trong scope độc lập của chúng ta. Controller của chúng ta định nghĩa biến name trong scope của nó. Mẫu (template) của directive tham chiếu tới biến scope name. Chúng ta thêm phần scope vào định nghĩa directive, nói lên 2 điều: 1, directive sẽ tạo 1 scope độc lập, và 2 là scope này chỉ có 1 tham số name theo dạng ràng buộc văn bản (@).

React JS

React chèn dữ liệu vào views vào lúc khởi tạo class, lúc root view được tạo hoặc thông qua nested view. Bên trong class, dữ liệu được truyền vào và truy xuất thông qua thuộc tính props của ngữ cảnh hiện tại.

Xem code

Chúng ta truyền dữ liệu { name: "Chris Harrington" } vào hàm khởi tạo view từ class HelloWorld, dữ liệu này sẽ được chuyển vào thuộc tính props bên trong class. Bây giờ JSX tạo một tham chiếu tới this.props.name để chúng ta có thể hiển thị thông điệp hello theo tên ai đó. Event Handlers

Angular JS

Trong Angular, hàm xử lý sự kiện thường được gán cho nhiều thành phần khác nhau trong view thông qua directives. Chắc bạn cũng thấy, Angular có nhiều tính năng là do dựa trên lực lượng hùng hậu những directive này. Chúng được dùng khắp nơi, đảm nhiệm “khắp” nhiệm vụ. Directive ng-click là 1 thuộc tính có thể đặt cho những thành phần HTML để đăng ký (hooks up) hàm xử lý sự kiện click chuột được định nghĩa trong local scope.

Xem code

Chúng ta thêm 1 button vào trong template của directive, có nhiệm vụ thiết lập thông điệp chào khi được click. Nó sử dụng directive ng-click để gọi hàm greet. Trong directive có thêm phần link. Phần này được gọi khi directive được hiển thị và được dùng để thiết lập những hàm xử lý sự kiện và những giá trị mặc định, bạn hãy để ý code. Thuộc tính greeting được thiết lập là một chuỗi rỗng, và chúng ta tạo hàm xử lý sự kiện (greet) để xử lý click chuột lên button. React JS Xử lý sự kiện trong React làm việc thông qua việc thiết lập những thuộc tính tùy chỉnh (custom attributes) cho những thành phần HTML bạn đang tạo ra. Hầu hết các thành phần phổ biến điều có những hàm xử lý sự kiện liên quan.

Xem code

Phần này có một số điểm mới. Nổi trội là khái niệm state. Mỗi class React có state nội tại của riêng nó mà bên ngoài không thể sửa đổi. Bạn có thể sửa đổi state của một class/view bằng việc gọi phương thức setState của view’s context. Việc làm này sẽ kích hoạt một sự tái hiển thị (rerender) cho những thành phần bị thay đổi. Bạn lấy những thuộc tính của state như ví dụ this.state.name, là lấy thuộc tính name, và bạn cũng được gán giá trị theo cách này, nhưng sẽ không có sự tái hiển thị như trên (setState). Do đó setState được khuyên dùng.

Hàm getInitialState được dùng để thiết lập state ban đầu của view. Không có hàm này, this.state sẽ trả về null, và việc xây dựng view hỏng bét. Nếu bạn có dùng state trong class, thì phải có hàm này. Trong ví dụ của chúng ta, chúng ta thiết lập thuộc tính state greeting bằng một chuỗi rỗng. Hàm greet xử lý sự kiện click chuột lên button. Thuộc tính onClick được liên kết tới hàm greet để thiết lập xử lý sự kiện. Nested Views

Angular JS

Nested Views trong Angular được hiện thực bằng việc dùng nhiều directives. Một directive có thể chứa một tham chiếu (reference) tới một directive khác. Chú ý là mỗi directive có thể có scope độc lập của riêng nó. Angular dùng ý tưởng transclusion (nhúng) để hiển thị những thành phần con. Transclusion thì hơi phức tạp, đây là documentation để bạn tìm hiểu. Ví dụ của chúng ta là:

Ở đây, tôi tạo ra một directive wrapper để bọc directive hello-world, sau đó chỉnh padding và màu nền xám trong CSS. Tìm hiểu code nhé?

Xem code

Trước tiên. Tôi dời templates vào trong thẻ script trong HTML để code được gọn gàng. Trong code khởi tạo directive, tôi sẽ lấy mẫu HTML từ những thẻ script này. Tôi tạo directive wrapper. Nó đơn giản chỉ chứa một thẻ div được style như đã nói ở trên. Việc khởi tạo directive wrapper chỉ chứa một thuộc tính (không có trong directive hello-world) là: thuộc tính transclude. Thuộc tính này nói rằng những thành phần con của directive sẽ được hiển thị thông qua directive Angular có sẵn là ng-transclude. Bạn có thể thấy ng-transclude trong mã HTML của mẫu wrapper. Bạn cũng thấy trong mã HTML của directive hello-world, thẻ wrapper chứa những thành phần con hiển thị câu chào. Những thành phần con này được hiển thị vào directive ng-transclude trong directive wrapper. CSS như đã nói.

React JS

Nested element trong React, theo tôi, là dễ dùng hơn nhiều. Chúng ta khai báo nhiều classes, rồi tham chiếu class con trong hàm render của class cha.

Xem code

Giống như trong ví dụ Angular, chúng ta bọc nội dung của HelloWorld trong class Wrapper, rồi cũng thiết lập CSS lên wrapper này.

Class Wrapper có trước class HelloWorld bởi vì nó được dùng trong hàm render của class HelloWorld. Hàm render của class Wrapper chứa một thẻ div. Bên trong div này là tham chiếu tới một biến đặc biệt: this.props.children. Biến này chứa tất cả thành phần của một class khi được hiển thị bên trong một class khác. Class HelloWorld chứa những thành phần HTML chính của ví dụ, lại nằm trong thẻ Wrapper, và những thành phần này sẽ được hiển thị thông qua biến this.props.children. Class HelloWorld chứa tham chiếu tới class Wrapper để tạo 1 wrapping container. Vậy thôi. Không có thêm thay đổi nào nữa. Theo tôi, nó trong sáng hơn Angular. Bạn có thể đọc thêm về cài đặt thành phần con của React, ở đây.

Vòng lặp

Cả AngularReact đều cung cấp những cách thức để hiển thị views bằng cách lặp qua tập hợp dữ liệu. Với Angular, nó dùng directive có sẵn ng-repeat. Còn React, chúng ta lặp và tạo nhiều thành phần HTML, gom chúng vào trong 1 mảng.

Angular JS

Chúng ta dùng directive ng-repeat để tạo 3 câu chào khi có click lên button greet.

Xem code

Directive hello-world có một thẻ div sau button greet, thẻ div này dùng directive ng-repeat. Điều này có nghĩa là thẻ div này sẽ được lặp lại ứng với mỗi câu chào greeting trong mảng greetings. Hàm greet trong scope của directive thêm 3 câu chào riêng biệt vào mảng greetings, được dùng để hiển thị lên view.

React JS

Lặp trong React thì đơn giản, vì JSX cơ bản là JavaScript, nên chúng ta sẽ viết 1 vòng lặp JavaScript để hiển thị mảng.

Xem code

  • Hàm render method trong class HelloWorld được sửa lại để tham chiếu hàm renderGreetings, có nhiệm vụ hiển thị tất cả câu chào greetings trong state. Trong JSX, mọi hàm đều có thể chứa mã HTML, chứ không phải chỉ có hàm render.
  • Hàm greet đẩy 3 câu chào riêng biệt vào biến state greetings.
  • Hàm getInitialState thay biến greeting bằng biến greetings, được khởi tạo là 1 mảng rỗng.

Kết luận

Angular JS và React JS có nhiều khác biệt lớn, nhưng chúng đều có thể thực hiện những việc/nhiệm vụ giống nhau. Tôi có thiên vị React. Angular là 1 framework đầy đủ tính năng hơn nhiều so với React, React thì không thực sự là 1 framework, nhưng điều đó chẳng có nghĩa gì khi mà tôi không cần quá nhiều những tính năng mà Angular cung cấp. Với React, tôi viết ít code hơn nhưng làm được nhiều việc hơn, và React có hiệu năng tốt hơn Angular do React cài đặt DOM ảo (virtual DOM). Nhưng cũng phải nói, hỗ trợ cho Angular thì lớn hơn rất rất nhiều, cộng đồng lớn hơn, còn React chỉ mới bắt đầu.

Cảm ơn bạn đã đọc bài!


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí