Tips

目的

コーディングをする上で役に立つノウハウや、知らないとバグにつながるようなことを記載してあります。本項を読み、より保守性・品質を高く保ったコーディングをしましょう。

HTML編

アクセシビリティ

  • セマンティック(セマンティクス)なコーディング

    目的に応じた要素を使用します。

    例えば、見出しには見出し要素(h1~h6)、段落にはp要素、リンクにはa要素を使用します。

    目的に応じた要素を使用することは、アクセシビリティ、再利用、およびコード効率の理由から重要です。

    <!-- 非推奨 -->
    <div onclick="goToRecommendations();">All recommendations</div>
     
    <!-- 推奨 -->
    <a href="recommendations/">All recommendations</a>
  • ユーザーが操作できる要素は、フォーム要素以外では<a>もしくは<button>でマークアップする

  • 装飾以外の <img> には 空でもいいのでalt属性を設定し、余裕があれば内容を記述する(alt 属性の省略は行わないようにしましょう)

    装飾目的の画像はcssのbackground-imageを推奨。img要素は文書の内容上、必要不可欠なものに使用

  • フォームの要素がどんな役割をもつか明示するために、要素ごとに <label> で関連づける

  • sns等でシェアされた際のためにmeta ogpを設定する(パスは絶対パスで記述)

画像最適化

web ページの転送データの半分以上を、画像が占めることが多々あります。必ず画像は適切な形式を選んで、圧縮するようにしてください。

画像形式の選び方

  • アニメーション ... gif
  • 色数の多い画像 ... jpg
  • 色数が少なく、形状が単純なもの。ロゴなど ... svg
  • それ以外、透過データがあるもの(背景を透明にして表示したい) ... png
  • jpg, png-24 で大きな画像 ... WebP
<!-- webpを使用した例 -->
<picture>
  <source type="image/webp" media="(max-width: 600px)"
          srcset="./img/kv_sp.webp">
  <source media="(max-width: 600px)"
          srcset="./img/kv_sp.jpg">
  <source type="image/webp" media="(min-width: 601px)"
          srcset="./img/kv.webp">
  <img src="./img/kv.jpg"
       alt="キービジュアル" width="1500" height="900">
</picture>

CSS/SCSS編

pxではなくremを使用する

理由

ブラウザのフォントサイズ変更機能が適用されるから

pxなどの絶対値で指定すると、ブラウザの機能でフォントサイズが変更されません。UI/UXを考慮した際にそういったことは避けたいのでremを使用します。

remの設定方法

html {
	font-size: 62.5%; //62.5% = 10rem = 10px(ブラウザのフォントサイズの基本が16pxの場合)
}
 
//適用したいセレクタにremで設定
p {
	font-size: 1.6rem;
}

変数の管理

色やfont-size、font-weightなどを管理するようにします。変数を格納するファイルは_variable.scssです。

$cWhite = #fff;
 
p {
	color: $cWhite;
}

変数は無理に役割名を付与しなくともよい

例えば色を変数に入れて管理している時、わかりやすく役割で変数を管理することが多いと思います。しかし、役割の区別が明確ではなかったりする場合は色そのものの名前をつけてもいいです。

色を変数で管理する際には**Name That Color(VSCode拡張)**が便利なので使用してみてください。

JavaScript編

JSDocの使用

関数やクラスを宣言したら適宜関数やクラスについての情報を記載します。その際には通常のコメントではなく「JSDocコメント」を使用します。

まずはJSDocがどういうものかを知る必要があります ⏩ JSDocとは (opens in a new tab)

次にJSDocを使用するメリット・デメリットを理解しましょう。

JSDocを使用するメリット

  • プログラムの可読性が高まり、自分以外の人が関わる開発や保守・運用の面でも生産効率が高まる
  • 曖昧になりがちな方やオブジェクトの種類(配列、関数、コンストラクタ、クラスなど)が明確になり、予期しないバグの防止につながる
  • JSDocに対応したエディタを使用することで、コーディングの補完が強化される

JSDocを使用するデメリット

  • 開発用とデプロイ用の2つのファイルを用意する必要がある
  • コメント記述のための手間と時間がかかる

デメリットですが、開発用とデプロイ用のファイルはWebpackというバンドラーを使用すれば自動的に分割してくれます。また、コメントも最初は慣れないので時間がかかるかもしれません。しかし、納品後の修正等を考慮した時、コメントなどが後々になって効力を発揮します。多少時間はかかっても書くようにしましょう。

具体的な書き方

  • 基本的な書き方

    "/**"で初めて、"*/"で終わります。その間に記述がある分だけ"*"を書きましょう。

    /**
     * [記述]
     */
  • @type

    変数の型を示す@type。JavaScriptではTypeScriptのような型注釈がつかえないので、純粋なJavaScriptを使うときは積極的に利用しましょう。型はブラケット{}内に記載し、その後に変数の説明を書きます。

    /**
     * @type {String} メッセージです。
     */
    const messsage = "こんにちは";
     
    /**
     * @type {Number} 数値の型の例です。
     */
    let count = 0;
     
    /**
     * @type {Array} 配列の型の例です。
     */
    const list = [];
     
    /**
     * @type {Object} オブジェクトの例です。
     */
    const obj = { name: "タナカ", age: 24 };
     
    //より詳細に説明する場合は
     
    /**
    * @type {Object} オブジェクトの例です。
    */
    const obj = {
    	/**
    	* @type { string } 名前です。
    	*/
    	name: "タナカ",
    	/**
    	* @type { number } 年齢です。
    	*/
    	age: 24
    }
  • @param

    関数の引数を示す@param。関数の引数が何を指すのか明確にできます。複数の引数が存在する場合は、引数の個数だけ@paramを順番に記載します。

    /**
     * DOM要素を移動させます。
     * @param {HTMLElement} element DOM要素です。
     * @param {Number} duration 移動時間(単位:ミリ秒)です。
     * @param {String} direction 方向を指定します。
     */
    function moveTo(element, duration, direction) {
      // anything...
    }
  • @return

    関数の戻り値を示す@return。関数の戻り値が何を指すのか明確にできます。次の例では時間を返却する関数として定義していますが、単位が不明瞭であるためJSDocの@returnのコメントで「ミリ秒」であることを明確にしています。

    /**
     * @return {number} 経過時間(単位:ミリ秒)を返します。
     */
    function getTime() {
      const time = Date.now() - oldTime;
      return time;
    }

上記を使用して関数やクラスを記述した例

  • 関数

    初めに関数の説明をします。

    次に@paramで引数の説明を書きます。

    最後に@returnで返り値の説明を書きます。

    /**
     * 10年後の年齢を返す
     * @param {Number} age 年齢
     * @return {Number} 10年後の年齢
     */
    function tenYearsLater(age){
      let tenYearsLater = age + 10;
      return tenYearsLater;
    }
  • クラス

    初めにクラスの説明をします。

    @thisが何を指しているのかの中に書きます。

    @paramで関数の引数の説明を書きます。{}の中には関数の引数の型を書きます。

    必要に応じてメソッドの説明や、使用されている変数についてもコメントを加えます。

    /**
     * Personクラスです。
     * @this {Person}
     * @param {String} name 名前
     * @param {Number} age 年齢
     */
     
    class Person {
    	constructor(name, age) {
    		this.name = name;
    		this.age = age;
    	}
     
    	/**
    	* Personから生成したインスタンスが歩いた距離をコンソールに表示
    	* @param {Number} meter 歩いた距離(単位: m)
    	*/
    	move(meter) {
    		console.log(`${this.name}${meter}m動きました`);
    	}
    }

なおコメントを書く上で一番重要なことですが、あくまで仕様や振る舞いはなるべくコードで表現するようにしましょう。コメントはあくまで補足です。書いたコメントにも保守・運用のコストがかかります。

JavaScript Tips

letよりconstを使用する

前提

constもletも両方とも変数を定義するものであるが、ここではconstは「定数」を宣言する初期化子として話を進めます

結論

「letよりもconstを使用する」

理由

letは再代入が可能だから

再代入が可能ということは、どこからでもletで宣言した変数を変更することができてしまいます。しかし、constで宣言した定数なら値を変更しようとするとエラーが発生するので、思わぬバグを生む可能性を低く保つことができます(オブジェクトは変更できてしまいますが)。

また、実装者以外がletで宣言された変数を見ると、「この変数はどこで再代入されるのだろう」という疑問を持ちながらコードを読み進めることになってしまいます。読み進めた結果、変数に再代入されなかったなんてことがあれば他の実装者に大きな迷惑をかけることになります。

letを使用する際には、letでなければいけない理由がある限り使用するにようにしましょう。

関数とクラスで処理を共通化する

コードを書いている時の「この記述冗長だな、、」とか「この記述何度も書くのめんどくさい」と思う心はエンジニアとして素晴らしいことです。

そう思ったら「共通化」です。同じような処理を共通化してしまいましょう。ここでは、実際のユースケースの紹介は割愛しますが、共通化することのメリットと共通化するときに気をつけるべきことについて言及しています。

共通化することのメリット

  • 保守性が高まる

    機能に変更があれば、共通化している元の関数やクラスを変更するだけで、修正が済みます。

  • 記述量が少なく済む

    同じような処理を何回も記述することがないので、記述が簡潔になります。

共通化の際に意識すること

  • 小さな単位で共通化する

    処理を共通化するときは小さな処理の単位で共通化しましょう。コードに変更や修正があったときに共通化してある処理が小さければ、変更の影響もほとんど受けません。

  • 柔軟に共通化する

    例えば、①と②の処理を共通化する関数を作成しているとします。①ではAという変数を使用しているが、②ではBという変数を使用している。実際そのような場面は多くあります。そういったときは、共通化した関数でパラメーターを受け取れるようにして柔軟な関数を作成しましょう。

  • 自由すぎない共通化を目指す

    さきほど言っていた「柔軟に共通化する」とは反対のことのようですが、大事な考え方です。先ほどの話を聞くと柔軟であればあるほどいいように感じますが、そうではありません。確かに、機能を実装してる段階では柔軟な方が恩恵を多く受けます。しかし、修正や機能の変更があった際に問題が発生します。自由にしすぎた関数はどこでも使用されます。ある数箇所のみの変更に対応したら、共通化した関数の影響が意図しない場所にまで影響し、バグが発生してしまうなんてことにも。処理を共通化する際は、「変更をしても他に影響を及ぼさない」「この処理は結局この箇所にこういう処理をする」といった形で、限定的に関数の仕様を決めましょう。その関数を使用する人が、自分のみとは限りません。

セレクターの取得

JavaScriptで要素を取得する際、CSSでスタイルをあてているクラス名で取得するのはできるだけ避けましょう。理由は以下です。

  1. CSSのクラス名を変えた場合、同時にJavaScriptもハンドリングする要素名を変えなければならない
  2. 同じスタイルを反映させるためにクラス名を使いまわした時、JavaScript イベントハンドラも一緒にくっついてきてコーディングの自由度が下がる

JavaScriptはスタイリング用のクラスとは別のクラスもしくはidで取得するようにしましょう!

<!-- 推奨 -->
<button class="button js-button">ボタン</button>
 
<script>
	const btn = document.querySelector('.js-button');
</script>
 
<!-- 非推奨 -->
<button class="button">ボタン</button>
 
<style>
.button {
  display: inline-block;
}
</style>
 
<script>
	const btn = document.querySelectorAll('.button');
</script>

高頻度イベント処理を最適化する

高頻度イベントとは例えばscrollやresizeなど、高い頻度で動作するイベントのことです。こういった処理は高い頻度で直接DOMのプロパティにアクセスするため、ブラウザのパフォーマンスが著しく低下します。

Intersection Observer API

スクロールに連動したDOM要素の変更(パララックスとかlazyloadとか)がIntersection Observer APIを使用することで結構簡単に実装できます。

Intersection Observer とは (opens in a new tab)

他にもイベントを間引いたり、イベント検知と描画ロジックを分けるなどのやり方があります。この記事 (opens in a new tab)が結構役に立つかなと思います。(記事だとjQueryについて書いていますが素のJavaScriptにも共通することばかりですので、ぜひ読んでみてください。)

凝集度と結合度(発展)

凝集度や結合度はJavaScriptだけではなく全てのプログラミング言語の開発に関係する内容になります。

凝集度とは

1つのコンポーネント(関数やクラス)が持つ機能要素と情報要素間の関連性の強さを表す指標です。機能が限定的で情報が他に分散していない=凝集度が高いと言えます。

凝集度が低いとどうなるのか

凝集度が低いと

  • 理解するのが難しい
  • 別に再利用が難しい
  • メンテナンスするのが難しい
  • 他のクラスの変化に弱い

などといった保守性の低いプログラムになってしまします。

結合度とは

コンポーネント間での結びつきの度合いです。コンポーネント同士の関係が希薄である=**結合度が低い(疎結合)**と言えます。

結合度が高いとどうなるのか

これも凝集度が高いコンポーネントと同じで、可読性、保守性、再利用性の低いプログラムになってしまします。

JavaScriptのライブラリなどを想像してもらえればわかりやすいですが、機能が限定的でインターフェースが明確であるライブラリはとても使いやすいですよね。

下記で凝集度と結合度のレベルを見てみてください。

コードを書く際には「凝集度 高結合度 弱」を意識しましょう。

下記に記載した記事は大変勉強になります。時間がある時に読んでみてください。サイバーのやつは「良いコードとは何か」のスライドを見るとイメージしやすいかもです。

凝集度と結合度のレベル (opens in a new tab)

サイバーエージェントのエンジニア研修資料 (opens in a new tab)