turbolinksソース分析(回転)
6859 ワード
Turbolinks 5はCoffeescriptで記述する.
Turbolinks 5を学ぶことでブラウザがウェブページをロードする際の処理フロー を根本的に把握する. Turbolinks 5の核心原理を掌握して、どのように1つの“大きい”の先端プロジェクト をモジュール化することをマスターします老鳥学会に従ってソースコード を分析する方法
準備作業
cloneプロジェクト: Turbolinksが見つかりました.start()入口 老鳥は、コードを研究する前に、あなたの研究対象の適用範囲を明確にすることが非常に重要で、大部分の時間はまずドキュメントを見ることが非常に効果的にプロジェクトアーキテクチャを熟知する手段であることを示唆した.ですので、事前にREADMEを読むことをお勧めします.
スタート
入り口はとても簡単です.
次の情報が表示されます. Turbolinksはグローバルwindowにマウントされます.Turbolinksオブジェクト、単一の例(すなわちグローバルに1つしかない). Turbolinks.Controllerはコアであり、単例でもある(グローバルには1つしかない). Turbolinks.controller.start()は本物の入り口です.
老鳥は、静的なコードから空間想像力で実行時の各クラスやコンポーネントの関係を抽出することを提示し、これはコードを読む精粋である.必要に応じて、動的なdebugツールを用いて動的解析を行うことができる.
コントロールは何をしていますか?
コントローラに飛び込みましたcoffee、start()メソッドを見つけます:
非中堅コードには関心がないが、最も重要なエントリはすでに現れている:clickCaptured関数はグローバルなclickイベントにマウントされ、pageLoaded関数はDOMContentLoadedイベントDOMContentLoadedとLoadイベントの違いは前者がcssを待たず、imageロードが完了するとトリガされ、後者などのページが完全にロードされた後にトリガされることである.
ここから、最初の重要な実装を見ました.
a要素のイベントをバインドする方法:addEventListener
この時私たちはもうvisit()という入り口に着いた.下を見続けます.
最後にcontroller.に来ましたcoffeeのstartVisityToLocationWithAction:
次の情報が表示されます.ユーザがリンクをクリックすると、実際にTurbolinks 5はVisitのインスタンスを作成して呼び出す.start()は、具体的なアクセスプロセスを開始する.
この場合もcontrollerの役割を基本的に分析することができます. controllerは、各モジュールを関連付けるすべての関連クラスのコンテナですが、Visitは例外であり、アクセスするたびに新しいインスタンスが生成され、@currentVisit に格納されます.
Visitの本格的なアクセス
これは真の処理プロセスです. HTTP Request(非同期、Javascriptでのリクエストはデフォルトで非同期) を送信ブラウザ履歴の更新(History API経由) cacheページ をロード
別れる時だ.
HttpRequest
http_request.coffeeでは、HTTP Requestがどのように送信されるかを検討します.
案の定、XMLHttpRequestオブジェクトを介して、非同期リクエストが開始された.コールバックが設定されている.私たちはしばらく異常処理を見ないでrequestLoadedを直接見ます.
@delegateって何?実際、delegateの命名はフレームワークの中で非常によく見られ、代理人を代表して、要求を対応するインタフェースに転送する.ここで明らかに従来のVisit例である.このような設計により、HttpRequestオブジェクトを特定の実装クラス(例えばvisit)に依存することなく、より汎用的にすることができる.
解析を続けると、最後に呼び出されたことがわかります.
これが最終HttpRequest以降の動作であり,@controller.が呼び出されていることがわかる.renderインタフェースレンダーに行かないで前の分岐点に戻ります.
老鳥は、良い命名はコードを読む作業量を極めて低減することができ、最後まで追いかけないで、一つのインタフェースの意味を明確にした後、他の重要なインタフェースに分析することができるとヒントを与えた.例えばrenderは非常に明確な意味であり、私たちはほとんど分析しなくてもその役割を理解することができます.
素晴らしいですね.cache pageの最終ロードも@controller.を通ります.renderが行いました.
最終的には最も重要なrender関数に入る必要があります
controller.coffee
ビューに入ります.coffee
snapshot_に入りますrenderer.coffee
私たちは一周して、最終的にrenderの実際の入り口を見つけました.renderが以下のことをしたのを見ました.連結ヘッド body を置き換えるその他 マーガレットヘッドを見続けて
非常に明らかな命名
私たちはheadから続けます.details.coffeeでは、特定の操作を分析します.
すなわち、mergeHeadは、ヘッダ情報を同期してロード.ここではJavascript操作scriptタグ要素の役割を明確に理解する.(srcプロパティの内容を自動的に非同期で取り戻して実行します)
同様に、replaceBodyの操作の鍵は:
非常に明確な命名で、ここの論理をすぐに理解することができます.
原著--深セン市80パーセント科学技術有限会社李亜飛
Turbolinks 5を学ぶことで
準備作業
cloneプロジェクト:
git clone https://github.com/turbolinks/turbolinks
エディタを用意してatomまたはsublime textをお勧めしますスタート
入り口はとても簡単です.
Turbolinks.start = ->
if installTurbolinks()
Turbolinks.controller ?= createController()
Turbolinks.controller.start()
installTurbolinks = ->
window.Turbolinks ?= Turbolinks
moduleIsInstalled()
createController = ->
controller = new Turbolinks.Controller
controller.adapter = new Turbolinks.BrowserAdapter(controller)
controller
moduleIsInstalled = ->
window.Turbolinks is Turbolinks
Turbolinks.start() if moduleIsInstalled()
次の情報が表示されます.
老鳥は、静的なコードから空間想像力で実行時の各クラスやコンポーネントの関係を抽出することを提示し、これはコードを読む精粋である.必要に応じて、動的なdebugツールを用いて動的解析を行うことができる.
コントロールは何をしていますか?
コントローラに飛び込みましたcoffee、start()メソッドを見つけます:
start: ->
unless @started
addEventListener("click", @clickCaptured, true)
addEventListener("DOMContentLoaded", @pageLoaded, false)
@scrollManager.start()
@startHistory()
@started = true
@enabled = true
非中堅コードには関心がないが、最も重要なエントリはすでに現れている:clickCaptured関数はグローバルなclickイベントにマウントされ、pageLoaded関数はDOMContentLoadedイベントDOMContentLoadedとLoadイベントの違いは前者がcssを待たず、imageロードが完了するとトリガされ、後者などのページが完全にロードされた後にトリガされることである.
ここから、最初の重要な実装を見ました.
a要素のイベントをバインドする方法:addEventListener
この時私たちはもうvisit()という入り口に着いた.下を見続けます.
visit() -> @adapter.visitProposedToLocationWithAction -> @controller.startVisitToLocationWithAction
最後にcontroller.に来ましたcoffeeのstartVisityToLocationWithAction:
startVisit: (location, action, properties) ->
@currentVisit?.cancel()
@currentVisit = @createVisit(location, action, properties)
@currentVisit.start()
@notifyApplicationAfterVisitingLocation(location)
次の情報が表示されます.
この場合もcontrollerの役割を基本的に分析することができます.
Visitの本格的なアクセス
start() -> @adapter.visitStarted(this) -> visit.issueRequest(); visit.changeHistory(); visit.loadCachedSnapshot()
これは真の処理プロセスです.
別れる時だ.
HttpRequest
http_request.coffeeでは、HTTP Requestがどのように送信されるかを検討します.
createXHR: ->
@xhr = new XMLHttpRequest
@xhr.open("GET", @url, true)
@xhr.timeout = @constructor.timeout * 1000
@xhr.setRequestHeader("Accept", "text/html, application/xhtml+xml")
@xhr.setRequestHeader("Turbolinks-Referrer", @referrer)
@xhr.onprogress = @requestProgressed
@xhr.onload = @requestLoaded
@xhr.onerror = @requestFailed
@xhr.ontimeout = @requestTimedOut
@xhr.onabort = @requestCanceled
案の定、XMLHttpRequestオブジェクトを介して、非同期リクエストが開始された.コールバックが設定されている.私たちはしばらく異常処理を見ないでrequestLoadedを直接見ます.
requestLoaded: =>
@endRequest =>
if 200 <= @xhr.status < 300
@delegate.requestCompletedWithResponse(@xhr.responseText, @xhr.getResponseHeader("Turbolinks-Location"))
else
@failed = true
@delegate.requestFailedWithStatusCode(@xhr.status, @xhr.responseText)
@delegateって何?実際、delegateの命名はフレームワークの中で非常によく見られ、代理人を代表して、要求を対応するインタフェースに転送する.ここで明らかに従来のVisit例である.このような設計により、HttpRequestオブジェクトを特定の実装クラス(例えばvisit)に依存することなく、より汎用的にすることができる.
解析を続けると、最後に呼び出されたことがわかります.
loadResponse: ->
if @response?
@render ->
@cacheSnapshot()
if @request.failed
@controller.render(error: @response, @performScroll)
@adapter.visitRendered?(this)
@fail()
else
@controller.render(snapshot: @response, @performScroll)
@adapter.visitRendered?(this)
@complete()
これが最終HttpRequest以降の動作であり,@controller.が呼び出されていることがわかる.renderインタフェースレンダーに行かないで前の分岐点に戻ります.
老鳥は、良い命名はコードを読む作業量を極めて低減することができ、最後まで追いかけないで、一つのインタフェースの意味を明確にした後、他の重要なインタフェースに分析することができるとヒントを与えた.例えばrenderは非常に明確な意味であり、私たちはほとんど分析しなくてもその役割を理解することができます.
loadCachedSnapshot
loadCachedSnapshot: ->
if snapshot = @getCachedSnapshot()
isPreview = @shouldIssueRequest()
@render ->
@cacheSnapshot()
@controller.render({snapshot, isPreview}, @performScroll)
@adapter.visitRendered?(this)
@complete() unless isPreview
素晴らしいですね.cache pageの最終ロードも@controller.を通ります.renderが行いました.
最終的には最も重要なrender関数に入る必要があります
controller.coffee
render: (options, callback) ->
@view.render(options, callback)
ビューに入ります.coffee
renderSnapshot: (snapshot, callback) ->
Turbolinks.SnapshotRenderer.render(@delegate, callback, @getSnapshot(), Turbolinks.Snapshot.wrap(snapshot))
snapshot_に入りますrenderer.coffee
render: (callback) ->
if @trackedElementsAreIdentical()
@mergeHead()
@renderView =>
@replaceBody()
@focusFirstAutofocusableElement()
callback()
else
@invalidateView()
私たちは一周して、最終的にrenderの実際の入り口を見つけました.renderが以下のことをしたのを見ました.
mergeHead: ->
@copyNewHeadStylesheetElements()
@copyNewHeadScriptElements()
@removeCurrentHeadProvisionalElements()
@copyNewHeadProvisionalElements()
非常に明らかな命名
私たちはheadから続けます.details.coffeeでは、特定の操作を分析します.
document.head.appendChild(element)
すなわち、mergeHeadは、ヘッダ情報を同期してロード.ここではJavascript操作scriptタグ要素の役割を明確に理解する.(srcプロパティの内容を自動的に非同期で取り戻して実行します)
同様に、replaceBodyの操作の鍵は:
for replaceableElement in @getNewBodyScriptElements()
element = @createScriptElement(replaceableElement)
replaceableElement.parentNode.replaceChild(element, replaceableElement)
非常に明確な命名で、ここの論理をすぐに理解することができます.
原著--深セン市80パーセント科学技術有限会社李亜飛