fetch markers with AJAX (dynamic loading markers)

Map

MarkerはJavaScript API。住所 <--> 緯度経度変換はGeocoding API。経路検索はDirections API or distance matrix API

Airbnb

南西と北東の座標値をサーバに送信している。これでfetchすべき四角形がわかるので、サーバ側でDBから検索するイメージ

https://www.airbnb.jp/search/search_results?page=1&source=map&airbnb_plus_only=false&
sw_lat=35.667819519522304&sw_lng=139.7638565898268&
ne_lat=35.70103244505895&ne_lng=139.78093689683362&
search_by_map=true&location=%E6%9D%B1%E4%BA%AC%E9%83%BD&checkin=2016%2F09%2F21&checkout=2016%2F09%2F22&guests=1&ss_id=wlhdh25y

食べログ

lat lonなので注意(longitudegoogleではlngと略している)。サーバに送信している座標値自体はAirbnbと同じかんじ。

https://s.tabelog.com/smartphone/restaurant_list/get_next_restaurants_for_map?LstCos=0&LstCosT=0&LstReserve=0&LstRev=&LstSitu=0&LstSmoking=0&additional_cond_flg=1&area_datatype=Prefecture&area_id=13&keyword_datatype=Genre2&keyword_id=39&pcd=13&sa=%E6%9D%B1%E4%BA%AC%EF%BC%88%E3%81%99%E3%81%B9%E3%81%A6%EF%BC%89&search_region=japan&sk=%E5%B1%85%E9%85%92%E5%B1%8B&svd=20160904&svps=2&svt=2100&sw=&tid=&utf8=%E2%9C%93&
maxLat=35.94620312705464&maxLon=139.78890843949216&
minLat=35.42530271535668&minLon=139.2739243086328&
SrtT=trend&page=1

Responseデータ

Markers: [{,…}, {,…}, {,…}, {,…}, {,…}, {,…}, {,…}, {,…}, {,…}, {,…}, {,…}, {,…}, {,…}, {,…}, {,…}, {,…}, {,…},…]
PrMarkers: []
PrRestaurants: []
RstCount: 32063

各Markerの中身はこんな。InfoWindowのHTMLをそのまま送ってる。

content: "↵<article class="rst-popup-box">↵  <a href="https://s.tabelog.com/tokyo/A1311/A131101/13168661/" class="rst-selected">↵    <img src="https://tabelog.ssl.k-img.com/restaurant/images/Rvw/41401/50x50_square_41401718.jpg" width="50" height="50" alt="" />↵    <div class="rst-info">↵      <p class="rst-title">まいか アトレ上野店</p>↵      <p class="rst-ag">(上野、京成上野、上野御徒町 / 居酒屋、魚介料理・海鮮料理、和食(その他))</p>↵      <div class="rst-score">↵        <p class="score30"><span>★</span><span>★</span><span>★</span><span>☆</span><span>☆</span><b>3.07</b></p>↵      </div>↵    </div>↵  </a>↵</article>↵↵<p class="close-popup"><a href="#" id="restaurant_info_hide">×</a></p>↵<div class="floatclear"></div>↵"
lat: "35.711847931271926"
lng: "139.77587433440803"
score: 3.07
vac: false

Distance API vs. distance matrix API

[GoogleMapAPI]ルート計算&距離と所用時間の算出 | つくりんぐ

Google公式

nameは店名。店を登録する際(あるいはバッチ)にaddress --> lat lng

CREATE TABLE `markers` (
  `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
  `name` VARCHAR( 60 ) NOT NULL ,
  `address` VARCHAR( 80 ) NOT NULL ,
  `lat` FLOAT( 10, 6 ) NOT NULL ,
  `lng` FLOAT( 10, 6 ) NOT NULL ,
  `type` VARCHAR( 30 ) NOT NULL
) ENGINE = MYISAM ;

navigator.geolocation

低精度だがHTML5APIでも一応座標値は取得可能

code splittingのchunkのしくみ

疑問

異なるchunkA Bそれぞれで、同じモジュール(例えばlodash)をimportしている場合、ビルド後のA Bのサイズはどうなるのか

Answer

  • require.ensure System.import bundle-loader どれを用いても結果は変わらず。
  • A B 双方「200kb」ほどサイズ増加しており、両方でlodashを別々にloadしていることが伺える。

Code Splitting (公式)

Chunk optimization

If two chunks contain the same modules, they are merged into one. This can cause chunks to have multiple parents.

If a module is available in all parents of a chunk, it’s removed from that chunk.

If a chunk contains all modules of another chunk, this is stored. It fulfills multiple chunks.

とあるが、lodashの場合はうまくいかないということ?

追記(19:09)

→どうやらそういうわけではなく、共通部分があってもentry chunkには決してmergeされない仕様らしい。つまり、複数の孫モジュール間での共通部分は抽出されて別のchunkとしてmergeされるはず…(親=entry chunk)

Webpack will take care of it by merging chunks (it will prefer merging chunk that have duplicate modules). Nothing will be merged into the entry chunk, so as not to impact initial page loading time.

optimization · webpack/docs Wiki · GitHub

試した

下記のように

  • entry chunk --> Home --> lodash
  • entry chunk --> About --> lodash

というchunkわけをしてビルドした結果。(lodashは単一ファイルのchunk)

entry chunk
------
function resolveIndex(nextState, cb) {
  System.import('../components/Home')
    .then(module => cb(null, module.default))
    .catch(handleError);
}

function resolveAbout(nextState, cb) {
  System.import('../components/About')
    .then(module => cb(null, module.default))
    .catch(handleError);
}
components/Home
------
import Home from './Home';
// sync loading is not merged...
// import _ from 'lodash';

// async loading is merged into a new chunk !!
System.import('lodash')

export default Home;
components/About
------
import About from './About';

// sync loading is not merged...
// import _ from 'lodash';

// async loading is merged into a new chunk !!
System.import('lodash')

export default About;

みごと2-c87d85131caeed2b63ea.jsにlodashのコードがmergeされている。

Version: webpack 2.1.0-beta.20
Time: 5341ms
                            Asset       Size  Chunks             Chunk Names
        0-ad0eb1228846aae746d1.js  549 bytes       0  [emitted]
        1-2fcd73cc179c047a58c9.js  449 bytes       1  [emitted]
        2-c87d85131caeed2b63ea.js    69.5 kB       2  [emitted]
     main-d489ed5a12ea8ccac8d5.js     220 kB       3  [emitted]  main
    main-d489ed5a12ea8ccac8d5.css    2.37 kB       3  [emitted]  main
main-d489ed5a12ea8ccac8d5.css.map  106 bytes       3  [emitted]  main

さらに追記:アグレッシブにOptimizeする

DedupePlugin

https://webpack.github.io/docs/list-of-plugins.html#dedupeplugin

AggressiveMergingPlugin

https://webpack.github.io/docs/list-of-plugins.html#aggressivemergingplugin

react-universallyがよさげ

github.com

Feature

  • Code Splitting with System.import()
  • ExtractTextPlugin for bundling CSS

感触

  • 割りと必要最低限なものだけ入っていて、SSRできそう。
  • gaearon/react-hot-loader は不要かも
    • react-hot-loader自体のバグとかSystem.import()のためにいらぬコードが入っているので。変更を監視して素直にブラウザリロードだけで良い気がする
  • development環境では、es-lintが自動で走ってくれるとよいかも。
  • System.import()に変数を渡した際にサーバ側だと上手くパースできない(事前コンパイルの時に、ファイル分割するので、リテラルじゃないときついということか)

create-react-appのwebpack + CSS設定。

      // `ExtractTextPlugin` first applies the "postcss" and "css" loaders
      // (second argument), then grabs the result CSS and puts it into a
      // separate file in our build process. This way we actually ship
      // a single CSS file in production instead of JS code injecting <style>
      // tags. If you use code splitting, however, any async bundles will still
      // use the "style" loader inside the async code so CSS from them won't be

create-react-app/webpack.config.prod.js at master · facebookincubator/create-react-app · GitHub

課題

  • つまりrequire.ensure() 等でcode splittingしているページがF5された時、何も考えずにstyle-loader css-loaderだけでSSRを試みると、FOUCが発生してしまう。
  • これはサーバ側でコンテンツ内容はHTMLレンダリングしているのに、CSS情報を埋め込むことができないからである。クライアント側ではJS内からCSS展開を試みるので、その間CSSが非適用のコンテンツがユーザに表示される。

対応

  • SSR時に<head></head>内に当該ページで使用するCSSを展開する(isomorphic-style-loaderのアプローチ)。あるいはExtractTextPluginCSSファイルに外出ししてしまう。

ExtractTextPlugin

  • 全部を1つのCSSファイルにまとめると、容量がやばいので出来ればCSS自体もcode splittingしたい… そういうオプションないかな?ないっぽい。
  • allChunks はあくまで1つのCSSファイルに全てを埋め込むか or 最初のchunkだけ埋め込むかを選択するものらしい。
ExtractTextWebpackPlugin code splitting

Webpackのdocs

stylesheets

Enabling assets for Server-Side Rendering in Webpack (file-loader, url-loader, css-loader/locals)

medium.com

file-loader and url-loader support not-emitting-files themselves now, so fake-file-loader and fake-url-loader are deprecated. fake-style-loader can be replaced by using css-loader/locals as explained in fake-style-loader#3. So, do not use the fake module family anymore. It is recommended to use css-loader, file-loader and url-loader directly instead.

もともとのfakeシリーズの狙い

  • Client, Serverで2回ビルドを走らせると、Webpack would emit the files twice, possibly resulting in different URLs という問題がある。
  • じゃあ、そもそも成果物をemit(生成)しなければいいじゃないか。という発想。
  • fake-style-loader に関しては、heading sourceという2つの情報を含んだオブジェクトを吐き出すので、必要なら自分で其の結果を利用してSSRしてね、みたいなスタンス

css/localsをサーバ側のビルド設定で用いる

github.com

isomorphic-style-loaderの使い方

github.com

概要

  • Critical CSS<head></head> の中にサーバ側で展開するためのプラグイン。クライアント側もサーバ側も同じwebpack設定で用いる。
  • ただのstyle-loaderだとサーバ側は使用できない(DOM操作を伴うのでビルド時にエラーになる)。そのためサーバ側でHTMLコンテンツだけ組み立てて(半SSRみたいな形で)Responseを返すと、クライアント側で FOUC が発生してしまう(HTMLレンダリング --> JS読み込み --> <head>内にCSS展開 --> 適用 という順番で評価されるため)。これを回避するためにSSR時にCSSも展開しちゃおうぜ!みたいなモチベーション。

fix support server side usage by magalhas · Pull Request #115 · webpack/style-loader · GitHub

URL

Using with react-router · Issue #15 · kriasoft/isomorphic-style-loader · GitHub

[request] Example without usage of a boilerplate kit, css-modules or withStyles · Issue #10 · kriasoft/isomorphic-style-loader · GitHub

webpack server side rendering

  • 結局問題なのは、CSSとか画像とか動画とか、webpackのrequire() or import に依存したReact Componentが存在するということ。
  • Server Side Renderingしたい場合、Nodeサーバ側もそれらのリソースを解釈できないとエラーになる。つまり、サーバ側も事前にwebpackでビルドしてからプロセス起動する必要があるということだ。
  • 開発のイテレーションを早くするには、下記の手順をいかに効率良く行うかに尽きる。

具体的な開発手順

  1. サーバファイル変更
  2. webpackリビルド
  3. Nodeサーバ再起動

github

github.com

所感

isomorphic CSS or isomorphic images

  • client側でビルドした際のstaticリソース。自前のwebpack設定でビルドした際のstaticリソース。これらが2重生成される?
  • とすると、Server Side Rendering時はサーバ側でビルドしたリソースが読み込まれる。
  • クライアント側で遷移した際(require.ensureなど)はクライアント側でビルドしたリソースが読み込まれる。