自己紹介
会社: ローカルワークス
名前: 伊藤敦之
年齢: 24歳 ← ダブったので新卒です!!
趣味: 囲碁
仕事: パソコンを触ること
作ったもの紹介...
の前に...
言い訳タイム
- Rails のプログラミングを始めて1年の初心者です!
- JS を勉強し始めて半年の超絶初心者です!!
- 開発期間はそれぞれ2週間です!!!
作ったもの紹介!!!
-
本を登録して検索できるWEBアプリ
- Angular 4.2.4
- Angular Material
- Rails 5.1
-
Evernote みたいな日記WEBアプリ
- React 15.5.4
- react-router-dom
- redux
- Material UI
- axios
- Rails 5.1
今日話したいこと
初心者からみた
- 学習コスト度
- ハマる度
- 感動度
フレームワークとしてくらべてみました
Angular
学習コスト度: ★☆☆
ハマる度: ★★☆
感動度: ★☆☆
good
- view, router, HTTP requestなどなど,開発に必要なものは一通り揃ってる
- Angular Way に乗っかれば単一責務を実現しやすい
- JS, html, CSS のファイルが別れていて分かりやすい
bad
- TypeScript を勉強しなきゃいけない
- Angular4 の参考文献は英語がほとんど
- Angular内のルールが多い
勉強してみて
- Angular Tutorial をやれば誰でも動かせるようになる
- 応用箇所は先行事例が少ないので、公式リファレンスを読むしかない
React
学習コスト度: ★★☆
ハマる度: ★★☆
感動度: ★☆☆
good
- プロダクトに入れ込む粒度を選択できる
- 日本語の参考文献が多い
- 先行事例が多い
bad
- jsx が気持ち悪い
- ライブラリーの依存が多くなるので簡単にアップデートできない
- 勉強するものが多い
勉強してみて
- React Tutorial だけやってもフロント全般を任せられる程の知識はつかない
- ライブラリーごとに勉強が必要
- ClassName が罠...
開発ツールでくらべてみました
Angular
学習コスト度: ★☆☆
ハマる度: ★☆☆
感動度: ★★★
- Angular Material
- angular cli
- Angury
React
学習コスト度: ★☆☆
ハマる度: ★☆☆
感動度: ★★★
- Material UI
- create react app
- React Developer Tools
勉強してみて
- 開発ツールは Angular の方が使いやすく、分かりやすい
- Material UI はCSSを当てたい時にインラインCSSみたいに書き込まなくちゃいけなくて、最悪の場合JSXにべた書きして気持ち悪い
- create react app はコマンドラインインタフェースとしては貧弱
アーキテクチャでくらべてみました
勉強する前
「Angular も React も所詮コンポーネントフレームワークっしょ。
チュートリアルもやったし、余裕っしょ。(鼻、ホジホジ)」
勉強し始めて...
「設計、むずすぎだろォォォォォォォォォォオオオオ!!!!!!!!!!!!!(ぐはっ)(ばたっ)」
図解します
じゃ、くらべていきましょうか...
コンポーネントの設計
ここは Angular, React 同じ考え方
Container Component と Presentational Component
学習コスト度: ★☆☆
ハマる度: ★☆☆
感動度: ★☆☆
- データの流れを一方向にするための役割分担
- Container Component は how things work (データ収集や取り扱い方)
- Presentational Component は how things look (データの表示)
Angular
# Container Component
export class HomeComponent implements OnInit {
books: Observable<Book[]>;
private searchTerms = new BehaviorSubject<string>('');
constructor(
private getBookService: GetBookService,
private bookSearchService: BookSearchService
) { }
onSearch(term: string): void {
this.searchTerms.next(term);
}
ngOnInit(): void {
this.books = this.searchTerms
.debounceTime(300)
.distinctUntilChanged()
.switchMap(term => term
? this.bookSearchService.search(term) : this.getBookService.all())
.catch(error => {
console.log(error);
return Observable.of<Book[]>([]);
});
}
}
# Presentational Component
export class ListsComponent {
@Input() books: Book[]
}
React
# Container Component
class Home extends Component {
componentDidMount() {
const { diariesActions } = this.props
diariesActions.getDiaries()
}
render() {
const { diariesActions, selectedIndex, diaries } = this.props
return (
<div className='body'>
<div className='side'>
<Options />
<Lists lists={diaries.getResult} onClick={(id) => diariesActions.selectDiary(id)} />
</div>
<div className='main'>
<Page page={diaries.getResult[selectedIndex]}/>
</div>
</div>
);
}
}
Home.propTypes = {
diaries: PropTypes.object.isRequired,
selectedIndex: PropTypes.number.isRequired,
diariesActions: PropTypes.object.isRequired,
};
function mapStateToHomeProps(state) {
return {
diaries: state.diaries,
selectedIndex: state.selectDiary.index
};
}
function mapDispatchToHomeProps(dispatch) {
return {
diariesActions: bindActionCreators(DiariesActions, dispatch),
};
}
export default connect(
mapStateToHomeProps,
mapDispatchToHomeProps
)(Home);
# Presentational Component
const Lists = ({ lists, onClick }) => (
<div>
<ul>
{lists.map(list => (
<li key={list.id} onClick={() => onClick(list.id)}>
<List list={list} />
</li>
))}
</ul>
</div>
);
export default Lists;
state と prop の管理
ここ、一人で勉強していくのつらかったです...
Angular RxJS
学習コスト度: ★★★
ハマる度: ★★★
感動度: ★★★
Angular は React x Redux みたいに完成された設計方法がなく、state と prop の管理の設計で悩む必要がある。
今回は Angular 開発が推奨している RxJS に基いて、state と prop の管理や非同期処理を行ってみました。
こんな感じでやってみました
- prop をいじるのはバックエンドに任せる
- HTTP request は全部 Service に切り出す
- prop は Observable の形で保持する
- state が変更された時に Observer を Push するようなメソッドを用意する
今回は Angular の双方向データバインディングを使用し、イベントの発火時に state を Container Component に知らせ、そのイベントで Observer を Push するようなメソッドを用意する
# Container Component
export class HomeComponent implements OnInit {
books: Observable<Book[]>;
private searchTerms = new BehaviorSubject<string>('');
constructor(
private getBookService: GetBookService,
private bookSearchService: BookSearchService
) { }
onSearch(term: string): void {
this.searchTerms.next(term);
}
ngOnInit(): void {
this.books = this.searchTerms
.debounceTime(300)
.distinctUntilChanged()
.switchMap(term => term
? this.bookSearchService.search(term) : this.getBookService.all())
.catch(error => {
console.log(error);
return Observable.of<Book[]>([]);
});
}
}
# Presentational Component
export class SearchComponent {
@Output() onSearch = new EventEmitter<string>();
search(term: string) {
this.onSearch.emit(term);
}
}
勉強してみて
- RxJS は鬼むずい
- 参考文献がほとんどなくい
やったことは...
- 公式リファレンスを読みます <- 英語つらい...
-
console.log
で逐一データの中身を確認する
こんな牛歩戦術で RxJS を突破しました。
おそらくこんなつらいことが将来待ち受けるでしょう
- prop 取得後の処理を Container Component に書くので、 Container Component がものすごくファットになる
- Component が複数入れ子になっている時、双方向データバインディングでイベントを Container Component まで持っていくのはめんどくさい
- どこからでも Observable に Push できてしまうので、テストを書くのが大変
React Redux
学習コスト度: ★★☆
ハマる度: ★★★
感動度: ★★★
「React やるなら Redux 一択でしょ!」ってことで、思考停止で Redux を始めました。笑
Redux は「Action と Reducer を state を管理してる場所に渡す」ってことなので、
登場人物は Action と Reducer ということになります。
# こんな感じでやってみました
- Redux の Action のファイルを、バックエンドの Rails の controller の単位に合わせる
- prop の変更はバックエンドの処理を経由して Redux の Reducer にセットさせる
- state の変更はバックエンドの処理を経由せず Redux の Reducer にセットさせる
Action と Reducer を Rails の Controller の一部だと思うと理解は早かったと思います。
# バックエンドの処理を経由するAction
export function getDiaries() {
return dispatch => {
dispatch(getDiaryListRequest());
Axios.get('http://127.0.0.1:9292/diaries.json').then(
response => dispatch(getDiaryListResult(response.data))
).catch(
() => dispatch(getDiaryListResult(false))
);
};
}
function getDiaryListRequest() {
return {
type: Diary.GET_LIST_AJAX_REQUEST,
};
}
function getDiaryListResult(result) {
return {
type: Diary.GET_LIST_AJAX_RESULT,
result,
};
}
# バックエンドの処理を経由しないAction
export function selectDiary(id) {
return {
type: Diary.SELECT_DIARY,
id,
};
}
lass DiariesController < ApplicationController
before_action :set_diary, only: [:show, :update, :destroy]
def index
@diaries = Diary.all
end
def show
end
def create
@diary = Diary.new(diary_params)
if @diary.save
render :show, status: :created, location: @diary
else
render json: @diary.errors, status: :unprocessable_entity
end
end
private
def set_diary
@diary = Diary.find(params[:id])
end
def diary_params
params.require(:diary).permit(:title, :content)
end
end
勉強してみて
- Redux はめんどくさい
- ルールがかっちりしている分、ちょっとしたことに対する手続き的なものが多い
また、非同期処理をやる場合はさらに、redux-thunk
,redux-saga
,redux-promise
などなど学ぶことは多い
やったことは...
- 初心者向けの日本語記事を10個ぐらい読む
- Youtube で説明しながらコーディングしている人がいるので、それをひたすら写経 <- 英語しかないのでつらい...
Redux は RxJS とは違い、データの慣れは一方向で輪っかを描いているので、ルールがわかればそんなに苦労しないです。(理解するまで Youtube の動画10回ぐらい見ました...)
おそらくこんなつらいことが将来待ち受けるでしょう
- どんなに小さい state の変更でも Redux のループを回さなきゃいけないので Action と Reducer が際限なく増えていく
- 依存ライブラリーのバージョンアップ、新しいライブラリーに切り替えで動かなくなる
まとめ
- SPA 作りたいなら Angular の方が良さそう
- 既存フレームワークの View の部分にJSライブラリーを入れたいなら React の方が良さそう
なぜなら...
- Angular はそれ自体で完結したフレームワーク
- React は自分でカスタマイズしてフレームワークにする
次は、 Rails5.1 に Webpacker が標準搭載されたので、 view ライブラリーとして React を使ってみたいと思います。笑