webhack / ウェブ技術が好き

javascriptやcssやHTML5とかサーバーサイド等のウェブ技術全般を好きに書くブログ

よくある構成のサイトを高速化してPageSpeed Insightsで100点を取る方法

f:id:tkosuga:20160716150725j:plain 写真はうちのみーこ。

サイト制作でよく使われる以下のライブラリ/CSSに依存したサイトを高速化してGoogleのPageSpeed Insightsで100点を取る方法を説明します。

実際にどのぐらい速度に違いがあるのかデモページを設置しました。使っているウェブサーバーはさくらの共有レンタルサーバーです。

高速化前

https://tkosuga.jp/experimental/pagespeed-unoptimized.html

f:id:tkosuga:20160731204541p:plain f:id:tkosuga:20160731204318p:plain

高速化後

https://tkosuga.jp/experimental/pagespeed-optimized.html

f:id:tkosuga:20160731204600p:plain f:id:tkosuga:20160731204305p:plain

リクエスト数が8件から5件に。ページの表示完了速度が1.41秒から0.54秒と38%に短縮されました。 諦めがちな PageSpeed Insights のスコアも71点から100点になりましたね。

では速くするために必要な項目を順番に説明して行きます。

CSSの高速化

高速化前のHTMLはCDN経由で3つのCSSファイルを読み込んでいます。

<link href='https://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css" rel="stylesheet">

CDNの fonts.googleapis / bootstrapcdn は安定していて比較的に速い方だと思いますがHTMLのレンダリングをブロックするためブラウザがHTMLを描画するのに遅延が発生します。

詳しくはCSS の配信を最適化するを参照して下さい。

これを解決するために以下の手順を取ってCSSファイルを最適します。

  1. CSSファイルを1つにしてminify(最小化)する
  2. ページの最後で非同期に読み込む
  3. クリティカル・レンダリングパスをHTMLに埋め込む

1. CSSファイルを1つにしてminify(最小化)する

Node.jsのタスクランナーgulpを使ってcssを最適化して行きます。

2016年夏時点でフロントエンドのビルドと高速化のためにNode.js環境が必須になってきています。gulpでビルド書くのMakefileを書くよりも辛くない?と思わずにはいられませんが、そこは郷に入れば郷に従えで npmコマンド もしくは sh で代替えするか、gulpを身に着けるのが肝要だと思っています。

話が逸れたので戻します。sassをcssコンパイルしてminifyするスクリプトです。

var gulp = require('gulp');
var rename = require("gulp-rename");
var sass = require('gulp-sass');
var cleanCSS = require('gulp-clean-css');
var uglify = require('gulp-uglify');

const app_path = './app';
const build_path = './build';

gulp.task('sass:compile', function() {
  return gulp.src(`${app_path}/sass/app.scss`)
    .pipe(sass.sync().on('error', sass.logError))
    .pipe(gulp.dest(build_path));
});
gulp.task('sass:minify', ["sass:compile"], function() {
  return gulp.src([`${build_path}/app.css`])
    .pipe(cleanCSS({
      compatibility: 'ie8'
    }))
    .pipe(rename("pagespeed-optimized.min.css"))
    .pipe(gulp.dest(build_path));
});

app/sass/app.scss をコンパイルして build/app.css に出力。build/app.css をminifyして build/pagespeed-optimized.min.css に出力しています。

2. ページの最後で非同期に読み込む

できあがったcssファイルをHTMLの最後、body閉じタグ手前で非同期に読み込むようにします。

<script>
  (function(d) {
    var c = d.createElement('link');
    c.type = 'text/css';
    c.rel = 'stylesheet';
    c.href = 'pagespeed-optimized.min.css';
    var s = d.getElementsByTagName('script')[0];
    s.parentNode.insertBefore(c, s);
  })(document);
</script>

ブラウザのレンダリングをブロックする事なくcssの読み込みと適用が行われます。

こうすると読み込みと描画が高速化されますが、最初にページを表示した時にはCSSが反映されていない崩れたHTMLが表示されます。

これを回避するにはクリティカル・レンダリングパスの最適化を行います。具体的にはファーストビューで見える範囲のCSSをHTMLに埋め込みます。

3.クリティカル・レンダリングパスをHTMLに埋め込む

クリティカル・レンダリングパスの詳細は以下ページを読んでください。

developers.google.com

難しい話ですよね。誤解を招きそうですが、1行で説明すると

ページを開いてスクロールしない範囲のCSSがHTMLにあると表示が速いよ!

という事です。

gulp内でビルド中にファストビュー範囲のインラインCSSを作るライブラリに critical があります。

www.npmjs.com

手作業でファストビュー範囲のインラインCSSを作るには Critical Path CSS Generator がおススメです。今回はこのツールを利用しました。

jonassebastianohlsson.com

SPA(シングルページアプリケーション)でページ内の全てが動的に初期化されるサイトは上記2つのツールでは正しく動作しません。

そのためヘッドレスブラウザのPhantomJSを使ってjavascriptを実行できる penthouse があります。

github.com

クリティカル・レンダリングパスの生成はけっこう複雑です。ツールに困った方は以下の説明が良くまとまっていたので参考にして下さい。

github.com

Apache/Nginxで使えるpagespeedモジュールのPrioritize Critical CSSにクリティカル・レンダリングパスの生成を自動化する機能があるようです。リスクの章で説明されているようにデバイスに合わせてコンテンツの振り分けが厳密に行われている場合であれば、自動化できて便利かも知れません。

JSの高速化

高速化前のHTMLはCDN経由で2つのjsファイルを読み込んでいます。

  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>

CDNの fonts.googleapis / bootstrapcdn は速い方だと思いますが、スクリプトの読み込みはリソースの読み込みが終わるまでHTMLのレンダリングをブロックします

詳しくはレンダリングを妨げる JavaScript を削除するを参照して下さい。

これを解決するために以下の手順を取ってJSファイルを最適します。

  1. JSファイルを1つにしてminify(最小化)する
  2. ページの最後で非同期に読み込む

1. JSファイルを1つにしてminify(最小化)する

jsをコンパイルして1つにまとめるスクリプトです。jquery.jsとbootstrap.jsはローカルに保存しておきます。

var gulp = require('gulp');
var rename = require("gulp-rename");
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
var browserify = require("browserify");
var watchify = require('watchify')
var babelify = require("babelify");
var source = require('vinyl-source-stream');

const app_path = './app';
const build_path = './build';

gulp.task('js:compile', function() {
  return browserify(`${app_path}/js/app.js`)
    .transform(babelify, {
      // sourceMaps: true,
      presets: ['es2015'],
    })
    .bundle()
    .on('error', function(e) {
      console.log("Error : " + e.message);
      this.emit("end");
    })
    .pipe(source('app.compiled.js'))
    .pipe(gulp.dest(build_path));
});
gulp.task('js:concat', ['js:compile'], function() {
  return gulp.src([
      `${app_path}/js/jquery.js`,
      `${app_path}/js/bootstrap.js`,
      `${app_path}/js/app.compiled.js`,
    ])
    .pipe(concat('app.js'))
    .pipe(gulp.dest(build_path));
});
gulp.task('js:minify', ['js:concat'], function() {
  function createErrorHandler(name) {
    return function(e) {
      console.error('Error from ' + name + ' in compress task', e.toString());
    };
  }
  return gulp.src(`${build_path}/app.js`)
    .on('error', createErrorHandler('gulp.src'))
    .pipe(uglify())
    .on('error', createErrorHandler('uglify'))
    .pipe(rename("pagespeed-optimized.min.js"))
    .pipe(gulp.dest(build_path))
    .on('error', createErrorHandler('gulp.dest'));
});

app/sass/app.js をコンパイルして build/app.compiled.js に出力。build/app.compiled.jsと他jsを1つにまとめてminifyして build/pagespeed-optimized.min.js に出力しています。

2. ページの最後で非同期に読み込む

できあがったjsファイルをbody閉じタグの手前に入れます。scriptには一応ですがasyncを入れて非同期で実行されるようにします。

  <script async src="pagespeed-optimized.min.js"></script>

初期化順が関係する複数jsファイル読み込みにasyncを使うと初期化の流れが入れ違いになる等でエラーが起こりますが、今回のケースでは1ファイルにまとめてあるので async を付けて非同期読み込みで問題ありません。

scriptタグのasyncについてMozillaが詳しく説明しています。asyncを付けた時の挙動が気になる方はこちらを参照して下さい。

developer.mozilla.org

HTMLの軽量化

html-minifierを使ってHTMLを最小化します。

var gulp = require('gulp');
var rename = require("gulp-rename");
var ejs = require('gulp-ejs');
var minify = require('gulp-html-minifier');

const app_path = './app';
const build_path = './build';

gulp.task('html:minify', function() {
  function createErrorHandler(name) {
    return function(e) {
      console.error('Error from ' + name + ' in compress task', e.toString());
    };
  }
  return gulp.src(`${build_path}/pagespeed-optimized.origin.html`)
    .on('error', createErrorHandler('gulp.src'))
    .pipe(minify({
      collapseWhitespace: true,
      includeAutoGeneratedTags: false,
      minifyCSS: true,
      minifyJS: true,
      removeComments: true,
      removeEmptyAttributes: true,
    }))
    .on('error', createErrorHandler('minify'))
    .pipe(rename("pagespeed-optimized.html"))
    .pipe(gulp.dest(build_path))
    .on('error', createErrorHandler('gulp.dest'));
});

HTML等のリソースを圧縮すると転送速度が速くなります。リソース(HTML、CSS、JavaScript)を圧縮するを参考にして下さい。

サーバーサイドでリソースの圧縮とキャッシュ

お約束のmod_deflatemod_expiresです。

<IfModule mod_deflate.c>
  AddOutputFilterByType DEFLATE image/svg+xml
  AddOutputFilterByType DEFLATE text/plain
  AddOutputFilterByType DEFLATE text/html
  AddOutputFilterByType DEFLATE text/xml
  AddOutputFilterByType DEFLATE text/css
  AddOutputFilterByType DEFLATE text/javascript
  AddOutputFilterByType DEFLATE application/xml
  AddOutputFilterByType DEFLATE application/xhtml+xml
  AddOutputFilterByType DEFLATE application/rss+xml
  AddOutputFilterByType DEFLATE application/javascript
  AddOutputFilterByType DEFLATE application/x-javascript
  AddOutputFilterByType DEFLATE application/x-font-ttf
  AddOutputFilterByType DEFLATE application/vnd.ms-fontobject
  AddOutputFilterByType DEFLATE font/opentype font/ttf font/eot font/otf
</IfModule>

<ifModule mod_expires.c>
  ExpiresActive On
  ExpiresDefault "access plus 7 days"
  ExpiresByType text/css "access plus 7 days"
  ExpiresByType text/javascript "access plus 7 days"
  ExpiresByType application/x-javascript "access plus 7 days"
  ExpiresByType text/html "access plus 7 days"
</ifModule>

読み込むリソースを減らす

FontAwesomeのフォントファイルをインラインSVGに置き換える

FontAwesomeはCSSの中からフォントファイルを読み込みます。使うアイコンが限られている場合はインラインSVGに置き換えてしまうとFontAwesomeのCSSファイルとフォントファイル、2つのファイルの読み込みを削減できます。

FontAwesomeを使うにはclassにfaとfa-xxxxを指定します。

<i class="fa fa-cogs"></i>

これを以下のようにSVGをインライン展開します。

<i class="fa-svg">
 <svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1764 11q33 24 27 64l-256 1536q-5 29-32 45-14 8-31 8-11 0-24-5l-527-215-298 327q-18 21-47 21-14 0-23-4-19-7-30-23.5t-11-36.5v-452l-472-193q-37-14-40-55-3-39 32-59l1664-960q35-21 68 2zm-342 1499l221-1323-1434 827 336 137 863-639-478 797z"/></svg>
</i>

展開しているSVGのスタイルをCSSで定義します。

.fa-svg svg {
  height: 9rem;
  max-width: 9.5rem;
}
.fa-svg path {
  fill: #fff;
}

FontAwesomeを個別にSVGにする説明は以下を参照して下さい。

github.com

まとめ

ページ表示が速くなると気持ちいいですね。

FontAwesomeのようにクロスオリジンの影響でCSS内でリソースを追加取得しているものはCDN経由を止めて自前でウェブサーバーに設置するか、この記事のようにSVGに置き換えてしまうと良いかも知れません。

minifyしたHTML/JS/CSSAmazon CloudFront等のCDNに設置するとさらに速くなります。

ディスプレイ広告やアクセス解析タグが複数入ってくるとPageSpeedで100点を取るのがさらに手間で難しくなりますが、速いサイトは永遠のテーマですので頑張って追及して下さい。

この記事はレコーディングダイエットができるウェブアプリを開発した中で「Twitterで共有する」機能を実装している中で色々調べた内容をまとめたものです。

tkosuga.jp

インストール必要なしブラウザで動作するのでよかったら試して見て下さいね。

おまけ

WordPressを高速化するには?

自前で色々な労力を割くよりもKUSAMAGIを使った方が確実に速くて堅牢です。

kusanagi.tokyo

高速化したWordPressの保守で苦労しないためには?

保守で色々と苦労するなら最初からWordPress.comを使った方が確実で堅牢です。全ドメインHTTPS対応しています。トラフィックの急増におびえる必要もありません。

wordpress.com

SEOのためにページ速度を上げるためには?

まず以下の記事に目を通して下さい。

moz.com

www.semrush.com

次にGoogle Developersのパフォーマンスの最適化を読んで下さい。

developers.google.com

これらページ速度最適化を行うにはCSS/JS/HTMLの知識の他に、サイトで利用しているフレームワークプログラミング言語の知識が必須です。例えばRubyOnRailsで作られているサイトであれば以下のようなスライドを読んで理解できる必要があります。

多くのサイトではネットワークよりもデータベースの性能がページ表示速度に大きく影響します。例えばMySQLPostgreSQLが遅いならば以下のようなスライドを参考にしてDBAやプログラマに相談する必要があります。

データベースのボトルネックが解消されれば次にネットワークのボトルネックを解消します。具体的にはロードバランサーやリバースプロキシ、CDNの活用、リクエストを捌くウェブサーバーとアプリケーションサーバーの構成等の問題を発見し的確なアプローチを出来なければ速くなりません。

クラウドコンピューティングやベアメタルサーバーの導入検討もここに分類されます。

長くなってきたので話を折ります。SEOを目的としたページ速度向上はサイトのトップページだけ速くしてPageSpeedで良い点を取っても効果が薄いです。

極端に遅いページが多数存在している、アクセスが集中したらすぐに遅くなる、アクセスエラーが頻発するようなサイトではなく、どのページに誰がアクセスしても速くて快適な状態を作るのがSEOでのページ速度向上です。

そのためにはサイトを構成する一番上から下までに存在するボトルネックを1つづつ見つけて解消する必要があり、それを通して行うには相応の知識が必要です。

SEOのためサイト速くする、という話は決して軽い話ではありません。サイトを開発・運用している現場と相談の上で進めるのをおススメします。

夏だしダイエットしたいから痩せれるウェブアプリを作った

f:id:tkosuga:20160118190902j:plain 写真はうちのみーこ。

今年も夏がやってきました。毎年この季節になると思うことはなんでしょうか?

そう、ダイエットですよね。

海にプールにフェスにビアガーデンと夏特有のイベントがあるたびに痩せなきゃ!と気を引き締めるのですが3日後にはすっかり忘れているものです。

毎日のカロリーバランスを消費 > 摂取にすれば自然と痩せられると頭では分かっていても実現するのが難しい。

そんな自分のために、毎日のカロリーバランスを記録して摂取カロリーが消費カロリーを上回らないようにするウェブアプリを開発しました。

tkosuga.jp

使い方はかんたん、摂取カロリーを毎日入力するだけです。

はじめて使うと使い方説明のツアーが始まるので、説明を見て進めるだけで利用開始できます。

このカロリーを記録するダイエット方法をレコーディングダイエットと呼びます。数年前に流行りましたよね。

基礎代謝量と生活強度から日常生活で自然消費されるカロリーを計算するので、摂取カロリーが消費カロリーを上回らないよう生活に気を付けて暮らせば自然と痩せて行く皮算用です。

さらにこのウェブアプリではダイエットの成果をはちみつ・鮭・りんご・どんぐり・おにぎり換算で共有する機能もあるので、痩せてきたら友達に鮭x匹分も痩せたよ!と自慢しましょう。

f:id:tkosuga:20160726214740p:plain ※キャプチャ1「おにぎり40個分のダイエットに成功したよ!やったね!」

f:id:tkosuga:20160726214746p:plain ※キャプチャ2「鮭4匹相当のダイエットに成功だね!すごいね!」

データ保持について

データは全てブラウザ内に保存しています。またダイエットレポートを共有する場合を除き、データを外部に転送・保存など通信していません。複数ユーザーでPC/スマホを共有していてブラウザも共有している場合、共有している他ユーザーに見られてしまう可能性がありますのでご注意下さい。

※プライバシーに関する情報(基礎代謝計算のための体重・年齢・性別)はダイエットレポートの共有および移行データに含まれません。

個人的なダイエット成果と開発の動機

ぼくはレコーディングダイエットのくまスリムの開発を始めた=個人で使い始めた6月~7カ月末の2カ月で80kgから74kgの6kgのダイエットに成功しました。

ダイエット開始初日はスマホのメモアプリで摂取カロリーと消費カロリーを記録して、電卓アプリで合算してカロリーコントロールしていたのですが、2日目には早くも辛くなってきたので以下の3つ要件を満たしたアプリを自作する事にしました。

  1. スマホでお手軽に使える。
  2. ボタンを押すだけでカロリーコントロールできる。
  3. モチベーションを維持できる。

要件を満たしていく中で幾つかの課題を抽出できました。以下解決した課題とその解決方法です。

  1. アプリにするとインストール面倒、保守も面倒
    • 解決方法
      • ブラウザだけで使えるウェブアプリにする。
  2. 読み込みや反応が遅いとストレス
    • 解決方法
      • 外部リソースをとにかく減らす。
      • 動作をとにかく高速化する。
      • AdBlock/コンテンツブロッカー環境で動作するようにする。
  3. 痩せたら褒められたい
    • 解決方法
      • はちみつ・鮭などの謎単位で何だか凄い感を演出。
      • SNS共有機能を付ける(LINE/Facebook/Twitter)。
  4. ポジティブな数字しか見たくない
    • 解決方法
      • 目標値やノルマを表示しない。
      • 現在の体重とか見える所に表示しない。
  5. メンテナンスに苦労したくない
    • 解決方法
      • サーバーサイドを使わずHTMLで完結する形にする。
      • Node.js + gulpでビルドを自動化。
  6. お金かけたくない
    • 解決方法
  7. セキュリティ/プライバシーで苦労したくない
    • 解決方法
      • HTTPSの導入
      • ウェブストレージの利用。サーバーサイドで保存しない。
      • 生年月日にニックネーム入力等をしない。

技術的な情報

ここからツール紹介からがらっと変わって技術的な情報です。

StartSSL

本アプリのドメイン(tkosuga.jp)には無料で使えるStartSSLを利用しています。

StartSSL™ Certificates; Public Key Infrastructure

1アカウントで上限5つのサーバー証明書を無料で発行できます。企業情報や個人事業主の情報を入力する必要がありません。クラス1の証明書です。

f:id:tkosuga:20160726223445p:plain

※上記キャプチャはhttps://cryptoreport.websecurity.symantec.com/checker/の結果

せっかくなのでSSL脆弱性診断(SSL Server Test)を試して見ます。Fか。

f:id:tkosuga:20160726224058p:plain

※上記キャプチャはhttps://www.ssllabs.com/ssltest/の結果

SSLハンドシェイクの欄を見るとStartSSLは昔のブラウザに対応していないんですね。

f:id:tkosuga:20160726224307p:plain

※上記キャプチャはhttps://www.ssllabs.com/ssltest/の結果

さくらレンタルサーバーでのSSL転送

本アプリはさくらレンタルサーバーで動作しているのですが、SSLを有効にするとサーバーにはHTTPSが解除された状態で転送されます。リバースプロキシを経由してレンタルサーバーに辿りついているようです。

そのため.htaccess等で「httpならhttpsに転送する設定」をするとリダイレクトループが起こります。これを回避するにはさくらレンタルサーバー特有のカスタムHTTPヘッダに付与されているX-Sakura-Forwarded-Forを利用します。

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteCond %{HTTP:X-Sakura-Forwarded-For} ^$
  RewriteRule ^(.*)$ https://tkosuga.jp%{REQUEST_URI} [R=301,L]
</IfModule>

SVG

本アプリはPNG/JPEG画像等(ラスター画像)を使わずにインラインSVGベクター画像)を利用しています。以下のアイコンは全てインラインSVGです。

  • タイトルロゴ(くまのアイコン)
  • カロリー換算(はちみつ・鮭・おにぎり等)
  • 機能アイコン(Font Awesomeアイコン)

SVGアニメーション

ロゴアイコンのアニメーションにhttp://maxwellito.github.io/vivus/を利用しています。

CodePenにロゴアイコンとVivusを使ったデモを設置しました。

codepen.io

Vivusを使うと下のコードだけでSVGアウトラインのアニメーションができます。100msecでポーリングしながら開始と終了を延々とループさせています。

var animationVector = 1;
var vivus = new Vivus('app-icon-svg', {}, function() {
  $('#app-icon-svg').removeClass('app-icon-svg');
});
setInterval(function() {
  if (vivus.getStatus() == 'start') {
    $('#app-icon-svg').addClass('app-icon-svg');
    vivus.reset().play(animationVector = 1);
  } else if (vivus.getStatus() == 'end') {
    $('#app-icon-svg').addClass('app-icon-svg');
    vivus.stop().play(animationVector = -1);
  }
}, 100);

Font AwesomeをSVGに置き換え

Font Awesomeのフォントアイコンを利用するには以下HTMLのように「fa fa-icon-name」とします。お馴染みの形式ですね。

<i class="fa fa-cogs"></i>

本アプリではFont Awesomeのフォントファイルを使わず全てSVGを利用するようにしたので以下のようにしてFont Awesomeのアイコンを表示しています。

<i class="fa-svg"><%- include("font-awesome-svg/cogs.svg") %></i>

CSSでは以下のように文字幅に合わせてSVGの横幅を指定し、横幅に合わせてSVGの高さ上限を指定しています。納得でしょ。

.fa-svg svg {
  color: $text-color;
  display: inline-block;
  max-height: 1.1rem;
  width: 1.1rem;
}

rem単位(root em)についてはMozillaの説明が分かり易いです。

developer.mozilla.org

remは2016年現在の主要ブラウザでサポートされているので利用して問題ないと思います。

http://caniuse.com/#feat=rem

Font-AwesomeアイコンのSVG化には以下を利用しています。

github.com

ウェブストレージ利用で注意した点

本アプリではダイエットデータの保存にWeb Storage APIを利用しています。

developer.mozilla.org

ウェブストレージは便利な反面、もしブラウザに脆弱性が見つかりその範囲にウェブストレージが含まれていればウェブストレージに保管されている重要な情報が漏えいする可能性があります。

そのためダイエットアプリでお約束になっている生年月日の入力・保存といった個人の特定に繋がる情報(他には姓名やニックネーム)を保存しないようにしています。

ウェブストレージ利用のサンプルコード

以下のコードはウェブストレージAPIを使ってハッシュマップをsave/loadするサンプルです。JSON#parseとJSON#stringifyでシリアライズを実現しています。

  function loadFromStorage() {
    return JSON.parse(storage.getItem("options"));
  }
  function saveToStorage(options) {
    storage.setItem("options", JSON.stringify(options));
  }
  var storage = localStorage;
  var options = loadFromStorage();
  saveToStorage(options);

共有するデータのシリアライズ+圧縮

データ共有の仕組みにはウェブストレージに保存しているデータをシリアライズしURLに付与して、他ブラウザで開いた時のそのURLからデータを復元する方法を取っています。

このURLに付与するシリアライズされた文字列を生成するのをブラウザ内のJavascriptで行うのに lz-string を利用しています。ものすごく便利なライブラリです。

github.com

以下lz-string を利用をしたサンプルコードです。文字列をURIセーフな形に圧縮して復元しています。

const lzstring = require('lz-string');
const string = "stringstringstring";
const compressedString = lzstring.compressToEncodedURIComponent(string);
lzstring.decompressFromEncodedURIComponent(compressedString);

短縮URL

データ共有のための短縮URLにはGoogle URL Shortener APIを利用しています。

developers.google.com

ここだけサーバーサイドでrubyCGIを利用しています。

require 'net/http'
require 'net/https'
require 'yaml'
require 'cgi'

def shorten(original_url)
  req = Net::HTTP::Post.new("/urlshortener/v1/url?key=#{API_KEY}", initheader = {'Content-Type' => 'application/json'})
  req.body = %|{"longUrl":"#{original_url}"}|
  https = Net::HTTP.new("www.googleapis.com", 443)
  https.use_ssl = true
  https.verify_mode = OpenSSL::SSL::VERIFY_NONE
  res = https.start{|http|
    http.request(req)
  }
  hash = YAML.load(res.body)
  hash['id']
end
shorten(URI.unescape(cgi.params['u'].first))

毎日のカロリー変動を見るラインチャート

MetricsGraphics.jsを利用しています。

metricsgraphicsjs.org

CodePenにデモを準備しました。見やすくするため線と文字を太く大きくしています。

codepen.io

四方の余白と高さを指定して2本の線で描画するデータを渡すとキレイなラインチャートが表示されます。

MG.data_graphic({
  data: [intakeKcals, usedKcals],
  width: $(".header").width(),
  height: 90,
  left: 30,
  right: 40,
  top: 10,
  bottom: 30,
  target: ".chart-daily-kcal",
  x_accessor: 'date',
  y_accessor: 'value',
  legend: ['摂取kcal', '消費kcal'],
  colors: ['#e80703', '#909090'],
  //show_secondary_x_label: false,
  //show_confidence_band: ['l', 'u'],
  //x_extended_ticks: true,
});

その日のカロリー摂取量を見るパイチャート

日付の横にある小さいチャートには Peity を利用しています。

http://benpickles.github.io/peity/

CodePenのデモを準備しました。

codepen.io

このチャートライブラリはとても分かり易くて便利で、使い方は以下の通りです。直感的でシンプルですよね。

<span class="pie">1/5</span>
$(function() {
  $("span.pie").peity("pie")
);

スマホのクリックを速くするfastclick

github.com

fastclickを利用してスマホでボタンを押したときの反応速度を向上させています。何をしているのかなと思ってこのライブラリの実装を見てみるとclickやtouchstartのEventListenerを入れ替えたりイベントの伝播に変更を加えたりしています。

あと残りの説明

長くなってきたのでアプリ内で利用していて説明していないライブラリを列挙します。

まとめ

お手軽に使えるダイエットアプリ作ったので使って見てね!

tkosuga.jp

ぼくはこのウェブアプリでダイエットに成功、さらにフロントエンドとNode.js/gulpと今風のサイト高速化にSVGの勉強もできたので万々歳です。

意見要望があれば気軽にブログのコメントやTwitter宛てにコメントをください。

Googleクラウド自然言語API(Cloud Natural Language API)の利用制限・利用料金を手短に。

f:id:tkosuga:20150730203602j:plain 写真はうちのみーこ。

Googleからクラウド自然言語API(Cloud Natural Language API)が公開されたので気になる利用制限・利用料金を手短にまとめます。

※ 2016/7/21日時点。

cloud.google.com

利用制限

  • 対応言語
  • テキスト量
    • 1リクエストで1Mバイトまで。1Mバイトを超えるとAPIErrorを返す。
  • トークン量
    • 1リクエストで10万まで。10万を超えるとIgnoredを返す。
  • リクエスト回数制限 ※プロジェクト・アプリケーション(IP)で制限を共有
    • 100秒で1,000回まで。
    • 1日で80万回まで(ただしフリーβ期間の2016/8/1日までは1日5万回まで)。

料金

※料金はテキストレコード(text records)単位です。API呼び出し回数ではありません。

5,000テキストレコードまで無料。料金計算は1,000テキストレコード単位。

以下、テキストレコード数を軸にした料金テーブルを作りました。

テキストレコード数 Entity Recognition Sentiment Analysis Syntax Analysis
5,000 $5 $5 $3
10,000 $5 $5 $3
15,000 $8 $8 $4
20,000 $10 $10 $5
50,000 $13 $13 $7
100,000 $25 $25 $13
200,000 $50 $50 $26

月に20万テキストレコードを分析して$50(6,000円程)を安いと見るか高いと見るか。

顧客向けプロジェクトでテキストレコード数が月に2千万を超えるならGoogleに見積もり依頼して要相談。

参照情報