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
4k
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
170
Webアプリケーション設計の第一歩は ディレクトリの整理から / Encraft 1
okunokentaro
34
10k
JSONとJSON Schemaを改めて理解する / tokyo_study
okunokentaro
9
2.3k
それでもどうしてRecoilを使うのか / Harajuku.ts Meetup Recoil
okunokentaro
19
5.5k
TypeScriptは10年でこんなに進化しました / TechFeed Experts Night 11
okunokentaro
6
1.7k
Hasura.io RDBをサクサク作る方法はARやO/RMだけじゃなくなりました/hasura-io
okunokentaro
5
640
コードには型アノテーションよりも要件アノテーションを増やせ!/harajukuts2
okunokentaro
14
6.3k
10年と3ヶ月でWebサービスを作った話 / Piyogrammer Conference 2021
okunokentaro
2
1k
Other Decks in Programming
See All in Programming
PHP ステートレス VS ステートフル 状態管理と並行性 / php-stateless-stateful
ytake
0
100
How mixi2 Uses TiDB for SNS Scalability and Performance
kanmo
38
14k
CDK開発におけるコーディング規約の運用
yamanashi_ren01
2
140
Amazon ECS とマイクロサービスから考えるシステム構成
hiyanger
2
570
ARA Ansible for the teams
kksat
0
150
Spring gRPC について / About Spring gRPC
mackey0225
0
220
1年目の私に伝えたい!テストコードを怖がらなくなるためのヒント/Tips for not being afraid of test code
push_gawa
0
220
Flutter × Firebase Genkit で加速する生成 AI アプリ開発
coborinai
0
160
Ruby on cygwin 2025-02
fd0
0
150
2024年のWebフロントエンドのふりかえりと2025年
sakito
3
250
Amazon S3 TablesとAmazon S3 Metadataを触ってみた / 20250201-jawsug-tochigi-s3tables-s3metadata
kasacchiful
0
170
CI改善もDatadogとともに
taumu
0
120
Featured
See All Featured
Keith and Marios Guide to Fast Websites
keithpitt
411
22k
Unsuck your backbone
ammeep
669
57k
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
280
13k
Bootstrapping a Software Product
garrettdimon
PRO
306
110k
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
330
21k
Git: the NoSQL Database
bkeepers
PRO
427
64k
Building a Modern Day E-commerce SEO Strategy
aleyda
38
7.1k
Code Reviewing Like a Champion
maltzj
521
39k
Adopting Sorbet at Scale
ufuk
74
9.2k
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
30
2.2k
Optimizing for Happiness
mojombo
376
70k
Why You Should Never Use an ORM
jnunemaker
PRO
55
9.2k
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と移植戦略