ReactとAngular2の使い方やコードの違いを状態管理、CSS適用、単体テストで比較する:モダンなフロントエンド開発者になるためのSPA超入門(終)(2/3 ページ)
フロントエンド開発のアーキテクチャである「SPA(Single Page Application)」について、開発に必要となる各種フレームワークの特徴や作り方の違いなどを紹介する連載。今回は、状態管理の方法やCSSの適用、単体テストの実装方法について比較し、ReactとAngular2の違いを明らかにする。
コラム Fluxアーキテクチャ
Reactは、あくまでViewを提供するライブラリであるため、たくさんのコンポーネント間で状態を共有するような、複雑な状態管理は本来の役割ではありません。今回のサンプルでは使っていませんが、Reactで大規模な状態管理を実現するための仕組みに「Flux」と呼ばれるアーキテクチャがあります。
FluxアーキテクチャはFacebook社が提唱している状態管理のためのアーキテクチャです。「View」「Action」「Dispatcher」「Store」の4つの役割が存在し、データの流れを1方向に制限するという特徴を持っています。それぞれの役割は表1の通りです。
役割 | 説明 |
---|---|
View | データの表示 |
Action | データ更新のためのアクション発行 |
Dispatcher | データ更新のためのイベント通知 |
Store | データの表示管理 |
アプリケーションでの状態管理が構造化されることで、処理の役割が明確に分離できます。そのため、役割ごとにソースコードを分割可能で、より大規模な開発がしやすくなります。
コラム Reduxフレームワーク
Fluxはあくまで考え方であり、Fluxの考えを取り込んだフレームワークが、現在人気のある「Redux」です。
ReduxはFluxアーキテクチャの考えを派生させたフレームワークで、「Reducers」と呼ばれる機構が登場するなど、Fluxと少し異なる部分がありますが、考え方はFluxと同様、役割の分割と1方向のデータフローが特徴です。
Angular2を使った状態管理
Angular2では、「サービス」を使って状態を一元管理しています。「サービス」とは、ログ出力やコンポーネントに依存しないビジネスロジックなど、画面から独立してさまざまな箇所で呼ばれる処理を定義するための機構です。
今回の実装では、「サービス」内の「プロパティ」に各Todoの情報をリストとして保持しています。それぞれのコンポーネントから共通して1つのサービスインスタンスを呼び出すことで、アプリケーションの状態を一元管理しています。
import { Injectable } from '@angular/core'; import { Todo } from '../todo'; @Injectable() export class TodoService { // アプリ内の状態をプロパティで保持 todos: Todo[]; constructor() { this.todos = []; } // 追加アクション addTodo(title:string): void { if(title){ this.todos.push({ name: title ,enabled: true}); } } // チェックアクション checkTodo(todo: Todo): void { todo.enabled = ! todo.enabled; } }
コンポーネントから「サービス」を呼び出す際は、「DI(Dependency Injection)」の仕組みを利用します。
DIについては連載第2回で触れましたが、「サービス」を使用するクラス(クライアント)に対して、外部からオブジェクトを注入するデザインパターンです。オブジェクトの生成と使用が分離することにより、柔軟性やテスト容易性が向上するといったメリットがあります。
Angular2でDIの仕組みを利用するためには、コンポーネント内の「@Component」アノテーション内で、「providers」属性を指定し、サービスプロバイダをインジェクタに登録する必要があります。
@Component({ selector: 'root', templateUrl: './app.component.html', providers: [TodoService], // DIするための設定 styleUrls: ['./app.component.css'] }) export class AppComponent { todos:Todo[]; //コンストラクタでサービスの受け取りが可能となる constructor(private service: TodoService) { this.todos = service.todos; } }
サービスプロバイダが登録された後は、コンストラクタの引数でサービスを受け取ることが可能になります。ここでは、子コンポーネントであるTodoListに渡すための、Todoの一覧をサービスから取得しています。
今回の実装では、サービス内に状態を変更する関数をまとめて定義しているため、データを更新する際も各コンポーネントからサービスを呼び出す必要があります。
Todoを追加する機能を持つAddComponentでは、画面から受け取ったinputValueの値をプロパティとして保持し、onClickのタイミングでaddTodo関数を呼び出しています。addTodo関数内ではロジックは保持せずサービスの呼び出しのみを行っています。
<div class="add"> <h2 class="title">TODOサンプルアプリケーション</h2> <input type="text" class="inputBox" placeholder="TODOを入力" [(ngModel)]="inputValue" > <input type="button" class="addBtn" (click)="addTodo()" value="登録"> </div>
import { Component, Input } from '@angular/core'; import {TodoService} from '../services/todo.service'; @Component({ selector: 'add', templateUrl: './add.component.html', styleUrls: ['./add.component.css'] }) export class AddComponent { inputValue; // コンストラクタで「サービス」の受け取り constructor(private service: TodoService) { } // 定義した関数内で「サービス」の呼び出し addTodo(): void { this.service.addTodo(this.inputValue); this.inputValue = ''; } }
スタイル(CSS)の適用
React、Angular2、それぞれにおけるCSSの適用方法について説明します。
css-modulesを使ったReactにおけるCSSの適用方法
Reactでは通常のHTML作成と同様、CSSにスタイルを定義する形で開発をすることが可能です。
ul li { position: relative; padding: 12px 8px 12px 10px; font-size: 18px; margin-top: 5px; border: solid 1px; background-color: #ffffff; border-radius: 10px; box-shadow: 0 10px 6px -6px #777; } ul li.checked { text-decoration: line-through; }
CSSで定義したスタイルに対して、コンポーネントでは「className」でclassを定義することでスタイルを適用することが可能です。
import React from 'react'; import '../styles/todo.css'; const Todo = (props) => { const { todo, checkTodo } = props; if (todo.isChecked) { return ( <li className="checked" onClick={() => checkTodo(todo.id)}>{todo.text}</li> ) } else { return ( <li onClick={() => checkTodo(todo.id)}>{todo.text}</li> ) } }; export default Todo;
これまでのCSS管理と同様、CSSのスコープがアプリケーション全体となるため、コーディングルールなどを厳格に設けないと「class名がバッティングしてしまう」など、大規模な開発は難しくなります。そこで、「css-modules」と呼ばれる考え方があり、Reactのコンポーネント単位でCSSにスコープを与えることが可能になります。
スコープがコンポーネント単位となるため、コンポーネントごとに同じclass名を使用することが可能になり、厳格なコーデイングルールが不要となります。その際はモジュールバンドラーと呼ばれる「Webpack」などが提供するCSSを動的に読み込む機能を利用することで、実装が可能です。
Angular2におけるCSSの適用
Angular-cliでコンポーネントを作成した場合、コンポーネントに対応したCSSファイルが同一のディレクトリ内に生成されるため、そのファイル内にスタイルを書き込むことで適応されます(手動でCSSファイルを作成した場合には、コンポーネント内のstyleUrlsプロパティでCSSとのひも付けを行う必要があります)。
@Component({ …… styleUrls: ['./todo.component.css'] // コンポーネントとCSSファイルとのひも付け }) export class TodoComponent { …… }
他にも、「スタイルを適用する方法としてテンプレートファイル内に直接styleタグを書き込む」「コンポーネント内にスタイル情報を書き込む」といった方法が提供されています。
どの方法を使っても、css-modulesの考え方と同様、スタイルはそれぞれのコンポーネントに閉じたスコープにのみ適用されるようになっています。
Copyright © ITmedia, Inc. All Rights Reserved.
関連記事
- JavaScriptを中心としたWebアプリ開発の栄枯盛衰まとめ――LiveScriptからAngularJS/React.jsまで
@ITが誕生した2000年頃はJavaScriptが不遇だった時代。そこから現在のような人気のプログラミング言語になるまでには、どのような歴史があったのか。15周年を迎えた@ITの豊富なWeb開発関連記事とともに振り返る。 - いまさら聞けないReact、Virtual DOM、JSX超入門
Facebookが公開しているJavaScriptライブラリ「React」について、その概要や特徴、Webページに導入する方法や基本的な使い方を解説します。 - アメブロでReactやIsomorphic Web Applicationを採用した理由――その成果と構成技術
2004年から続くブログサービス「アメブロ」が2016年9月にシステムをリニューアル。本連載では、そこで取り入れた主要な技術や、その効果を紹介していく。初回は、Isomorphic Web Applicationについて。