JSのブラウザ環境毎のエラーを無くすためにやったこと

JSのブラウザ環境毎のエラーを無くすためにやったこと

僕がクロスブラウザ対応のためにJSのブラウザ環境毎に発生するエラーを無くすためにやったことをまとめました。

TL;DR

  • エラーハンドリングを行いエラーを収集した
  • Class構文+Babelでコードの可読性を向上させた
  • Polyfill.ioも利用

エラーの収集

先ず、現状どんなエラーが発生しているのか知らないとエラーを治すことができないので、JSのエラーハンドリングを行い簡単にエラーを収集しました。

エラーハンドリング

JSでは、window.onerrorを用いることでエラーハンドリングができます。

window.onerror = function(message, source, lineno, colno, error) { ... }

パラメータ

  • message: エラーメッセージ (string)
  • source: エラーが発生したファイルのURL (string)
  • lineno: エラーが発生した箇所の行番号 (number)
  • colno: エラーが発生した箇所の列番号 (number)
  • errorError Object (object)

サンプルコード

// エラーハンドリング
window.onerror = function (msg, url, lineNo, columnNo, error) {
  var endpoint = '/api/v1/'; // Web api endpoint

  var string = msg.toLowerCase();
  if (string.indexOf(substring) > -1){
    var json = JSON.stringify({ 'Message': 'Script Error: See Browser Console for Detail' });
  } else {
    var json = JSON.stringify({
      'Message: ' + msg,
      'URL: ' + url,
      'Line: ' + lineNo,
      'Column: ' + columnNo,
      'Error object: ' + JSON.stringify(error)
    });
  }

  fetch(endpoint, {
    method : 'POST',
    body : json,
    headers : new Headers({ "Content-type" : "application/json" })
  }); 

  return false;
};

バックエンド側では、ua-parserを使いUAの判定を行いUAとエラー内容を記録していきました。

require_once 'vendor/autoload.php';
use UAParser\Parser;

$ua = "Mozilla/5.0 (Macintosh; Intel Ma...";

$parser = Parser::create();
$result = $parser->parse($ua);

print $result->ua->family;            // Safari
print $result->ua->major;             // 6
print $result->ua->minor;             // 0
print $result->ua->patch;             // 2
print $result->ua->toString();        // Safari 6.0.2
print $result->ua->toVersion();       // 6.0.2

print $result->os->family;            // Mac OS X
print $result->os->major;             // 10
print $result->os->minor;             // 7
print $result->os->patch;             // 5
print $result->os->patchMinor;        // [null]
print $result->os->toString();        // Mac OS X 10.7.5
print $result->os->toVersion();       // 10.7.5

print $result->device->family;        // Other

print $result->toString();            // Safari 6.0.2/Mac OS X 10.7.5
print $result->originalUserAgent;     // Mozilla/5.0 (Macintosh; Intel Ma...

もっとしっかりとしたデータが欲しい場合は、sentryなどのツールをお使いになられる方ことをおすすめします

BabelでES6対応

まだ、ES6(ES2015)に対応していないブラウザ(主にIEなど)がたくさんあるので、ES6対応にするためにBabelを導入しました。

ES6のJSはBabelを使うことでES5で動くようにコンパイルすることができます。

無意識の内にES6の実装をしてしまうこともあるので、Babelによるコンパイルはやっておいた方が良いでしょう。

ES6で実装されたClass構文を使ってソースを書き直した

最初は、ES5でオブジェクトを定義していたのですが、継承などが複雑だったり、メソットがインデントで分からなかったりと使いにくくバグの温床だったので、ES6で実装されたClass構文を使って書き直しました。

下のES5とES6で実装されたクラスを見ていただけると分かりやすいのですが、Class構文で実装した方が可読性が上がり、バグなども起きにくくなった思います。

// ES5
function Animal() { }

Animal.prototype.speak = function() {
  return this;
}

Animal.eat = function() {
  return this;
}

let obj = new Animal();
let speak = obj.speak;
speak(); // グローバルオブジェクト

let eat = Animal.eat;
eat(); // グローバルオブジェクト
// ES6
class Animal { 
  speak() {
    return this;
  }
  static eat() {
    return this;
  }
}

let obj = new Animal();
obj.speak(); // Animal {}
let speak = obj.speak;
speak(); // undefined

Animal.eat() // class Animal
let eat = Animal.eat;
eat(); // undefined

念の為、Polyfill.ioも利用

ES6への対応は、正直Babelで十分ですが…

念の為、Polyfill.ioも利用しました。

下記のJSを読み込むとUA毎に合わせたES6対応に必要なソースコードを読み込んでくれます。

<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=es6"></script>

バグの修正

ES6の対応などの根本的な対応を行った後、収集したエラー内容を元に細かなバグの修正を繰り返し行っていくことで、最終的には、JSのエラーを無くすことができました。