Elmはどうやって超小さいアセットを実現したのだろう
Elmはとても小さいアセットを作ることができる
We had a community member working on a 49,315 line application try the new version and he got 114kb after compilation, minification, and gzip. That is in the ballpark of the 100kb produced by the 2000 lines in the Vue implementation!
blog/small-assets-without-the-headache
リンク先の画像が示すように、Elmはリリースされた0.19で十分に小さいJavaScriptファイルを生成することができるようになりました。RealWorldアプリケーションでの検証結果ですが、ひと目で主要なフロントエンドのライブラリ利用時よりも小さくなっている事がわかります。
0.19リリース時に公開されたこのブログ記事を見て僕は「すごいなー」と感心しました。一体どうやってこの小さなファイルを生成することができるようになったのでしょう。
デッドコードを削除する
僕はしばしば課題に直面すると同じような課題に対してアプローチしているライブラリがないか探す事があります。JavaScriptにはNPMという素晴らしいシステムがあるので、簡単にプロジェクトにライブラリを導入できます。
しかし、ライブラリのコードをすべて利用することは殆どないのです。ライブラリが公開しているAPIはどれも魅力的なものですが課題を突破するためにはいくつかのAPIで事足りる事が多いからです。
デッドコードはこうしたときに生まれます。利用してないコードがあってもライブラリをインストールした以上、ビルド時には無視できない存在となっています。もちろんライブラリだけではなくプロジェクト内のコードだって同じように考えることができるでしょう。
巨大なプロジェクトでは利用されてないコードが生まれることを見落としていることもあるかもしれません。webpackに代表されるようにツリーシェイキングによってこのようなデッドコードは削ぎ落とされることになりますが、完璧に削ぎ落とせてないそうです。
どうやらwebpackはモジュール単位でルーチンを走らせており、モジュールの粒度が大きくなればなるほど利用してないコードが紛れ込む可能性が高くなります。直接APIを呼んでない場合でも別のルーチンから呼ばれている場合もあるためライブラリが増えれば増えるほどデッドコードは増加する傾向にあります。
対策としてlodashに代表されるように関数ごとにモジュールを分ける方法があるものの、JavaScriptではどのモジュールでも実行時に関数の変更ができるので今呼ばれてないからと言って簡単に削除することはできないのが実情です。
しかし、Elmの関数は実行時に再定義と削除ができないため、メイン関数から到達可能であれば残して到達できなければ削除することができます。これがElmがアセットを小さく保つことができた1つ目の理由です。
また、Elmの場合はNPMとは異なり、package.elm-lang.orgでライブラリを公開しており、ここで公開されているライブラリはすべてElmで書かれているので安心して同じように削除できるのです。
レコードフィールドをリネームする
Elmにおけるレコードとは次のようなものです。
student = { name = "Haruka" , age = 17 }
フィールドとはname
とage
を指していいます。これを参照する場合はstudent.name
となりますが、0.19ではコンパイルの前後でs.n
とリネームされるのです。
驚くべきことにこれで5% ~ 10%縮小するそうです。やはりこれもライブラリを含めたエコシステム全体で機能できます。
サマリ
- Elmは0.19で主要なライブラリのどれよりも小さいJSファイルを生成する事ができる
- デッドコードを削除する
- レコードフィールドをリネームする
所感
非常に小さなアセットの生成が可能になった背景には言語の特性とその特性沿ったエコシステムがありました。一方でElm 0.19には多くの破壊的変更が含まれています。
compiler/0.19.md at master · elm/compiler · GitHub
コンパイラの最適化にはこのような変更を盛り込むしかなかったのでしょうか。Elmは純粋な関数型プログラミングの土壌を用意してくれていますが、JavaScriptに変換する際に問題を作り出してしまうこともあったのではないでしょうか。
変更の裏にはこうした課題を突破できないのでやむを得ず適用した変更したものもあるかもしれません。世界観を一貫して守りきる思想の強い言語という雰囲気を感じ取ることができました。それにしても破壊的変更が多くて大変です。
Elmのアプリケーションコードを読んでみよう
ElmアプリケーションはElmアーキテクチャーに沿って構築していきます。構築したアプリケーションコードはElmのコアコード上でアプリケーションとして振る舞います。
Elmのコアコードがどんなことをしているのか気になったので、今回はElmアプリケーションが起動するまでにどういうことをやっているのかを追っていきたいと思います。
起動呼び出し
Elmアプリケーションを起動するには以下の呼び出しを最初に行います。
var app = Elm.Main.fullscreen()
他にもいろいろ起動するために呼び出せる関数はありますが、とりあえずこちらから考えます。見て分かる通り Elm.Main
オブジェクトを操作しているので、まずはこれがどういうオブジェクトなのか知ることから始めていきましょう。
Elm.Main
var Elm = {}; Elm['Main'] = Elm['Main'] || {}; if (typeof _user$project$Main$main !== 'undefined') { _user$project$Main$main(Elm['Main'], 'Main', undefined); }
非常にシンプルなコードですね。_user$project$Main$main
をみると、どうやらコンパイラは モジュール名$関数名
という名前付けをしているようです。
Elm['Main']
にどういう変更が加えられるのか見ていきましょう。
makeProgram
function makeProgram(flagChecker) { return F2(function(debugWrap, impl) { return function(flagDecoder) { return function(object, moduleName, debugMetadata) // == _user$project$Main$main { var checker = flagChecker(flagDecoder, moduleName); if (typeof debugMetadata === 'undefined') { normalSetup(impl, object, moduleName, checker); } else { debugSetup(A2(debugWrap, debugMetadata, impl), object, moduleName, checker); } }; }; }); }
_user$project$Main$main
は object, moduleName, debugMetadata
を引数に取る無名関数みたいです。flagChecker
は checkNoFlags
と checkYesFlags
が用意されています。
F2
は関数を受け取ってカリー化して返します。逆に A2
はカリー化された関数に引数を渡して評価する関数です。 debugWrap
は _elm_lang$virtual_dom$VirtualDom_Debug$wrap
です。
impl
は Html.program
に渡すおなじみのレコードです。見てわかるように Elm['Main']
は debugMetadata
の有無で呼び出す関数が異なります。
今回は undefined
でした。Html.program
はこの関数の実行結果で実装が定義されます。
normalSetup
function normalSetup(impl, object, moduleName, flagChecker) { object['embed'] = function embed(node, flags) { while (node.lastChild) { node.removeChild(node.lastChild); } return _elm_lang$core$Native_Platform.initialize( flagChecker(impl.init, flags, node), impl.update, impl.subscriptions, normalRenderer(node, impl.view) ); }; object['fullscreen'] = function fullscreen(flags) { return _elm_lang$core$Native_Platform.initialize( flagChecker(impl.init, flags, document.body), impl.update, impl.subscriptions, normalRenderer(document.body, impl.view) ); }; }
ここで Elm['Main']
に変更が加えられます。これ以上、 Elm['Main']
に変更は加えてなさそうです。
initialize
function initialize(init, update, subscriptions, renderer) { // ambient state var managers = {}; var updateView; // init and update state in main process var initApp = _elm_lang$core$Native_Scheduler.nativeBinding(function(callback) { var model = init._0; updateView = renderer(enqueue, model); var cmds = init._1; var subs = subscriptions(model); dispatchEffects(managers, cmds, subs); callback(_elm_lang$core$Native_Scheduler.succeed(model)); }); function onMessage(msg, model) { return _elm_lang$core$Native_Scheduler.nativeBinding(function(callback) { var results = A2(update, msg, model); model = results._0; updateView(model); var cmds = results._1; var subs = subscriptions(model); dispatchEffects(managers, cmds, subs); callback(_elm_lang$core$Native_Scheduler.succeed(model)); }); } var mainProcess = spawnLoop(initApp, onMessage); function enqueue(msg) { _elm_lang$core$Native_Scheduler.rawSend(mainProcess, msg); } var ports = setupEffects(managers, enqueue); return ports ? { ports: ports } : {}; }
続けて fullscreen
呼び出しによる起動に至るまでの処理を眺めていきます。この関数はかなり重要そうです。
_elm_lang$core$Native_Scheduler.nativeBinding
に渡されている関数はどちらも同じような内容です。おそらく view
を更新して effectManager
に cmds
と subs
を渡して変更を通知しています。
_elm_lang$core$Native_Scheduler.nativeBinding
を始めとする _elm_lang$core$Native_Scheduler
モジュールの関数はElmアプリケーションのイベントループに大いに関係しています。
core/Scheduler.js at 5.1.1 · elm-lang/core · GitHub
nativeBinding
が返すオブジェクトは後にenqueueされて処理されます。initApp
は初期化に必要な処理、onMessage
はメッセージを受け取った時に呼び出す処理だと推測できます。
setupEffects
は dispatchEffects
のための準備ではないでしょうか。もしかするとEffectManager関係の処理かもしれないです。ここで返しているオブジェクトは app
に代入されますが利用してないですね。
spawnLoop
function spawnLoop(init, onMessage) { var andThen = _elm_lang$core$Native_Scheduler.andThen; function loop(state) { var handleMsg = _elm_lang$core$Native_Scheduler.receive(function(msg) { return onMessage(msg, state); }); return A2(andThen, loop, handleMsg); } var task = A2(andThen, loop, init); return _elm_lang$core$Native_Scheduler.rawSpawn(task); }
一見するとこんがらがりそうなんですが、return
文に注目すると task
を enqueue
しているのがわかります。
core/Scheduler.js at 5.1.1 · elm-lang/core · GitHub
この task
は _elm_lang$core$Native_Scheduler.nativeBinding
の仲間です。これは _elm_lang$core$Native_Scheduler.andThen
で処理がラップされています。
core/Scheduler.js at 5.1.1 · elm-lang/core · GitHub
コールバック関数の loop
は handleMsg
という task
を設定して再度 andThen
でラップしています。_elm_lang$core$Native_Scheduler.receive
も _elm_lang$core$Native_Scheduler.nativeBinding
の仲間です。
core/Scheduler.js at 5.1.1 · elm-lang/core · GitHub
rawSpawn
core/Scheduler.js at 5.1.1 · elm-lang/core · GitHub
process
で task
をラップしています。 process
を enqueue
します。
work
キューに溜まった process
を処理します。取り出して process.root
があれば step
関数に渡します。process
がなくなると活動を停止します。
core/Scheduler.js at 5.1.1 · elm-lang/core · GitHub
step
enqueue
された process
は step
関数で処理されます
core/Scheduler.js at 5.1.1 · elm-lang/core · GitHub
enqueue
された task
をおさらいします。
process(andThen(loop, nativeBinding(task)))
順に処理していきますが、 loop
が実行される気配がありません。 loop
は process.stack.callback
に積まれたままです。
ちょっと謎ですね。 loop
はいつ呼ばれるのでしょうか。
nativeBinding(task)
の処理では登録していた関数を呼び出しています。ここで呼び出しているのは初期化処理をする関数です。
var initApp = _elm_lang$core$Native_Scheduler.nativeBinding(function(callback) { var model = init._0; updateView = renderer(enqueue, model); var cmds = init._1; var subs = subscriptions(model); dispatchEffects(managers, cmds, subs); callback(_elm_lang$core$Native_Scheduler.succeed(model)); });
renderer
は normalRenderer(document.body, impl.view)
の実行結果です。
function normalRenderer(parentNode, view) { return function(tagger, initialModel) { var eventNode = { tagger: tagger, parent: undefined }; var initialVirtualNode = view(initialModel); var domNode = render(initialVirtualNode, eventNode); parentNode.appendChild(domNode); return makeStepper(domNode, view, initialVirtualNode, eventNode); }; }
normalRenderer
では初期DOMの生成を行い、stepper
関数の作成を行います。
function makeStepper(domNode, view, initialVirtualNode, eventNode) { var state = 'NO_REQUEST'; var currNode = initialVirtualNode; var nextModel; function updateIfNeeded() { switch (state) { case 'NO_REQUEST': throw new Error( 'Unexpected draw callback.\n' + 'Please report this to <https://github.com/elm-lang/virtual-dom/issues>.' ); case 'PENDING_REQUEST': rAF(updateIfNeeded); state = 'EXTRA_REQUEST'; var nextNode = view(nextModel); var patches = diff(currNode, nextNode); domNode = applyPatches(domNode, currNode, patches, eventNode); currNode = nextNode; return; case 'EXTRA_REQUEST': state = 'NO_REQUEST'; return; } } return function stepper(model) { if (state === 'NO_REQUEST') { rAF(updateIfNeeded); } state = 'PENDING_REQUEST'; nextModel = model; }; }
makeStepper
は 1フレームごとに実行される関数の登録を行う関数 stepper
の作成を行います。virtualDOMの振る舞いを記述しているのもここになります。DOM差分を計算して適用している様子がわかります。
applyPatches
では差分適用のほかイベントに対する関数の登録も行っています。renderer
によって作成された関数 updateView
は onMessage
関数の中で利用されています。
dispatchEffects
では特に何もしないため最後にコールバック関数を呼び出します。
model
を succeed
でラップしてものをコールバック関数は受け取り新たな process.root
としています。同時に process
を enqueue
しています。
succeed
は step
関数で処理されます。process.stack.callback
に登録した loop
関数がようやく実行されます。
が、特に何事もなく process
は処理されて初期化処理は終了します。
まとめ
Elmアプリケーションの初期化処理でした。今回の重要登場人物は次のとおりです。
makeProgram
... デバッグの有無を判定normalSetup
...fullscreen
,embed
を定義initialize
... 初期化タスク定義と初期化実行spawnLoop
... 初期化タスクの合成rawSpawn
... processでラップ、キューにタスクを登録work
... キューに溜まったprocess
を処理step
... タスク処理makeStepper
... DOMの差分処理
流れもざっくり振り返りましょう。
Elmオブジェクト準備
- オブジェクトを作る
normalSetup
を呼んでfullscreen
を定義する
初期化する
initialize
を呼ぶspawnLoop
で初期化タスクを作成rawSpawn
を呼び、キューに追加work
を呼び初期化タスクを順番に実行step
を呼びタスクを処理、process.root
を設定initApp
に登録された初期化処理を実行- 初期viewの追加
「超速! Webページ速度改善ガイド 使いやすさは「速さ」から始まる」を読んだ初学者の感想
今までパフォーマンスといえばSQLチューニングやらWebサーバのチューニングやらしかやってこなかったpastelIncです。 Webパフォーマンスについて必要に駆られなかったこともあり、勉強してなかったんですがSPA開発をしていて必要にかられたので表題の本で勉強してみました。
なぜこの本だったのか
あほむさん (twitter: @ahomu) と煎茶さん (twitter: @1000ch) の存在が自分の中で大きかったからです。 以前から勉強会や記事、podcastでお見かけすることの多かったお二人です。なかでもパフォーマンスを語るシーンにおいては勉強になることも多く僕にとってのWebパフォーマンスの代名詞でした。そのお二人が書かれた本ということでこの本を手に取った次第です。
パフォーマンス初学者が読んでも良い内容か
間違いなく良いです。個人的には初学者が読んじゃいけない本なんてないと思ってます。興味があれば読む、それだけです。いきなり高度すぎたら少しずつ読み解いていけばよいのです。
超大満足
本書はそんなパフォーマンス初学者の僕にもうってつけな最高の本でした。読みながらただひたすらに「へぇ〜」って感じでした。 もう少し詳しく言うと本書は読み進めていくと「実際に自分でコード書いて確認したい〜」欲が溢れてきます。面白いです。
DevToolsとお友達になれる
Chrome DevToolsは Console
タブをメインで使ってましたが、実はいろいろできることを本書で知りました。
パフォーマンスボトルネックの手がかりをつかむための基礎知識が分かる本
本書を読むとパフォーマンス調査をするための基礎知識が手に入ります。パフォーマンス調査は仮説 -> 検証の繰り返しになると思いますが、仮説を立てるときの材料が豊富に用意されています。勘で仮説を立てるシーンが減りそうな気がします。また、仮説を怠り検証ばかり繰り返しなぜかわからないけどうまくいった経験がある方いると思いますが、なぜうまくいったのかを知らなければ同じことの繰り返しになってしまいます。本書は推測するな、計測せよを掲げていきなり検証をするのではなくまずは計測することの大切さを訴えています。本書を読むとなぜ計測が大切なのか身にしみると思います。
非機能要件を勉強する必要あるのか
あると思います。機能要件は開発チームの誰でも割りとヘルプできると思いますが非機能要件となると途端に職人を必要とする現場は多いのではないでしょうか。非機能要件は学ぶことでエンジニアとして一つ尖る事ができると思うのです。 また、パフォーマンスを知りビジネスサイドとも話ができると一口にパフォーマンスを担保してほしいと言われた時に、どこまで出来て何を妥協してほしいのか説明できるようになります。お金を積んで解決する問題であれば訴えることも出来ますし、ビジネスサイドも天秤にかけることができます。結果としてプロダクトは継続して受け入れられやすくなるのではないでしょうか。
まとめ
買いましょう。読みましょう。おすすめです。
Salesforce大切なことはドキュメントに詰まってる
自分が初めてSalesforce開発で必要となった知識の参照元を並べてみます。
Lightning Componentの初期化時に何かしたい
aura:attributeに入れる型って何があかんの?
既存のJSライブラリを使いたいんだけど
Lightning ComponentをLightningページで利用するための準備
私のドメインって何?
Lightning ComponentをLightning アプリケーションビルダーで利用する時に必須となる
何か参考にできるプロジェクトないの?
DreamHouse JP サンプルアプリケーション | Salesforce Developers
SOQLでリレーションクエリ作りたい
sObjectの関係が知りたい
ER図があるぞ
JavaScriptでのイベントの捌き方
条件分岐するマークアップ書けるの?
aura:if
Lightning アプリケーションと Lightning アプリケーションビルダーって違うの?
違う。Lightning アプリケーションは作ってもリリースする方法がわからなかったのでLightning アプリケーションビルダーを使ってLightning アプリケーションを作った。
Lightning Design System を使用した Lightning アプリケーションの作成 | Salesforce
コンポーネント間でJavaScriptコードを共有したいんだけど
静的リソースとして読み込んで使うしかないんや
Lightning Componentの型情報ってApexではどうなるの
Lightning Components & AuraEnabled method parameters: What’s working and what’s not
sObjectってなにさ
SOQLでの操作を一通り覚えたいんやけど
DML を使用したレコードの操作 単元 | Salesforce Trailhead
package.xml
の書き方教えて
開発環境とステージング環境作りたいんだけど
Sandbox利用しよう。DevHubがオンになっている組織ならスクラッチ環境を開発で使って、ステージングはSandboxでやるといいと思います。
Sandbox の詳細 単元 | Salesforce Trailhead
VisualforceってLightning Componentと比べてどうなの
Visualforce と Lightning Experience | Salesforce Trailhead
クイックアクション、アプリケーションビルダーではまだサポートしてなかった
カスタム項目を必須項目にしたら全てのレコードタイプで必須扱いになる
全てのページレイアウトから削除できないので困るときには必須項目とせずにページレイアウト条で必須項目にしよう
Apexサーバ側コントローラからエラーをLightning Componentに伝えたい
一つのレコードを返すSOQLクエリ
Apexのプリミティブ型には何があるの
組み込みの例外
Date型とDatetime型を変換する際にはタイムゾーンを意識しよう
毎月の最終日の日付が知りたい
apex - How do we get the last day of the month from a given date - Salesforce Stack Exchange
Cronジョブみたいなのないの
ある
Apex スケジューラを使用したジョブのスケジュール 単元 | Salesforce Trailhead
サーバサイドのアクションを呼び出したい
マークアップ内で式使いたい
データバックアップの指針が知りたい
外部APIを呼び出して使うけど気をつけることあるかな
ある
リリース方法
変更セットを使用した Sandbox からのリリース 単元 | Salesforce Trailhead
Standard Pricebookがテストケースで見つからないんだけど
Standard pricebook is not found in test class - Salesforce Developer Community
まとめ
一通り自分が初めてSalesforce開発で必要となった知識の参照元を並べてみました。 自分の言葉が少なめなのは補足の必要が無いからです。エラーが起きてもルールの範囲内であることが多く思いもしない物は少ないです。こういうところにプロダクトとしての素晴らしさがにじみ出てますね。
Apexコードを書くのに必要な知識は何
Apex
Apexは強く型付けされたオブジェクト指向のプログラミング言語です。構文はJavaライクであり、Force.com プラットフォームサーバで処理を描く時に使用します。Apex を使うと、ボタンクリック、関連レコードの更新などのほとんどのシステムイベントにビジネスロジックを追加できます。
Lightning ComponentがクライアントサイドでApexがサーバサイドと言う理解でも良いと思います。
Lightning Componentからのリクエストをサーバサイドで処理できるようになります。
Lightning Componentから $A
キーワードを利用することでエンドポイントの呼び出しができるので調べてみてください。
SOQL (Salesforce Object Query Language)
Force.com プラットフォームのデータベース操作に利用するクエリ言語です。SQLのような構文で書く事ができますが両者は違うものです。例えばSOQLでは SELECT *
が出来ません。Salesforceはマルチテナント環境にありますが、マルチテナント環境では全員が「データベースを共有」しているようなものであるため、 *
などのワイルドカード文字を使用すると問題が発生するからです。
ガバナ制限
これがApexコード格上での曲者。実はApexにはマルチテナント環境ゆえの制限事項が幾つかあります。
実行ガバナと制限 - Salesforce Developers
実行プロセスが共有リソースを独占しないように制限しており、超えた場合は 処理できない実行時例外 がthrowされるようになっています。何度も update
や insert
命令を発行したりすると簡単にSOQLの制限に引っかかってしまいます。
他にもforループで上記クエリを発行するのは絶対にやめましょう。
SOQLの制限で困ったときには実行したいトランザクションがどこからどこまでなのか把握してクエリを削減することに勤めましょう。トランザクションが分割可能であれば分割しましょう。
Apexトリガ
Salesforce のレコードに対するイベント (挿入、更新、削除) の前または後に処理できるApexプログラムです。
やたら操作の多いSalesforce上の仕事を自動化する際に利用できます。
これもガバナ制限には注意が必要です。特にトリガが原因で他のトリガを誘発してしまうことがあります。
例えば Opportunity
のトリガは OpportunityLineItem
の変更にも反応してしまうので、 OpportunityLineItem
のトリガを作る際には Opportunity
のトリガ内容にも注意しなくてはいけません。
先に書いてしまいましたがトリガは一つのsObject(Opportunity...etc.)に関連付けます。
Apex一括処理
どうしてもガバナ制限を超えるような大規模ジョブを行う必要がある時にはApex一括処理を利用しましょう。
Apex 一括処理の内部的なしくみは次のとおりです。Apex 一括処理を使用して 100 万件のレコードを処理するとします。一括処理クラスの実行ロジックは、処理するレコードのバッチごとに 1 回コールされます。一括処理クラスを呼び出すたびに、ジョブは Apex ジョブキューに置かれ、別個のトランザクションとして実行されます。この機能には 2 つの大きな利点があります。
Apex 一括処理の使用 単元 | Salesforce Trailhead
単体テスト
Salesforceのリリース時には変更セットという機能を使ってリリースするのですが、この際には必ず単体テストが実行されます。 失敗すればリリースできないですし、 コードカバレッジ も75%なければエラー扱いとなりリリースできません。 なので単体テストは必ず書きましょう。テストしやすいように疎結合なコードを書くことももちろん大切ですよ。
まとめ
かなり簡素に大切なことだけ書きました。 逆にもっと細かいけどメモとして残していた知識がいくつかあるのでそちらは別途記事にします。
Lightning Componentの開発は昨今のフロントエンドの開発と大差ないのか
Lightning Component
Lightning ComponentとはLightning Experience(LEX)上で動作するUIです。 従来のフロントエンド開発と同様にComponentはUIの部品を指しています。 従来はLEXのようなSPAではなく、もっと別のコンセプトがありました。 近年移行が進んでいますが、ユーザーはLEXとClassicを使い分けることが出来ます。 どうしても組織でClassicを使いたいという要件があってもLEXに移行する雰囲気がするのでLightning Component開発に踏み切りましょう。 僕は特にSalesforceのような業務アプリケーションにおいては使いやすさよりもできるか出来ないかのほうが重要視されると考えます。 であれば慣れ親しんだClassicでわざわざ作る理由はないのではないでしょうか。
Sandbox
SalesforceはSaaSです。Lightning Componentを作成しても実行環境が利用中のSalesforce上だと開発中のデバッグには向かないですし、何よりユーザーに迷惑をかける可能性があります。
Sandboxは利用中のSalesforceのスナップショットです。 利用中のSalesforceのデータとメタデータをコピーして環境を作ることが出来ます。
ForceCodeを使った開発
割りと情報がなくて困ったLightning Componentの開発のやり方について説明します。 やり方はいろいろありますが、今回はSalesforceの開発者ツールとVSCodeを使って開発します。
Salesforceの開発者ツールからLigntning Componentを作成します。 開発はこの開発者ツール上で完結することも出来ます。 ですが、今回は慣れ親しんだエディタで開発して、Gitでバージョン管理することを考えて作成したComponentをローカルにダウンロードします。
VSCodeには予めForceCodeをインストールして、Salesforceと認証おきましょう。
$ mkdir -p my-proj/src $ cd my-proj $ touch src/package.xml
src
ディレクトリの下に package.xml
を作ったら
<?xml version="1.0" encoding="UTF-8"?> <Package xmlns="http://soap.sforce.com/2006/04/metadata"> <types> <members>HelloComonent</members> <name>AuraDefinitionBundle</name> </types> </Package>
中身にダウンロードしたいComponentの名前を書いてしまいましょう。 後はRetrieve PackageをRetrieve by package.xmlで実行するとダウンロードできます。 アップロードもForceCodeから行えます。 Lightning Componentの作成だけ開発者ツールでやらないといけないのが煩わしいですね。 注意が必要なのは同期していないということです。アップロードとダウンロードを繰り返すので常に上書きされることに注意してください。
Salesforce Developer Experience (SFDX)
2018年10月から始まった新たな体験です。 Force.com プラットフォーム *1 のアプリケーションを従来の慣れ親しんだ方法に近い形 *2 で開発することができます。 Sandboxではなくスクラッチ組織を作ってすぐに壊すことのできる環境を用意できます。 普段の開発はスクラッチ組織で行い。Staging環境をSandboxとして利用する方法がよいと思います。 その他にもCLIによるスムーズな開発がサポートされており、開発者ツールを利用せず従来のスピード感で開発することが出来ます。 利用するには組織のProduction環境で設定をONにするだけです。
*1: Force.com プラットフォームはSalesforceを構築するための基盤、つまりPaaS のサービス
*2: バージョン管理やCI, CLIツールの利用を指しています
言語
Lightning Componentの開発言語はJavaScriptとHtml、CSSです。 ただし、Salesforceの独自のタグが存在しているので要所では学習が必要になります。 また、JavaScriptはES2015に完全に対応していないため、Objectリテラルに対するスプレッド構文が利用できないです。 キホンはES5での開発だと思ったほうが良いでしょう。
ライブラリ
昨今のフロントエンド開発では切っても切り離せない多数のライブラリがありますが、これは静的ファイルとしてSalesforceに登録することで利用できます。 webpackを使ったbundleではなくライブラリは別にダウンロードされて実行されます。 また、AngularやReactといったライブラリを使いたい方もいると思います。 実際やってみた系の記事を見つけることが出来ますが、Visualforceページを利用したものが見つかると思います。 Lightning ComponentとVisualforceページは異なります。 VisualforceページをLightning Componentの中で利用することができます。
さて、AngularやReactを使う理由はなんでしょうか。Lightning Componentの開発はサポートされており、出来上がるものに差がないのであれば亜流の方法を選択する必要はないと考えます。 独自の価値を生み出すのであれば、利用した方がいいと思いますが慣れないから使うのであれば避けたほうが良いでしょう。 冷静になってください。 実際僕も余りの開発のやりにくさによって衝動に駆られました。
Lightning Design System
Lightning Design SystemはSalesforceのDesign Systemです。 組み込みのLightning Componentにも利用されており、美麗で使いやすいComponentパーツを素早く利用できる仕組みが整っています。 カスタムComponentを作るときには大いに利用しましょう。
結論
SFDXのような取り組みがはじまるくらいには開発がしにくいです。 JavaScriptライブラリの中にはバンドルされて利用されることを前提としてものも少なくありません。 ES2015も満足に使えないため昨今のJavaScript開発に慣れ親しんだ方はヤキモキするでしょう。
一方Lightning Desing Systemは素晴らしいです。 状態に応じたComponentのUIのDOMが手軽に利用できるため見た目に悩むことは殆どありませんでした。
Salesforceを使った自動化はできるのか
ひょんなことからSalesforceを使ってバックエンド業務を自動化することになりました。これはその際に学んだことの記録です。同じくひょんなことからSalesforceを触ることになっちゃった人への道標になれば幸いです。
なぜ記録するのか
Salesforceは巨大なシステムです。そのため、Google検索で手がかりを得ようにも断片的なキーワードから過去のコンセプトに沿った知識を取得してしまい大きく回り道をする可能性があります。そこでLightningだけに焦点を当てて記録することで無関連のワードに惑わされることなく知識を得てもらうことを目的としています。
何を目指したか
例えば次のような要件があるとしましょう。
- ユーザーが入力を行うUI (ページ) が必要である
- ユーザーが入力したレコードは永続化される必要がある
- レコードを使って外部サービスを呼び出し、処理を依頼する
ユーザーのインタラクションをきっかけにSalesforceでの業務を自動化することが目標です。
Salesforceではこの要件をどうやって最短で叶えることができるのでしょう。
Lightning
Salesforceが現在推し進めているコンセプトです。いくつかのサービスやツールをまとめて呼称しています。
この内、Salesforce上でアプリケーション開発をする際のコアテクノロジーとしてLightning Experience、Lightning ComponentやLightning App Builderがあります。
この概要を掴むことのできる最高の資料があります。
はじめようLightningコンポーネント開発 | Salesforce Developers
これをみるとLightningをコンセプトに据えたSalesforceの最新事情がわかります。
Lightning ComponentでUIを作る前に
ここまででSalesforce上でUIを作るにはLightning Componentを作れば良さそうということがわかりますが、作る前に既存のLightning Componentでなんとかならないのか確認をするべきです。
既存のComponentは下記ドキュメントにまとまっています。
Salesforce Developers - コンポーネントの参照
これらの素晴らしいComponentで叶えることができるのであればこれを使いましょう。また、既存の機能をそのまま使えば叶えられることだってあります。リストビューもその一つです。
Lightning App Builderでページを作る
Salesforce上でページを簡単に作るにはLightning App Builderを使います。 ページはLightning Componentの組み合わせから成ります。公開するとSalesforce上のアプリケーションに紐付けることができます。
ApexコントローラーでLightning Componentのリクエストを処理する
Lightning ComponentはSalesforceのレコードを直接読み書きすることができません。そこでApexコントローラーに依頼します。ApexはJavaライクなDSLです。Salesforceからレコードを読み書きするにはSOQLという別のDSLを使います。
Salesforce Developers - Apex とは?
結論
Lightning Component、Lightning App Builderを使ってUIを作り、ApexでSalesforceからレコードを読み書きすることで自動化できそうです。 次回はより詳細なやり方と注意事項についてまとめてみようと思います。