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
9.6k
Podcastを継続する技術 / refactoradio-240119
okunokentaro
1
160
Webアプリケーション設計の第一歩は ディレクトリの整理から / Encraft 1
okunokentaro
34
9.9k
JSONとJSON Schemaを改めて理解する / tokyo_study
okunokentaro
9
2.2k
それでもどうしてRecoilを使うのか / Harajuku.ts Meetup Recoil
okunokentaro
19
5.4k
TypeScriptは10年でこんなに進化しました / TechFeed Experts Night 11
okunokentaro
6
1.7k
Hasura.io RDBをサクサク作る方法はARやO/RMだけじゃなくなりました/hasura-io
okunokentaro
5
620
コードには型アノテーションよりも要件アノテーションを増やせ!/harajukuts2
okunokentaro
14
6.2k
10年と3ヶ月でWebサービスを作った話 / Piyogrammer Conference 2021
okunokentaro
2
1k
Other Decks in Programming
See All in Programming
3rd party scriptでもReactを使いたい! Preact + Reactのハイブリッド開発
righttouch
PRO
1
600
Jakarta EE meets AI
ivargrimstad
0
200
Duckdb-Wasmでローカルダッシュボードを作ってみた
nkforwork
0
130
エンジニアとして関わる要件と仕様(公開用)
murabayashi
0
300
Webの技術スタックで マルチプラットフォームアプリ開発を可能にするElixirDesktopの紹介
thehaigo
2
1k
WebフロントエンドにおけるGraphQL(あるいはバックエンドのAPI)との向き合い方 / #241106_plk_frontend
izumin5210
4
1.4k
聞き手から登壇者へ: RubyKaigi2024 LTでの初挑戦が 教えてくれた、可能性の星
mikik0
1
130
初めてDefinitelyTypedにPRを出した話
syumai
0
420
ペアーズにおけるAmazon Bedrockを⽤いた障害対応⽀援 ⽣成AIツールの導⼊事例 @ 20241115配信AWSウェビナー登壇
fukubaka0825
6
2k
タクシーアプリ『GO』のリアルタイムデータ分析基盤における機械学習サービスの活用
mot_techtalk
4
1.4k
GitHub Actionsのキャッシュと手を挙げることの大切さとそれに必要なこと
satoshi256kbyte
5
430
ローコードSaaSのUXを向上させるためのTypeScript
taro28
1
620
Featured
See All Featured
The Illustrated Children's Guide to Kubernetes
chrisshort
48
48k
Intergalactic Javascript Robots from Outer Space
tanoku
269
27k
What's new in Ruby 2.0
geeforr
343
31k
Being A Developer After 40
akosma
87
590k
Large-scale JavaScript Application Architecture
addyosmani
510
110k
How to Ace a Technical Interview
jacobian
276
23k
Thoughts on Productivity
jonyablonski
67
4.3k
The Cost Of JavaScript in 2023
addyosmani
45
6.8k
Adopting Sorbet at Scale
ufuk
73
9.1k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
26
1.4k
BBQ
matthewcrist
85
9.3k
Testing 201, or: Great Expectations
jmmastey
38
7.1k
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と移植戦略