Node.jsでJavaScriptによるレンダリングブロックを検証する

# はじめに
本記事では、ブラウザレンダリングについての説明は割愛します。
ブラウザレンダリングについての説明が必要な方は、
こちらの記事でわかりやすく説明されていたのでご覧ください。

https://zenn.dev/ak/articles/c28fa3a9ba7edb

また、この記事ではレンダリングブロックの中でも、JavaScriptによるものをご紹介します。

# レンダリングブロックとは
ブラウザレンダリングにおいて、レンダリングを遅延させてしまうことを指します。
レンダリングブロックの要因としては、以下が挙げられます。

1. JavaScriptによるもの
1. CSSによるもの

いずれも、外部リソースの読み込みによる遅延が大きな原因になります。

```html:外部リソース読み込みの例
<!-- JSによるもの -->
<script src="外部JSファイルへのパス"></script>

<!-- CSSによるもの -->
<link rel="stylesheet" href="外部CSSファイルへのパス" />
```

# Node.jsでレンダリングブロックを検証する
今回は、JSによるレンダリングブロックを実際に検証します。

### 前提条件
- Node.jsがインストールされていること
新しめのバージョンであれば大丈夫かと思います。
動作確認は14.16.1で行っています。

    ```bash
    $node -v
    v14.16.1
    ```

### HTMLとJavaScriptのファイルを作成し、表示してみる
1. 同じディレクトリ内に、以下の2つのファイルを作成する

    ```html:index.html
    <!DOCTYPE html>
    <html lang="ja">

    <head>
      <meta charset="utf-8">
      <title>タイトル</title>
      <script type="text/javascript" src="./index.js"></script>
    </head>

    <body>
      コンテンツ
    </body>

    </html>
    ```

    ```js:index.js
    console.log("Hello!")
    ```

2. index.htmlをブラウザで表示し、コンソールに「Hello!」と表示されればOK
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2160256/9d69d409-06a1-6d34-d567-342285766ff4.png)

JavaScriptが一瞬で読み込めるのであれば、特に表示が遅いと感じることはないと思います。
では、JavaScriptの取得に時間がかかってしまった場合はどうでしょう。

### JavaScriptの取得を遅延させてみる
1. 先ほど作成したHTMLファイルと同じディレクトリに、以下のファイルを作成する

    ```js:server.js
    const http = require('http');
    const fs = require('fs');

    const server = http.createServer();
    server.on('request', doRequest);
    server.listen(1234);
    console.log('Server running at http://localhost:1234/');

    function doRequest(req, res) {
      setTimeout(() => {
        fs.readFile('./index.js', 'UTF-8',
          function (err, data) {
            res.writeHead(200, { 'Content-Type': 'text/html' });
            res.write(data);
            res.end();
          });
      }, 5000);
    }
    ```
setTimeoutで5000msを指定することで、JSファイルの返却を5秒遅らせています。


1. HTMLファイルの外部JSファイルのパスを修正する

    ```html:index.html
    <!DOCTYPE html>
    <html lang="ja">

    <head>
      <meta charset="utf-8">
      <title>タイトル</title>
      <!--
      srcを書き換える
      <script type="text/javascript" src="./index.js"></script>
      -->
      <script type="text/javascript" src="http://127.0.0.1:1234/" defer></script>
    </head>

    <body>
      コンテンツ
    </body>

    </html>
    ```

1. Node.jsのサーバーを立てる
ファイルを作成したディレクトリで次のコマンドを実行します。

    ```
    node server.js
    ```

1. index.htmlをブラウザで表示し、コンソールに「Hello!」と表示されればOK
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2160256/567ac205-a555-f192-5688-aba67febe91d.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2160256/759f1931-2d53-88a0-d963-8815e0d585d6.png)

ローカルのJSファイルを直接読み込んだ際に比べ、
「コンテンツ」という文字が表示されるまでに少し時間がかかったと思います。
server.jsのsetTimeoutにより、JSファイルの取得に時間がかかったため、
レンダリングがブロックされたことになります。
新しいタブでindex.htmlにアクセスすると、違いがわかりやすいかと思います。


### JSによるブロックを防ぐ方法
ここでJSによるレンダリングブロックを防ぐ手法を1つご紹介します。

1. scriptタグに`defer`という属性を追加する

    ```html:index.html
    <!DOCTYPE html>
    <html lang="ja">

    <head>
      <meta charset="utf-8">
      <title>タイトル</title>
      <!--
      scriptタグに、defer属性を追加する
      -->
      <script type="text/javascript" src="http://127.0.0.1:1234/" defer></script>
    </head>

    <body>
      コンテンツ
    </body>

    </html>
    ```

2. 先程と同様に、Node.jsのサーバーを立て、index.htmlをブラウザで表示する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2160256/bcab3b5e-9af6-4075-e075-9c93f90ad66e.png)
今回は、「コンテンツ」という文字がすぐに表示されるものの、
コンソールの「Hello!」は遅れて表示されたかと思います。
`defer`を使うと、外部ファイルの読み込みを待たずにレンダリングを行い、
レンダリング後にJSの読み込み、実行を行うことができます。


#　おわりに
レンダリングブロックを簡単に体験する方法を紹介しました。
`defer`はレンダリングブロックを防ぐ1つの手法に過ぎないので、
興味を持った方は、レンダリングブロックの原因や対策について詳しく調べてみてください。