Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
ngUpgradeと移植戦略
Search
OKUNOKENTARO
March 21, 2016
Programming
6
4.1k
ngUpgradeと移植戦略
2016/3/21 ng-japan 2016の登壇に使用した資料です。
OKUNOKENTARO
March 21, 2016
Tweet
Share
More Decks by OKUNOKENTARO
See All by OKUNOKENTARO
トレタO/X アーキテクチャ移行記 Next.js App Router化への道のり / TORETA TECH UPDATE 1
okunokentaro
5
11k
Podcastを継続する技術 / refactoradio-240119
okunokentaro
1
190
Webアプリケーション設計の第一歩は ディレクトリの整理から / Encraft 1
okunokentaro
34
10k
JSONとJSON Schemaを改めて理解する / tokyo_study
okunokentaro
9
2.4k
それでもどうしてRecoilを使うのか / Harajuku.ts Meetup Recoil
okunokentaro
19
5.6k
TypeScriptは10年でこんなに進化しました / TechFeed Experts Night 11
okunokentaro
6
1.7k
Hasura.io RDBをサクサク作る方法はARやO/RMだけじゃなくなりました/hasura-io
okunokentaro
5
670
コードには型アノテーションよりも要件アノテーションを増やせ!/harajukuts2
okunokentaro
14
6.4k
10年と3ヶ月でWebサービスを作った話 / Piyogrammer Conference 2021
okunokentaro
2
1.1k
Other Decks in Programming
See All in Programming
Azure AI Foundryではじめてのマルチエージェントワークフロー
seosoft
0
170
Goで作る、開発・CI環境
sin392
0
230
WebViewの現在地 - SwiftUI時代のWebKit - / The Current State Of WebView
marcy731
0
120
おやつのお供はお決まりですか?@WWDC25 Recap -Japan-\(region).swift
shingangan
0
130
ISUCON研修おかわり会 講義スライド
arfes0e2b3c
1
440
明示と暗黙 ー PHPとGoの インターフェイスの違いを知る
shimabox
2
510
AI時代の『改訂新版 良いコード/悪いコードで学ぶ設計入門』 / ai-good-code-bad-code
minodriven
12
3.2k
dbt民主化とLLMによる開発ブースト ~ AI Readyな分析サイクルを目指して ~
yoshyum
3
1k
Deep Dive into ~/.claude/projects
hiragram
14
2.5k
VS Code Update for GitHub Copilot
74th
2
640
Code as Context 〜 1にコードで 2にリンタ 34がなくて 5にルール? 〜
yodakeisuke
0
130
Railsアプリケーションと パフォーマンスチューニング ー 秒間5万リクエストの モバイルオーダーシステムを支える事例 ー Rubyセミナー 大阪
falcon8823
5
1.1k
Featured
See All Featured
The MySQL Ecosystem @ GitHub 2015
samlambert
251
13k
How GitHub (no longer) Works
holman
314
140k
Agile that works and the tools we love
rasmusluckow
329
21k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
50
5.5k
Keith and Marios Guide to Fast Websites
keithpitt
411
22k
The Cost Of JavaScript in 2023
addyosmani
51
8.5k
No one is an island. Learnings from fostering a developers community.
thoeni
21
3.4k
Rails Girls Zürich Keynote
gr2m
95
14k
Chrome DevTools: State of the Union 2024 - Debugging React & Beyond
addyosmani
7
740
GraphQLの誤解/rethinking-graphql
sonatard
71
11k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
357
30k
Designing for humans not robots
tammielis
253
25k
Transcript
ngUpgradeと移植戦略 Mar 21, 2016 / ng-japan armorik83
はちさん 奥野 賢太郎 @armorik83 ng-kyoto代表 ChatWork株式会社
Qiita
ng-kyoto 4/10 渋谷ヒカリエ by teratail / ng-kyoto, ng-japan 4/16 サイボウズ大阪梅田オフィス
/ ng-kyoto, GDG神戸 ng-kyoto Angular 2ハンズオン勉強会 東西合同開催!!
お話しすること • なぜ移行戦略ではなく移植戦略なのか • 移植の前に現在地を知る • 移植の複数のフェーズ • Angular 1のこれって2ではどう書くの?
なぜ移行戦略ではなく 移植戦略なのか
その前に前提 • 業務としてAngular 2化を進めている上での知見 • 1と2は全く互換性のない同名の別フレームワーク というのは誤解 • UPGRADING FROM
1.X https://angular.io/docs/ts/latest/guide/upgrade.html • Angular 1から2へは移行可能
なのになぜ移植と呼ぶか • すべての手順を公式サイトの通りに進めるのは煩雑 • 世界中のAngularのコーディング・スタイルはバラバラ • 「ドキュメント通りに移行しなければ」と悩むより Angular 2流儀を覚えてイチから書いたほうが むしろ早い可能性もある
とはいえ移植はしんどい! • これを全て移植するのか…と憂鬱になる • できることなら移行したい • 楽に移行したい
段階的移植法 • 段階的移植法(armorik83命名) • 公式サイトの移行手順は活かしながら ある程度は移植(書き直し)する • 移行を複数のフェーズで捉える • 少しずつ移植を進めて
最終的には全てがAngular 2な状態へ
移植の前に現在地を知る
現在地はどこだ TypeScript Angular 2 ES2015 ES5 Coffee Script AngularJS 1.4,
1.5 1.3 1.2
TypeScript Angular 2 ES2015 ES5 Coffee Script AngularJS 1.4, 1.5
1.3 1.2 この距離で 移植難度が異なる 現在地はどこだ
言語をどうするか • CoffeeScriptが最も遠く TypeScriptが最も近い • ES5, ES2015の場合 「--allowJsを付けてJS, TSを混ぜてコンパイル」でも 動くので段階的TypeScript化でなんとかなる
• Angular 2はTypeScript専用ではないので ES5でも扱える
Angular 1.xのバージョン • 最新にするに越したことはない • 1.2を使っている場合 残念ながら移行への道のりは険しい • 一旦1.5まで移行してから2への段階的移植を 試みたほうがよい(もしくは諦める)
• 1.4を使っている場合 1.4 → 1.5は楽なのでまず1.5にして2化に備えておく
いまAngular 1をどう書くか • 手前味噌ですがAngularJSモダンプラクティスは 現在も有効 http://qiita.com/armorik83/items/5542daed0c408cb9f605 • AngularJS老化チェックの記事で現在のプロダクトの 老化度を測り若返らせる(1.5に寄せていく) http://qiita.com/laco0416/items/edfa917583af4593ad6c
1.5 Componentにすべきか • Angular 1.5から追加された angular.module().component() • 1.4までのDirectiveは1.5のComponentに 一旦移行してからAngular 2化すべきか?
1.5でのDirectiveの扱い • 現在のプロダクトで無理にDirectiveとComponentを 両方を混ぜる必要はない(好みの話) • 無理にすべて移行する必要も無い • ただしDirectiveを構造的に細分化しているなら不要だが そこの考慮が無い場合一旦Componentにしていくとよい •
$scopeを多用している場合、先にComponent化すべき • Angular 1.5を今から新しく始めるならば 最初からComponentを使うべき
移植の複数のフェーズ
移植戦略マップ TypeScript Angular 2 ES2015 ES5 Coffee Script AngularJS 1.4,
1.5 1.3 1.2 この箇所を細分化する
複数のフェーズ 1. 準備 2. ng2 Component作成 3. ng1 ServiceのUpgrade 4.
ng2 Componentへの機能移植 5. ng1 Serviceのng2 Service化 6. Router周り、bootstrap周りの移植
1. 準備 • 幾つものUIパーツで構成されるWebアプリケーションを想定 • チーム内で合意を得る、稟議を通す 稟議の通し方は様々だが、パフォーマンス改善は説得力ある • キャッチアップの社内勉強会、外部の勉強会に参加 •
branchを分ける、依存関係の修正 ビルド環境の調整など足回りを組み立てる • 一度Angular 2チュートリアルでミニアプリ開発を 試しておくと吉
2. ng2 Component作成 app-main app-nav app-contents app-nav-items app-contents-body ng1 Directiveと同要素名でng2
Componentを作成 とにかくガワを作る まだマウスやウインドウのイベント・ハンドリング移植はしない ng-ifやng-repeatは、ここで*ngIfや*ngForに書き直す app-main app-nav app-contents app-nav-items app-contents-body app-root
2. ng2 Component作成 ルートだけはng1 Directiveで そこに連なる要素をすべてng2 Componentにする ルートに置くng2 Componentを upgradeAdapter.downgradeNg2Component()でng1でも利用できるよう変換
app-main-ng2 app-nav app-contents app-nav-items app-contents-body app-root 変換 ng1の世界 ng2の世界
2. ng2 Component作成 テンプレートからng-appを除去 angular.bootstrap()形式にリファクタリングしておく さらにupgradeAdapter.bootstrap()に書き直すとng1内でng2を扱える app-main-ng2 app-nav app-contents app-nav-items
app-contents-body app-root 変換 ng1の世界 ng2の世界
3. ng1 ServiceのUpgrade • ng1 ServiceをupgradeAdapter.upgradeNg1Provider()に通して ng2で利用可能な状態にする • ng2 Componentにng1
ServiceをDIする • コードの書き方を詳細に話すと時間がないので割愛 • UPGRADING FROM 1.X https://angular.io/docs/ts/latest/guide/upgrade.html
4. ng2 Componentへの機能移植 • 機能をng1 Directiveからng2 Componentへ移植していく • コピペでいけるものもあるが、書き直す必要がある箇所もある •
ng1 Directiveにロジックをベタッと書かず あらかじめng1 Serviceから利用する形にしておくと だいぶこのフェーズは楽になる • ng2 ServiceはupgradeAdapter.bootstrap()で起動する 前にupgradeAdapter.addProvider()で登録しておく
ng1 Serviceをng2 Serviceに移植する 移植作業中はng2 Componentからng1 Serviceを使っておく 5. ng1 Serviceのng2 Service化
app-main-ng2 app-nav app-contents app-nav-items app-contents-body app-root Ng1APIService Ng1StorageService Ng2APIService 裏で移植作業 Ng2StorageService
必要な移植作業が終わったらng1 Serviceはお役御免となる app-main-ng2 app-nav app-contents app-nav-items app-contents-body app-root Ng1APIService Ng1StorageService
Ng2APIService 移植できた Ng2StorageService 5. ng1 Serviceのng2 Service化
ng2 Service化できたらng1 Serviceをng2 Serviceに付け替える テストで切り替え前後の挙動に変化が無いか担保できるとよし 最悪目視・手動操作で確認 app-main-ng2 app-nav app-contents app-nav-items
app-contents-body app-root Ng2APIService ng2化 Ng2StorageService 5. ng1 Serviceのng2 Service化
app-main app-nav app-contents app-nav-items app-contents-body app-root 6. Router周り、bootstrap周りの移植 ルート要素もng2化、アダプタを用いた接合部分もアダプタを外す ui-routerやbootstrap周りもAngular
2に移植 おそらくけっこう大変なフェーズ(うちもまだ辿り着いていない) app-main-ng2 app-nav app-contents app-nav-items app-contents-body app-root
Angular 1のこれって 2ではどう書くの?
Filterの使用 ng1 <td>{{movie.title | uppercase}}</td> ng2 <td>{{movie.title | uppercase}}</td> Angular
2ではFilterのことをPipeと呼ぶ Angular 1 to 2 Quick Refを読めば対応が表にまとめられているので必読 https://angular.io/docs/ts/latest/cookbook/a1-a2-quick-reference.html
Filterの定義 ng1 angular.module().filter("reverse", function(){}); ng2 @Pipe({name: "reverse"}) export class FilterPipe
implements PipeTransform { transform(value: string): string { return // } } Pipe Annotationを付けたclassにtransform()メソッドを実装する
Serviceの定義 ng1 angular.module().service("Service", function(){}); ng2 @Injectable() export class Service {
// } bootstrap(RootComponent, [Service]); Injectable Annotationを付けたclassをbootstrap第二引数に渡す または、DIしたいComponentのprovidersプロパティに渡す
イベントのハンドリング ng1 $element[0].addEventListener("mouseenter", function(){ this.color = "#0000ff"; $scope.$apply(); }); ng2
@HostListener("mouseenter", ["$event"]) onMouseenter($event: MouseEvent) { this.color = "#0000ff"; } 自身のイベントをハンドリングするなら @HostListener()をメソッドに付ける
$apply ng1 $scope.$apply(); ng2 ChangeDetectorRef.detectChanges() Zonesのお陰で基本的に手動での変更検知発火は不要 どうしても手動で再bindを走らせたいときは ChangeDetectorRefをDIし、detectChanges()を呼ぶ
$watch ng1 $scope.$watch("name", function(newVal, oldVal) { // }); ng2 ngOnChanges(changes)
{ console.log(changes["name"].currentValue); } ngOnChanges()にて可能
$watchCollection ng1 $scope.$watchCollection("obj", function(newObj, oldObj) { // }); ng2 ngDoCheck()
{ const changes = this.differ.diff(this.list); if (changes) { // } } 配列ならばIterableDiffers、オブジェクトならばKeyValueDiffersを用いて ngDoCheck()内で差分を検出することで可能 準備が面倒な上に処理負荷が大きめなので極力ngOnChanges()で済ませたい
One-time Binding ng1 {{::param}} ng2 @Component({ selector: "my-component", changeDetection: ChangeDetectionStrategy.OnPush,
template: `{{param}}` }) ChangeDetectionStrategy.OnPushを指定する その中で手動で再bindしたいときは ChangeDetectorRef.markForCheck()を呼ぶ
compile, link, destroy • ng1 DirectiveのlinkはngOnInit(), ngAfterViewInit()などの Lifecycle Hooksで代替可能 https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html
• $scope.$on("$destroy")はLifecycle HooksのngOnDestroy() で代替可能 • ng1 Directiveのcompileプロパティで行っていたフックは ng2 Componentでは代替不可 • ngOnInit()で近いことはできる jQueryを使って別要素を生成していたりすると移植困難
まとめ
なんとかなる
ありがとうございました ngUpgradeと移植戦略