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
なので注意(longitude
はgoogleでは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
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がよさげ
Feature
- Code Splitting with
System.import()
- ExtractTextPlugin for bundling
CSS
感触
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のアプローチ)。あるいはExtractTextPlugin
でCSSファイルに外出ししてしまう。
ExtractTextPlugin
- 全部を1つのCSSファイルにまとめると、容量がやばいので出来ればCSS自体もcode splittingしたい… そういうオプションないかな?ないっぽい。
allChunks
はあくまで1つのCSSファイルに全てを埋め込むか or 最初のchunkだけ埋め込むかを選択するものらしい。
ExtractTextWebpackPlugin code splitting
- そうはいっても、CSSだってsplitしたい! ExtractTextPlugin extracts from asynchronous split point chunks · Issue #120 · webpack/extract-text-webpack-plugin · GitHub
Webpackのdocs
Enabling assets for Server-Side Rendering in Webpack (file-loader, url-loader, css-loader/locals)
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をサーバ側のビルド設定で用いる
isomorphic-style-loaderの使い方
概要
- 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
webpack server side rendering
- 結局問題なのは、CSSとか画像とか動画とか、webpackの
require()
orimport
に依存したReact Componentが存在するということ。 - Server Side Renderingしたい場合、Nodeサーバ側もそれらのリソースを解釈できないとエラーになる。つまり、サーバ側も事前にwebpackでビルドしてからプロセス起動する必要があるということだ。
- 開発のイテレーションを早くするには、下記の手順をいかに効率良く行うかに尽きる。
具体的な開発手順
- サーバファイル変更
- webpackリビルド
- Nodeサーバ再起動
github
所感
- 上記のプラグイン+pm2でそれっぽい挙動になる。
isomorphic CSS or isomorphic images
- client側でビルドした際のstaticリソース。自前のwebpack設定でビルドした際のstaticリソース。これらが2重生成される?
- とすると、Server Side Rendering時はサーバ側でビルドしたリソースが読み込まれる。
- クライアント側で遷移した際(
require.ensure
など)はクライアント側でビルドしたリソースが読み込まれる。