LoginSignup
285
232

More than 1 year has passed since last update.

Lighthouseの計測結果を見ていく

Last updated at Posted at 2019-01-01

Google Chrome 60からデベロッパーツールのAuditsタブからLighthouseが簡単に実行できるようになっているので使い方を見ていく。

Chrome89で日本語化されるので、この記事の内容とはだいぶ差異が出てきそう。
パフォーマンス測定に必須のLighthouseがChrome 89で日本語化される

Chrome79から各評価項目の参照URLがだいぶ変わってしまっている。web.dev内のページに置き換わっているようだ。

Chrome 103からTimeSpan、Snapshotレポートが追加になり、ユーザーが操作した状態のページの分析も行うことが出来るようになりました。
DevTools の新機能 (Chrome 103)

Lighthouseとは

Lighthouseとはウェブページの品質改善の指針を「パフォーマンス」、「PWA」、「アクセシビリティ」、「ベストプラクティス」、「SEO」点でチェックしてくれるツール。

Chrome拡張やコマンドラインで提供されていましたが、Google Chrome 60でデベロッパーツールに統合された。
また、オンラインでチェックしてくれるツールGoogle PageSpeed Insightsも、分析エンジンにLighthouseを採用するようになりました。とは言っても全く同じ結果とはならなくて、Google PageSpeed Insightsの判定の方が低い場合が多いようだ。

使い方

Chrome 79 以前

公式マニュアルは下記の通り。公式はChrome拡張の例だが、他の場合もだいたい同じ。
Lighthouse によるウェブアプリの監査
Github.com GoogleChrome/lighthouse/docs/

ディベロッパーツールを使う場合は、
Chromeでチェック対象のページを開いてデベロッパーツールのAuditsを開く。
※ Chrome82だと「Audits」ではなく「Lighthouse」タブになる

audits.png

Chrome 79 以降

Chrome79以降?はレイアウトが変わる。

lh0608sh.png

「Run Audits」で実行出来るが「Device」、「Audits」、「Throttling」、「Clear Storage」の項目が事前に設定できる。
※ chrome79から「Generate repot」ボタンだけになる。

Device

Deviceは「Mobile」か「Desktop」を選ぶ。

Audits

Auditsで「Performance」、「Progressive Web App」、「Accessibility」、「Best Practices」
、「SEO」を選択する。特に理由がなければ全部チェックでOK。
※ chrome79の時点から「Publisher Ads」が追加されている。

Throttling

モバイル端末・回線で通信しているようにスロットリングを行う事ができる。以下のモードがある。
※ chrome79の時点で項目が削除された。

Simulated Fast 3G, 4x CPU Slowdown
モバイル端末・回線の状態を推定して実行する。実際にNetwork、CPUのスロットリングは行わない。
Applied Fast 3G, 4x CPU Slowdown
モバイル端末・回線の状態に合わせて実際にNetwork、CPUのスロットリングを行う。
No throttling
スロットリングは行わない。

Simulatedの方は実際にスロットリングしないので、数十秒ぐらい実行時間が短い。
詳細は下記を参考に
Optimize Website Speed With Chrome DevTools

Clear Storage

初回アクセス時点と同じようにする為にストレージをクリアにする。
※ chrome79の時点で項目が削除された。

Chrome 103 以降

Chrome 103より項目が変更になっているようです。

DevTools の新機能 (Chrome 103)
image.png

Navigation、TimeSpan、Snapshotレポートが選べるようになっています。

Navigation レポートは、単一ページの読み込みを分析します。 Navigation は最も一般的なレポートタイプです。現在のバージョンより前のすべての Lighthouse レポートは、 Navigation レポートです。
Timespans レポートは、通常はユーザーの操作を含む任意の期間を分析します。
Snapshots レポートは、特定の状態でのページを分析します。通常は、ユーザーがページを操作した後です。

Timespans レポートは 「Start timespan」で開始してから、「End timespan」で終了するまでの間を分析するようです。
Snapshots レポートは、分析したいページの状態を設定してから分析を開始します。
https://coffee-cart.netlify.app/
例えば上記のようnクリックしたりすることで画面の表示内容が切り替わった状態を分析することが出来ます。

結果レポート

「Run Audits」が実行すると結果レポートが出力される。

report.jpg

Chrome89以降は日本語化されている。

lh0608.png

「Performance」、「Progressive Web App」、「Accessibility」、「Best Practices」
、「SEO」のそれぞれの指摘項目について
各項目をGoogle公式のLighthouse によるウェブアプリの監査の記載とリンクする様にした。
詳しい内容は公式の説明をみた方が良いかも。
ただし、公式は日本語版と英語版が混在しているし、公式説明に無いレポート内容もあるみたいだ。

以下は、それぞれの項目についてのざっとした説明や補足事項や参照リンクを乗せておく。

Performance

参考したサイト

Metrics(指標)

ページ読み込み始めて、完全に描画が完了して操作できる様になるまでにいくつかの段階がある。
それぞれの状態の実行時間を短くしてスコアを低くする。
「Navigation Start」>「First Paint」>「First Contenful Paint」>「First Meaningful Paint」>「Visually ready」>「Time To Interactive」>「Fully Loaded」

※ 下記の図はYosuke Furukawa さんのServer Side Rendering における注意点と対策より引用

20190210220602.png

First Contentful Paint(コンテンツの初回ペイント)

テキストまたは画像が初めてペイントされるまでにかかった時間。

First Meaningful Paint(意味のあるコンテンツの初回ペイント)

ページの主要なコンテンツが可視化されるまでにかかった時間

Speed Index(速度インデックス)

パフォーマンスが最適かどうか判断するのに重要な項目。
ページのコンテンツが目に見える状態になるまでの時間。

スコアを下げるためには「コンテンツの効率の最適化」、「クリティカル レンダリング パスの最適化」が必要。
重要なコンテンツを優先して最適な順番で表示する様にHTML、CSS、Javascriptの実行順番の調整、ファイルのサイズを小さく、圧縮、キャッシュの活用を行うこと。

First CPU Idle(CPU の初回アイドル)

ページのメインスレッド処理が静止し、初めて入力の処理が可能になるまでにかかった時間

Time to Interactive(インタラクティブになるまでの時間)

ページが完全にインタラクティブになるまでの時間

Estimated Input Latency(入力の推定待ち時間)

ページ読み込みの最もビジーな 5 秒間における、ユーザーの入力に対するアプリの応答時間(ミリ秒)。

改善できる項目

Minimize Critical Requests Depth(クリティカル リクエストの深さを最小にする)
Defer unused CSS(未使用なCSSの読み込みを先延ばしにする)

Lighthouseでは2KB以上削減できる場合は通知対象となる。

Enable Text Compression(テキスト圧縮を有効にする)

Webサーバーのテキスト圧縮の機能を有効にすること。
Lighthouseではレスポンスのオリジナルサイズが1.4KB以下、または圧縮してオリジナルサイズから10%以下しか削れない場合は通知されない。

Avoids Enormous Network Payloads(莫大なネットワークペイロードを避ける)

Lighthouseでは3G回線上で10秒以内に操作可能になるタイミング(TTL)を1600KBとしている

Has multiple page redirects(複数のページリダイレクトが発生)

リダイレクトが指定されている場合に通知される。リダイレクトはスピードを遅くする要因になるため、端末に判別してモバイルサイトに転送するくらいだったらレスポンシブなページにした方が良いらしい。

JavaScript Bootup Time Is Too High(Javascriptの起動時間が長すぎる)

JavaScriptの起動を速くするために、コードのサイズを小さく圧縮して不要なコードは除いてキャッシュを活用しましょうと言うこと。

Keep Server Response Times Low(サーバーのレスポンスタイムを低く保つ)

Googleはページのダウンロード開始まで0.6秒未満を推奨としている。Webサーバーのアプリケーションの改善、データベースのクエリーの見直し、メモリ、CPUの増強をしましょうと言うこと。

Minify CSS(CSSを圧縮する)

LightHouseはコメントや空白をチェックしてもっと圧縮できるか判断しているので、minifierを使ってLightHouseがチェックしている以上の圧縮をすることが出来るかもしれない。

Defer offscreen images(オフスクリーン画像を遅延させる)

スクロールしないと見ることが出来ない画面領域にある画像はページロード時に読み込む必要がないので、IntersectionObserverを使って遅延で読み込むようにしましょうと言うこと。
操作可能になるタイミング (TTI)より前にオフスクリーン画像が呼ばれたら通知対象となる。

Optimize Images(画像を最適化する)

JPEGの画像圧縮レベルを85に圧縮して4KB以上減らせる場合、通知対象となる。

Oversized Images(大き過ぎる画像)

実際の画像と表示された画像のサイズを比較して、実際のサイズより25KB以上小さい場合は通知対象となる。

Preload key requests(キーリクエストのPreload)
Reduce Render-Blocking Scripts(レンダリング ブロック スクリプトを減らす)

レンダリングをブロックするようなリンクや、Javascriptが無いようにする。scriptタグであればasyncまたは defer属性を活用する。

以下の場合に通知される。

  • scriptタグがhead内にあって、defer、async属性が指定されていない場合
  • <link rel="stylesheet"> タグにmedia属性が無い場合。(disabled属性がある場合はダウンロードしないので無視される)
Reduce Render-Blocking Stylesheets(サイトに初回ペイントの遅延を引き起こすリソースを使用しない)

初回ペイント時に必要なスクリプトやリンクをインライン化して、それ以外の要素は後回しにする・

Serve Images In Next-Gen Formats(サーバー画像を次世代フォーマットにする)

次世代画像フォーマットWebPを利用する。「Edge」「Firefox」「Chrome」は対応しているが、「Safari」は使えない。
WebP形式に変換して8KB以上削れる場合は通知される。

Unoptimized Images(最適化されていない画像)

画像の圧縮レベル85の状態のものとオリジナルの画像を比較して4KB以上削れるものは通知される。

User Timing Marks and Measures(User Timing の Mark と Measure)

合格不合格を判定するものではなく、User Timing APIを使ってJavascriptのパフォーマンス計測を行いましょうと言うこと。

Uses An Excessive DOM Size(過度なDOMサイズを使用)

DOMサイズが大きければ、それだけページの読み込み時間がかかり、レンダリングも遅くなる。また、document.querySelectorAll('li')を使用した時に大量のノードを拾ってメモリを圧迫してしまう。

  • ノードの数を1500以下にする
  • ノードの深さを32以下にする
  • 60以上子ノードを持つ親ノードを無くす
Uses inefficient cache policy on static assets(静的コンテンツ上で非効率なキャッシュポリシーを使用)

Lighthouseはfont/image/mediaファイル/script/stylesheetがあり、HTTP status codeが200/203/206で、明確なno-cacheポリシーが設定されていない場合、キャッシュ可能と考える。

Reduce JavaScript execution time(JavaScript の実行にかかる時間の低減)

JavaScriptの実行時間が2秒以上かかる場合は警告対象となる。3.5秒以上かかる場合は落第とする。

まとめ
  • 使っていないCSSを削る。不要な箇所が2KB以下になるようにする。
  • テキストデータのサイズを1.4KB以下、もしくは10%以上削れる余地が無いくらい圧縮する。
  • ページのダウンロード開始までの時間が0.6秒未満になるようにする。
  • 操作可能になるタイミング (TTI)で表示が必要な画像以外は、遅延で必要な時点で読み込む。
  • 実際のサイズと表示サイズの差が25KB以上ある画像はリサイズする。
  • scriptタグは下のbodyタグ直前に書く。もしくはdefer、asyncを指定する。
  • JPG画像は85%の品質に最適化する。
  • ノードの数を1500以下にする。
  • ノードの深さを32以下にする。
  • 60以上子ノードを持つ親ノードを無くす。

Progressive Web App

PWA対応されているかのチェック。

Address Bar Matches Brand Colors(アドレスバーはブランドカラーと一致させる)

Android版Chrome39以降だとアドレスバーの色を指定することが出来る。

<head>
  <meta name="theme-color" content="#317EFB"/>
  ...
Cache Contains start_url From Manifest(キャッシュにマニフェストの start_url を保持する)
Configured For A Custom Splash Screen(スプラッシュスクリーンを設定する)

ネイティブアプリの様に起動中画面(スプラッシュスクリーン)を指定することが出来る。
manifest.jsonファイルを指定する必要があり。

Contains Some Content When Its Scripts Are Not Available(ページでスクリプトが利用できない場合に表示するコンテンツを用意する)

JavaScriptが無効な場合に空白のページされないことのチェックが行われる。
noscript要素を使って代わりのコンテンツが表示される様にする。

<noscript> 
<p>JavaScirptが必要であることをユーザーに警告する説明文</p>
</noscript> 
Content Sized Correctly for Viewport(ビューポートのサイズに適合したコンテンツ)
Has A Viewport Meta Tag With width Or initial-scale(HTML にビューポートのメタタグを含める)
Manifest Contains Icons at Least 192px(マニフェストで少なくとも 192px のアイコンを定義する)
Manifest Contains background_color(マニフェストで背景色を指定する)
Manifest Contains name(マニフェストでアプリ名を定義する)
Manifest Contains short_name(マニフェストでアプリの省略名を定義する)
Manifest Contains Start URL(マニフェストで開始 URL を指定する)
Manifest Exists(マニフェストを使用する)
Manifest's display Property Is Set(マニフェストで display プロパティを指定する)
Page Load Is Fast Enough On 3G(ページの読み込みが3Gで十分に速い)
Redirects HTTP Traffic To HTTPS(サイトで HTTP トラフィックを HTTPS にリダイレクトする)
Registers A Service Worker(Service Worker を登録する)
Responds With A 200 When Offline(オフライン時に URL でステータスコード 200 を返す)
User Can Be Prompted To Install The Web App(ウェブアプリのインストールを促される可能性がある。)
Uses HTTPS(サイトを HTTPS で配信する)

HTTPS接続出来ないとPWAが使えない。

Accessibility

Buttons Have An Accessible Name(ボタンにアクセシブルな名前を使用する)
Document Doesn't Have A Title Element(Title要素の指定がない)
Every Form Element Has A Label(全てのフォーム要素にラベルを使用する)
Every Image Has An alt Attribute(全ての画像にalt属性を使用する)
No Element Has A tabindex Attribute Greater Than 0(tabindex属性を持つ要素が一つもない)

Best Practices

非推奨の機能を使っていないかとか、セキュリティに問題がないか、モダンな機能を適切に使っているかなど。

Page has the HTML doctype(HTML doctypeが指定されている)

HTML doctypeを特定することで後方互換モードに切り替わる事を防ぐ

Avoids Application Cache(サイトでアプリケーション キャッシュを使用しない)

AppCacheはWeb標準から削除されているため、代わりにService Worker の Cache API を使用する。

Avoids console.time()(サイトの独自スクリプトで console.time() を使用しない)

console.time() を使用するのは止めて、performance.mark() performance.measure() を使用する。

Avoids Date.now()(サイトの独自スクリプトで Date.now() を使用しない)

Date.now()より高精度のperformance.now()を使用する。

Avoids Deprecated APIs(サイトで非推奨のAPIを使わない)

Chrome Platform Statusに出ているような非推奨のAPIを使用しないこと。

Avoids document.write()(サイトで document.write() を使用しない)

document.write() を使用すると数十秒単位で表示が遅れる場合があり使用しないこと。

Avoids Mutation Events In Its Own Scripts(サイトの独自スクリプトで変更イベントを使用しない)

以下の変更イベントはパフォーマンスを低下させるため、DOM イベントの仕様において廃止されている。

DOMAttrModified
DOMAttributeNameChanged
DOMCharacterDataModified
DOMElementNameChanged
DOMNodeInserted
DOMNodeInsertedIntoDocument
DOMNodeRemoved
DOMNodeRemovedFromDocument
DOMSubtreeModified

代わりにMutationObserverを使う。

Avoids Old CSS Flexbox(サイトで古い CSS Flexbox を使用しない)

display:box形式はdisplay:flexに置き換える必要がある。

Avoids Requesting The Geolocation Permission On Page Load(ページの読み込み時に自動的に位置情報をリクエストしない)

サイトの読み込み時にホームページで位置情報を求めると、ユーザーにあまり良くない印象を与えてしまいます。
ボタンをクリック時などのユーザーの操作時に限って位置情報へのアクセスをリクエストする必要があります。

Avoids Requesting The Notification Permission On Page Load(ページの読み込み時に自動的に通知パーミッションをリクエストしない)

ページの読み込み時に、プッシュ通知のパーミッションを要求するのではなく、ユーザー操作への応答としてリクエストを行うようにする必要があります。

Browser Errors Were Logged To The Console(ブラウザエラーがコンソールにログ出力されている)
Displays Images With Incorrect Aspect Ratio(誤ったアスペクト比で画像を表示しない)

元画像と比べて5%以上アスペクト比が異なる場合は通知対象となる。

Includes Front-End JavaScript Libraries With Known Security Vulnerabilities(セキュリティ脆弱性があると知られているJavascriptライブラリが含まれている)
Manifest's short_Name Won't Be Truncated When Displayed On Homescreen(マニフェストに定義したアプリの省略名が切れずにホーム画面上に表示される)

ウェブアプリ マニフェストの short_name プロパティを 12 文字以下にしないとホーム画面上で切れて表示されてしまう。

Links to cross-origin destinations are unsafe(サイトで rel="noopener" を使用して外部アンカーを開く)

target="_blank"を使って外部リンクする場合、 rel="noopener"を付けていないと、
リンク先のページと同じプロセスで動くので、リンク先のJavascriptの負荷が高い場合、リンク元にも影響を受ける。また、リンク先のページからwindow.openerを使ってリンク元のページを操作される可能性がある。

<a href="https://examplepetstore.com" target="_blank" rel="noopener">...</a>
Prevents Users From Pasting Into Password Fields(パスワードフィールドにペーストするのを防ぐ)

パスワードがペーストするのをJavascriptでブロックしているサイトがあるがセキュリティ改善策にはならないので辞めましょうということ。
Password managersを使って、複雑なパスワードを自動生成してそれをコピペできるようにした方がセキュリティ的に良いとのこと。

Some Insecure Resources Can Be Upgraded To HTTPS(セキュアでは無いリソース(HTTP)がHTTPSに置き換えようとされる可能性がある)
Uses HTTPS(サイトを HTTPS で配信する)

https以外の場合は通知対象になる。プライバシー情報を扱っていなかったとしても全てのサイトはhttps通信すべきとのこと。

Uses HTTP/2 For Its Own Resources(サイトの独自リソースに HTTP/2 を使用する)
Uses Passive Event Listeners To Improve Scrolling Performance(Passive Event Listener を使用してサイトでのスクロール パフォーマンスを向上させる)

イベントリスナにpassiveオプションを追加することで、preventDefault()を実行していないことを明示して、スクロールのパフォーマンスの改善を行うことが出来る。


let ticking = false;
document.addEventListener('scroll', function(evt) {
  if (!ticking) {
    requestAnimationFrame(function() {
      ticking = false;
      // 処理

    });
    ticking = true;
  }
}, !document.documentMode ? { passive: true } : false);
Avoids Web SQL(サイトに Web SQL を使用しない)

Web SQL Databaseは廃止されたので、代わりにIndexedDBを使う。

SEO

SEO対策はこのチェック項目だけOKであれば良いと言うわけでも無いので、
基本的には、下記の様なサイトや書籍も参考にする。

Document Does Not Have A Meta Description(Meta Descriptionの指定が無い)

headタグの中にMeta Descriptionタグを用意する事。ページ毎に重複しないようにする。文字数は300文字以内ぐらい。

<meta name="Description" content="Put your description here.">
Document Doesn't Have A Title Element(Title要素の指定がない)

Title要素はSEOにおいて重要な情報なので全てのページに指定する必要がある。


<!doctype html>
<html>
  <head>
    <title>Create Good Titles</title>
  </head>
  ...
</html>

Search Consoleを使用していればHTML の改善レポートでタイトルタグの問題などのレポートを受け取ることが出来る。

以下の点を気にすると良いらしい。

  • 具体的にわかりやすく簡潔にする。
    「Home」の様な漠然とした説明は避ける。
    不必要に長いものや無駄な情報が含まれるものも避ける。
  • キーワードスタッフィング(キーワードの詰め込み)は避ける。
    「Foobar, foo bar, foobars, foo bars」のようなタイトルはユーザーの利便性を下げる。
    キーワードの乱用は検索エンジンがスパムと判断してしまうかもしれない。
  • 同じタイトルの繰り返しや、定型文は避ける。
    サイトのそれぞれのページに異なる具体的なタイトルを付けることが重要。
    ページの実際のコンテンツに即したタイトルにする。そのページに含まれていないコンテンツについての単語は含めない。
  • タイトルを目立たせるのはOKだが簡潔にする。
    「ExampleSocialSite: 新しいアカウントの登録」の様にハイフンやコロン、パイプなどでタイトルを使って、サイト名と残りの部分が区別できる様にする。
       

title要素にどんな内容を書くのが良いのかの詳しくについては、SEO上重要項目の一つだけあってネットや書籍で探せばたくさん出てくる。

Document doesn't have a valid hreflang(正しいhreflangが指定されていない)

ページに言語や地域ごとの複数のバージョンがある場合、headタグ内に言語別の代替ページを指定する。

<link rel="alternate" hreflang="en" href="https://example.com" />
<link rel="alternate" hreflang="es" href="https://es.example.com" />
<link rel="alternate" hreflang="de" href="https://de.example.com" />

Lighthouseだと正しい言語コードかチェックするのみ。

Document doesn't have a valid rel=canonical(正しいrel=canonicalが指定されていない)

複数のURLで同じページにアクセス出来たり、類似ページが複数ある場合など、「重複」URLと判断されて意図した通りにクロールされない事を防ぐため、ページ毎に事に固有の正規(canonical)URLを設定する。

<head>
<link rel="canonical" href="https://example.com/dresses/green-dresses" />
</head>
Document uses plugins(プラグインを使っている)

モバイル端末で使用できない様なプラグインを使用していないか。
 embed / object / applet
 

Document Doesn't Use Legible Font Sizes(読みやすいフォントサイズを使用していない)

フォントサイズは12pxより小さいとモバイル端末では見づらくなる。また、ピンチによる拡大で読みやすいサイズに調整できる様にした方が良い。

Links Do Not Have Descriptive Text(リンクに説明文が指定されていない)

リンクテキストの文言が「click here」ではなく「basketball videos」とどんな内容かわかる様に記述する。

<p>To see all of our basketball videos, <a href="videos.html">click here</a>.</p><p>Check out all of our <a href="videos.html">basketball videos</a>.</p>

ただし、lighthouseでは下記の単語しかチェックをしていないので、日本語の単語だとヒットしない。

click here/ click this / go / here / this / start / right here / more / learn more

Page has unsuccessful HTTP status code(エラーHTTPステータスコードが出力される)

lighthouseはHTTPステータスコードが400〜599の場合エラーとしている。
通常特に設定にミスがなければきにする必要はないと思われる。

Page Is Blocked From Indexing(検索インデックス登録がブロックされている)

<meta name="robots" content="noindex"/>の様な検索エンジンをブロックする設定あるか。 もし検索エンジンをブロックするつもりが無いなら外す。

robots.txt is not valid(robots.txtが正しくない)

Search Consoleにrobots.txt テスターツールがあるのでチェック出来る。

Does not have a tag with width or initial-scale(widthやinitial-scaleを指定したタグが無い)

ビューポートの タグが 無い場合は指定する。
ビューポートの設定項目はいくつかあるが多くのサイトは下記の設定がされていることが多い。

<head>
  ...
  <meta name="viewport" content="width=device-width, initial-scale=1">
  ...
</head>

Publisher Ads

Lighthouse によるサイト運営者広告監査

Load tags only once per frame(タグが 1 フレームにつき 1 回のみ読み込まれるようにする)
Tag load time
First ad request time
First bid request time
Latency of first ad render(タグの読み込み時間を短縮する)
GPT and bids loaded in parallel
Header bidding is parallelized(入札リクエストを並行処理する)
No bottleneck requests found
Ad scripts are loaded statically(スクリプト挿入タグの使用を避ける)
Ads not blocked by load events
Minimal render-blocking resources found(サイトに初回ペイントの遅延を引き起こすリソースを使用しない)
No long tasks blocking ad-related network requests(広告関連のネットワーク リクエストを妨げる、時間のかかるタスクを避ける)
Ad request waterfall(広告読み込みのクリティカル パスを減らす)
Few or no ads loaded outside viewport(表示領域に近づくまで広告が読み込まれないようにする)
Ad tag is loaded asynchronously(Google サイト運営者タグ(GPT)を非同期で読み込む)
Ad tag is loaded over HTTPS(HTTPS 経由で広告タグを読み込む)
GPT tag is loaded from recommended host(Google サイト運営者タグ(GPT)が推奨ホストから読み込まれるようにする)
Ad density in initial viewport is within recommended range(最初の表示領域の広告密度を減らす)
No ad found at the very top of the viewport(上部の広告をページの下の方に移動する)

その他参考サイト

285
232
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
285
232