2017年06月13日の日記です


遅い Javascript を iframe に追い出す方法  2017-06-13 12:18:54  コンピュータ

お仕事で、すでにあるコンテンツに「広告」を入れることになった。


特に大変そうな話ではないのだけど、これが大変なことになっていたので僕が呼ばれた。

まぁ、それなりのお金をいただけるのでいいのだけど。


難点はいくつかあったのだけど、それは個別に解決した。

ざっくり書くと、設計の悪い「テンプレートシステム」がまともに働いておらず、1つのコンテンツのデータが1カ所にまとまっていない上に、1つのコンテンツ群のテンプレートが似て非なる複数に分散していて、さらにそれらを処理するプログラムがページの数だけある、という惨状だった。


なんでプログラムをデータと同じ数だけ複製して、少しづつ書き換えようと思ったのだろう。

html で全部べた書きしていたほうが、データが分散しないだけまだましだったと思うよ。



これを、まともな処理方法に改めないといけなかったのだけど、実のところ技術的な問題はそれほどなかった。

単純に、しっかりしたシステムを作ってデータを移行するだけだったから。


問題は、そうした手間を導入することに理解を得る必要があったことだな。

今までのシステムで動いているのに、手間をかけて(ということは金も時間もかけて)、なぜ大規模に改修を行う必要があるのか、ということの賛同を得るのが一番の難題。




しかしまぁ、そちらは解決した。技術話ではないので特に書かないけど。

テンプレートとプログラムは統一され、コンテンツデータは適切に集約された。

これで、全ページに広告を適用することができた。


最後に残った難題が、広告システムの動作が遅すぎる、という技術的な問題だった。



広告システムは「いろいろ試してみたい」というクライアントの意向で、3社用意された。


広告の表示場所は、いろいろな都合があって Javascript で生成することになっていた。

Javascript で生成する中に、広告表示のためのタグを埋め込む。


…埋め込めばいいんでしょ? と思ってタグを Javascript から生成したら、動かない。

なんで? とおもって、広告表示用の Javascipt を解析してみる。


document.write が入っていた。なんてこった。現代的なシステムとは思えない。




元々、Javascript は HTML 内に混ぜて書くような方法で考えられていた。

この際、document.write で何か「表示」すると、Javascript が埋め込まれた位置に、「表示」を生成する。


より詳しく言うと、HTML のパース中に Javascript コードを発見すると、その場で HTML のパース(文法解釈)は一時停止し、Javascript コードのパース、実行が始まる。


コードの実行が終わると、HTML のパースが再開する。

その際に、 document.write で書かれた内容があれば、その内容からパースが始まる。


ということは、「後から」Javascript で Javascript コードを埋め込んでも、document.write は動かない。




じゃぁ、最初から HTML に広告タグを埋め込んでおくしかない。

div で囲んで「表示しない」領域を作り、必要な広告をすべて読み込む。


これらの読み込みが終わるまで、HTML のパースは完了しない。

HTML のパースが完了すると、HTML 内容がいじれるようになるので、Javascript で広告を出したい位置にコピーする。


…という戦略で、document.write の問題を解決した。


そしたら、次の問題が出た。

この広告システムがすごく遅くて、数枚の広告を出すために、2秒以上待たされたのだ。

他の広告と合わせ、それまで1秒で表示できていたページが、4秒待たないと表示されないようになった。




すべては document.write のせいなのだ。

javascript で後から生成できれば、とりあえず画面の表示をしてしまい、ユーザーが読み始めてから周囲の広告などを整えればいい。

でも、document.write されているとそれができない。


じゃぁ、最初から広告を HTML 内に配置していると、document.write の結果を待つために HTML パースが停止し、画面表示が遅くなる。


こんな腐ったシステムを提供している広告会社も、それを使おうというクライアントもどうかしているとしか思えないが、それをどうにかするのが僕の仕事だ。




HTML の解釈が阻害されるなら、ユーザーに見せる HTML と、広告を呼び出す HTML を分離してしまえばよい。


具体的には、iframe を使う。


iframe タグを記述する HTML …「外側」と、iframe の中で読み込まれる HTML …「内側」は、別々の HTML だ。

外側にとっては「iframe がある」ということが HTML パースの情報であり、その iframe 内の HTML がどのような内容かは関知しない。


だから、内側で遅い Javascript が動いていても、外側はすぐに HTML パースを終わらせられる。



ただ、こうしたことで問題も出た。

広告を含めた各種情報を配置する Javascript が動き出したときに、まだ広告が読み終わってないのだ。



これは、仮に「広告が後で入るスペース」を埋め込むことにした。

そして、iframe 内の広告がすべて読み終わった、というタイミングを待って、実際の広告に差し替える。


実際にはもう少し問題解決しないと動かなかったのだけど、大体こんな感じ。


これで、ページ表示まで4秒かかっていたのが、1秒に短縮された。

劇的な高速化だ。


…というか、それが本来の速度だったのだけど。




しばらく運用していると、もう一つ問題が出た。

いくつかの広告は、ページに書かれたことを認識して、内容に合わせて表示を切り替えるようになっていた。

しかし、どうも内容が認識できないようで、汎用の広告ばかり出るのだ。


…と、文字で説明してしまうと、情報が整理されているので理由がわかってしまうな。

作業中は混沌としていて自分でも気づかなかったのだけど。



広告が読み込まれると、その時点でサーバーに「読み込み元」のページの URL が referrer として送られる。


サーバーはこの URL をクロールすることでページの内容を知るのだけど、iframe 内のページには広告しか入っていないので、適切な内容を読み出せない。



これはつまり、referrer を偽装して、外側ページの URL を返せばよい。


内側のページにとっては、外側のページは呼び出し元であり、referrer で知ることができる。

なのでこれを使って、内側のページが、外側のページの URL を名乗れるように環境を整えてやる。


これで、「現在のページを問われた際に、その呼び出し元の(実際の記事の) URL を返す」ようになる。解決。


#http のリクエストヘッダで送られるのは referer だけど、Javascript などで参照するときは referrer 。

 後者が正しい綴りだけど、http の RFC 書いた人が綴りミスして、公式にそのままになってしまった。

 上の文章では正しいつづりで揃えている。


#iframe 内だからこれで解決だけど、普通のページでこの referrer 偽装をやってはならない。

 なぜやってはならないか…が理解できないうちは、気軽に手を出さないこと。




同じ問題で悩んでいる人がいるかはわからないが、以下プログラム方法を示唆だけしておこう。

示唆だけ、というのは意地悪ではなく、実際のコードはそれぞれだと思うから。



・document.write が入っているような Javascript をどうしても動かさないといけないなら、別の HTML に押し込めてしまい、iframe 内に呼び出す。

 iframe は CSS などで非表示にしておき (display:none) 、得られた結果はあとで別の個所にコピーすることで表示する。


・iframe 内に表示する html には、URL を呼び出し元に偽装するための Javascript を仕込んで置く。

 history.replaceState(null,null,document.referrer);

 これで、iframe 内に書かれたプログラムは、URL 的には iframe に入れられたことに気付かなくなる。


・iframe の描画完了は、load イベントで知ることができる。

 jquery を使っているなら $("iframe").on("load",function(){~}); という感じ。

 iframe の部分は、もちろん #id などにしてよい。


・iframe 内のアクセスは、$("iframe").contents().find("#foobar") という感じ。

 これで、iframe 内の #foobar にアクセスできる。

 (iframe 内から外側にアクセスする方法はないので、外から操作すること)



これで、描画をとりあえず終わらせてしまい、遅いデータを後からゆっくり料理できるようになった。


あとは、煮るなり焼くなりお好きなように。




同じテーマの日記(最近の一覧)

コンピュータ

関連ページ

Programming Tips

別年同日の日記

02年 アップデート完了

02年 サーバー完全復活

04年 夏至祭

07年 大人買い

18年 携帯電話コンテンツ


申し訳ありませんが、現在意見投稿をできない状態にしています


戻る
トップページへ

-- share --

0000

-- follow --




- Reverse Link -