[{"rendered_body":"\u003ch2 data-sourcepos=\"1:1-1:15\"\u003e\n\u003cspan id=\"はじめに\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eはじめに\u003c/h2\u003e\n\u003cp data-sourcepos=\"3:1-4:164\"\u003e一昔前はAtomic Designをベースとしたディレクトリ構成をよく使っていましたが、今では見かけることも少なくなっています\u003cbr\u003e\n最近新規リポジトリのディレクトリ構成を考える中でAtomic Designの存在を思い出したので2026年の今改めて考え直してみます\u003c/p\u003e\n\u003ch2 data-sourcepos=\"6:1-6:22\"\u003e\n\u003cspan id=\"atomic-designとは\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#atomic-design%E3%81%A8%E3%81%AF\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eAtomic Designとは\u003c/h2\u003e\n\u003cp data-sourcepos=\"8:1-10:83\"\u003eAtomic Designは2013年に生まれた、コンポーネントを5つの大きさに分類する考え方です\u003cbr\u003e\n私が知ったのは2018年だったので、その頃には日本でも普及していたはずです\u003cbr\u003e\n小さいものから順にAtoms/Molecules/Organisms/Templates/Pagesに分けます\u003c/p\u003e\n\u003cp data-sourcepos=\"12:1-13:121\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F7cd3e2fb-5079-4f4e-ab7a-12ec03f12ac6.png?ixlib=rb-4.1.1\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=abfe3cdda7efa45e90abf5a2a75da00b\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F7cd3e2fb-5079-4f4e-ab7a-12ec03f12ac6.png?ixlib=rb-4.1.1\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=abfe3cdda7efa45e90abf5a2a75da00b\" alt=\"Atomic Designの5段階\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F7cd3e2fb-5079-4f4e-ab7a-12ec03f12ac6.png?ixlib=rb-4.1.1\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=7aae8dd72632a50ecb595f1c8cb9cc13 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/7cd3e2fb-5079-4f4e-ab7a-12ec03f12ac6.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003cbr\u003e\n公式サイト（\u003ca href=\"https://atomicdesign.bradfrost.com/chapter-2/\" rel=\"nofollow noopener\" target=\"_blank\"\u003eAtomic Design by Brad Frost - Chapter 2\u003c/a\u003e）より引用\u003c/p\u003e\n\u003cul data-sourcepos=\"15:1-28:0\"\u003e\n\u003cli data-sourcepos=\"15:1-16:78\"\u003eAtoms（原子）\n\u003cul data-sourcepos=\"16:5-16:78\"\u003e\n\u003cli data-sourcepos=\"16:5-16:78\"\u003eボタンや入力欄などそれ以上分割できない最小の部品\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"17:1-19:78\"\u003eMolecules（分子）\n\u003cul data-sourcepos=\"18:5-19:78\"\u003e\n\u003cli data-sourcepos=\"18:5-18:53\"\u003eAtomsを組み合わせた小さなまとまり\u003c/li\u003e\n\u003cli data-sourcepos=\"19:5-19:78\"\u003e入力欄と検索ボタンを組み合わせた検索フォームなど\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"20:1-22:36\"\u003eOrganisms（有機体）\n\u003cul data-sourcepos=\"21:5-22:36\"\u003e\n\u003cli data-sourcepos=\"21:5-21:65\"\u003eMoleculesやAtomsを組み合わせた大きなまとまり\u003c/li\u003e\n\u003cli data-sourcepos=\"22:5-22:36\"\u003eヘッダーやカードなど\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"23:1-25:51\"\u003eTemplates（テンプレート）\n\u003cul data-sourcepos=\"24:5-25:51\"\u003e\n\u003cli data-sourcepos=\"24:5-24:54\"\u003e部品の配置を示したページの骨組み\u003c/li\u003e\n\u003cli data-sourcepos=\"25:5-25:51\"\u003e中身のデータはまだ入っていない\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"26:1-28:0\"\u003ePages（ページ）\n\u003cul data-sourcepos=\"27:5-28:0\"\u003e\n\u003cli data-sourcepos=\"27:5-28:0\"\u003eTemplatesに実際のデータを流し込んだページ\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp data-sourcepos=\"29:1-29:91\"\u003eコンポーネントのディレクトリ構成もこの5分類を用いて作られます\u003c/p\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"text\" data-sourcepos=\"31:1-44:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003esrc/components/\n├── atoms/\n│   ├── Button.tsx\n│   └── Input.tsx\n├── molecules/\n│   └── SearchForm.tsx\n├── organisms/\n│   └── Header.tsx\n├── templates/\n│   └── HomeTemplate.tsx\n└── pages/\n    └── HomePage.tsx\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003ch2 data-sourcepos=\"46:1-46:24\"\u003e\n\u003cspan id=\"良かったところ\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E8%89%AF%E3%81%8B%E3%81%A3%E3%81%9F%E3%81%A8%E3%81%93%E3%82%8D\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e良かったところ\u003c/h2\u003e\n\u003cp data-sourcepos=\"48:1-48:126\"\u003eAtomic Designには以下のような利点があったためReactの普及と共に広く使われるようになりました\u003c/p\u003e\n\u003cul data-sourcepos=\"50:1-59:0\"\u003e\n\u003cli data-sourcepos=\"50:1-52:149\"\u003eコンポーネントの粒度の指針になる\n\u003cul data-sourcepos=\"51:5-52:149\"\u003e\n\u003cli data-sourcepos=\"51:5-51:81\"\u003e実装者の感覚に委ねられていたところにルールができた\u003c/li\u003e\n\u003cli data-sourcepos=\"52:5-52:149\"\u003eどこまで細かく分けるかの根拠になる（それまではAtomsレベルまで小さく分けるべきか悩む場面もあった）\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"53:1-55:153\"\u003e粒度を語る共通言語となる\n\u003cul data-sourcepos=\"54:5-55:153\"\u003e\n\u003cli data-sourcepos=\"54:5-54:132\"\u003e例えば、「Organisms」と言っただけでどれくらいの大きさのコンポーネントなのかすぐに伝わる\u003c/li\u003e\n\u003cli data-sourcepos=\"55:5-55:153\"\u003eどの粒度のコンポーネントでどのような処理（例えばAPIアクセス）をしていいといったルール決めにも役立つ\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"56:1-59:0\"\u003eコンポーネント駆動開発との親和性が高い\n\u003cul data-sourcepos=\"57:5-59:0\"\u003e\n\u003cli data-sourcepos=\"57:5-57:117\"\u003e小さい単位で分ける思想がStorybookを用いたコンポーネント駆動の開発と相性がいい\u003c/li\u003e\n\u003cli data-sourcepos=\"58:5-59:0\"\u003e各粒度のコンポーネントに対してStoryを作ることでUIバリエーションの確認もしやすい\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"60:1-60:30\"\u003e\n\u003cspan id=\"良くなかったところ\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E8%89%AF%E3%81%8F%E3%81%AA%E3%81%8B%E3%81%A3%E3%81%9F%E3%81%A8%E3%81%93%E3%82%8D\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e良くなかったところ\u003c/h2\u003e\n\u003cp data-sourcepos=\"62:1-62:169\"\u003eしかし、採用事例が増える中で以下のような使いづらさが見えてきて、Atomic Designをやめたという事例も聞くようになりました\u003c/p\u003e\n\u003cul data-sourcepos=\"64:1-73:0\"\u003e\n\u003cli data-sourcepos=\"64:1-66:159\"\u003eMoleculesとOrganismsの分類に迷う\n\u003cul data-sourcepos=\"65:5-66:159\"\u003e\n\u003cli data-sourcepos=\"65:5-65:106\"\u003e実際作っているとこの2つのどちらに置くか迷うという声をよく聞きました\u003c/li\u003e\n\u003cli data-sourcepos=\"66:5-66:159\"\u003e私の過去プロダクトではOrganismsに寄せるルールとしていたのでMoleculesがほとんど空っぽになったこともありました\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"67:1-69:119\"\u003eTemplatesが使われない\n\u003cul data-sourcepos=\"68:5-69:119\"\u003e\n\u003cli data-sourcepos=\"68:5-68:177\"\u003e分類の思想としては理解できるものの、実装へ落とした時にTemplatesのような骨格のコンポーネントはほとんど作りませんでした\u003c/li\u003e\n\u003cli data-sourcepos=\"69:5-69:119\"\u003eそもそもTemplatesとPagesは役割が重なりやすく分けることのメリットを感じにくいです\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"70:1-73:0\"\u003e各分類内での構造化に悩む\n\u003cul data-sourcepos=\"71:5-73:0\"\u003e\n\u003cli data-sourcepos=\"71:5-71:152\"\u003eプロダクトが成長するとatomsやorganismsディレクトリに並ぶコンポーネントの数が増えて管理がつらくなります\u003c/li\u003e\n\u003cli data-sourcepos=\"72:5-73:0\"\u003eサブディレクトリを切るにも独自のルールを考える必要があり、せっかく決めたAtomic Designのルールとは別の整理を考え直すことになっていました\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"74:1-74:33\"\u003e\n\u003cspan id=\"今どう使われているか\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E4%BB%8A%E3%81%A9%E3%81%86%E4%BD%BF%E3%82%8F%E3%82%8C%E3%81%A6%E3%81%84%E3%82%8B%E3%81%8B\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e今どう使われているか\u003c/h2\u003e\n\u003cp data-sourcepos=\"76:1-77:96\"\u003e最近では、昔のようにAtomic Designの5分類をディレクトリ名に使うような設計はほとんど見なくなりました\u003cbr\u003e\n代わりに、以下のような考え方で構造化することが多いように思います\u003c/p\u003e\n\u003cul data-sourcepos=\"79:1-85:0\"\u003e\n\u003cli data-sourcepos=\"79:1-81:119\"\u003e大きさよりも役割を重視した分類\n\u003cul data-sourcepos=\"80:5-81:119\"\u003e\n\u003cli data-sourcepos=\"80:5-80:123\"\u003e特定のドメインに依存しない汎用的な部品と、特定のドメインに特化した部品で分ける\u003c/li\u003e\n\u003cli data-sourcepos=\"81:5-81:119\"\u003epropsを受け取って表示するだけの部品と、APIアクセスなどの処理を持つ部品で分ける\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"82:1-85:0\"\u003e粒度ではなくドメインや機能を単位としたディレクトリ構成\n\u003cul data-sourcepos=\"83:5-85:0\"\u003e\n\u003cli data-sourcepos=\"83:5-83:78\"\u003eドメイン単位で階層を分け、その中で細分化していく\u003c/li\u003e\n\u003cli data-sourcepos=\"84:5-85:0\"\u003e上位の階層で分かれるので、atomsやorganismsに大量のコンポーネントが並ぶ問題も解消する\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp data-sourcepos=\"86:1-86:124\"\u003e最初に挙げた5分類の構成と比べると、ディレクトリの分け方が変わっているのがわかります\u003c/p\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"text\" data-sourcepos=\"88:1-100:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003esrc/\n├── features/\n│   ├── auth/\n│   │   ├── LoginForm.tsx\n│   │   └── SignupForm.tsx\n│   └── product/\n│       ├── ProductCard.tsx\n│       └── ProductList.tsx\n└── shared/\n    ├── Button.tsx\n    └── Input.tsx\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cul data-sourcepos=\"102:1-105:0\"\u003e\n\u003cli data-sourcepos=\"102:1-105:0\"\u003eこうした機能単位で分ける考え方を体系化したものに、FSD（Feature-Sliced Design）があります\n\u003cul data-sourcepos=\"103:5-105:0\"\u003e\n\u003cli data-sourcepos=\"103:5-103:120\"\u003eより上位の階層で機能ごとに分けて、汎用的な部品は共通の置き場にまとめる方法論\u003c/li\u003e\n\u003cli data-sourcepos=\"104:5-105:0\"\u003eAtomic Designと対立するものではなく、機能ごとに分けた中でAtomic Designの考え方を部分的に組み合わせることもできます\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp data-sourcepos=\"106:1-108:115\"\u003eそもそもAtomic Design自体、厳密なルールとして作られたものではありませんでした\u003cbr\u003e\n原典には、5階層は決まったルールではなくUIを捉えるためのメンタルモデルだと書かれています\u003cbr\u003e\n5分類をそのまま守る使い方は減りましたが、その考え方は今の設計にも生きています\u003c/p\u003e\n\u003ch2 data-sourcepos=\"110:1-110:12\"\u003e\n\u003cspan id=\"まとめ\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%81%BE%E3%81%A8%E3%82%81\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eまとめ\u003c/h2\u003e\n\u003cp data-sourcepos=\"112:1-114:171\"\u003eAtomic Designをそのまま使う場面は減りましたが、考え方のエッセンスは今の設計にも引き継がれています\u003cbr\u003e\nAtomic Designに限らず、ある時代の課題を解決する考え方が生まれ、やがてその考え方の課題が見えてきて、それを解決する次の考え方が生まれていきます\u003cbr\u003e\n今となっては古い手法かもしれませんが、この歴史があることで今のプラクティスが生まれているということなんだと思います\u003c/p\u003e\n","body":"## はじめに\n\n一昔前はAtomic Designをベースとしたディレクトリ構成をよく使っていましたが、今では見かけることも少なくなっています\n最近新規リポジトリのディレクトリ構成を考える中でAtomic Designの存在を思い出したので2026年の今改めて考え直してみます\n\n## Atomic Designとは\n\nAtomic Designは2013年に生まれた、コンポーネントを5つの大きさに分類する考え方です\n私が知ったのは2018年だったので、その頃には日本でも普及していたはずです\n小さいものから順にAtoms/Molecules/Organisms/Templates/Pagesに分けます\n\n![Atomic Designの5段階](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/7cd3e2fb-5079-4f4e-ab7a-12ec03f12ac6.png)\n公式サイト（[Atomic Design by Brad Frost - Chapter 2](https://atomicdesign.bradfrost.com/chapter-2/)）より引用\n\n- Atoms（原子）\n    - ボタンや入力欄などそれ以上分割できない最小の部品\n- Molecules（分子）\n    - Atomsを組み合わせた小さなまとまり\n    - 入力欄と検索ボタンを組み合わせた検索フォームなど\n- Organisms（有機体）\n    - MoleculesやAtomsを組み合わせた大きなまとまり\n    - ヘッダーやカードなど\n- Templates（テンプレート）\n    - 部品の配置を示したページの骨組み\n    - 中身のデータはまだ入っていない\n- Pages（ページ）\n    - Templatesに実際のデータを流し込んだページ\n\nコンポーネントのディレクトリ構成もこの5分類を用いて作られます\n\n```\nsrc/components/\n├── atoms/\n│   ├── Button.tsx\n│   └── Input.tsx\n├── molecules/\n│   └── SearchForm.tsx\n├── organisms/\n│   └── Header.tsx\n├── templates/\n│   └── HomeTemplate.tsx\n└── pages/\n    └── HomePage.tsx\n```\n\n## 良かったところ\n\nAtomic Designには以下のような利点があったためReactの普及と共に広く使われるようになりました\n\n- コンポーネントの粒度の指針になる\n    - 実装者の感覚に委ねられていたところにルールができた\n    - どこまで細かく分けるかの根拠になる（それまではAtomsレベルまで小さく分けるべきか悩む場面もあった）\n- 粒度を語る共通言語となる\n    - 例えば、「Organisms」と言っただけでどれくらいの大きさのコンポーネントなのかすぐに伝わる\n    - どの粒度のコンポーネントでどのような処理（例えばAPIアクセス）をしていいといったルール決めにも役立つ\n- コンポーネント駆動開発との親和性が高い\n    - 小さい単位で分ける思想がStorybookを用いたコンポーネント駆動の開発と相性がいい\n    - 各粒度のコンポーネントに対してStoryを作ることでUIバリエーションの確認もしやすい\n\n## 良くなかったところ\n\nしかし、採用事例が増える中で以下のような使いづらさが見えてきて、Atomic Designをやめたという事例も聞くようになりました\n\n- MoleculesとOrganismsの分類に迷う\n    - 実際作っているとこの2つのどちらに置くか迷うという声をよく聞きました\n    - 私の過去プロダクトではOrganismsに寄せるルールとしていたのでMoleculesがほとんど空っぽになったこともありました\n- Templatesが使われない\n    - 分類の思想としては理解できるものの、実装へ落とした時にTemplatesのような骨格のコンポーネントはほとんど作りませんでした\n    - そもそもTemplatesとPagesは役割が重なりやすく分けることのメリットを感じにくいです\n- 各分類内での構造化に悩む\n    - プロダクトが成長するとatomsやorganismsディレクトリに並ぶコンポーネントの数が増えて管理がつらくなります\n    - サブディレクトリを切るにも独自のルールを考える必要があり、せっかく決めたAtomic Designのルールとは別の整理を考え直すことになっていました\n\n## 今どう使われているか\n\n最近では、昔のようにAtomic Designの5分類をディレクトリ名に使うような設計はほとんど見なくなりました\n代わりに、以下のような考え方で構造化することが多いように思います\n\n- 大きさよりも役割を重視した分類\n    - 特定のドメインに依存しない汎用的な部品と、特定のドメインに特化した部品で分ける\n    - propsを受け取って表示するだけの部品と、APIアクセスなどの処理を持つ部品で分ける\n- 粒度ではなくドメインや機能を単位としたディレクトリ構成\n    - ドメイン単位で階層を分け、その中で細分化していく\n    - 上位の階層で分かれるので、atomsやorganismsに大量のコンポーネントが並ぶ問題も解消する\n\n最初に挙げた5分類の構成と比べると、ディレクトリの分け方が変わっているのがわかります\n\n```\nsrc/\n├── features/\n│   ├── auth/\n│   │   ├── LoginForm.tsx\n│   │   └── SignupForm.tsx\n│   └── product/\n│       ├── ProductCard.tsx\n│       └── ProductList.tsx\n└── shared/\n    ├── Button.tsx\n    └── Input.tsx\n```\n\n- こうした機能単位で分ける考え方を体系化したものに、FSD（Feature-Sliced Design）があります\n    - より上位の階層で機能ごとに分けて、汎用的な部品は共通の置き場にまとめる方法論\n    - Atomic Designと対立するものではなく、機能ごとに分けた中でAtomic Designの考え方を部分的に組み合わせることもできます\n\nそもそもAtomic Design自体、厳密なルールとして作られたものではありませんでした\n原典には、5階層は決まったルールではなくUIを捉えるためのメンタルモデルだと書かれています\n5分類をそのまま守る使い方は減りましたが、その考え方は今の設計にも生きています\n\n## まとめ\n\nAtomic Designをそのまま使う場面は減りましたが、考え方のエッセンスは今の設計にも引き継がれています\nAtomic Designに限らず、ある時代の課題を解決する考え方が生まれ、やがてその考え方の課題が見えてきて、それを解決する次の考え方が生まれていきます\n今となっては古い手法かもしれませんが、この歴史があることで今のプラクティスが生まれているということなんだと思います\n","coediting":false,"comments_count":0,"created_at":"2026-06-03T03:04:00+09:00","group":null,"id":"cbef2617c6f9bf335b4f","likes_count":1,"private":false,"reactions_count":0,"stocks_count":0,"tags":[{"name":"AtomicDesign","versions":[]}],"title":"2026年にAtomic Designについて考える","updated_at":"2026-06-03T09:28:24+09:00","url":"https://qiita.com/ozaki25/items/cbef2617c6f9bf335b4f","user":{"description":"","facebook_id":"","followees_count":0,"followers_count":79,"github_login_name":"ozaki25","id":"ozaki25","items_count":112,"linkedin_id":"","location":"","name":"","organization":"","permanent_id":175213,"profile_image_url":"https://qiita-image-store.s3.amazonaws.com/0/175213/profile-images/1491825881","team_only":false,"twitter_screen_name":"ozaki25rn","website_url":""},"page_views_count":null,"team_membership":null,"organization_url_name":null,"slide":false,"posting_campaign_uuid":null},{"rendered_body":"\u003cp data-sourcepos=\"1:1-1:121\"\u003eこの投稿は\u003ca href=\"https://qiita.com/advent-calendar/2020/jamstack\"\u003eJamstack Advent Calendar 2020\u003c/a\u003eの20日目の記事です\u003c/p\u003e\n\u003ch2 data-sourcepos=\"3:1-3:9\"\u003e\n\u003cspan id=\"概要\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E6%A6%82%E8%A6%81\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e概要\u003c/h2\u003e\n\u003cul data-sourcepos=\"5:1-7:0\"\u003e\n\u003cli data-sourcepos=\"5:1-7:0\"\u003e\n\u003ca href=\"https://jamstack.org/generators/vuepress/\" rel=\"nofollow noopener\" target=\"_blank\"\u003ejamstack.orgでも紹介\u003c/a\u003eされている\u003ca href=\"https://vuepress.vuejs.org/\" rel=\"nofollow noopener\" target=\"_blank\"\u003eVuePress\u003c/a\u003eを使ってよくハンズオン資料を作成します\n\u003cul data-sourcepos=\"6:5-7:0\"\u003e\n\u003cli data-sourcepos=\"6:5-7:0\"\u003e↓こんな感じ\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp data-sourcepos=\"8:1-8:162\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Ff1fb1327-0807-9287-95ee-2ecc68ecac32.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=1cc1794dff98d2f953d04648cf921e40\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Ff1fb1327-0807-9287-95ee-2ecc68ecac32.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=1cc1794dff98d2f953d04648cf921e40\" alt=\"スクリーンショット 2020-12-20 1.42.44.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Ff1fb1327-0807-9287-95ee-2ecc68ecac32.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=2fe78f1b544f11592c405764d044df2d 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/f1fb1327-0807-9287-95ee-2ecc68ecac32.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cul data-sourcepos=\"10:1-12:0\"\u003e\n\u003cli data-sourcepos=\"10:1-10:158\"\u003eVuePressはいわゆるStaticSiteGeneratorでmarkdownで書いたコンテンツをHTML化することができるお手軽で便利なライブラリです\u003c/li\u003e\n\u003cli data-sourcepos=\"11:1-12:0\"\u003e特に不満があるわけではないのですが\u003ca href=\"https://vitepress.vuejs.org/\" rel=\"nofollow noopener\" target=\"_blank\"\u003eVitePress\u003c/a\u003eという後続プロジェクトが進んでいるのでそちらを試してみました\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"13:1-13:12\"\u003e\n\u003cspan id=\"vitepress\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#vitepress\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eVitePress\u003c/h2\u003e\n\u003cul data-sourcepos=\"15:1-20:0\"\u003e\n\u003cli data-sourcepos=\"15:1-15:64\"\u003eVuePressはVueのv2ベースでWebpackが使われています\u003c/li\u003e\n\u003cli data-sourcepos=\"16:1-16:117\"\u003eVitePressはVueのv3ベースで\u003ca href=\"https://github.com/vitejs/vite\" rel=\"nofollow noopener\" target=\"_blank\"\u003eVite\u003c/a\u003eが使われいるのが最大の特徴です\u003c/li\u003e\n\u003cli data-sourcepos=\"17:1-17:84\"\u003eViteを使っているのでビルドが爆速というのが売りのようです\u003c/li\u003e\n\u003cli data-sourcepos=\"18:1-20:0\"\u003e※VitePressは\u003ca href=\"https://vitepress.vuejs.org/#what-is-vitepress\" rel=\"nofollow noopener\" target=\"_blank\"\u003e公式サイトにも記載\u003c/a\u003eがあるように現在開発中のプロジェクトです\n\u003cul data-sourcepos=\"19:5-20:0\"\u003e\n\u003cli data-sourcepos=\"19:5-20:0\"\u003e今回使用したVitePressのバージョンはv0.9.2です\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cblockquote data-sourcepos=\"21:1-21:158\"\u003e\n\u003cp data-sourcepos=\"21:3-21:158\"\u003eVitePress is early WIP! Currently the focus is on making Vite stable and feature complete first. It is not recommended to use this for anything serious yet.\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 data-sourcepos=\"24:1-24:42\"\u003e\n\u003cspan id=\"vitepress化のためにやったこと\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#vitepress%E5%8C%96%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AB%E3%82%84%E3%81%A3%E3%81%9F%E3%81%93%E3%81%A8\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eVitePress化のためにやったこと\u003c/h2\u003e\n\u003cul data-sourcepos=\"26:1-27:0\"\u003e\n\u003cli data-sourcepos=\"26:1-27:0\"\u003e既存のVuePressプロジェクトをVitePressに置き換えてみました\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 data-sourcepos=\"28:1-28:40\"\u003e\n\u003cspan id=\"ライブラリ追加と命名変更\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA%E8%BF%BD%E5%8A%A0%E3%81%A8%E5%91%BD%E5%90%8D%E5%A4%89%E6%9B%B4\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eライブラリ追加と命名変更\u003c/h3\u003e\n\u003cul data-sourcepos=\"30:1-31:0\"\u003e\n\u003cli data-sourcepos=\"30:1-31:0\"\u003eVitePressの追加\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"sh\" data-sourcepos=\"32:1-34:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003eyarn add \u003cspan class=\"nt\"\u003e-D\u003c/span\u003e vitepress\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cul data-sourcepos=\"36:1-37:0\"\u003e\n\u003cli data-sourcepos=\"36:1-37:0\"\u003e\n\u003ccode\u003enpm scripts\u003c/code\u003eのコマンドを\u003ccode\u003evuepress\u003c/code\u003eから\u003ccode\u003evitepress\u003c/code\u003eに変更\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"diff\" data-sourcepos=\"38:1-43:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"gd\"\u003e-    \"start\": \"vuepress dev docs\",\n-    \"build\": \"vuepress build docs\"\n\u003c/span\u003e\u003cspan class=\"gi\"\u003e+    \"start\": \"vitepress dev docs\",\n+    \"build\": \"vitepress build docs\"\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cul data-sourcepos=\"45:1-47:0\"\u003e\n\u003cli data-sourcepos=\"45:1-47:0\"\u003eフォルダ名変更\n\u003cul data-sourcepos=\"46:5-47:0\"\u003e\n\u003cli data-sourcepos=\"46:5-47:0\"\u003e\n\u003ccode\u003edocs/.vuepress\u003c/code\u003e → \u003ccode\u003edocs/.vitepress\u003c/code\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 data-sourcepos=\"48:1-48:40\"\u003e\n\u003cspan id=\"publicフォルダの位置を修正\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#public%E3%83%95%E3%82%A9%E3%83%AB%E3%83%80%E3%81%AE%E4%BD%8D%E7%BD%AE%E3%82%92%E4%BF%AE%E6%AD%A3\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003epublicフォルダの位置を修正\u003c/h3\u003e\n\u003cul data-sourcepos=\"50:1-53:0\"\u003e\n\u003cli data-sourcepos=\"50:1-53:0\"\u003eここまでで起動できましたが画像が表示されません\n\u003cul data-sourcepos=\"51:5-53:0\"\u003e\n\u003cli data-sourcepos=\"51:5-51:99\"\u003e試行錯誤したけっかpublicフォルダの配置場所が変更されたようでした\u003c/li\u003e\n\u003cli data-sourcepos=\"52:5-53:0\"\u003e\n\u003ccode\u003edocs/.vitepress/public\u003c/code\u003e → \u003ccode\u003edocs/public\u003c/code\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 data-sourcepos=\"54:1-54:26\"\u003e\n\u003cspan id=\"sidebarの設定修正\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#sidebar%E3%81%AE%E8%A8%AD%E5%AE%9A%E4%BF%AE%E6%AD%A3\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003esidebarの設定修正\u003c/h3\u003e\n\u003cul data-sourcepos=\"56:1-59:0\"\u003e\n\u003cli data-sourcepos=\"56:1-59:0\"\u003e起動はできていますがサイドメニューが表示されません\n\u003cul data-sourcepos=\"57:5-59:0\"\u003e\n\u003cli data-sourcepos=\"57:5-57:106\"\u003eネストしないタイプのsidebarを使っていましたが書き方が変わったようです\u003c/li\u003e\n\u003cli data-sourcepos=\"58:5-59:0\"\u003e\n\u003ca href=\"https://github.com/vuejs/vitepress/blob/master/src/client/theme-default/config.ts#L95\" rel=\"nofollow noopener\" target=\"_blank\"\u003e型定義\u003c/a\u003eを見ればでどう設定するばいいか分かっていいですね\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"diff\" data-sourcepos=\"60:1-78:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003edocs/.vitepress/config.js\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e     sidebar: [\n\u003cspan class=\"gd\"\u003e-      '/1_setup',\n-      '/2_initialfile',\n-      '/3_hello',\n-      '/4_counter',\n-      '/5_todolist',\n-      '/6_routing',\n-      '/7_communication',\n\u003c/span\u003e\u003cspan class=\"gi\"\u003e+      { text: '1.開発環境構築', link: '/1_setup' },\n+      { text: '2.雛形の整理', link: '/2_initialfile' },\n+      { text: '3.HelloWorld編', link: '/3_hello' },\n+      { text: '4.Counter編', link: '/4_counter' },\n+      { text: '5.TodoList編', link: '/5_todolist' },\n+      { text: '6.ページ遷移編', link: '/6_routing' },\n+      { text: '7.通信処理編', link: '/7_communication' },\n\u003c/span\u003e     ],\n   },\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cul data-sourcepos=\"80:1-81:0\"\u003e\n\u003cli data-sourcepos=\"80:1-81:0\"\u003eここまでで基本機能は今まで通り動くようになりました！！\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 data-sourcepos=\"82:1-82:31\"\u003e\n\u003cspan id=\"テーマカラーの設定\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%83%86%E3%83%BC%E3%83%9E%E3%82%AB%E3%83%A9%E3%83%BC%E3%81%AE%E8%A8%AD%E5%AE%9A\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eテーマカラーの設定\u003c/h3\u003e\n\u003cul data-sourcepos=\"84:1-88:0\"\u003e\n\u003cli data-sourcepos=\"84:1-85:53\"\u003eVuePressはStylusが使われていて\u003ccode\u003edocs/.vitepress/styles/palette.styl\u003c/code\u003eを定義することでテーマカラーを設定できます\n\u003cul data-sourcepos=\"85:5-85:53\"\u003e\n\u003cli data-sourcepos=\"85:5-85:53\"\u003e\u003ca href=\"https://vuepress.vuejs.org/config/#palette-styl\" class=\"autolink\" rel=\"nofollow noopener\" target=\"_blank\"\u003ehttps://vuepress.vuejs.org/config/#palette-styl\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"86:1-88:0\"\u003eVitePressはStylusを使っておらずCSSのみで構成されているようでテーマの設定のしかたが変わったようです\n\u003cul data-sourcepos=\"87:5-88:0\"\u003e\n\u003cli data-sourcepos=\"87:5-88:0\"\u003e設定方法が分からなかったので今回は見送りで\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 data-sourcepos=\"89:1-89:25\"\u003e\n\u003cspan id=\"プラグイン周り\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3%E5%91%A8%E3%82%8A\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eプラグイン周り\u003c/h3\u003e\n\u003cul data-sourcepos=\"91:1-92:0\"\u003e\n\u003cli data-sourcepos=\"91:1-92:0\"\u003eVuePress版ではプラグインをいろいろ入れてましたがVitePress版はまだないようなので今回は諦めます\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"93:1-93:33\"\u003e\n\u003cspan id=\"ビルド時間のチェック\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%83%93%E3%83%AB%E3%83%89%E6%99%82%E9%96%93%E3%81%AE%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eビルド時間のチェック\u003c/h2\u003e\n\u003cul data-sourcepos=\"95:1-97:0\"\u003e\n\u003cli data-sourcepos=\"95:1-95:95\"\u003e動くようになったのでビルドの高速化がどれほどか確認してみました\u003c/li\u003e\n\u003cli data-sourcepos=\"96:1-97:0\"\u003e上記の通りVitePress版はプラグインが使えなかったのでVuePressもプラグインは外した状態で比較します\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 data-sourcepos=\"98:1-98:25\"\u003e\n\u003cspan id=\"devモードで起動\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#dev%E3%83%A2%E3%83%BC%E3%83%89%E3%81%A7%E8%B5%B7%E5%8B%95\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eDevモードで起動\u003c/h3\u003e\n\u003cul data-sourcepos=\"100:1-101:0\"\u003e\n\u003cli data-sourcepos=\"100:1-101:0\"\u003eVuePressは10秒弱です\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp data-sourcepos=\"102:1-102:128\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F5e8356a7-ea4e-c2e7-24c2-db592194b3ab.gif?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=d654c6e2046c25229d497e4ddd9b3503\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F5e8356a7-ea4e-c2e7-24c2-db592194b3ab.gif?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=d654c6e2046c25229d497e4ddd9b3503\" alt=\"vuepress_dev.gif\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F5e8356a7-ea4e-c2e7-24c2-db592194b3ab.gif?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=af02426d891175ed06e2905171772af6 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/5e8356a7-ea4e-c2e7-24c2-db592194b3ab.gif\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cul data-sourcepos=\"104:1-106:0\"\u003e\n\u003cli data-sourcepos=\"104:1-106:0\"\u003eVitePressは1秒程度でした！!\n\u003cul data-sourcepos=\"105:5-106:0\"\u003e\n\u003cli data-sourcepos=\"105:5-106:0\"\u003e最初速すぎて何が起きたか分かりませんでした・・\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp data-sourcepos=\"107:1-107:129\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F4edba81d-d108-29ab-1c21-9413c0300262.gif?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=afd9b715ff0095b0e6b1807f5e606c36\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F4edba81d-d108-29ab-1c21-9413c0300262.gif?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=afd9b715ff0095b0e6b1807f5e606c36\" alt=\"vitepress_dev.gif\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F4edba81d-d108-29ab-1c21-9413c0300262.gif?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=eda241f3c5931ab2bfc4fce895f5cb0a 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/4edba81d-d108-29ab-1c21-9413c0300262.gif\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003ch3 data-sourcepos=\"109:1-109:25\"\u003e\n\u003cspan id=\"ホットリロード\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%83%9B%E3%83%83%E3%83%88%E3%83%AA%E3%83%AD%E3%83%BC%E3%83%89\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eホットリロード\u003c/h3\u003e\n\u003cul data-sourcepos=\"111:1-112:0\"\u003e\n\u003cli data-sourcepos=\"111:1-112:0\"\u003eVuePressは慣れてれば違和感ないですが一瞬再ビルドしてる感じですね\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp data-sourcepos=\"113:1-113:131\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F96b6975c-c789-5af7-bd91-0b43508967b0.gif?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=fb69518d21fcd79988fb3d9e44e86c97\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F96b6975c-c789-5af7-bd91-0b43508967b0.gif?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=fb69518d21fcd79988fb3d9e44e86c97\" alt=\"vuepress_reload.gif\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F96b6975c-c789-5af7-bd91-0b43508967b0.gif?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=c5154671039d20043911480753f1d4d7 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/96b6975c-c789-5af7-bd91-0b43508967b0.gif\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cul data-sourcepos=\"115:1-116:0\"\u003e\n\u003cli data-sourcepos=\"115:1-116:0\"\u003eVitePressは保存した瞬間に反映されてます！すごい！\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp data-sourcepos=\"117:1-117:132\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Faea1c6ac-bea5-31a8-1cde-4a581a08d656.gif?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=b507e8c241acd2ee9f21f515e5652b60\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Faea1c6ac-bea5-31a8-1cde-4a581a08d656.gif?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=b507e8c241acd2ee9f21f515e5652b60\" alt=\"vitepress_reload.gif\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Faea1c6ac-bea5-31a8-1cde-4a581a08d656.gif?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=138e75473c255341fd0d885fad5d1033 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/aea1c6ac-bea5-31a8-1cde-4a581a08d656.gif\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003ch3 data-sourcepos=\"119:1-119:23\"\u003e\n\u003cspan id=\"productionビルド\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#production%E3%83%93%E3%83%AB%E3%83%89\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eProductionビルド\u003c/h3\u003e\n\u003cul data-sourcepos=\"121:1-122:0\"\u003e\n\u003cli data-sourcepos=\"121:1-122:0\"\u003eVuePressは16秒でした\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp data-sourcepos=\"123:1-123:162\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F550997d3-7f80-bf10-f3d2-4f3726e556b1.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=ba2399923668f800bff52c27a38a902e\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F550997d3-7f80-bf10-f3d2-4f3726e556b1.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=ba2399923668f800bff52c27a38a902e\" alt=\"スクリーンショット 2020-12-20 2.36.20.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F550997d3-7f80-bf10-f3d2-4f3726e556b1.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=8c7a41965a7b0ce12c11f31b75467709 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/550997d3-7f80-bf10-f3d2-4f3726e556b1.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cul data-sourcepos=\"125:1-126:0\"\u003e\n\u003cli data-sourcepos=\"125:1-126:0\"\u003eVitePressは10秒でした！\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp data-sourcepos=\"127:1-127:162\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fe20233ae-4591-6d03-1943-ad29d1e893f2.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=1f0718a89b93fcb33aae35fe79beb3ee\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fe20233ae-4591-6d03-1943-ad29d1e893f2.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=1f0718a89b93fcb33aae35fe79beb3ee\" alt=\"スクリーンショット 2020-12-20 2.36.43.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fe20233ae-4591-6d03-1943-ad29d1e893f2.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=9ebf341c814f03ad142fea73ce5b84eb 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/e20233ae-4591-6d03-1943-ad29d1e893f2.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003ch2 data-sourcepos=\"129:1-129:9\"\u003e\n\u003cspan id=\"余談\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E4%BD%99%E8%AB%87\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e余談\u003c/h2\u003e\n\u003cul data-sourcepos=\"131:1-133:0\"\u003e\n\u003cli data-sourcepos=\"131:1-133:0\"\u003e最後にビルドしたものをデプロイして動かしてみたところ何故か以下のように二重に表示されてしまいました、、、\n\u003cul data-sourcepos=\"132:5-133:0\"\u003e\n\u003cli data-sourcepos=\"132:5-133:0\"\u003ehtmlにSSGされたものとJSでCSRされたものがそれぞれ表示されてしまっているようで、、、まだ開発版なので今後修正されるでしょう！\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp data-sourcepos=\"134:1-134:162\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F6bb399b3-a344-fc98-1ce5-1735b1ef0896.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=44274fef937d9c6efe1cbd18bfb09e26\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F6bb399b3-a344-fc98-1ce5-1735b1ef0896.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=44274fef937d9c6efe1cbd18bfb09e26\" alt=\"スクリーンショット 2020-12-20 2.43.11.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F6bb399b3-a344-fc98-1ce5-1735b1ef0896.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=59108e8c75b0a33e8067662bc8bfbd7f 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/6bb399b3-a344-fc98-1ce5-1735b1ef0896.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003ch2 data-sourcepos=\"137:1-137:12\"\u003e\n\u003cspan id=\"まとめ\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%81%BE%E3%81%A8%E3%82%81\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eまとめ\u003c/h2\u003e\n\u003cul data-sourcepos=\"139:1-142:127\"\u003e\n\u003cli data-sourcepos=\"139:1-140:166\"\u003eVuePressで作った資産をVitePressに移行することはそこまで苦労せずにできそうです\n\u003cul data-sourcepos=\"140:5-140:166\"\u003e\n\u003cli data-sourcepos=\"140:5-140:166\"\u003eただしプラグイン周りであったりまだ未対応？/ドキュメント未記載？の部分もあるので今後のアップデートに期待です\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"141:1-142:127\"\u003eViteを使ったことによる高速化はとても実感できました！\n\u003cul data-sourcepos=\"142:5-142:127\"\u003e\n\u003cli data-sourcepos=\"142:5-142:127\"\u003eきっとViteのスピードに慣れてしまうと今の感覚には戻れないんだろうなーと思いました！\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n","body":"この投稿は[Jamstack Advent Calendar 2020](https://qiita.com/advent-calendar/2020/jamstack)の20日目の記事です\n\n## 概要\n\n- [jamstack.orgでも紹介](https://jamstack.org/generators/vuepress/)されている[VuePress](https://vuepress.vuejs.org/)を使ってよくハンズオン資料を作成します\n    - ↓こんな感じ\n\n![スクリーンショット 2020-12-20 1.42.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/f1fb1327-0807-9287-95ee-2ecc68ecac32.png)\n\n- VuePressはいわゆるStaticSiteGeneratorでmarkdownで書いたコンテンツをHTML化することができるお手軽で便利なライブラリです\n- 特に不満があるわけではないのですが[VitePress](https://vitepress.vuejs.org/)という後続プロジェクトが進んでいるのでそちらを試してみました\n\n## VitePress\n\n- VuePressはVueのv2ベースでWebpackが使われています\n- VitePressはVueのv3ベースで[Vite](https://github.com/vitejs/vite)が使われいるのが最大の特徴です\n- Viteを使っているのでビルドが爆速というのが売りのようです\n- ※VitePressは[公式サイトにも記載](https://vitepress.vuejs.org/#what-is-vitepress)があるように現在開発中のプロジェクトです\n    - 今回使用したVitePressのバージョンはv0.9.2です\n\n\u003e VitePress is early WIP! Currently the focus is on making Vite stable and feature complete first. It is not recommended to use this for anything serious yet.\n\n\n## VitePress化のためにやったこと\n\n- 既存のVuePressプロジェクトをVitePressに置き換えてみました\n\n### ライブラリ追加と命名変更\n\n- VitePressの追加\n\n```sh\nyarn add -D vitepress\n```\n\n- `npm scripts`のコマンドを`vuepress`から`vitepress`に変更\n\n```diff\n-    \"start\": \"vuepress dev docs\",\n-    \"build\": \"vuepress build docs\"\n+    \"start\": \"vitepress dev docs\",\n+    \"build\": \"vitepress build docs\"\n```\n\n- フォルダ名変更\n    - `docs/.vuepress` → `docs/.vitepress`\n\n### publicフォルダの位置を修正\n\n- ここまでで起動できましたが画像が表示されません\n    - 試行錯誤したけっかpublicフォルダの配置場所が変更されたようでした\n    - `docs/.vitepress/public` → `docs/public`\n\n### sidebarの設定修正\n\n- 起動はできていますがサイドメニューが表示されません\n    - ネストしないタイプのsidebarを使っていましたが書き方が変わったようです\n    - [型定義](https://github.com/vuejs/vitepress/blob/master/src/client/theme-default/config.ts#L95)を見ればでどう設定するばいいか分かっていいですね\n\n```diff:docs/.vitepress/config.js\n     sidebar: [\n-      '/1_setup',\n-      '/2_initialfile',\n-      '/3_hello',\n-      '/4_counter',\n-      '/5_todolist',\n-      '/6_routing',\n-      '/7_communication',\n+      { text: '1.開発環境構築', link: '/1_setup' },\n+      { text: '2.雛形の整理', link: '/2_initialfile' },\n+      { text: '3.HelloWorld編', link: '/3_hello' },\n+      { text: '4.Counter編', link: '/4_counter' },\n+      { text: '5.TodoList編', link: '/5_todolist' },\n+      { text: '6.ページ遷移編', link: '/6_routing' },\n+      { text: '7.通信処理編', link: '/7_communication' },\n     ],\n   },\n```\n\n- ここまでで基本機能は今まで通り動くようになりました！！\n\n### テーマカラーの設定\n\n- VuePressはStylusが使われていて`docs/.vitepress/styles/palette.styl`を定義することでテーマカラーを設定できます\n    - https://vuepress.vuejs.org/config/#palette-styl\n- VitePressはStylusを使っておらずCSSのみで構成されているようでテーマの設定のしかたが変わったようです\n    - 設定方法が分からなかったので今回は見送りで\n\n### プラグイン周り\n\n- VuePress版ではプラグインをいろいろ入れてましたがVitePress版はまだないようなので今回は諦めます\n\n## ビルド時間のチェック\n\n- 動くようになったのでビルドの高速化がどれほどか確認してみました\n- 上記の通りVitePress版はプラグインが使えなかったのでVuePressもプラグインは外した状態で比較します\n\n### Devモードで起動\n\n- VuePressは10秒弱です\n\n![vuepress_dev.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/5e8356a7-ea4e-c2e7-24c2-db592194b3ab.gif)\n\n- VitePressは1秒程度でした！!\n    - 最初速すぎて何が起きたか分かりませんでした・・\n\n![vitepress_dev.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/4edba81d-d108-29ab-1c21-9413c0300262.gif)\n\n### ホットリロード\n\n- VuePressは慣れてれば違和感ないですが一瞬再ビルドしてる感じですね\n\n![vuepress_reload.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/96b6975c-c789-5af7-bd91-0b43508967b0.gif)\n\n- VitePressは保存した瞬間に反映されてます！すごい！\n\n![vitepress_reload.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/aea1c6ac-bea5-31a8-1cde-4a581a08d656.gif)\n\n### Productionビルド\n\n- VuePressは16秒でした\n\n![スクリーンショット 2020-12-20 2.36.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/550997d3-7f80-bf10-f3d2-4f3726e556b1.png)\n\n- VitePressは10秒でした！\n\n![スクリーンショット 2020-12-20 2.36.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/e20233ae-4591-6d03-1943-ad29d1e893f2.png)\n\n## 余談\n\n- 最後にビルドしたものをデプロイして動かしてみたところ何故か以下のように二重に表示されてしまいました、、、\n    - htmlにSSGされたものとJSでCSRされたものがそれぞれ表示されてしまっているようで、、、まだ開発版なので今後修正されるでしょう！\n\n![スクリーンショット 2020-12-20 2.43.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/6bb399b3-a344-fc98-1ce5-1735b1ef0896.png)\n\n\n## まとめ\n\n- VuePressで作った資産をVitePressに移行することはそこまで苦労せずにできそうです\n    - ただしプラグイン周りであったりまだ未対応？/ドキュメント未記載？の部分もあるので今後のアップデートに期待です\n- Viteを使ったことによる高速化はとても実感できました！\n    - きっとViteのスピードに慣れてしまうと今の感覚には戻れないんだろうなーと思いました！\n","coediting":false,"comments_count":0,"created_at":"2020-12-20T02:46:58+09:00","group":null,"id":"184e61d2aeaaa1396e26","likes_count":14,"private":false,"reactions_count":0,"stocks_count":5,"tags":[{"name":"vuepress","versions":[]},{"name":"VitePress","versions":[]}],"title":"VuePressをVitePressに置き換えてみる","updated_at":"2021-10-21T10:10:50+09:00","url":"https://qiita.com/ozaki25/items/184e61d2aeaaa1396e26","user":{"description":"","facebook_id":"","followees_count":0,"followers_count":79,"github_login_name":"ozaki25","id":"ozaki25","items_count":112,"linkedin_id":"","location":"","name":"","organization":"","permanent_id":175213,"profile_image_url":"https://qiita-image-store.s3.amazonaws.com/0/175213/profile-images/1491825881","team_only":false,"twitter_screen_name":"ozaki25rn","website_url":""},"page_views_count":null,"team_membership":null,"organization_url_name":null,"slide":false,"posting_campaign_uuid":null},{"rendered_body":"\u003ch2 data-sourcepos=\"1:1-1:9\"\u003e\n\u003cspan id=\"概要\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E6%A6%82%E8%A6%81\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e概要\u003c/h2\u003e\n\u003cul data-sourcepos=\"3:1-5:0\"\u003e\n\u003cli data-sourcepos=\"3:1-3:232\"\u003e先日\u003ca href=\"https://qiita.com/ozaki25/items/da08c332f487ef421760\" id=\"reference-c95618697dac6b78a5e9\"\u003eWebページのコントラスト比をチェックしてみよう\u003c/a\u003eという記事を書いてコントラスト比をチェックするChrome拡張を紹介しました\u003c/li\u003e\n\u003cli data-sourcepos=\"4:1-5:0\"\u003eですが、その後情報収集しているとChromeのDevToolsだけでもいろいろできることが分かったので紹介します\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"6:1-6:62\"\u003e\n\u003cspan id=\"1個別要素のコントラスト比をチェックする\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#1%E5%80%8B%E5%88%A5%E8%A6%81%E7%B4%A0%E3%81%AE%E3%82%B3%E3%83%B3%E3%83%88%E3%83%A9%E3%82%B9%E3%83%88%E6%AF%94%E3%82%92%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF%E3%81%99%E3%82%8B\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e1.個別要素のコントラスト比をチェックする\u003c/h2\u003e\n\u003cul data-sourcepos=\"8:1-10:0\"\u003e\n\u003cli data-sourcepos=\"8:1-8:90\"\u003eDevToolsのElementsタブを開いて矢印ボタンを押します（画像内の①）\u003c/li\u003e\n\u003cli data-sourcepos=\"9:1-10:0\"\u003eその状態でページにカーソルを持っていくとかざした要素のコントラスト比などの情報を見ることができます（画像内の②）\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp data-sourcepos=\"11:1-11:163\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fe380992e-67b9-1d9f-b3c4-dfe40165cdd1.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=ea26de3f80f46f40a7b7e3e352c4174d\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fe380992e-67b9-1d9f-b3c4-dfe40165cdd1.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=ea26de3f80f46f40a7b7e3e352c4174d\" alt=\"スクリーンショット 2020-12-10 13.30.48.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fe380992e-67b9-1d9f-b3c4-dfe40165cdd1.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=36047525079b81db6b8d4957fb8d9a3d 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/e380992e-67b9-1d9f-b3c4-dfe40165cdd1.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003ch2 data-sourcepos=\"13:1-13:56\"\u003e\n\u003cspan id=\"2個別要素のコントラスト比を修正する\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#2%E5%80%8B%E5%88%A5%E8%A6%81%E7%B4%A0%E3%81%AE%E3%82%B3%E3%83%B3%E3%83%88%E3%83%A9%E3%82%B9%E3%83%88%E6%AF%94%E3%82%92%E4%BF%AE%E6%AD%A3%E3%81%99%E3%82%8B\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e2.個別要素のコントラスト比を修正する\u003c/h2\u003e\n\u003cul data-sourcepos=\"15:1-17:0\"\u003e\n\u003cli data-sourcepos=\"15:1-15:63\"\u003e1では確認だけでしたがさらに修正もできます\u003c/li\u003e\n\u003cli data-sourcepos=\"16:1-17:0\"\u003eElementsタブに出てくるStylesタブで対象の要素のcolorを選択します\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp data-sourcepos=\"18:1-18:163\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F05a20096-2ddb-bf6f-c986-86331b72efbc.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=1f3d3392f46b3b01438eb323dc4098d2\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F05a20096-2ddb-bf6f-c986-86331b72efbc.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=1f3d3392f46b3b01438eb323dc4098d2\" alt=\"スクリーンショット 2020-12-17 17.45.24.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F05a20096-2ddb-bf6f-c986-86331b72efbc.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=a8c41f0936e3b3b7c06a6d5f54445f57 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/05a20096-2ddb-bf6f-c986-86331b72efbc.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cul data-sourcepos=\"20:1-21:0\"\u003e\n\u003cli data-sourcepos=\"20:1-21:0\"\u003eコントラスト比に問題がある場合は現在のコントラスト比とAA,AAAの基準を満たすための代替案が提案されます\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp data-sourcepos=\"22:1-22:163\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F389d947c-106f-a9cb-17ee-3d4536011043.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=cce3edca3de33e32207fa7c200a8c6a2\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F389d947c-106f-a9cb-17ee-3d4536011043.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=cce3edca3de33e32207fa7c200a8c6a2\" alt=\"スクリーンショット 2020-12-17 18.19.12.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F389d947c-106f-a9cb-17ee-3d4536011043.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=2bb3a99723fff4450935540fd69657c7 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/389d947c-106f-a9cb-17ee-3d4536011043.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cul data-sourcepos=\"24:1-25:0\"\u003e\n\u003cli data-sourcepos=\"24:1-25:0\"\u003e提案を受け入れると画面上にも反映されて見た目を確認することができます\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp data-sourcepos=\"26:1-26:163\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fd21a56f5-956f-2825-bfb1-79e9d332240a.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=68dba17d7f519fbba045ae5313ffc91b\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fd21a56f5-956f-2825-bfb1-79e9d332240a.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=68dba17d7f519fbba045ae5313ffc91b\" alt=\"スクリーンショット 2020-12-17 18.26.33.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fd21a56f5-956f-2825-bfb1-79e9d332240a.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=9a2344f9867fc4a3021c81ea8005009e 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/d21a56f5-956f-2825-bfb1-79e9d332240a.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003ch2 data-sourcepos=\"28:1-28:65\"\u003e\n\u003cspan id=\"3ページ全体のコントラスト比をチェックする\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#3%E3%83%9A%E3%83%BC%E3%82%B8%E5%85%A8%E4%BD%93%E3%81%AE%E3%82%B3%E3%83%B3%E3%83%88%E3%83%A9%E3%82%B9%E3%83%88%E6%AF%94%E3%82%92%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF%E3%81%99%E3%82%8B\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e3.ページ全体のコントラスト比をチェックする\u003c/h2\u003e\n\u003cul data-sourcepos=\"30:1-33:0\"\u003e\n\u003cli data-sourcepos=\"30:1-30:172\"\u003e1と2は事前にあたりをつけてチェックするものでしたが、ページ全体をチェックして問題のある箇所を表示する方法もあります\u003c/li\u003e\n\u003cli data-sourcepos=\"31:1-33:0\"\u003eDevToolsのCSS Overviewタブを開きます\n\u003cul data-sourcepos=\"32:5-33:0\"\u003e\n\u003cli data-sourcepos=\"32:5-33:0\"\u003eCapture overviewボタンを押すとページを解析し結果が表示されます\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp data-sourcepos=\"34:1-34:163\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fd86bf79e-fcd0-3217-da92-83a68f7cbde4.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=11dbe59e926204f95aed940539878697\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fd86bf79e-fcd0-3217-da92-83a68f7cbde4.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=11dbe59e926204f95aed940539878697\" alt=\"スクリーンショット 2020-12-17 17.48.40.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fd86bf79e-fcd0-3217-da92-83a68f7cbde4.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=d007b61c1dbd2adfdbd720bb87bc3855 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/d86bf79e-fcd0-3217-da92-83a68f7cbde4.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cul data-sourcepos=\"36:1-38:0\"\u003e\n\u003cli data-sourcepos=\"36:1-36:95\"\u003eページ内でどのような色が使われているかなどサマリが表示されます\u003c/li\u003e\n\u003cli data-sourcepos=\"37:1-38:0\"\u003eその中のContrast issuesにコントラスト比に問題のある箇所が表示されます\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp data-sourcepos=\"39:1-39:163\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F24cd6c25-e3b9-ebea-447d-100d68c74282.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=9c2fe198f06e99e29302d874210e0ab2\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F24cd6c25-e3b9-ebea-447d-100d68c74282.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=9c2fe198f06e99e29302d874210e0ab2\" alt=\"スクリーンショット 2020-12-17 17.48.59.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F24cd6c25-e3b9-ebea-447d-100d68c74282.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=ffae9ad61e9628b09305244f8d4f0d68 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/24cd6c25-e3b9-ebea-447d-100d68c74282.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cul data-sourcepos=\"41:1-42:0\"\u003e\n\u003cli data-sourcepos=\"41:1-42:0\"\u003eこれをクリックするとElementsタブの対象の要素に飛ぶことができるため2で紹介したの手順を行って色の調整をしていくことができます\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp data-sourcepos=\"43:1-43:163\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F6ae467d3-fc84-7326-e186-a216f210e408.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=5f3498ffe7e11aca7a561395edcb482c\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F6ae467d3-fc84-7326-e186-a216f210e408.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=5f3498ffe7e11aca7a561395edcb482c\" alt=\"スクリーンショット 2020-12-17 17.50.41.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F6ae467d3-fc84-7326-e186-a216f210e408.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=833129c8ce5e69bf61ca2b227f424938 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/6ae467d3-fc84-7326-e186-a216f210e408.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003ch2 data-sourcepos=\"46:1-46:12\"\u003e\n\u003cspan id=\"まとめ\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%81%BE%E3%81%A8%E3%82%81\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eまとめ\u003c/h2\u003e\n\u003cul data-sourcepos=\"48:1-49:101\"\u003e\n\u003cli data-sourcepos=\"48:1-48:49\"\u003eChromeのDevToolsは本当に高機能ですね\u003c/li\u003e\n\u003cli data-sourcepos=\"49:1-49:101\"\u003e便利なツールを使いこなして効率的によりよいWebを作っていきましょう！\u003c/li\u003e\n\u003c/ul\u003e\n","body":"## 概要\n\n- 先日[Webページのコントラスト比をチェックしてみよう](https://qiita.com/ozaki25/items/da08c332f487ef421760)という記事を書いてコントラスト比をチェックするChrome拡張を紹介しました\n- ですが、その後情報収集しているとChromeのDevToolsだけでもいろいろできることが分かったので紹介します\n\n## 1.個別要素のコントラスト比をチェックする\n\n- DevToolsのElementsタブを開いて矢印ボタンを押します（画像内の①）\n- その状態でページにカーソルを持っていくとかざした要素のコントラスト比などの情報を見ることができます（画像内の②）\n\n![スクリーンショット 2020-12-10 13.30.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/e380992e-67b9-1d9f-b3c4-dfe40165cdd1.png)\n\n## 2.個別要素のコントラスト比を修正する\n\n- 1では確認だけでしたがさらに修正もできます\n- Elementsタブに出てくるStylesタブで対象の要素のcolorを選択します\n\n![スクリーンショット 2020-12-17 17.45.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/05a20096-2ddb-bf6f-c986-86331b72efbc.png)\n\n- コントラスト比に問題がある場合は現在のコントラスト比とAA,AAAの基準を満たすための代替案が提案されます\n\n![スクリーンショット 2020-12-17 18.19.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/389d947c-106f-a9cb-17ee-3d4536011043.png)\n\n- 提案を受け入れると画面上にも反映されて見た目を確認することができます\n\n![スクリーンショット 2020-12-17 18.26.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/d21a56f5-956f-2825-bfb1-79e9d332240a.png)\n\n## 3.ページ全体のコントラスト比をチェックする\n\n- 1と2は事前にあたりをつけてチェックするものでしたが、ページ全体をチェックして問題のある箇所を表示する方法もあります\n- DevToolsのCSS Overviewタブを開きます\n    - Capture overviewボタンを押すとページを解析し結果が表示されます\n\n![スクリーンショット 2020-12-17 17.48.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/d86bf79e-fcd0-3217-da92-83a68f7cbde4.png)\n\n- ページ内でどのような色が使われているかなどサマリが表示されます\n- その中のContrast issuesにコントラスト比に問題のある箇所が表示されます\n\n![スクリーンショット 2020-12-17 17.48.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/24cd6c25-e3b9-ebea-447d-100d68c74282.png)\n\n- これをクリックするとElementsタブの対象の要素に飛ぶことができるため2で紹介したの手順を行って色の調整をしていくことができます\n\n![スクリーンショット 2020-12-17 17.50.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/6ae467d3-fc84-7326-e186-a216f210e408.png)\n\n\n## まとめ\n\n- ChromeのDevToolsは本当に高機能ですね\n- 便利なツールを使いこなして効率的によりよいWebを作っていきましょう！\n","coediting":false,"comments_count":0,"created_at":"2020-12-17T18:48:52+09:00","group":null,"id":"434101dd561c4242e30d","likes_count":11,"private":false,"reactions_count":0,"stocks_count":5,"tags":[{"name":"ChromeDevTool","versions":[]},{"name":"アクセシビリティ","versions":[]},{"name":"a11y","versions":[]}],"title":"ChromeDevToolsでコントラスト比をチェックする","updated_at":"2021-10-26T18:51:21+09:00","url":"https://qiita.com/ozaki25/items/434101dd561c4242e30d","user":{"description":"","facebook_id":"","followees_count":0,"followers_count":79,"github_login_name":"ozaki25","id":"ozaki25","items_count":112,"linkedin_id":"","location":"","name":"","organization":"","permanent_id":175213,"profile_image_url":"https://qiita-image-store.s3.amazonaws.com/0/175213/profile-images/1491825881","team_only":false,"twitter_screen_name":"ozaki25rn","website_url":""},"page_views_count":null,"team_membership":null,"organization_url_name":null,"slide":false,"posting_campaign_uuid":null},{"rendered_body":"\u003cp data-sourcepos=\"1:1-1:120\"\u003eこの記事は\u003ca href=\"https://qiita.com/advent-calendar/2020/react-2\"\u003eReact #2 Advent Calendar 2020\u003c/a\u003eの12日目の記事です\u003c/p\u003e\n\u003ch2 data-sourcepos=\"3:1-3:9\"\u003e\n\u003cspan id=\"概要\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E6%A6%82%E8%A6%81\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e概要\u003c/h2\u003e\n\u003cul data-sourcepos=\"5:1-9:0\"\u003e\n\u003cli data-sourcepos=\"5:1-5:285\"\u003e最近は\u003ca href=\"https://nextjs.org/\" rel=\"nofollow noopener\" target=\"_blank\"\u003eNext.js\u003c/a\u003eばかり使ってますが久しぶりに\u003ca href=\"https://create-react-app.dev/\" rel=\"nofollow noopener\" target=\"_blank\"\u003eCreateReactApp\u003c/a\u003eを使ってみたら\u003ccode\u003ereportWebVitals.ts\u003c/code\u003eという見慣れないファイルが生成されていたのを見たのが今日の記事のきっかけです\u003c/li\u003e\n\u003cli data-sourcepos=\"6:1-7:70\"\u003e\n\u003ca href=\"https://ja.reactjs.org/\" rel=\"nofollow noopener\" target=\"_blank\"\u003eReact\u003c/a\u003eアプリの雛形生成ツールであるCreateReactAppはv4で\u003ca href=\"https://web.dev/vitals/\" rel=\"nofollow noopener\" target=\"_blank\"\u003eWebVitals\u003c/a\u003eの計測ライブラリがデフォルトで組み込まれるようになりました\n\u003cul data-sourcepos=\"7:5-7:70\"\u003e\n\u003cli data-sourcepos=\"7:5-7:70\"\u003e\u003ca href=\"https://github.com/facebook/create-react-app/releases/tag/v4.0.0\" class=\"autolink\" rel=\"nofollow noopener\" target=\"_blank\"\u003ehttps://github.com/facebook/create-react-app/releases/tag/v4.0.0\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"8:1-9:0\"\u003eせっかくなのでドキュメントに沿って少し試してみたいと思います\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"10:1-10:24\"\u003e\n\u003cspan id=\"ファイルの確認\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E7%A2%BA%E8%AA%8D\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eファイルの確認\u003c/h2\u003e\n\u003cul data-sourcepos=\"12:1-14:0\"\u003e\n\u003cli data-sourcepos=\"12:1-12:59\"\u003eまずは生成されたコードを見てみましょう\u003c/li\u003e\n\u003cli data-sourcepos=\"13:1-14:0\"\u003e\n\u003ccode\u003ecreate-react-app\u003c/code\u003eコマンドで雛形を生成します\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"sh\" data-sourcepos=\"15:1-17:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003enpx create-react-app \u003cspan class=\"nt\"\u003e--template\u003c/span\u003e typescript web-vitals-sample\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cul data-sourcepos=\"19:1-20:0\"\u003e\n\u003cli data-sourcepos=\"19:1-20:0\"\u003e生成されたファイルの一覧はこんな感じです\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"text\" data-sourcepos=\"21:1-44:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e.\n├── README.md\n├── package.json\n├── public\n│   ├── favicon.ico\n│   ├── index.html\n│   ├── logo192.png\n│   ├── logo512.png\n│   ├── manifest.json\n│   └── robots.txt\n├── src\n│   ├── App.css\n│   ├── App.test.tsx\n│   ├── App.tsx\n│   ├── index.css\n│   ├── index.tsx\n│   ├── logo.svg\n│   ├── react-app-env.d.ts\n│   ├── reportWebVitals.ts\n│   └── setupTests.ts\n├── tsconfig.json\n└── yarn.lock\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cul data-sourcepos=\"46:1-48:0\"\u003e\n\u003cli data-sourcepos=\"46:1-46:64\"\u003e\n\u003ccode\u003ereportWebVitals.ts\u003c/code\u003eが今回注目するファイルですね\u003c/li\u003e\n\u003cli data-sourcepos=\"47:1-48:0\"\u003e中身はこんな感じです\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"ts\" data-sourcepos=\"49:1-65:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003esrc/reportWebVitals.ts\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"k\"\u003eimport\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"nx\"\u003eReportHandler\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"k\"\u003efrom\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eweb-vitals\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\n\u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003ereportWebVitals\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eonPerfEntry\u003c/span\u003e\u003cspan class=\"p\"\u003e?:\u003c/span\u003e \u003cspan class=\"nx\"\u003eReportHandler\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n  \u003cspan class=\"k\"\u003eif \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eonPerfEntry\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026amp;\u0026amp;\u003c/span\u003e \u003cspan class=\"nx\"\u003eonPerfEntry\u003c/span\u003e \u003cspan class=\"k\"\u003einstanceof\u003c/span\u003e \u003cspan class=\"nb\"\u003eFunction\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"k\"\u003eimport\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eweb-vitals\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e).\u003c/span\u003e\u003cspan class=\"nf\"\u003ethen\u003c/span\u003e\u003cspan class=\"p\"\u003e(({\u003c/span\u003e \u003cspan class=\"nx\"\u003egetCLS\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003egetFID\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003egetFCP\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003egetLCP\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003egetTTFB\u003c/span\u003e \u003cspan class=\"p\"\u003e})\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n      \u003cspan class=\"nf\"\u003egetCLS\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eonPerfEntry\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n      \u003cspan class=\"nf\"\u003egetFID\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eonPerfEntry\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n      \u003cspan class=\"nf\"\u003egetFCP\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eonPerfEntry\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n      \u003cspan class=\"nf\"\u003egetLCP\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eonPerfEntry\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n      \u003cspan class=\"nf\"\u003egetTTFB\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eonPerfEntry\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e});\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\n\u003cspan class=\"k\"\u003eexport\u003c/span\u003e \u003cspan class=\"k\"\u003edefault\u003c/span\u003e \u003cspan class=\"nx\"\u003ereportWebVitals\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cul data-sourcepos=\"67:1-71:0\"\u003e\n\u003cli data-sourcepos=\"67:1-67:92\"\u003e\n\u003ccode\u003eweb-vitals\u003c/code\u003eというライブラリを使っていろいろ設定してくれています\u003c/li\u003e\n\u003cli data-sourcepos=\"68:1-69:49\"\u003e\n\u003ccode\u003eweb-vitals\u003c/code\u003eはGoogleChromeが提供するWebVitals計測ライブラリですね\n\u003cul data-sourcepos=\"69:5-69:49\"\u003e\n\u003cli data-sourcepos=\"69:5-69:49\"\u003e\u003ca href=\"https://github.com/GoogleChrome/web-vitals/\" class=\"autolink\" rel=\"nofollow noopener\" target=\"_blank\"\u003ehttps://github.com/GoogleChrome/web-vitals/\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"70:1-71:0\"\u003eこのライブラリの追加とセットアップを事前にやってくれているということのようです\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"72:1-72:12\"\u003e\n\u003cspan id=\"使い方\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E4%BD%BF%E3%81%84%E6%96%B9\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e使い方\u003c/h2\u003e\n\u003cul data-sourcepos=\"74:1-79:0\"\u003e\n\u003cli data-sourcepos=\"74:1-75:62\"\u003e使い方はCreateReactAppのドキュメントに載っています\n\u003cul data-sourcepos=\"75:5-75:62\"\u003e\n\u003cli data-sourcepos=\"75:5-75:62\"\u003e\u003ca href=\"https://create-react-app.dev/docs/measuring-performance/\" class=\"autolink\" rel=\"nofollow noopener\" target=\"_blank\"\u003ehttps://create-react-app.dev/docs/measuring-performance/\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"76:1-77:87\"\u003e\n\u003ccode\u003ereportWebVitals.ts\u003c/code\u003eでexportされた\u003ccode\u003ereportWebVitals\u003c/code\u003eを呼び出して引数に関数を渡して使います\n\u003cul data-sourcepos=\"77:5-77:87\"\u003e\n\u003cli data-sourcepos=\"77:5-77:87\"\u003e渡した関数が内部で呼び出され各種メトリクスが渡されます\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"78:1-79:0\"\u003eサンプルにあるように\u003ccode\u003econsole.log\u003c/code\u003eを引数として渡してみます\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"ts\" data-sourcepos=\"80:1-98:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003esrc/index.tsx\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"k\"\u003eimport\u003c/span\u003e \u003cspan class=\"nx\"\u003eReact\u003c/span\u003e \u003cspan class=\"k\"\u003efrom\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003ereact\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003cspan class=\"k\"\u003eimport\u003c/span\u003e \u003cspan class=\"nx\"\u003eReactDOM\u003c/span\u003e \u003cspan class=\"k\"\u003efrom\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003ereact-dom\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003cspan class=\"k\"\u003eimport\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003e./index.css\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003cspan class=\"k\"\u003eimport\u003c/span\u003e \u003cspan class=\"nx\"\u003eApp\u003c/span\u003e \u003cspan class=\"k\"\u003efrom\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003e./App\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003cspan class=\"k\"\u003eimport\u003c/span\u003e \u003cspan class=\"nx\"\u003ereportWebVitals\u003c/span\u003e \u003cspan class=\"k\"\u003efrom\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003e./reportWebVitals\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\n\u003cspan class=\"nx\"\u003eReactDOM\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003erender\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\n  \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nx\"\u003eReact\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eStrictMode\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nx\"\u003eApp\u003c/span\u003e \u003cspan class=\"o\"\u003e/\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"sr\"\u003e/React.StrictMode\u0026gt;\u003c/span\u003e\u003cspan class=\"err\"\u003e,\n\u003c/span\u003e  \u003cspan class=\"nb\"\u003edocument\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003egetElementById\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eroot\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e\n\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\n\u003cspan class=\"c1\"\u003e// If you want to start measuring performance in your app, pass a function\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e// to log results (for example: reportWebVitals(console.log))\u003c/span\u003e\n\u003cspan class=\"c1\"\u003e// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals\u003c/span\u003e\n\u003cspan class=\"nf\"\u003ereportWebVitals\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cul data-sourcepos=\"100:1-101:0\"\u003e\n\u003cli data-sourcepos=\"100:1-101:0\"\u003eページを表示するとコンソールにLCPなどのWebVitalsの指標が表示されました！\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp data-sourcepos=\"102:1-102:163\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fdbfe8096-c42a-4274-eb94-68c3cb1368f1.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=80e520cc5e4c3c4486064380223f50bc\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fdbfe8096-c42a-4274-eb94-68c3cb1368f1.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=80e520cc5e4c3c4486064380223f50bc\" alt=\"スクリーンショット 2020-12-11 18.14.37.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fdbfe8096-c42a-4274-eb94-68c3cb1368f1.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=23cd5137e5e69214b475319f868eb419 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/dbfe8096-c42a-4274-eb94-68c3cb1368f1.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cul data-sourcepos=\"104:1-107:0\"\u003e\n\u003cli data-sourcepos=\"104:1-105:58\"\u003e取得できる指標は\u003ca href=\"https://web.dev/cls/\" rel=\"nofollow noopener\" target=\"_blank\"\u003eCLS\u003c/a\u003e、\u003ca href=\"https://web.dev/fid/\" rel=\"nofollow noopener\" target=\"_blank\"\u003eFID\u003c/a\u003e、\u003ca href=\"https://web.dev/lcp/\" rel=\"nofollow noopener\" target=\"_blank\"\u003eLCP\u003c/a\u003e、\u003ca href=\"https://web.dev/fcp/\" rel=\"nofollow noopener\" target=\"_blank\"\u003eFCP\u003c/a\u003e、\u003ca href=\"https://web.dev/time-to-first-byte/\" rel=\"nofollow noopener\" target=\"_blank\"\u003eTTFB\u003c/a\u003eの5種類です\n\u003cul data-sourcepos=\"105:5-105:58\"\u003e\n\u003cli data-sourcepos=\"105:5-105:58\"\u003e\u003ca href=\"https://github.com/GoogleChrome/web-vitals/#overview\" class=\"autolink\" rel=\"nofollow noopener\" target=\"_blank\"\u003ehttps://github.com/GoogleChrome/web-vitals/#overview\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"106:1-107:0\"\u003eサーバに送信したりGoogleAnalyticsに送信するサンプルも掲載されています\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"ts\" data-sourcepos=\"108:1-122:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"kd\"\u003efunction\u003c/span\u003e \u003cspan class=\"nf\"\u003esendToAnalytics\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003emetric\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nx\"\u003eMetrics\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n  \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003ebody\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003eJSON\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003estringify\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003emetric\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n  \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003eurl\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003ehttps://example.com/analytics\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\n  \u003cspan class=\"c1\"\u003e// Use `navigator.sendBeacon()` if available, falling back to `fetch()`\u003c/span\u003e\n  \u003cspan class=\"k\"\u003eif \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nb\"\u003enavigator\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003esendBeacon\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"nb\"\u003enavigator\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003esendBeacon\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eurl\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003ebody\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"k\"\u003eelse\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"nf\"\u003efetch\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eurl\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"nx\"\u003ebody\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"na\"\u003emethod\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003ePOST\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"na\"\u003ekeepalive\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e \u003cspan class=\"p\"\u003e});\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\n\u003cspan class=\"nf\"\u003ereportWebVitals\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003esendToAnalytics\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"js\" data-sourcepos=\"124:1-136:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"kd\"\u003efunction\u003c/span\u003e \u003cspan class=\"nf\"\u003esendToAnalytics\u003c/span\u003e\u003cspan class=\"p\"\u003e({\u003c/span\u003e \u003cspan class=\"nx\"\u003eid\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003evalue\u003c/span\u003e \u003cspan class=\"p\"\u003e})\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n  \u003cspan class=\"nf\"\u003ega\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003esend\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eevent\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"na\"\u003eeventCategory\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eWeb Vitals\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n    \u003cspan class=\"na\"\u003eeventAction\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nx\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n    \u003cspan class=\"na\"\u003eeventValue\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003eMath\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eround\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ename\u003c/span\u003e \u003cspan class=\"o\"\u003e===\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eCLS\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e \u003cspan class=\"p\"\u003e?\u003c/span\u003e \u003cspan class=\"nx\"\u003evalue\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e \u003cspan class=\"mi\"\u003e1000\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nx\"\u003evalue\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e \u003cspan class=\"c1\"\u003e// values must be integers\u003c/span\u003e\n    \u003cspan class=\"na\"\u003eeventLabel\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nx\"\u003eid\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"c1\"\u003e// id unique to current page load\u003c/span\u003e\n    \u003cspan class=\"na\"\u003enonInteraction\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"c1\"\u003e// avoids affecting bounce rate\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e});\u003c/span\u003e\n\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\n\u003cspan class=\"nf\"\u003ereportWebVitals\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003esendToAnalytics\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003ch2 data-sourcepos=\"138:1-138:18\"\u003e\n\u003cspan id=\"試してみた\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E8%A9%A6%E3%81%97%E3%81%A6%E3%81%BF%E3%81%9F\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e試してみた\u003c/h2\u003e\n\u003cul data-sourcepos=\"140:1-142:0\"\u003e\n\u003cli data-sourcepos=\"140:1-142:0\"\u003e送信されたデータをDBに保存するAPIを作って適当にグラフ表示してみました\n\u003cul data-sourcepos=\"141:5-142:0\"\u003e\n\u003cli data-sourcepos=\"141:5-142:0\"\u003eどういう風に集計するのが有用かはもうちょっとちゃんと考えないといけないですね・・\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp data-sourcepos=\"143:1-143:486\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fa084808a-1ab1-686d-ecfc-6de4214c60a9.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=c467fb2b0a720c0fefec8462b09bb66a\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fa084808a-1ab1-686d-ecfc-6de4214c60a9.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=c467fb2b0a720c0fefec8462b09bb66a\" alt=\"スクリーンショット 2020-12-12 3.40.55.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fa084808a-1ab1-686d-ecfc-6de4214c60a9.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=ad7fa11e45c27c4adaabb367f52f2e51 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/a084808a-1ab1-686d-ecfc-6de4214c60a9.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fa955c826-bc0d-3ddb-4418-cb190d86e488.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=91004b8aaf9698651d5244498c3f88f2\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fa955c826-bc0d-3ddb-4418-cb190d86e488.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=91004b8aaf9698651d5244498c3f88f2\" alt=\"スクリーンショット 2020-12-12 3.41.10.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fa955c826-bc0d-3ddb-4418-cb190d86e488.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=bcaf53f06f9d90e2a19e29472421eead 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/a955c826-bc0d-3ddb-4418-cb190d86e488.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fd094c2ad-dedc-6adc-5227-cb57fe56263d.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=2324dc0245e969e534b52004112bea7b\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fd094c2ad-dedc-6adc-5227-cb57fe56263d.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=2324dc0245e969e534b52004112bea7b\" alt=\"スクリーンショット 2020-12-12 3.41.18.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fd094c2ad-dedc-6adc-5227-cb57fe56263d.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=56bdba7367d91981fe5199a94cc60687 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/d094c2ad-dedc-6adc-5227-cb57fe56263d.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003ch2 data-sourcepos=\"145:1-145:12\"\u003e\n\u003cspan id=\"まとめ\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%81%BE%E3%81%A8%E3%82%81\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eまとめ\u003c/h2\u003e\n\u003cul data-sourcepos=\"147:1-148:143\"\u003e\n\u003cli data-sourcepos=\"147:1-147:166\"\u003eCreateReactAppに新しく入ったWebVitalの計測機能はweb-vitalsライブラリのセットアップを事前にしてくれているというものでした\u003c/li\u003e\n\u003cli data-sourcepos=\"148:1-148:143\"\u003e取得できるメトリクスを収集するのは簡単なのでそれをどう活用していくか考えていけばよさそうですね\u003c/li\u003e\n\u003c/ul\u003e\n","body":"この記事は[React #2 Advent Calendar 2020](https://qiita.com/advent-calendar/2020/react-2)の12日目の記事です\n\n## 概要\n\n- 最近は[Next.js](https://nextjs.org/)ばかり使ってますが久しぶりに[CreateReactApp](https://create-react-app.dev/)を使ってみたら`reportWebVitals.ts`という見慣れないファイルが生成されていたのを見たのが今日の記事のきっかけです\n- [React](https://ja.reactjs.org/)アプリの雛形生成ツールであるCreateReactAppはv4で[WebVitals](https://web.dev/vitals/)の計測ライブラリがデフォルトで組み込まれるようになりました\n    - https://github.com/facebook/create-react-app/releases/tag/v4.0.0\n- せっかくなのでドキュメントに沿って少し試してみたいと思います\n\n## ファイルの確認\n\n- まずは生成されたコードを見てみましょう\n- `create-react-app`コマンドで雛形を生成します\n\n```sh\nnpx create-react-app --template typescript web-vitals-sample\n```\n\n- 生成されたファイルの一覧はこんな感じです\n\n```\n.\n├── README.md\n├── package.json\n├── public\n│   ├── favicon.ico\n│   ├── index.html\n│   ├── logo192.png\n│   ├── logo512.png\n│   ├── manifest.json\n│   └── robots.txt\n├── src\n│   ├── App.css\n│   ├── App.test.tsx\n│   ├── App.tsx\n│   ├── index.css\n│   ├── index.tsx\n│   ├── logo.svg\n│   ├── react-app-env.d.ts\n│   ├── reportWebVitals.ts\n│   └── setupTests.ts\n├── tsconfig.json\n└── yarn.lock\n```\n\n- `reportWebVitals.ts`が今回注目するファイルですね\n- 中身はこんな感じです\n\n```ts:src/reportWebVitals.ts\nimport { ReportHandler } from 'web-vitals';\n\nconst reportWebVitals = (onPerfEntry?: ReportHandler) =\u003e {\n  if (onPerfEntry \u0026\u0026 onPerfEntry instanceof Function) {\n    import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) =\u003e {\n      getCLS(onPerfEntry);\n      getFID(onPerfEntry);\n      getFCP(onPerfEntry);\n      getLCP(onPerfEntry);\n      getTTFB(onPerfEntry);\n    });\n  }\n};\n\nexport default reportWebVitals;\n```\n\n- `web-vitals`というライブラリを使っていろいろ設定してくれています\n- `web-vitals`はGoogleChromeが提供するWebVitals計測ライブラリですね\n    - https://github.com/GoogleChrome/web-vitals/\n- このライブラリの追加とセットアップを事前にやってくれているということのようです\n\n## 使い方\n\n- 使い方はCreateReactAppのドキュメントに載っています\n    - https://create-react-app.dev/docs/measuring-performance/\n- `reportWebVitals.ts`でexportされた`reportWebVitals`を呼び出して引数に関数を渡して使います\n    - 渡した関数が内部で呼び出され各種メトリクスが渡されます\n- サンプルにあるように`console.log`を引数として渡してみます\n\n```ts:src/index.tsx\nimport React from 'react';\nimport ReactDOM from 'react-dom';\nimport './index.css';\nimport App from './App';\nimport reportWebVitals from './reportWebVitals';\n\nReactDOM.render(\n  \u003cReact.StrictMode\u003e\n    \u003cApp /\u003e\n  \u003c/React.StrictMode\u003e,\n  document.getElementById('root'),\n);\n\n// If you want to start measuring performance in your app, pass a function\n// to log results (for example: reportWebVitals(console.log))\n// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals\nreportWebVitals(console.log);\n```\n\n- ページを表示するとコンソールにLCPなどのWebVitalsの指標が表示されました！\n\n![スクリーンショット 2020-12-11 18.14.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/dbfe8096-c42a-4274-eb94-68c3cb1368f1.png)\n\n- 取得できる指標は[CLS](https://web.dev/cls/)、[FID](https://web.dev/fid/)、[LCP](https://web.dev/lcp/)、[FCP](https://web.dev/fcp/)、[TTFB](https://web.dev/time-to-first-byte/)の5種類です\n    - https://github.com/GoogleChrome/web-vitals/#overview\n- サーバに送信したりGoogleAnalyticsに送信するサンプルも掲載されています\n\n```ts\nfunction sendToAnalytics(metric: Metrics) {\n  const body = JSON.stringify(metric);\n  const url = 'https://example.com/analytics';\n\n  // Use `navigator.sendBeacon()` if available, falling back to `fetch()`\n  if (navigator.sendBeacon) {\n    navigator.sendBeacon(url, body);\n  } else {\n    fetch(url, { body, method: 'POST', keepalive: true });\n  }\n}\n\nreportWebVitals(sendToAnalytics);\n```\n\n```js\nfunction sendToAnalytics({ id, name, value }) {\n  ga('send', 'event', {\n    eventCategory: 'Web Vitals',\n    eventAction: name,\n    eventValue: Math.round(name === 'CLS' ? value * 1000 : value), // values must be integers\n    eventLabel: id, // id unique to current page load\n    nonInteraction: true, // avoids affecting bounce rate\n  });\n}\n\nreportWebVitals(sendToAnalytics);\n```\n\n## 試してみた\n\n- 送信されたデータをDBに保存するAPIを作って適当にグラフ表示してみました\n    - どういう風に集計するのが有用かはもうちょっとちゃんと考えないといけないですね・・\n\n![スクリーンショット 2020-12-12 3.40.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/a084808a-1ab1-686d-ecfc-6de4214c60a9.png)![スクリーンショット 2020-12-12 3.41.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/a955c826-bc0d-3ddb-4418-cb190d86e488.png)![スクリーンショット 2020-12-12 3.41.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/d094c2ad-dedc-6adc-5227-cb57fe56263d.png)\n\n## まとめ\n\n- CreateReactAppに新しく入ったWebVitalの計測機能はweb-vitalsライブラリのセットアップを事前にしてくれているというものでした\n- 取得できるメトリクスを収集するのは簡単なのでそれをどう活用していくか考えていけばよさそうですね\n","coediting":false,"comments_count":0,"created_at":"2020-12-12T03:48:04+09:00","group":null,"id":"6139cbc70cf988d1c870","likes_count":46,"private":false,"reactions_count":0,"stocks_count":21,"tags":[{"name":"React","versions":[]},{"name":"WebVitals","versions":[]}],"title":"CreateReactAppにWebVitals計測ライブラリが入ったので試してみた","updated_at":"2021-10-26T18:51:13+09:00","url":"https://qiita.com/ozaki25/items/6139cbc70cf988d1c870","user":{"description":"","facebook_id":"","followees_count":0,"followers_count":79,"github_login_name":"ozaki25","id":"ozaki25","items_count":112,"linkedin_id":"","location":"","name":"","organization":"","permanent_id":175213,"profile_image_url":"https://qiita-image-store.s3.amazonaws.com/0/175213/profile-images/1491825881","team_only":false,"twitter_screen_name":"ozaki25rn","website_url":""},"page_views_count":null,"team_membership":null,"organization_url_name":null,"slide":false,"posting_campaign_uuid":null},{"rendered_body":"\u003cp data-sourcepos=\"1:1-2:157\"\u003eこの記事は\u003ca href=\"https://qiita.com/advent-calendar/2020/nextjs\"\u003eNext.js Advent Calendar 2020\u003c/a\u003eの11日目の記事です\u003cbr\u003e\n（本当は\u003ca href=\"https://next-auth.js.org/\" rel=\"nofollow noopener\" target=\"_blank\"\u003eNextAuth.js\u003c/a\u003eを試してみようと思いましたが意外と難しくて時間が足りませんでした。。）\u003c/p\u003e\n\u003ch2 data-sourcepos=\"4:1-4:9\"\u003e\n\u003cspan id=\"概要\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E6%A6%82%E8%A6%81\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e概要\u003c/h2\u003e\n\u003cul data-sourcepos=\"6:1-9:0\"\u003e\n\u003cli data-sourcepos=\"6:1-7:99\"\u003e2020年10月にリリースされた\u003ca href=\"https://nextjs.org/\" rel=\"nofollow noopener\" target=\"_blank\"\u003eNext.js\u003c/a\u003eのv10で画像の最適化の仕組みが入りました\n\u003cul data-sourcepos=\"7:5-7:99\"\u003e\n\u003cli data-sourcepos=\"7:5-7:99\"\u003e\u003ca href=\"https://nextjs.org/blog/next-10#nextjs-image-component\" rel=\"nofollow noopener\" target=\"_blank\"\u003eNext.js v10のリリースブログ\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"8:1-9:0\"\u003eこの画像最適化の仕組みが適用されていることをサンプルアプリを通じて確認してみたいと思います\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"10:1-10:28\"\u003e\n\u003cspan id=\"nextjsの画像最適化\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#nextjs%E3%81%AE%E7%94%BB%E5%83%8F%E6%9C%80%E9%81%A9%E5%8C%96\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eNext.jsの画像最適化\u003c/h2\u003e\n\u003cul data-sourcepos=\"12:1-13:0\"\u003e\n\u003cli data-sourcepos=\"12:1-13:0\"\u003e上記ブログ記事によると主に以下のような背景で機能が導入されました\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 data-sourcepos=\"14:1-14:10\"\u003e\n\u003cspan id=\"課題\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E8%AA%B2%E9%A1%8C\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e課題\u003c/h3\u003e\n\u003cul data-sourcepos=\"16:1-18:0\"\u003e\n\u003cli data-sourcepos=\"16:1-16:113\"\u003eWebページにおける画像は\u003ca href=\"https://web.dev/lcp/\" rel=\"nofollow noopener\" target=\"_blank\"\u003eLargest Contentful Paint\u003c/a\u003eに大きな影響を与える\u003c/li\u003e\n\u003cli data-sourcepos=\"17:1-18:0\"\u003eレスポンシブ対応のために\u003ccode\u003eimg\u003c/code\u003eタグに\u003ccode\u003ewidth\u003c/code\u003e \u003ccode\u003eheight\u003c/code\u003eを指定しないケースがあり、その場合カクンとなるレイアウトシフトが発生してしまう\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 data-sourcepos=\"19:1-19:13\"\u003e\n\u003cspan id=\"解決策\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E8%A7%A3%E6%B1%BA%E7%AD%96\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e解決策\u003c/h3\u003e\n\u003cul data-sourcepos=\"21:1-24:0\"\u003e\n\u003cli data-sourcepos=\"21:1-24:0\"\u003eNext.jsで\u003ccode\u003eimg\u003c/code\u003eタグをラップした\u003ca href=\"https://nextjs.org/docs/api-reference/next/image\" rel=\"nofollow noopener\" target=\"_blank\"\u003eImageコンポーネント\u003c/a\u003eを提供し、それを使うだけで上記の課題を全て解決させる\n\u003cul data-sourcepos=\"22:5-24:0\"\u003e\n\u003cli data-sourcepos=\"22:5-22:133\"\u003eクライアントの環境に応じて適切な形式（WebPなど）に変換しファイルサイズも縮小してくれる\u003c/li\u003e\n\u003cli data-sourcepos=\"23:5-24:0\"\u003e\n\u003ccode\u003ewidth\u003c/code\u003e \u003ccode\u003eheight\u003c/code\u003eを指定しても縦横比を維持したまま自動でレスポンシブ対応してくれる\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"25:1-25:33\"\u003e\n\u003cspan id=\"サンプルアプリで検証\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%E3%82%A2%E3%83%97%E3%83%AA%E3%81%A7%E6%A4%9C%E8%A8%BC\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eサンプルアプリで検証\u003c/h2\u003e\n\u003cul data-sourcepos=\"27:1-31:0\"\u003e\n\u003cli data-sourcepos=\"27:1-27:149\"\u003eファイルサイズの大きい（12.6MB）画像を用意して、\u003ccode\u003eimg\u003c/code\u003eタグと\u003ccode\u003eImage\u003c/code\u003eコンポーネントでそれぞれ表示してみます\u003c/li\u003e\n\u003cli data-sourcepos=\"28:1-28:53\"\u003e使うタグ以外は同じ属性を設定します\u003c/li\u003e\n\u003cli data-sourcepos=\"29:1-31:0\"\u003e検証に使ったページはこちらです\n\u003cul data-sourcepos=\"30:5-31:0\"\u003e\n\u003cli data-sourcepos=\"30:5-31:0\"\u003e\u003ca href=\"https://next-image-sample.vercel.app/\" class=\"autolink\" rel=\"nofollow noopener\" target=\"_blank\"\u003ehttps://next-image-sample.vercel.app/\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"tsx\" data-sourcepos=\"32:1-44:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"k\"\u003eimport\u003c/span\u003e \u003cspan class=\"nx\"\u003eImage\u003c/span\u003e \u003cspan class=\"k\"\u003efrom\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003enext/image\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003cspan class=\"k\"\u003eimport\u003c/span\u003e \u003cspan class=\"nx\"\u003eLayout\u003c/span\u003e \u003cspan class=\"k\"\u003efrom\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003e../components/Layout\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\n\u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003eIndexPage\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nc\"\u003eLayout\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nt\"\u003eimg\u003c/span\u003e \u003cspan class=\"na\"\u003esrc\u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"/sample.png\"\u003c/span\u003e \u003cspan class=\"na\"\u003ewidth\u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"270\"\u003c/span\u003e \u003cspan class=\"na\"\u003eheight\u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"480\"\u003c/span\u003e \u003cspan class=\"p\"\u003e/\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nc\"\u003eImage\u003c/span\u003e \u003cspan class=\"na\"\u003esrc\u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"/sample.png\"\u003c/span\u003e \u003cspan class=\"na\"\u003ewidth\u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"270\"\u003c/span\u003e \u003cspan class=\"na\"\u003eheight\u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"480\"\u003c/span\u003e \u003cspan class=\"p\"\u003e/\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"nc\"\u003eLayout\u003c/span\u003e\u003cspan class=\"p\"\u003e\u0026gt;\u003c/span\u003e\n\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\n\u003cspan class=\"k\"\u003eexport\u003c/span\u003e \u003cspan class=\"k\"\u003edefault\u003c/span\u003e \u003cspan class=\"nx\"\u003eIndexPage\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003ch3 data-sourcepos=\"46:1-46:61\"\u003e\n\u003cspan id=\"検証1ファイルサイズ形式の最適化の確認\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E6%A4%9C%E8%A8%BC1%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%B5%E3%82%A4%E3%82%BA%E5%BD%A2%E5%BC%8F%E3%81%AE%E6%9C%80%E9%81%A9%E5%8C%96%E3%81%AE%E7%A2%BA%E8%AA%8D\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e検証1:ファイルサイズ/形式の最適化の確認\u003c/h3\u003e\n\u003cp data-sourcepos=\"48:1-48:162\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F9b9f3169-0ead-df9a-104a-b9cd27392cdd.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=c8b042a458ff5a8d0b6f023c5e79d4fa\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F9b9f3169-0ead-df9a-104a-b9cd27392cdd.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=c8b042a458ff5a8d0b6f023c5e79d4fa\" alt=\"スクリーンショット 2020-12-11 1.01.32.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F9b9f3169-0ead-df9a-104a-b9cd27392cdd.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=e5f095badf6a4df1281131999d921461 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/9b9f3169-0ead-df9a-104a-b9cd27392cdd.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cul data-sourcepos=\"50:1-55:0\"\u003e\n\u003cli data-sourcepos=\"50:1-52:101\"\u003e注目する箇所に赤枠をつけました\n\u003cul data-sourcepos=\"51:5-52:101\"\u003e\n\u003cli data-sourcepos=\"51:5-51:90\"\u003e\n\u003ccode\u003eimg\u003c/code\u003eタグで読み込まれた1枚目は元画像のままpngで12.6MBあります\u003c/li\u003e\n\u003cli data-sourcepos=\"52:5-52:101\"\u003e\n\u003ccode\u003eImage\u003c/code\u003eコンポーネントで読み込まれた2枚目はwebpで221kbになっています！\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"53:1-55:0\"\u003eちゃんと最適化されていることが確認できました\n\u003cul data-sourcepos=\"54:5-55:0\"\u003e\n\u003cli data-sourcepos=\"54:5-55:0\"\u003e極端な例ではありますがレスポンスタイムも大違いですね！\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 data-sourcepos=\"56:1-56:45\"\u003e\n\u003cspan id=\"検証2レスポンシブ対応の確認\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E6%A4%9C%E8%A8%BC2%E3%83%AC%E3%82%B9%E3%83%9D%E3%83%B3%E3%82%B7%E3%83%96%E5%AF%BE%E5%BF%9C%E3%81%AE%E7%A2%BA%E8%AA%8D\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e検証2:レスポンシブ対応の確認\u003c/h3\u003e\n\u003cp data-sourcepos=\"58:1-58:162\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F6ffcb1d9-8c88-ddcc-ee7b-b7eb030b99cf.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=bda75a3f26f7eefa94ef0d1e4da8403f\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F6ffcb1d9-8c88-ddcc-ee7b-b7eb030b99cf.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=bda75a3f26f7eefa94ef0d1e4da8403f\" alt=\"スクリーンショット 2020-12-11 1.07.26.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F6ffcb1d9-8c88-ddcc-ee7b-b7eb030b99cf.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=334bb629b87cc973363c386be6cc7d8f 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/6ffcb1d9-8c88-ddcc-ee7b-b7eb030b99cf.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cul data-sourcepos=\"60:1-64:0\"\u003e\n\u003cli data-sourcepos=\"60:1-64:0\"\u003eちょっと見づらいですがウィンドウサイズを狭めてみました\n\u003cul data-sourcepos=\"61:5-64:0\"\u003e\n\u003cli data-sourcepos=\"61:5-61:121\"\u003e\n\u003ccode\u003eimg\u003c/code\u003eタグで読み込まれた1枚目は\u003ccode\u003ewidth\u003c/code\u003eのサイズのままで横スクロールが発生しています\u003c/li\u003e\n\u003cli data-sourcepos=\"62:5-64:0\"\u003e\n\u003ccode\u003eImage\u003c/code\u003eコンポーネントで読み込まれた2枚目はウィンドウサイズに合わせて縮小されています！\n\u003cul data-sourcepos=\"63:9-64:0\"\u003e\n\u003cli data-sourcepos=\"63:9-64:0\"\u003eキャプチャではわかりませんがレイアウトシフトも発生しません！\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"65:1-65:12\"\u003e\n\u003cspan id=\"まとめ\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%81%BE%E3%81%A8%E3%82%81\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eまとめ\u003c/h2\u003e\n\u003cul data-sourcepos=\"67:1-68:111\"\u003e\n\u003cli data-sourcepos=\"67:1-67:149\"\u003e\n\u003ccode\u003eimg\u003c/code\u003eタグを使うのと同じ感覚で\u003ccode\u003eImage\u003c/code\u003eコンポーネントを使うだけで最適化が施されていることを確認できました\u003c/li\u003e\n\u003cli data-sourcepos=\"68:1-68:111\"\u003eNext.jsの利用者側は何の負担もないので今後当たり前に使っていくといいですね！\u003c/li\u003e\n\u003c/ul\u003e\n","body":"この記事は[Next.js Advent Calendar 2020](https://qiita.com/advent-calendar/2020/nextjs)の11日目の記事です\n（本当は[NextAuth.js](https://next-auth.js.org/)を試してみようと思いましたが意外と難しくて時間が足りませんでした。。）\n\n## 概要\n\n- 2020年10月にリリースされた[Next.js](https://nextjs.org/)のv10で画像の最適化の仕組みが入りました\n    - [Next.js v10のリリースブログ](https://nextjs.org/blog/next-10#nextjs-image-component)\n- この画像最適化の仕組みが適用されていることをサンプルアプリを通じて確認してみたいと思います\n\n## Next.jsの画像最適化\n\n- 上記ブログ記事によると主に以下のような背景で機能が導入されました\n\n### 課題\n\n- Webページにおける画像は[Largest Contentful Paint](https://web.dev/lcp/)に大きな影響を与える\n- レスポンシブ対応のために`img`タグに`width` `height`を指定しないケースがあり、その場合カクンとなるレイアウトシフトが発生してしまう\n\n### 解決策\n\n- Next.jsで`img`タグをラップした[Imageコンポーネント](https://nextjs.org/docs/api-reference/next/image)を提供し、それを使うだけで上記の課題を全て解決させる\n    - クライアントの環境に応じて適切な形式（WebPなど）に変換しファイルサイズも縮小してくれる\n    - `width` `height`を指定しても縦横比を維持したまま自動でレスポンシブ対応してくれる\n\n## サンプルアプリで検証\n\n- ファイルサイズの大きい（12.6MB）画像を用意して、`img`タグと`Image`コンポーネントでそれぞれ表示してみます\n- 使うタグ以外は同じ属性を設定します\n- 検証に使ったページはこちらです\n    - https://next-image-sample.vercel.app/\n\n```tsx\nimport Image from 'next/image';\nimport Layout from '../components/Layout';\n\nconst IndexPage = () =\u003e (\n  \u003cLayout\u003e\n    \u003cimg src=\"/sample.png\" width=\"270\" height=\"480\" /\u003e\n    \u003cImage src=\"/sample.png\" width=\"270\" height=\"480\" /\u003e\n  \u003c/Layout\u003e\n);\n\nexport default IndexPage;\n```\n\n### 検証1:ファイルサイズ/形式の最適化の確認\n\n![スクリーンショット 2020-12-11 1.01.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/9b9f3169-0ead-df9a-104a-b9cd27392cdd.png)\n\n- 注目する箇所に赤枠をつけました\n    - `img`タグで読み込まれた1枚目は元画像のままpngで12.6MBあります\n    - `Image`コンポーネントで読み込まれた2枚目はwebpで221kbになっています！\n- ちゃんと最適化されていることが確認できました\n    - 極端な例ではありますがレスポンスタイムも大違いですね！\n\n### 検証2:レスポンシブ対応の確認\n\n![スクリーンショット 2020-12-11 1.07.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/6ffcb1d9-8c88-ddcc-ee7b-b7eb030b99cf.png)\n\n- ちょっと見づらいですがウィンドウサイズを狭めてみました\n    - `img`タグで読み込まれた1枚目は`width`のサイズのままで横スクロールが発生しています\n    - `Image`コンポーネントで読み込まれた2枚目はウィンドウサイズに合わせて縮小されています！\n        - キャプチャではわかりませんがレイアウトシフトも発生しません！\n\n## まとめ\n\n- `img`タグを使うのと同じ感覚で`Image`コンポーネントを使うだけで最適化が施されていることを確認できました\n- Next.jsの利用者側は何の負担もないので今後当たり前に使っていくといいですね！\n","coediting":false,"comments_count":0,"created_at":"2020-12-11T01:18:31+09:00","group":null,"id":"88a10d170be75faa43b4","likes_count":9,"private":false,"reactions_count":0,"stocks_count":4,"tags":[{"name":"Next.js","versions":[]}],"title":"Next.jsの画像最適化を検証してみる","updated_at":"2021-10-26T18:51:03+09:00","url":"https://qiita.com/ozaki25/items/88a10d170be75faa43b4","user":{"description":"","facebook_id":"","followees_count":0,"followers_count":79,"github_login_name":"ozaki25","id":"ozaki25","items_count":112,"linkedin_id":"","location":"","name":"","organization":"","permanent_id":175213,"profile_image_url":"https://qiita-image-store.s3.amazonaws.com/0/175213/profile-images/1491825881","team_only":false,"twitter_screen_name":"ozaki25rn","website_url":""},"page_views_count":null,"team_membership":null,"organization_url_name":null,"slide":false,"posting_campaign_uuid":null},{"rendered_body":"\u003ch2 data-sourcepos=\"1:1-1:9\"\u003e\n\u003cspan id=\"概要\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E6%A6%82%E8%A6%81\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e概要\u003c/h2\u003e\n\u003cul data-sourcepos=\"3:1-4:0\"\u003e\n\u003cli data-sourcepos=\"3:1-4:0\"\u003ePuppeteer実行時にevaluateの中で日付操作する時はタイムゾーンを個別に指定しないといけないではまった\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"5:1-5:9\"\u003e\n\u003cspan id=\"実装\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E5%AE%9F%E8%A3%85\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e実装\u003c/h2\u003e\n\u003cul data-sourcepos=\"7:1-10:0\"\u003e\n\u003cli data-sourcepos=\"7:1-8:105\"\u003e以下の例はページの中にリストがあって、日付の要素数目を取得しようとした適当なサンプル\n\u003cul data-sourcepos=\"8:5-8:105\"\u003e\n\u003cli data-sourcepos=\"8:5-8:105\"\u003e\n\u003ccode\u003enew Date()\u003c/code\u003eしているところが実行環境のタイムゾーンが反映されずはまった\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"9:1-10:0\"\u003e\n\u003ccode\u003eawait page.emulateTimezone('Asia/Tokyo');\u003c/code\u003e でタイムゾーンを指定することで解決\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"js\" data-sourcepos=\"11:1-25:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e  \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003ebrowser\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"nx\"\u003epuppeteer\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003elaunch\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n  \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003epage\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"nx\"\u003ebrowser\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003enewPage\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n  \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"nx\"\u003epage\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eemulateTimezone\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eAsia/Tokyo\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n  \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"nx\"\u003epage\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003egoto\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003ehttps://example.com\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\n  \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003etext\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"nx\"\u003epage\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eevaluate\u003c/span\u003e\u003cspan class=\"p\"\u003e(()\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003eelements\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003edocument\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003equerySelectorAll\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003e.list\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n    \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003edate\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"nc\"\u003eDate\u003c/span\u003e\u003cspan class=\"p\"\u003e().\u003c/span\u003e\u003cspan class=\"nf\"\u003egetDate\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n    \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003etarget\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003eelements\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003edate\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e];\u003c/span\u003e\n    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"nx\"\u003etarget\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003etext\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e});\u003c/span\u003e\n  \u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003etext\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n  \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"nx\"\u003ebrowser\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eclose\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003ch2 data-sourcepos=\"27:1-27:12\"\u003e\n\u003cspan id=\"まとめ\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%81%BE%E3%81%A8%E3%82%81\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eまとめ\u003c/h2\u003e\n\u003cul data-sourcepos=\"29:1-29:89\"\u003e\n\u003cli data-sourcepos=\"29:1-29:89\"\u003eタイムゾーン問題はちゃんとテストしないとはまりますね・・・\u003c/li\u003e\n\u003c/ul\u003e\n","body":"## 概要\n\n- Puppeteer実行時にevaluateの中で日付操作する時はタイムゾーンを個別に指定しないといけないではまった\n\n## 実装\n\n- 以下の例はページの中にリストがあって、日付の要素数目を取得しようとした適当なサンプル\n    - `new Date()`しているところが実行環境のタイムゾーンが反映されずはまった\n- `await page.emulateTimezone('Asia/Tokyo');` でタイムゾーンを指定することで解決\n\n```js\n  const browser = await puppeteer.launch();\n  const page = await browser.newPage();\n  await page.emulateTimezone('Asia/Tokyo');\n  await page.goto('https://example.com');\n\n  const text = await page.evaluate(() =\u003e {\n    const elements = document.querySelectorAll('.list');\n    const date = new Date().getDate();\n    const target = elements[date - 1];\n    return target.text;\n  });\n  console.log(text);\n  await browser.close();\n```\n\n## まとめ\n\n- タイムゾーン問題はちゃんとテストしないとはまりますね・・・\n","coediting":false,"comments_count":0,"created_at":"2020-12-09T08:18:48+09:00","group":null,"id":"dee1a5c9d05820d39648","likes_count":1,"private":false,"reactions_count":0,"stocks_count":1,"tags":[{"name":"JavaScript","versions":[]},{"name":"puppeteer","versions":[]}],"title":"Puppeteer実行時のタイムゾーン","updated_at":"2021-10-26T18:50:57+09:00","url":"https://qiita.com/ozaki25/items/dee1a5c9d05820d39648","user":{"description":"","facebook_id":"","followees_count":0,"followers_count":79,"github_login_name":"ozaki25","id":"ozaki25","items_count":112,"linkedin_id":"","location":"","name":"","organization":"","permanent_id":175213,"profile_image_url":"https://qiita-image-store.s3.amazonaws.com/0/175213/profile-images/1491825881","team_only":false,"twitter_screen_name":"ozaki25rn","website_url":""},"page_views_count":null,"team_membership":null,"organization_url_name":null,"slide":false,"posting_campaign_uuid":null},{"rendered_body":"\u003cul data-sourcepos=\"1:1-3:0\"\u003e\n\u003cli data-sourcepos=\"1:1-1:118\"\u003eこの記事は\u003ca href=\"https://qiita.com/advent-calendar/2020/mdc\"\u003eMDCアドベントカレンダー\u003c/a\u003e9日目の記事です\u003c/li\u003e\n\u003cli data-sourcepos=\"2:1-3:0\"\u003e埋まってない日に書いてるのでもう4回目の登場ですがこれ以降はほぼ埋まってますね！\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"4:1-4:9\"\u003e\n\u003cspan id=\"概要\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E6%A6%82%E8%A6%81\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e概要\u003c/h2\u003e\n\u003cul data-sourcepos=\"6:1-9:0\"\u003e\n\u003cli data-sourcepos=\"6:1-6:110\"\u003eWebアクセシビリティと聞いて皆さんはどのようなことを思い浮かべるでしょうか\u003c/li\u003e\n\u003cli data-sourcepos=\"7:1-7:161\"\u003eあまり詳しくない人でも、スクリーンリーダーで読めるように画像にalt属性をつけるなど聞いたことがあると思います\u003c/li\u003e\n\u003cli data-sourcepos=\"8:1-9:0\"\u003e今回は文字のコントラストを確保するべきという項目をテーマにとりあげます\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"10:1-10:42\"\u003e\n\u003cspan id=\"テキストのコントラスト確保\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88%E3%81%AE%E3%82%B3%E3%83%B3%E3%83%88%E3%83%A9%E3%82%B9%E3%83%88%E7%A2%BA%E4%BF%9D\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eテキストのコントラスト確保\u003c/h2\u003e\n\u003cul data-sourcepos=\"12:1-22:0\"\u003e\n\u003cli data-sourcepos=\"12:1-18:148\"\u003eテキストのコントラストに関しては\u003ca href=\"https://www.w3.org/TR/WCAG21/\" rel=\"nofollow noopener\" target=\"_blank\"\u003eWCAG 2.1\u003c/a\u003eでは以下のように定義されています(抜粋)\n\u003cul data-sourcepos=\"13:5-18:148\"\u003e\n\u003cli data-sourcepos=\"13:5-15:146\"\u003e\n\u003ca href=\"https://waic.jp/docs/WCAG21/#contrast-minimum\" rel=\"nofollow noopener\" target=\"_blank\"\u003eコントラスト(最低限) レベルAA\u003c/a\u003e\n\u003cul data-sourcepos=\"14:9-15:146\"\u003e\n\u003cli data-sourcepos=\"14:9-14:124\"\u003eテキスト及び文字画像の視覚的提示に、少なくとも \u003ccode\u003e4.5:1\u003c/code\u003e のコントラスト比がある。\u003c/li\u003e\n\u003cli data-sourcepos=\"15:9-15:146\"\u003eサイズの大きなテキスト及びサイズの大きな文字画像に、少なくとも \u003ccode\u003e3:1\u003c/code\u003e のコントラスト比がある。\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"16:5-18:148\"\u003e\n\u003ca href=\"https://waic.jp/docs/WCAG21/#contrast-enhanced\" rel=\"nofollow noopener\" target=\"_blank\"\u003eコントラスト(高度) レベルAAA\u003c/a\u003e\n\u003cul data-sourcepos=\"17:9-18:148\"\u003e\n\u003cli data-sourcepos=\"17:9-17:122\"\u003eテキスト及び文字画像の視覚的提示に、少なくとも \u003ccode\u003e7:1\u003c/code\u003e のコントラスト比がある。\u003c/li\u003e\n\u003cli data-sourcepos=\"18:9-18:148\"\u003eサイズの大きなテキスト及びサイズの大きな文字画像に、少なくとも \u003ccode\u003e4.5:1\u003c/code\u003e のコントラスト比がある。\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"19:1-20:136\"\u003eこれらはテキスト色と背景色のコントラスト比が一定以上であることを求めています\n\u003cul data-sourcepos=\"20:5-20:136\"\u003e\n\u003cli data-sourcepos=\"20:5-20:136\"\u003eテキスト色と背景色が同じ色であれば \u003ccode\u003e1:1\u003c/code\u003e でコントラスト比が大きいほど数値の差が広がります\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"21:1-22:0\"\u003e以下は\u003ca href=\"https://webaim.org/resources/contrastchecker/\" rel=\"nofollow noopener\" target=\"_blank\"\u003eコントラスト比チェックツール\u003c/a\u003eのキャプチャです\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp data-sourcepos=\"23:1-24:162\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F684724e4-03c9-6704-c8b6-a967df0dc464.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=f36c8499cf43eec4adfd2fcd69ec3a0e\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F684724e4-03c9-6704-c8b6-a967df0dc464.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=f36c8499cf43eec4adfd2fcd69ec3a0e\" alt=\"スクリーンショット 2020-12-09 0.07.15.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F684724e4-03c9-6704-c8b6-a967df0dc464.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=be08c6d469b36cbbb707440e2dd4e3d1 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/684724e4-03c9-6704-c8b6-a967df0dc464.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003cbr\u003e\n\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Ff3a673b2-ea7e-53dd-a6d5-9a65acf5438d.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=20f4f6ac68371f127bc12609cb057c70\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Ff3a673b2-ea7e-53dd-a6d5-9a65acf5438d.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=20f4f6ac68371f127bc12609cb057c70\" alt=\"スクリーンショット 2020-12-09 0.07.36.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Ff3a673b2-ea7e-53dd-a6d5-9a65acf5438d.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=0efd7d7ecce4e830636ee0cf0f53be8b 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/f3a673b2-ea7e-53dd-a6d5-9a65acf5438d.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cul data-sourcepos=\"26:1-29:0\"\u003e\n\u003cli data-sourcepos=\"26:1-29:0\"\u003eいろいろ試してみると分かると思いますが、健常者であると思っている人であってもコントラスト比が大きい方が読みやすいと感じるのではないでしょうか\n\u003cul data-sourcepos=\"27:5-29:0\"\u003e\n\u003cli data-sourcepos=\"27:5-29:0\"\u003eより読みやすいページを作るためにもコントラスト比を気にしてみるといいかもしれませんね\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"30:1-30:48\"\u003e\n\u003cspan id=\"webページのコントラストチェック\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#web%E3%83%9A%E3%83%BC%E3%82%B8%E3%81%AE%E3%82%B3%E3%83%B3%E3%83%88%E3%83%A9%E3%82%B9%E3%83%88%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eWebページのコントラストチェック\u003c/h2\u003e\n\u003cul data-sourcepos=\"32:1-35:0\"\u003e\n\u003cli data-sourcepos=\"32:1-32:294\"\u003e上で紹介したような2つの色を指定してコントラスト比をチェックするサイトはいくつも出てきますが、すでにあるWebページに対してコントラスト比に問題がある部分をチェックするツールがないかなと探してみました\u003c/li\u003e\n\u003cli data-sourcepos=\"33:1-35:0\"\u003eChrome Devtoolsで使える\u003ca href=\"https://developers.google.com/web/tools/lighthouse?hl=ja\" rel=\"nofollow noopener\" target=\"_blank\"\u003eLighthouse\u003c/a\u003eでもチェックすることができますが、\u003ca href=\"https://chrome.google.com/webstore/detail/wcag-color-contrast-check/plnahcmalebffmaghcpcmpaciebdhgdf\" rel=\"nofollow noopener\" target=\"_blank\"\u003eWCAG Color contrast checker\u003c/a\u003eというChrome拡張が便利だったので紹介します\n\u003cul data-sourcepos=\"34:5-35:0\"\u003e\n\u003cli data-sourcepos=\"34:5-35:0\"\u003eLighthouseは画面のどこが指摘されているかは分かるんですが、それぞれどんな色を使っていて何対何なのかが表示されずちょっと不便でした\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp data-sourcepos=\"36:1-36:162\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F3d3149b0-3b34-4459-758b-a5c44553de29.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=f8f577062cd0447fa3b849c3d7807f55\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F3d3149b0-3b34-4459-758b-a5c44553de29.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=f8f577062cd0447fa3b849c3d7807f55\" alt=\"スクリーンショット 2020-12-09 0.50.15.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F3d3149b0-3b34-4459-758b-a5c44553de29.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=1547c7a17a1a644166e82deb11f234b2 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/3d3149b0-3b34-4459-758b-a5c44553de29.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003ch2 data-sourcepos=\"39:1-39:30\"\u003e\n\u003cspan id=\"wcag-color-contrast-checker\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#wcag-color-contrast-checker\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eWCAG Color contrast checker\u003c/h2\u003e\n\u003cul data-sourcepos=\"41:1-43:0\"\u003e\n\u003cli data-sourcepos=\"41:1-41:26\"\u003e使い方は簡単です\u003c/li\u003e\n\u003cli data-sourcepos=\"42:1-43:0\"\u003eChrome拡張を入れてチェックしたいページでアイコンをクリックすると画面左部に検証結果が表示されます\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp data-sourcepos=\"44:1-44:162\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fbf256206-60f3-7b1a-1df3-226769f19dc0.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=51e69361806d4142ac460603c2464a14\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fbf256206-60f3-7b1a-1df3-226769f19dc0.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=51e69361806d4142ac460603c2464a14\" alt=\"スクリーンショット 2020-12-09 0.22.30.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fbf256206-60f3-7b1a-1df3-226769f19dc0.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=b8b28eb2d175737e991bae629210c30e 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/bf256206-60f3-7b1a-1df3-226769f19dc0.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cul data-sourcepos=\"46:1-48:0\"\u003e\n\u003cli data-sourcepos=\"46:1-48:0\"\u003e画面上のテキストのコントラスト比が一覧で表示されます\n\u003cul data-sourcepos=\"47:5-48:0\"\u003e\n\u003cli data-sourcepos=\"47:5-48:0\"\u003e目のマークに斜線が入った行は非表示となっている情報なので無視しても大丈夫でしょう\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp data-sourcepos=\"49:1-49:162\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F483cb224-c04a-f1d6-1889-20c3e56f7c41.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=56db9b62dfc953f95a925ae315c1af3e\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F483cb224-c04a-f1d6-1889-20c3e56f7c41.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=56db9b62dfc953f95a925ae315c1af3e\" alt=\"スクリーンショット 2020-12-09 0.27.18.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F483cb224-c04a-f1d6-1889-20c3e56f7c41.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=1754b2c1e000d3d54252be5cb0af56b7 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/483cb224-c04a-f1d6-1889-20c3e56f7c41.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cul data-sourcepos=\"51:1-54:0\"\u003e\n\u003cli data-sourcepos=\"51:1-54:0\"\u003eChrome拡張で表示された行を選択すると対応する部分に赤枠が表示され、どの部分が指摘されているのか確認することができます\n\u003cul data-sourcepos=\"52:5-54:0\"\u003e\n\u003cli data-sourcepos=\"52:5-52:75\"\u003e白背景に緑の字でこれが \u003ccode\u003e2.24:1\u003c/code\u003eなんですね・・・！\u003c/li\u003e\n\u003cli data-sourcepos=\"53:5-54:0\"\u003e画面左下の部分で色を変更してコントラスト比をチェックすることもできます\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"55:1-55:12\"\u003e\n\u003cspan id=\"最後に\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E6%9C%80%E5%BE%8C%E3%81%AB\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e最後に\u003c/h2\u003e\n\u003cul data-sourcepos=\"57:1-60:0\"\u003e\n\u003cli data-sourcepos=\"57:1-57:134\"\u003e今回調べる中でいろいろなサイトでツールを使ってみましたが指摘が入るサイトがほとんどでした\u003c/li\u003e\n\u003cli data-sourcepos=\"58:1-58:134\"\u003eガイドラインに従うことも大切ですが、それぞれの事情に応じて判断している感じなんでしょうね\u003c/li\u003e\n\u003cli data-sourcepos=\"59:1-60:0\"\u003eただ、何も知らずに作るのと知った上で判断するのでは大違いだと思いますので、まずは自分の作ったWebページにツールを適用してどのような結果になるか見てみてはいかがでしょうか\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"61:1-61:9\"\u003e\n\u003cspan id=\"追記\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E8%BF%BD%E8%A8%98\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e追記\u003c/h2\u003e\n\u003cul data-sourcepos=\"63:1-64:0\"\u003e\n\u003cli data-sourcepos=\"63:1-64:0\"\u003e記事を書いた後に\u003ca href=\"https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd\" rel=\"nofollow noopener\" target=\"_blank\"\u003eaxe - Web Accessibility Testing\u003c/a\u003eというChrome拡張を知りました。こちらの方が使い勝手がいいかも。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"65:1-65:10\"\u003e\n\u003cspan id=\"追記2\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E8%BF%BD%E8%A8%982\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e追記2\u003c/h2\u003e\n\u003cul data-sourcepos=\"67:1-69:55\"\u003e\n\u003cli data-sourcepos=\"67:1-69:55\"\u003eよくよく調べるとChrome DevToolsでもいろいろできることが分かったので別記事で書きました\n\u003cul data-sourcepos=\"68:5-69:55\"\u003e\n\u003cli data-sourcepos=\"68:5-69:55\"\u003e\u003ca href=\"https://qiita.com/ozaki25/items/434101dd561c4242e30d\" id=\"reference-e3ef95056146e99cd5a3\"\u003eChromeDevToolsでコントラスト比をチェックする\u003cbr\u003e\n\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n","body":"- この記事は[MDCアドベントカレンダー](https://qiita.com/advent-calendar/2020/mdc)9日目の記事です\n- 埋まってない日に書いてるのでもう4回目の登場ですがこれ以降はほぼ埋まってますね！\n\n## 概要\n\n- Webアクセシビリティと聞いて皆さんはどのようなことを思い浮かべるでしょうか\n- あまり詳しくない人でも、スクリーンリーダーで読めるように画像にalt属性をつけるなど聞いたことがあると思います\n- 今回は文字のコントラストを確保するべきという項目をテーマにとりあげます\n\n## テキストのコントラスト確保\n\n- テキストのコントラストに関しては[WCAG 2.1](https://www.w3.org/TR/WCAG21/)では以下のように定義されています(抜粋)\n    - [コントラスト(最低限) レベルAA](https://waic.jp/docs/WCAG21/#contrast-minimum)\n        - テキスト及び文字画像の視覚的提示に、少なくとも `4.5:1` のコントラスト比がある。\n        - サイズの大きなテキスト及びサイズの大きな文字画像に、少なくとも `3:1` のコントラスト比がある。\n    - [コントラスト(高度) レベルAAA](https://waic.jp/docs/WCAG21/#contrast-enhanced)\n        - テキスト及び文字画像の視覚的提示に、少なくとも `7:1` のコントラスト比がある。\n        - サイズの大きなテキスト及びサイズの大きな文字画像に、少なくとも `4.5:1` のコントラスト比がある。\n- これらはテキスト色と背景色のコントラスト比が一定以上であることを求めています\n    - テキスト色と背景色が同じ色であれば `1:1` でコントラスト比が大きいほど数値の差が広がります\n- 以下は[コントラスト比チェックツール](https://webaim.org/resources/contrastchecker/)のキャプチャです\n\n![スクリーンショット 2020-12-09 0.07.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/684724e4-03c9-6704-c8b6-a967df0dc464.png)\n![スクリーンショット 2020-12-09 0.07.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/f3a673b2-ea7e-53dd-a6d5-9a65acf5438d.png)\n\n- いろいろ試してみると分かると思いますが、健常者であると思っている人であってもコントラスト比が大きい方が読みやすいと感じるのではないでしょうか\n    - より読みやすいページを作るためにもコントラスト比を気にしてみるといいかもしれませんね\n\n\n## Webページのコントラストチェック\n\n- 上で紹介したような2つの色を指定してコントラスト比をチェックするサイトはいくつも出てきますが、すでにあるWebページに対してコントラスト比に問題がある部分をチェックするツールがないかなと探してみました\n- Chrome Devtoolsで使える[Lighthouse](https://developers.google.com/web/tools/lighthouse?hl=ja)でもチェックすることができますが、[WCAG Color contrast checker](https://chrome.google.com/webstore/detail/wcag-color-contrast-check/plnahcmalebffmaghcpcmpaciebdhgdf)というChrome拡張が便利だったので紹介します\n    - Lighthouseは画面のどこが指摘されているかは分かるんですが、それぞれどんな色を使っていて何対何なのかが表示されずちょっと不便でした\n\n![スクリーンショット 2020-12-09 0.50.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/3d3149b0-3b34-4459-758b-a5c44553de29.png)\n\n\n## WCAG Color contrast checker\n\n- 使い方は簡単です\n- Chrome拡張を入れてチェックしたいページでアイコンをクリックすると画面左部に検証結果が表示されます\n\n![スクリーンショット 2020-12-09 0.22.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/bf256206-60f3-7b1a-1df3-226769f19dc0.png)\n\n- 画面上のテキストのコントラスト比が一覧で表示されます\n    - 目のマークに斜線が入った行は非表示となっている情報なので無視しても大丈夫でしょう\n\n![スクリーンショット 2020-12-09 0.27.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/483cb224-c04a-f1d6-1889-20c3e56f7c41.png)\n\n- Chrome拡張で表示された行を選択すると対応する部分に赤枠が表示され、どの部分が指摘されているのか確認することができます\n    - 白背景に緑の字でこれが `2.24:1`なんですね・・・！\n    - 画面左下の部分で色を変更してコントラスト比をチェックすることもできます\n\n## 最後に\n\n- 今回調べる中でいろいろなサイトでツールを使ってみましたが指摘が入るサイトがほとんどでした\n- ガイドラインに従うことも大切ですが、それぞれの事情に応じて判断している感じなんでしょうね\n- ただ、何も知らずに作るのと知った上で判断するのでは大違いだと思いますので、まずは自分の作ったWebページにツールを適用してどのような結果になるか見てみてはいかがでしょうか\n\n## 追記\n\n- 記事を書いた後に[axe - Web Accessibility Testing](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd)というChrome拡張を知りました。こちらの方が使い勝手がいいかも。\n\n## 追記2\n\n- よくよく調べるとChrome DevToolsでもいろいろできることが分かったので別記事で書きました\n    - [ChromeDevToolsでコントラスト比をチェックする\n](https://qiita.com/ozaki25/items/434101dd561c4242e30d)\n","coediting":false,"comments_count":0,"created_at":"2020-12-09T00:40:12+09:00","group":null,"id":"da08c332f487ef421760","likes_count":7,"private":false,"reactions_count":0,"stocks_count":6,"tags":[{"name":"アクセシビリティ","versions":[]},{"name":"a11y","versions":[]}],"title":"Webページのコントラスト比をチェックしてみよう","updated_at":"2021-10-26T18:50:49+09:00","url":"https://qiita.com/ozaki25/items/da08c332f487ef421760","user":{"description":"","facebook_id":"","followees_count":0,"followers_count":79,"github_login_name":"ozaki25","id":"ozaki25","items_count":112,"linkedin_id":"","location":"","name":"","organization":"","permanent_id":175213,"profile_image_url":"https://qiita-image-store.s3.amazonaws.com/0/175213/profile-images/1491825881","team_only":false,"twitter_screen_name":"ozaki25rn","website_url":""},"page_views_count":null,"team_membership":null,"organization_url_name":null,"slide":false,"posting_campaign_uuid":null},{"rendered_body":"\u003cul data-sourcepos=\"1:1-2:0\"\u003e\n\u003cli data-sourcepos=\"1:1-2:0\"\u003eこの記事は\u003ca href=\"https://qiita.com/advent-calendar/2020/pwa\"\u003ePWAアドベントカレンダー\u003c/a\u003eの5日目の記事です\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"3:1-3:45\"\u003e\n\u003cspan id=\"webアプリにおけるプッシュ通知\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#web%E3%82%A2%E3%83%97%E3%83%AA%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B%E3%83%97%E3%83%83%E3%82%B7%E3%83%A5%E9%80%9A%E7%9F%A5\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eWebアプリにおけるプッシュ通知\u003c/h2\u003e\n\u003cul data-sourcepos=\"5:1-7:0\"\u003e\n\u003cli data-sourcepos=\"5:1-5:165\"\u003ePWAを代表する機能としてプッシュ通知があげられることがよくあると思います（iPhone/Safariだとまだ使えないんですがね）\u003c/li\u003e\n\u003cli data-sourcepos=\"6:1-7:0\"\u003eWebアプリにおけるプッシュ通知は\u003ca href=\"https://developer.mozilla.org/ja/docs/Web/API/Push_API\" rel=\"nofollow noopener\" target=\"_blank\"\u003ePushAPI\u003c/a\u003eと\u003ca href=\"https://developer.mozilla.org/ja/docs/Web/API/Notifications_API\" rel=\"nofollow noopener\" target=\"_blank\"\u003eNotificationAPI\u003c/a\u003eというの2つの仕組みからなっています\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp data-sourcepos=\"8:1-9:142\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F477e9833-6d19-1be2-49c0-715a7ce6462a.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=fb960081a7f06dd08f40d9e885a8f709\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F477e9833-6d19-1be2-49c0-715a7ce6462a.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=fb960081a7f06dd08f40d9e885a8f709\" alt=\"スクリーンショット 2020-12-05 2.36.19.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F477e9833-6d19-1be2-49c0-715a7ce6462a.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=2eaff2b5f9035f146646780285a16b2d 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/477e9833-6d19-1be2-49c0-715a7ce6462a.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003cbr\u003e\n\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fa5987bf3-ca5c-5006-011a-72f67f411e55.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=3b373c790b6b41d65d289913f6b9225e\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fa5987bf3-ca5c-5006-011a-72f67f411e55.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=3b373c790b6b41d65d289913f6b9225e\" alt=\"Screenshot_20201205-023648.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fa5987bf3-ca5c-5006-011a-72f67f411e55.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=736ad402df87b2acd6d8ddfe030afc94 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/a5987bf3-ca5c-5006-011a-72f67f411e55.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003ch3 data-sourcepos=\"11:1-11:11\"\u003e\n\u003cspan id=\"pushapi\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#pushapi\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003ePushAPI\u003c/h3\u003e\n\u003cul data-sourcepos=\"13:1-14:0\"\u003e\n\u003cli data-sourcepos=\"13:1-14:0\"\u003ePushAPIはサーバからの通知を受け取ることができるAPIです\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 data-sourcepos=\"15:1-15:19\"\u003e\n\u003cspan id=\"notificationapi\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#notificationapi\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eNotificationAPI\u003c/h3\u003e\n\u003cul data-sourcepos=\"17:1-18:0\"\u003e\n\u003cli data-sourcepos=\"17:1-18:0\"\u003eNotificationAPIはユーザに対して通知を送ることができるAPIです\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 data-sourcepos=\"19:1-19:19\"\u003e\n\u003cspan id=\"一連の流れ\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E4%B8%80%E9%80%A3%E3%81%AE%E6%B5%81%E3%82%8C\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e一連の流れ\u003c/h3\u003e\n\u003cul data-sourcepos=\"21:1-22:0\"\u003e\n\u003cli data-sourcepos=\"21:1-22:0\"\u003eWebアプリにおけるプッシュ通知送信までの一連の流れは、PushAPIがサーバからの通知を受け取り、NotificationAPIを呼び出してユーザに通知を送ります\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp data-sourcepos=\"23:1-23:162\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fad5f30ae-bd9e-1253-8f59-126af53a14f9.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=797283c6d3ed2fb1e754af6bdcd83278\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fad5f30ae-bd9e-1253-8f59-126af53a14f9.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=797283c6d3ed2fb1e754af6bdcd83278\" alt=\"スクリーンショット 2020-12-05 2.12.54.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fad5f30ae-bd9e-1253-8f59-126af53a14f9.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=916b386a5508b57210b382b1c3151ca2 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/ad5f30ae-bd9e-1253-8f59-126af53a14f9.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cul data-sourcepos=\"25:1-26:0\"\u003e\n\u003cli data-sourcepos=\"25:1-26:0\"\u003eそれぞれの使い方を整理しようと思いましたが思いの外大変だったので今回はNotificationAPIを紹介します\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"27:1-27:30\"\u003e\n\u003cspan id=\"notificationapiの使い方\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#notificationapi%E3%81%AE%E4%BD%BF%E3%81%84%E6%96%B9\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eNotificationAPIの使い方\u003c/h2\u003e\n\u003cul data-sourcepos=\"29:1-32:0\"\u003e\n\u003cli data-sourcepos=\"29:1-29:59\"\u003eNotificationAPIはユーザに通知を送るAPIでした\u003c/li\u003e\n\u003cli data-sourcepos=\"30:1-32:0\"\u003e通知を送るためには事前に許可を得る必要があります\n\u003cul data-sourcepos=\"31:5-32:0\"\u003e\n\u003cli data-sourcepos=\"31:5-32:0\"\u003e見た瞬間に拒否を押してしまうあれですね・・・（許可を得るタイミングはよく考えて実装しましょう！）\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp data-sourcepos=\"33:1-33:162\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F867ef5ab-33e6-2a07-cb62-50b82b942487.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=d09f9380f2b95e4189e159774868ca79\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F867ef5ab-33e6-2a07-cb62-50b82b942487.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=d09f9380f2b95e4189e159774868ca79\" alt=\"スクリーンショット 2020-12-05 2.16.03.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F867ef5ab-33e6-2a07-cb62-50b82b942487.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=b660a5f9637136c3827372b003caa1b8 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/867ef5ab-33e6-2a07-cb62-50b82b942487.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cul data-sourcepos=\"35:1-36:0\"\u003e\n\u003cli data-sourcepos=\"35:1-36:0\"\u003eというわけで\u003cstrong\u003e許可をとるフェーズ\u003c/strong\u003eと\u003cstrong\u003e通知を送るフェーズ\u003c/strong\u003eの2つに分けて説明していきます\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 data-sourcepos=\"37:1-37:31\"\u003e\n\u003cspan id=\"許可を取るフェーズ\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E8%A8%B1%E5%8F%AF%E3%82%92%E5%8F%96%E3%82%8B%E3%83%95%E3%82%A7%E3%83%BC%E3%82%BA\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e許可を取るフェーズ\u003c/h3\u003e\n\u003cul data-sourcepos=\"39:1-43:0\"\u003e\n\u003cli data-sourcepos=\"39:1-39:138\"\u003e通知の許可を得るためには\u003ccode\u003eNotification.requestPermission()\u003c/code\u003eを実行することでユーザに尋ねることができます\u003c/li\u003e\n\u003cli data-sourcepos=\"40:1-41:90\"\u003e\n\u003ccode\u003erequestPermission()\u003c/code\u003eはPromiseを返し、ユーザが 許可 or ブロック を選択するとresolveされて結果を取得できます\n\u003cul data-sourcepos=\"41:5-41:90\"\u003e\n\u003cli data-sourcepos=\"41:5-41:90\"\u003e許可なら\u003ccode\u003egranted\u003c/code\u003e、ブロックなら\u003ccode\u003edenided\u003c/code\u003eという値を取得できます\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"42:1-43:0\"\u003e以下のコードで試すことができます\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"html\" data-sourcepos=\"44:1-53:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"nt\"\u003e\u0026lt;button\u003c/span\u003e \u003cspan class=\"na\"\u003eid=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"confirm\"\u003c/span\u003e\u003cspan class=\"nt\"\u003e\u0026gt;\u003c/span\u003e通知の許可を取得する\u003cspan class=\"nt\"\u003e\u0026lt;/button\u0026gt;\u003c/span\u003e\n\u003cspan class=\"nt\"\u003e\u0026lt;script\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"nb\"\u003edocument\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003egetElementById\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003econfirm\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e).\u003c/span\u003e\u003cspan class=\"nf\"\u003eaddEventListener\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eclick\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"nx\"\u003eNotification\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003erequestPermission\u003c/span\u003e\u003cspan class=\"p\"\u003e().\u003c/span\u003e\u003cspan class=\"nf\"\u003ethen\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003epermission\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n      \u003cspan class=\"nf\"\u003ealert\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003epermission\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e \u003cspan class=\"c1\"\u003e// granted or denied\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e});\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e});\u003c/span\u003e\n\u003cspan class=\"nt\"\u003e\u0026lt;/script\u0026gt;\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cul data-sourcepos=\"55:1-57:0\"\u003e\n\u003cli data-sourcepos=\"55:1-55:111\"\u003e現在の許可状態を確認することができる\u003ccode\u003eNotification.permission\u003c/code\u003eというものもあります\u003c/li\u003e\n\u003cli data-sourcepos=\"56:1-57:0\"\u003e許可状態は\u003ccode\u003edefault\u003c/code\u003e（未回答）、\u003ccode\u003egranted\u003c/code\u003e(許可済み)、\u003ccode\u003edenied\u003c/code\u003e(ブロック)の3つのいずれかが返ります\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"js\" data-sourcepos=\"58:1-61:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003epermission\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003eNotification\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003epermission\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003epermission\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e \u003cspan class=\"c1\"\u003e// default, granted or denied\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003ch3 data-sourcepos=\"63:1-63:31\"\u003e\n\u003cspan id=\"通知を送るフェーズ\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E9%80%9A%E7%9F%A5%E3%82%92%E9%80%81%E3%82%8B%E3%83%95%E3%82%A7%E3%83%BC%E3%82%BA\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e通知を送るフェーズ\u003c/h3\u003e\n\u003cul data-sourcepos=\"65:1-69:0\"\u003e\n\u003cli data-sourcepos=\"65:1-65:65\"\u003e許可を得ると通知を送信できるようになります\u003c/li\u003e\n\u003cli data-sourcepos=\"66:1-66:398\"\u003e以前は\u003ccode\u003enew Notification('通知の内容')\u003c/code\u003eでも送信できましたが手元のAndroidではエラーとなり、\u003ca href=\"https://stackoverflow.com/questions/29774836/failed-to-construct-notification-illegal-constructor#answer-29915743\" rel=\"nofollow noopener\" target=\"_blank\"\u003eちょっとググってみたところこのやり方は非推奨で\u003c/a\u003e以下で紹介するServiveWorkerから送るやり方が推奨されるとのことです\u003c/li\u003e\n\u003cli data-sourcepos=\"67:1-67:142\"\u003eメッセージの送信はServiveWorker上で\u003ccode\u003eself.registration.showNotification('通知の内容')\u003c/code\u003eといった感じで送信できます\u003c/li\u003e\n\u003cli data-sourcepos=\"68:1-69:0\"\u003e以下のコードは動作確認しやすいようにブラウザからServiceWorkerに\u003ccode\u003epostMessage\u003c/code\u003eでメッセージを送り、ServiceWorkerはそれを受け取ってプッシュ通知を送っています\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"html\" data-sourcepos=\"70:1-92:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003eindex.html\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"nt\"\u003e\u0026lt;button\u003c/span\u003e \u003cspan class=\"na\"\u003eid=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"send\"\u003c/span\u003e\u003cspan class=\"nt\"\u003e\u0026gt;\u003c/span\u003e通知を送信する\u003cspan class=\"nt\"\u003e\u0026lt;/button\u0026gt;\u003c/span\u003e\n\u003cspan class=\"nt\"\u003e\u0026lt;script\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"c1\"\u003e// ServiceWorkerの登録\u003c/span\u003e\n  \u003cspan class=\"nb\"\u003ewindow\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eaddEventListener\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eload\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"k\"\u003eif \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eserviceWorker\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e \u003cspan class=\"nb\"\u003enavigator\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n      \u003cspan class=\"nb\"\u003enavigator\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eserviceWorker\u003c/span\u003e\n        \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eregister\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003esw.js\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n        \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003ethen\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eregistration\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eregistered\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eregistration\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n        \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"k\"\u003ecatch\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eerror\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eerror\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eerror\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e});\u003c/span\u003e\n\n  \u003cspan class=\"c1\"\u003e// 通知の送信\u003c/span\u003e\n  \u003cspan class=\"nb\"\u003edocument\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003egetElementById\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003esend\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e).\u003c/span\u003e\u003cspan class=\"nf\"\u003eaddEventListener\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eclick\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"k\"\u003eif \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eNotification\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003epermission\u003c/span\u003e \u003cspan class=\"o\"\u003e===\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003egranted\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n      \u003cspan class=\"nb\"\u003enavigator\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eserviceWorker\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eready\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003ethen\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eregistration\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n        \u003cspan class=\"nx\"\u003eregistration\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eactive\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003epostMessage\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003ehello!!!\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n      \u003cspan class=\"p\"\u003e});\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e});\u003c/span\u003e\n\u003cspan class=\"nt\"\u003e\u0026lt;/script\u0026gt;\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"js\" data-sourcepos=\"94:1-98:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003esw.js\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"nb\"\u003eself\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eaddEventListener\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003emessage\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nf\"\u003efunction \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eevent\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n  \u003cspan class=\"nb\"\u003eself\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eregistration\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eshowNotification\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eevent\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003cspan class=\"p\"\u003e});\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003ch2 data-sourcepos=\"100:1-100:15\"\u003e\n\u003cspan id=\"サンプル\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eサンプル\u003c/h2\u003e\n\u003cul data-sourcepos=\"102:1-106:0\"\u003e\n\u003cli data-sourcepos=\"102:1-103:49\"\u003e上で紹介した記事をベースに作成したサンプルはこちらです\n\u003cul data-sourcepos=\"103:5-103:49\"\u003e\n\u003cli data-sourcepos=\"103:5-103:49\"\u003e\u003ca href=\"http://push-notification-sample.vercel.app/\" class=\"autolink\" rel=\"nofollow noopener\" target=\"_blank\"\u003ehttp://push-notification-sample.vercel.app/\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"104:1-106:0\"\u003eソースコードはこちらです\n\u003cul data-sourcepos=\"105:5-106:0\"\u003e\n\u003cli data-sourcepos=\"105:5-106:0\"\u003e\u003ca href=\"https://github.com/ozaki25/push-notification-sample\" class=\"autolink\" rel=\"nofollow noopener\" target=\"_blank\"\u003ehttps://github.com/ozaki25/push-notification-sample\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"107:1-107:12\"\u003e\n\u003cspan id=\"まとめ\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%81%BE%E3%81%A8%E3%82%81\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eまとめ\u003c/h2\u003e\n\u003cul data-sourcepos=\"109:1-110:72\"\u003e\n\u003cli data-sourcepos=\"109:1-109:104\"\u003e調べながらだと時間かかりましたが自前でも十分実装できるレベル感ですね\u003c/li\u003e\n\u003cli data-sourcepos=\"110:1-110:72\"\u003ePushAPIの部分も時間があればまた調べたいと思います\u003c/li\u003e\n\u003c/ul\u003e\n","body":"- この記事は[PWAアドベントカレンダー](https://qiita.com/advent-calendar/2020/pwa)の5日目の記事です\n\n## Webアプリにおけるプッシュ通知\n\n- PWAを代表する機能としてプッシュ通知があげられることがよくあると思います（iPhone/Safariだとまだ使えないんですがね）\n- Webアプリにおけるプッシュ通知は[PushAPI](https://developer.mozilla.org/ja/docs/Web/API/Push_API)と[NotificationAPI](https://developer.mozilla.org/ja/docs/Web/API/Notifications_API)というの2つの仕組みからなっています\n\n![スクリーンショット 2020-12-05 2.36.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/477e9833-6d19-1be2-49c0-715a7ce6462a.png)\n![Screenshot_20201205-023648.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/a5987bf3-ca5c-5006-011a-72f67f411e55.png)\n\n### PushAPI\n\n- PushAPIはサーバからの通知を受け取ることができるAPIです\n\n### NotificationAPI\n\n- NotificationAPIはユーザに対して通知を送ることができるAPIです\n\n### 一連の流れ\n\n- Webアプリにおけるプッシュ通知送信までの一連の流れは、PushAPIがサーバからの通知を受け取り、NotificationAPIを呼び出してユーザに通知を送ります\n\n![スクリーンショット 2020-12-05 2.12.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/ad5f30ae-bd9e-1253-8f59-126af53a14f9.png)\n\n- それぞれの使い方を整理しようと思いましたが思いの外大変だったので今回はNotificationAPIを紹介します\n\n## NotificationAPIの使い方\n\n- NotificationAPIはユーザに通知を送るAPIでした\n- 通知を送るためには事前に許可を得る必要があります\n    - 見た瞬間に拒否を押してしまうあれですね・・・（許可を得るタイミングはよく考えて実装しましょう！）\n\n![スクリーンショット 2020-12-05 2.16.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/867ef5ab-33e6-2a07-cb62-50b82b942487.png)\n\n- というわけで**許可をとるフェーズ**と**通知を送るフェーズ**の2つに分けて説明していきます\n\n### 許可を取るフェーズ\n\n- 通知の許可を得るためには`Notification.requestPermission()`を実行することでユーザに尋ねることができます\n- `requestPermission()`はPromiseを返し、ユーザが 許可 or ブロック を選択するとresolveされて結果を取得できます\n    - 許可なら`granted`、ブロックなら`denided`という値を取得できます\n- 以下のコードで試すことができます\n\n```html\n\u003cbutton id=\"confirm\"\u003e通知の許可を取得する\u003c/button\u003e\n\u003cscript\u003e\n  document.getElementById('confirm').addEventListener('click', () =\u003e {\n    Notification.requestPermission().then(permission =\u003e {\n      alert(permission); // granted or denied\n    });\n  });\n\u003c/script\u003e\n```\n\n- 現在の許可状態を確認することができる`Notification.permission`というものもあります\n- 許可状態は`default`（未回答）、`granted`(許可済み)、`denied`(ブロック)の3つのいずれかが返ります\n\n```js\nconst permission = Notification.permission;\nconsole.log(permission); // default, granted or denied\n```\n\n### 通知を送るフェーズ\n\n- 許可を得ると通知を送信できるようになります\n- 以前は`new Notification('通知の内容')`でも送信できましたが手元のAndroidではエラーとなり、[ちょっとググってみたところこのやり方は非推奨で](https://stackoverflow.com/questions/29774836/failed-to-construct-notification-illegal-constructor#answer-29915743)以下で紹介するServiveWorkerから送るやり方が推奨されるとのことです\n- メッセージの送信はServiveWorker上で`self.registration.showNotification('通知の内容')`といった感じで送信できます\n- 以下のコードは動作確認しやすいようにブラウザからServiceWorkerに`postMessage`でメッセージを送り、ServiceWorkerはそれを受け取ってプッシュ通知を送っています\n\n```html:index.html\n\u003cbutton id=\"send\"\u003e通知を送信する\u003c/button\u003e\n\u003cscript\u003e\n  // ServiceWorkerの登録\n  window.addEventListener('load', () =\u003e {\n    if ('serviceWorker' in navigator) {\n      navigator.serviceWorker\n        .register('sw.js')\n        .then(registration =\u003e console.log('registered', registration))\n        .catch(error =\u003e console.log('error', error));\n    }\n  });\n\n  // 通知の送信\n  document.getElementById('send').addEventListener('click', () =\u003e {\n    if (Notification.permission === 'granted') {\n      navigator.serviceWorker.ready.then(registration =\u003e {\n        registration.active.postMessage('hello!!!');\n      });\n    }\n  });\n\u003c/script\u003e\n```\n\n```js:sw.js\nself.addEventListener('message', function (event) {\n  self.registration.showNotification(event.data);\n});\n```\n\n## サンプル\n\n- 上で紹介した記事をベースに作成したサンプルはこちらです\n    - http://push-notification-sample.vercel.app/\n- ソースコードはこちらです\n    - https://github.com/ozaki25/push-notification-sample\n\n## まとめ\n\n- 調べながらだと時間かかりましたが自前でも十分実装できるレベル感ですね\n- PushAPIの部分も時間があればまた調べたいと思います\n","coediting":false,"comments_count":0,"created_at":"2020-12-05T02:42:42+09:00","group":null,"id":"b35e5c907c756e704d23","likes_count":37,"private":false,"reactions_count":0,"stocks_count":32,"tags":[{"name":"PWA","versions":[]}],"title":"[PWA]プッシュ通知を実現するPushAPIとNotificationAPI","updated_at":"2021-10-26T18:50:41+09:00","url":"https://qiita.com/ozaki25/items/b35e5c907c756e704d23","user":{"description":"","facebook_id":"","followees_count":0,"followers_count":79,"github_login_name":"ozaki25","id":"ozaki25","items_count":112,"linkedin_id":"","location":"","name":"","organization":"","permanent_id":175213,"profile_image_url":"https://qiita-image-store.s3.amazonaws.com/0/175213/profile-images/1491825881","team_only":false,"twitter_screen_name":"ozaki25rn","website_url":""},"page_views_count":null,"team_membership":null,"organization_url_name":null,"slide":false,"posting_campaign_uuid":null},{"rendered_body":"\u003cul data-sourcepos=\"1:1-2:0\"\u003e\n\u003cli data-sourcepos=\"1:1-2:0\"\u003eこの記事は\u003ca href=\"https://qiita.com/advent-calendar/2020/mdc\"\u003eMDCアドベントカレンダー\u003c/a\u003e4日目の記事です\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"3:1-3:9\"\u003e\n\u003cspan id=\"概要\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E6%A6%82%E8%A6%81\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e概要\u003c/h2\u003e\n\u003cul data-sourcepos=\"5:1-6:0\"\u003e\n\u003cli data-sourcepos=\"5:1-6:0\"\u003eCSSライブラリは\u003ca href=\"https://getbootstrap.com/\" rel=\"nofollow noopener\" target=\"_blank\"\u003eBootstrap\u003c/a\u003eなどが有名ですが、それらはライブラリが定義してくれたCSSに沿って以下のサンプルのように自分でHTMLタグにClass名をつけて使うというものです\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"html\" data-sourcepos=\"7:1-9:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"nt\"\u003e\u0026lt;button\u003c/span\u003e \u003cspan class=\"na\"\u003eclass=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"btn btn-primary\"\u003c/span\u003e\u003cspan class=\"nt\"\u003e\u0026gt;\u003c/span\u003eボタン\u003cspan class=\"nt\"\u003e\u0026lt;/button\u0026gt;\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cul data-sourcepos=\"11:1-13:0\"\u003e\n\u003cli data-sourcepos=\"11:1-11:272\"\u003eちゃんとしたアプリケーションを作る際はこれで特に問題はないですが、ちょっとしたサンプルアプリだったりレイアウトにこだわらない簡単なWebページを作るときはちょっと面倒くさくなりますよね\u003c/li\u003e\n\u003cli data-sourcepos=\"12:1-13:0\"\u003e今回紹介するClasslessCSSはそのようなケースで簡単に綺麗なUIを実現できるライブラリです\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"14:1-14:15\"\u003e\n\u003cspan id=\"classlesscss\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#classlesscss\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eClasslessCSS\u003c/h2\u003e\n\u003cul data-sourcepos=\"16:1-19:0\"\u003e\n\u003cli data-sourcepos=\"16:1-17:200\"\u003eClasslessCSSはライブラリが提供するCSSの\u003cstrong\u003eセレクタが全てタグ名で定義\u003c/strong\u003eされています\n\u003cul data-sourcepos=\"17:5-17:200\"\u003e\n\u003cli data-sourcepos=\"17:5-17:200\"\u003eなので単にHTMLを書くだけでライブラリが適用されるため、セマンティックなHTMLを書いていくだけで綺麗なレイアウトを実現することができます\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"18:1-19:0\"\u003e当然タグ名で定義されてるので気に入らないレイアウトを適用したくないといったケースでは面倒なことになりますが、それを気にするようなケースはClasslessCSSではなく通常のCSSライブラリを使うと良いでしょう\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"20:1-20:27\"\u003e\n\u003cspan id=\"ライブラリの紹介\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA%E3%81%AE%E7%B4%B9%E4%BB%8B\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eライブラリの紹介\u003c/h2\u003e\n\u003cul data-sourcepos=\"22:1-25:0\"\u003e\n\u003cli data-sourcepos=\"22:1-25:0\"\u003eClasslessCSSライブラリは\u003ca href=\"https://github.com/dbohdan/classless-css\" rel=\"nofollow noopener\" target=\"_blank\"\u003edbohdan/classless-css: A list of classless CSS themes/frameworks with screenshots\u003c/a\u003eというリポジトリでまとめられています\n\u003cul data-sourcepos=\"23:5-25:0\"\u003e\n\u003cli data-sourcepos=\"23:5-23:163\"\u003eClasslessCSSライブラリが24種類に加えて、少量のClass属性の定義で使えるClass-lightなライブラリ7種類が紹介されています\u003c/li\u003e\n\u003cli data-sourcepos=\"24:5-25:0\"\u003eREADMEの中で各ライブラリのレイアウトのキャプチャも掲載されているので好みのライブラリを探すのも簡単ですね\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp data-sourcepos=\"26:1-26:163\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F40d61910-b145-5b5a-b384-0c497ae691e9.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=31e77d9013b9f15b86c318451e962aa9\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F40d61910-b145-5b5a-b384-0c497ae691e9.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=31e77d9013b9f15b86c318451e962aa9\" alt=\"スクリーンショット 2020-12-03 18.42.37.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F40d61910-b145-5b5a-b384-0c497ae691e9.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=36bb765972d75ec7c3b3cf822c02df5a 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/40d61910-b145-5b5a-b384-0c497ae691e9.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cul data-sourcepos=\"28:1-29:0\"\u003e\n\u003cli data-sourcepos=\"28:1-29:0\"\u003eその中で私が使ったことのあるライブラリを2つ紹介します\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 data-sourcepos=\"30:1-30:11\"\u003e\n\u003cspan id=\"newcss\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#newcss\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003enew.css\u003c/h3\u003e\n\u003cul data-sourcepos=\"32:1-36:0\"\u003e\n\u003cli data-sourcepos=\"32:1-32:75\"\u003e1つめは\u003ca href=\"https://newcss.net/\" rel=\"nofollow noopener\" target=\"_blank\"\u003enew.css\u003c/a\u003eとういうライブラリです\u003c/li\u003e\n\u003cli data-sourcepos=\"33:1-33:56\"\u003e癖がなくてシンプルな感じがいいですね\u003c/li\u003e\n\u003cli data-sourcepos=\"34:1-34:152\"\u003eそして特徴的なのがデバイスのテーマ設定に応じて自動でライトモードとダークモードが切り替わるところです\u003c/li\u003e\n\u003cli data-sourcepos=\"35:1-36:0\"\u003e以下の2行を\u003ccode\u003ehead\u003c/code\u003eタグに入れるだけで使えます\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"html\" data-sourcepos=\"37:1-40:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"nt\"\u003e\u0026lt;link\u003c/span\u003e \u003cspan class=\"na\"\u003erel=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"stylesheet\"\u003c/span\u003e \u003cspan class=\"na\"\u003ehref=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"https://fonts.xz.style/serve/inter.css\"\u003c/span\u003e\u003cspan class=\"nt\"\u003e\u0026gt;\u003c/span\u003e\n\u003cspan class=\"nt\"\u003e\u0026lt;link\u003c/span\u003e \u003cspan class=\"na\"\u003erel=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"stylesheet\"\u003c/span\u003e \u003cspan class=\"na\"\u003ehref=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"https://cdn.jsdelivr.net/npm/@exampledev/new.css@1.1.2/new.min.css\"\u003c/span\u003e\u003cspan class=\"nt\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cp data-sourcepos=\"42:1-42:326\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F3848ae70-a569-c7cc-bb97-75f0e67a134b.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=f7b5b14f297067b95d271b1d5dc32e81\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F3848ae70-a569-c7cc-bb97-75f0e67a134b.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=f7b5b14f297067b95d271b1d5dc32e81\" alt=\"スクリーンショット 2020-12-03 18.44.54.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F3848ae70-a569-c7cc-bb97-75f0e67a134b.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=4c4c432256df119b3f9faf89a86e45ac 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/3848ae70-a569-c7cc-bb97-75f0e67a134b.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F231173e2-7e04-aff0-d516-6e64d6d048c7.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=90fb0300990811551dbcb66cbe98db88\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F231173e2-7e04-aff0-d516-6e64d6d048c7.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=90fb0300990811551dbcb66cbe98db88\" alt=\"スクリーンショット 2020-12-03 18.45.34.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F231173e2-7e04-aff0-d516-6e64d6d048c7.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=22de75636676410e1ce503d21b87b9cc 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/231173e2-7e04-aff0-d516-6e64d6d048c7.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003ch3 data-sourcepos=\"46:1-46:14\"\u003e\n\u003cspan id=\"sakuracss\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#sakuracss\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003esakura.css\u003c/h3\u003e\n\u003cul data-sourcepos=\"48:1-52:0\"\u003e\n\u003cli data-sourcepos=\"48:1-48:65\"\u003e2つめは\u003ca href=\"https://oxal.org/projects/sakura/\" rel=\"nofollow noopener\" target=\"_blank\"\u003esakura.css\u003c/a\u003eです\u003c/li\u003e\n\u003cli data-sourcepos=\"49:1-49:138\"\u003eカラーバリエーションが4種類用意されていますが、どれも柔らかい感じの色合いで気に入っています\u003c/li\u003e\n\u003cli data-sourcepos=\"50:1-52:0\"\u003e以下の1行を\u003ccode\u003ehead\u003c/code\u003eタグに入れるだけで使えます\n\u003cul data-sourcepos=\"51:5-52:0\"\u003e\n\u003cli data-sourcepos=\"51:5-52:0\"\u003e他のテーマを使いたい場合は\u003ccode\u003esakura.css\u003c/code\u003eの部分を\u003ccode\u003esakura-earthly.css\u003c/code\u003e、\u003ccode\u003esakura-vader.css\u003c/code\u003e、\u003ccode\u003esakura-dark.css\u003c/code\u003eといった感じで変更すればOKです\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"html\" data-sourcepos=\"53:1-55:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"nt\"\u003e\u0026lt;link\u003c/span\u003e \u003cspan class=\"na\"\u003erel=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"stylesheet\"\u003c/span\u003e \u003cspan class=\"na\"\u003ehref=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"https://unpkg.com/sakura.css/css/sakura.css\"\u003c/span\u003e \u003cspan class=\"na\"\u003etype=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"text/css\"\u003c/span\u003e\u003cspan class=\"nt\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cp data-sourcepos=\"58:1-58:652\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F521e1622-e8e5-630b-f1a9-809511e8e577.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=0d33784660b11b945f4e0f6e0339ea7c\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F521e1622-e8e5-630b-f1a9-809511e8e577.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=0d33784660b11b945f4e0f6e0339ea7c\" alt=\"スクリーンショット 2020-12-03 18.55.43.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F521e1622-e8e5-630b-f1a9-809511e8e577.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=0dcda756554073cf400ae95ce40c91cf 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/521e1622-e8e5-630b-f1a9-809511e8e577.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fa2e0f638-6c60-d228-99f3-66dd1530505e.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=3f48ce8d01794ad9f6302016e6877615\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fa2e0f638-6c60-d228-99f3-66dd1530505e.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=3f48ce8d01794ad9f6302016e6877615\" alt=\"スクリーンショット 2020-12-03 18.56.15.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fa2e0f638-6c60-d228-99f3-66dd1530505e.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=0836ccc03d87d3c24e59fbcd9f189c71 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/a2e0f638-6c60-d228-99f3-66dd1530505e.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F13bb1175-2ef1-7ca1-5958-7d456c109e5e.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=8e68e3ff2f23cc824ef9978330630928\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F13bb1175-2ef1-7ca1-5958-7d456c109e5e.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=8e68e3ff2f23cc824ef9978330630928\" alt=\"スクリーンショット 2020-12-03 18.56.44.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F13bb1175-2ef1-7ca1-5958-7d456c109e5e.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=48797922483930c80947483a89680507 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/13bb1175-2ef1-7ca1-5958-7d456c109e5e.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F3157d8cf-b9f2-5e9e-c5e2-d40e9f750a94.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=8ae744ddaebde8ab2f49b987d83b6117\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F3157d8cf-b9f2-5e9e-c5e2-d40e9f750a94.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=8ae744ddaebde8ab2f49b987d83b6117\" alt=\"スクリーンショット 2020-12-03 18.57.03.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F3157d8cf-b9f2-5e9e-c5e2-d40e9f750a94.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=5a5c7042e4a587d2c2e63ae72b7a926b 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/3157d8cf-b9f2-5e9e-c5e2-d40e9f750a94.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003ch2 data-sourcepos=\"60:1-60:12\"\u003e\n\u003cspan id=\"まとめ\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%81%BE%E3%81%A8%E3%82%81\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eまとめ\u003c/h2\u003e\n\u003cul data-sourcepos=\"62:1-64:68\"\u003e\n\u003cli data-sourcepos=\"62:1-62:86\"\u003eClasslessCSSは読み込むだけで使えるのでとてもお手軽で便利です\u003c/li\u003e\n\u003cli data-sourcepos=\"63:1-63:128\"\u003e自分的にはちょっとしたデモアプリの見た目を整えたい時などの必須アイテムになっています\u003c/li\u003e\n\u003cli data-sourcepos=\"64:1-64:68\"\u003eみなさんも機会があれば是非使ってみてください\u003c/li\u003e\n\u003c/ul\u003e\n","body":"- この記事は[MDCアドベントカレンダー](https://qiita.com/advent-calendar/2020/mdc)4日目の記事です\n\n## 概要\n\n- CSSライブラリは[Bootstrap](https://getbootstrap.com/)などが有名ですが、それらはライブラリが定義してくれたCSSに沿って以下のサンプルのように自分でHTMLタグにClass名をつけて使うというものです\n\n```html\n\u003cbutton class=\"btn btn-primary\"\u003eボタン\u003c/button\u003e\n```\n\n- ちゃんとしたアプリケーションを作る際はこれで特に問題はないですが、ちょっとしたサンプルアプリだったりレイアウトにこだわらない簡単なWebページを作るときはちょっと面倒くさくなりますよね\n- 今回紹介するClasslessCSSはそのようなケースで簡単に綺麗なUIを実現できるライブラリです\n\n## ClasslessCSS\n\n- ClasslessCSSはライブラリが提供するCSSの**セレクタが全てタグ名で定義**されています\n    - なので単にHTMLを書くだけでライブラリが適用されるため、セマンティックなHTMLを書いていくだけで綺麗なレイアウトを実現することができます\n- 当然タグ名で定義されてるので気に入らないレイアウトを適用したくないといったケースでは面倒なことになりますが、それを気にするようなケースはClasslessCSSではなく通常のCSSライブラリを使うと良いでしょう\n\n## ライブラリの紹介\n\n- ClasslessCSSライブラリは[dbohdan/classless-css: A list of classless CSS themes/frameworks with screenshots](https://github.com/dbohdan/classless-css)というリポジトリでまとめられています\n    - ClasslessCSSライブラリが24種類に加えて、少量のClass属性の定義で使えるClass-lightなライブラリ7種類が紹介されています\n    - READMEの中で各ライブラリのレイアウトのキャプチャも掲載されているので好みのライブラリを探すのも簡単ですね\n\n![スクリーンショット 2020-12-03 18.42.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/40d61910-b145-5b5a-b384-0c497ae691e9.png)\n\n- その中で私が使ったことのあるライブラリを2つ紹介します\n\n### new.css\n\n- 1つめは[new.css](https://newcss.net/)とういうライブラリです\n- 癖がなくてシンプルな感じがいいですね\n- そして特徴的なのがデバイスのテーマ設定に応じて自動でライトモードとダークモードが切り替わるところです\n- 以下の2行を`head`タグに入れるだけで使えます\n\n```html\n\u003clink rel=\"stylesheet\" href=\"https://fonts.xz.style/serve/inter.css\"\u003e\n\u003clink rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/@exampledev/new.css@1.1.2/new.min.css\"\u003e\n```\n\n![スクリーンショット 2020-12-03 18.44.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/3848ae70-a569-c7cc-bb97-75f0e67a134b.png)![スクリーンショット 2020-12-03 18.45.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/231173e2-7e04-aff0-d516-6e64d6d048c7.png)\n\n\n\n### sakura.css\n\n- 2つめは[sakura.css](https://oxal.org/projects/sakura/)です\n- カラーバリエーションが4種類用意されていますが、どれも柔らかい感じの色合いで気に入っています\n- 以下の1行を`head`タグに入れるだけで使えます\n    - 他のテーマを使いたい場合は`sakura.css`の部分を`sakura-earthly.css`、`sakura-vader.css`、`sakura-dark.css`といった感じで変更すればOKです\n\n```html\n\u003clink rel=\"stylesheet\" href=\"https://unpkg.com/sakura.css/css/sakura.css\" type=\"text/css\"\u003e\n```\n\n\n![スクリーンショット 2020-12-03 18.55.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/521e1622-e8e5-630b-f1a9-809511e8e577.png)![スクリーンショット 2020-12-03 18.56.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/a2e0f638-6c60-d228-99f3-66dd1530505e.png)![スクリーンショット 2020-12-03 18.56.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/13bb1175-2ef1-7ca1-5958-7d456c109e5e.png)![スクリーンショット 2020-12-03 18.57.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/3157d8cf-b9f2-5e9e-c5e2-d40e9f750a94.png)\n\n## まとめ\n\n- ClasslessCSSは読み込むだけで使えるのでとてもお手軽で便利です\n- 自分的にはちょっとしたデモアプリの見た目を整えたい時などの必須アイテムになっています\n- みなさんも機会があれば是非使ってみてください\n","coediting":false,"comments_count":0,"created_at":"2020-12-03T19:00:33+09:00","group":null,"id":"72c094cefe5603c3f923","likes_count":5,"private":false,"reactions_count":0,"stocks_count":1,"tags":[{"name":"CSS","versions":[]}],"title":"ClasslessCSSで手軽に綺麗なページを作ろう","updated_at":"2021-10-26T18:50:34+09:00","url":"https://qiita.com/ozaki25/items/72c094cefe5603c3f923","user":{"description":"","facebook_id":"","followees_count":0,"followers_count":79,"github_login_name":"ozaki25","id":"ozaki25","items_count":112,"linkedin_id":"","location":"","name":"","organization":"","permanent_id":175213,"profile_image_url":"https://qiita-image-store.s3.amazonaws.com/0/175213/profile-images/1491825881","team_only":false,"twitter_screen_name":"ozaki25rn","website_url":""},"page_views_count":null,"team_membership":null,"organization_url_name":null,"slide":false,"posting_campaign_uuid":null},{"rendered_body":"\u003cul data-sourcepos=\"1:1-3:0\"\u003e\n\u003cli data-sourcepos=\"1:1-1:118\"\u003eこの記事は\u003ca href=\"https://qiita.com/advent-calendar/2020/mdc\"\u003eMDCアドベントカレンダー\u003c/a\u003e3日目の記事です\u003c/li\u003e\n\u003cli data-sourcepos=\"2:1-3:0\"\u003eちなみに1日目は\u003ca href=\"https://qiita.com/ozaki25/items/7994e2cf03b61592bb1d\" id=\"reference-0e735c0b62ef1bd55953\"\u003e[PWA/A2HS]Webアプリをインストールしてホーム画面へ追加できるようにする\u003c/a\u003eを書きました\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"4:1-4:9\"\u003e\n\u003cspan id=\"概要\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E6%A6%82%E8%A6%81\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e概要\u003c/h2\u003e\n\u003cul data-sourcepos=\"6:1-11:0\"\u003e\n\u003cli data-sourcepos=\"6:1-7:96\"\u003eJavaScriptは言語仕様であるEcmaScriptのバージョンによって仕様が定義されています\n\u003cul data-sourcepos=\"7:5-7:96\"\u003e\n\u003cli data-sourcepos=\"7:5-7:96\"\u003e参考：\u003ca href=\"https://azu.github.io/slide/2018/node/ecmascript39.html\" rel=\"nofollow noopener\" target=\"_blank\"\u003eECMAScriptの使い方\u003c/a\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"8:1-9:123\"\u003eEcmaScriptのバージョン6(ES6/ES2015)で大きなアップデートが入って以降は様々な記法が追加され、ES5の時代しか知らない人からすると奇妙な書き方も増えてきました\n\u003cul data-sourcepos=\"9:5-9:123\"\u003e\n\u003cli data-sourcepos=\"9:5-9:123\"\u003eES6からは毎年VUPされることとなりES2015のように年をバージョンとして扱うようになった\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"10:1-11:0\"\u003e今回はJavaScriptのコードを異なるEcmaScriptのバージョンにトランスパイルできるBabelを使って奇妙な記法をES5に変換し、従来の書き方でいうとどう書くか見ることで動作を理解したいと思います\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"12:1-12:18\"\u003e\n\u003cspan id=\"アロー関数\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%82%A2%E3%83%AD%E3%83%BC%E9%96%A2%E6%95%B0\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eアロー関数\u003c/h2\u003e\n\u003cul data-sourcepos=\"14:1-15:0\"\u003e\n\u003cli data-sourcepos=\"14:1-15:0\"\u003eアロー関数は比較的わかりやすい構文ですが、初見では \u003ccode\u003e=\u0026gt;\u003c/code\u003e があったり一行だと省略できるものが多くて戸惑うこともあるかもしれません\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 data-sourcepos=\"16:1-16:30\"\u003e\n\u003cspan id=\"babel実行前後の比較\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#babel%E5%AE%9F%E8%A1%8C%E5%89%8D%E5%BE%8C%E3%81%AE%E6%AF%94%E8%BC%83\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eBabel実行前後の比較\u003c/h3\u003e\n\u003cul data-sourcepos=\"18:1-20:0\"\u003e\n\u003cli data-sourcepos=\"18:1-18:125\"\u003e大雑把にいうと \u003ccode\u003e=\u0026gt;\u003c/code\u003e と \u003ccode\u003efunction\u003c/code\u003e が置き換わっただけですね（厳密にはそれだけではないが）\u003c/li\u003e\n\u003cli data-sourcepos=\"19:1-20:0\"\u003e\u003ca href=\"https://babeljs.io/repl#?browsers=defaults\u0026amp;build=\u0026amp;builtIns=false\u0026amp;spec=false\u0026amp;loose=false\u0026amp;code_lz=MYewdgzgLgBAhgEwTAvDAFHANDARgSlQD54YBqPAbgChRJYAnAUwgFcAbWNRBdARhwAmfDToQQ7JgDp2IAObpmbToQD0qmAGZqQA\u0026amp;debug=false\u0026amp;forceAllTransforms=true\u0026amp;shippedProposals=false\u0026amp;circleciRepo=\u0026amp;evaluate=true\u0026amp;fileSize=false\u0026amp;timeTravel=false\u0026amp;sourceType=module\u0026amp;lineWrap=true\u0026amp;presets=env%2Ces2015%2Ces2016%2Ces2017%2Ces2015-loose\u0026amp;prettier=true\u0026amp;targets=\u0026amp;version=7.12.9\u0026amp;externalPlugins=\" rel=\"nofollow noopener\" target=\"_blank\"\u003eBabelの実行結果\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"js\" data-sourcepos=\"21:1-25:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003eES2015\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003eadd\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ea\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eb\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"nx\"\u003ea\u003c/span\u003e \u003cspan class=\"o\"\u003e+\u003c/span\u003e \u003cspan class=\"nx\"\u003eb\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003eresult\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nf\"\u003eadd\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eresult\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"c1\"\u003e// 3\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"js\" data-sourcepos=\"27:1-34:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003eES5\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003eadd\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kd\"\u003efunction\u003c/span\u003e \u003cspan class=\"nf\"\u003eadd\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ea\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eb\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n  \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"nx\"\u003ea\u003c/span\u003e \u003cspan class=\"o\"\u003e+\u003c/span\u003e \u003cspan class=\"nx\"\u003eb\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\n\u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003eresult\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nf\"\u003eadd\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eresult\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e \u003cspan class=\"c1\"\u003e// 3\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003ch2 data-sourcepos=\"36:1-36:30\"\u003e\n\u003cspan id=\"プロパティ名の省略\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%83%97%E3%83%AD%E3%83%91%E3%83%86%E3%82%A3%E5%90%8D%E3%81%AE%E7%9C%81%E7%95%A5\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eプロパティ名の省略\u003c/h2\u003e\n\u003cul data-sourcepos=\"38:1-40:0\"\u003e\n\u003cli data-sourcepos=\"38:1-38:80\"\u003e省略記法なので初見だと何をしているのか一瞬迷いますね\u003c/li\u003e\n\u003cli data-sourcepos=\"39:1-40:0\"\u003e一度覚えてしまえば簡単だしコードがすっきり見えていいですね\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 data-sourcepos=\"41:1-41:30\"\u003e\n\u003cspan id=\"babel実行前後の比較-1\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#babel%E5%AE%9F%E8%A1%8C%E5%89%8D%E5%BE%8C%E3%81%AE%E6%AF%94%E8%BC%83-1\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eBabel実行前後の比較\u003c/h3\u003e\n\u003cul data-sourcepos=\"43:1-45:0\"\u003e\n\u003cli data-sourcepos=\"43:1-43:115\"\u003eオブジェクトのkey名とvalueに設定する変数名が同様の場合省略できるというものです\u003c/li\u003e\n\u003cli data-sourcepos=\"44:1-45:0\"\u003e\u003ca href=\"https://babeljs.io/repl#?browsers=defaults\u0026amp;build=\u0026amp;builtIns=false\u0026amp;spec=false\u0026amp;loose=false\u0026amp;code_lz=MYewdgzgLgBGCGBbApjAvDA5CAXvA1gJYBMArJgNwBQoks8A5qhgMwAM1N40MArhMgBO6GAG8qMOEmQAaCTEayqAX060IIADbIAdJpAMAFPyEBKCjAD0lsVJQAuLLgIlyMyYsfsYyqkA\u0026amp;debug=false\u0026amp;forceAllTransforms=true\u0026amp;shippedProposals=false\u0026amp;circleciRepo=\u0026amp;evaluate=true\u0026amp;fileSize=false\u0026amp;timeTravel=false\u0026amp;sourceType=module\u0026amp;lineWrap=true\u0026amp;presets=env%2Ces2015%2Ces2016%2Ces2017%2Ces2015-loose\u0026amp;prettier=true\u0026amp;targets=\u0026amp;version=7.12.9\u0026amp;externalPlugins=\" rel=\"nofollow noopener\" target=\"_blank\"\u003eBabelの実行結果\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"js\" data-sourcepos=\"46:1-56:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003eES20XX\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003ename\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eozaki25\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003eage\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e30\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\n\u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003euser\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n  \u003cspan class=\"nx\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n  \u003cspan class=\"nx\"\u003eage\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\n\u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003euser\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e \u003cspan class=\"c1\"\u003e// { name: 'ozaki25',  age: 30 }\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"js\" data-sourcepos=\"58:1-66:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003eES5\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003ename\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"dl\"\u003e\"\u003c/span\u003e\u003cspan class=\"s2\"\u003eozaki25\u003c/span\u003e\u003cspan class=\"dl\"\u003e\"\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003eage\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e30\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003euser\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n  \u003cspan class=\"na\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nx\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n  \u003cspan class=\"na\"\u003eage\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nx\"\u003eage\u003c/span\u003e\n\u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003euser\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e \u003cspan class=\"c1\"\u003e// { name: 'ozaki25',  age: 30 }\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003ch2 data-sourcepos=\"68:1-68:15\"\u003e\n\u003cspan id=\"分割代入\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E5%88%86%E5%89%B2%E4%BB%A3%E5%85%A5\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e分割代入\u003c/h2\u003e\n\u003cul data-sourcepos=\"70:1-71:0\"\u003e\n\u003cli data-sourcepos=\"70:1-71:0\"\u003e分割代入はよく見ればなんとなく想像つきそうですが、左辺の書き方が見慣れないかもしれません。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 data-sourcepos=\"72:1-72:30\"\u003e\n\u003cspan id=\"babel実行前後の比較-2\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#babel%E5%AE%9F%E8%A1%8C%E5%89%8D%E5%BE%8C%E3%81%AE%E6%AF%94%E8%BC%83-2\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eBabel実行前後の比較\u003c/h3\u003e\n\u003cul data-sourcepos=\"74:1-75:0\"\u003e\n\u003cli data-sourcepos=\"74:1-75:0\"\u003e\n\u003ccode\u003econst { name } = user;\u003c/code\u003e が \u003ccode\u003evar name = user.name;\u003c/code\u003eの、\u003ccode\u003econst [dog, cat] = animals;\u003c/code\u003e が \u003ccode\u003evar dog = animals[0], cat = animals[1];\u003c/code\u003e のショートカットになってる感じですね\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch4 data-sourcepos=\"76:1-76:11\"\u003e\n\u003cspan id=\"object\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#object\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eObject\u003c/h4\u003e\n\u003cul data-sourcepos=\"78:1-79:0\"\u003e\n\u003cli data-sourcepos=\"78:1-79:0\"\u003e\u003ca href=\"https://babeljs.io/repl#?browsers=defaults\u0026amp;build=\u0026amp;builtIns=false\u0026amp;spec=false\u0026amp;loose=false\u0026amp;code_lz=PTAEHkCMCsFMGMAuAoeB7AdgZ0aArlrAE6gC8oA3sqKBgIYC2sAXKAORoBedA1gJYAmAKxsANNVB0A5i1ABmAAziAvgG5kqTDkq1GsUMrL5CRdemxoANrAB0ltFIAU9JgEpVoEKC69BQoA\u0026amp;debug=false\u0026amp;forceAllTransforms=true\u0026amp;shippedProposals=false\u0026amp;circleciRepo=\u0026amp;evaluate=true\u0026amp;fileSize=false\u0026amp;timeTravel=false\u0026amp;sourceType=module\u0026amp;lineWrap=true\u0026amp;presets=env%2Ces2015%2Ces2016%2Ces2017%2Ces2015-loose\u0026amp;prettier=true\u0026amp;targets=\u0026amp;version=7.12.9\u0026amp;externalPlugins=\" rel=\"nofollow noopener\" target=\"_blank\"\u003eBabelの実行結果\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"js\" data-sourcepos=\"80:1-89:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003eES20XX\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"c1\"\u003e// Object\u003c/span\u003e\n\u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003euser\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n  \u003cspan class=\"na\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eozaki25\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n  \u003cspan class=\"na\"\u003eage\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e30\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\n\u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"nx\"\u003ename\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003euser\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e \u003cspan class=\"c1\"\u003e// ozaki25\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"js\" data-sourcepos=\"91:1-99:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003eES5\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"c1\"\u003e// Object\u003c/span\u003e\n\u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003euser\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n  \u003cspan class=\"na\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e\"\u003c/span\u003e\u003cspan class=\"s2\"\u003eozaki25\u003c/span\u003e\u003cspan class=\"dl\"\u003e\"\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n  \u003cspan class=\"na\"\u003eage\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e30\u003c/span\u003e\n\u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003ename\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003euser\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e \u003cspan class=\"c1\"\u003e// ozaki25\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003ch4 data-sourcepos=\"101:1-101:10\"\u003e\n\u003cspan id=\"array\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#array\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eArray\u003c/h4\u003e\n\u003cul data-sourcepos=\"103:1-104:0\"\u003e\n\u003cli data-sourcepos=\"103:1-104:0\"\u003e\u003ca href=\"https://babeljs.io/repl#?browsers=defaults\u0026amp;build=\u0026amp;builtIns=false\u0026amp;spec=false\u0026amp;loose=false\u0026amp;code_lz=PTAEEECdIQwTwFAGMD2A7AzgF1DNBLAWxgBsNQBeUAbQHJAap1oBpRbBqZ1oF1l1saATFAHMWSGFk6VcBYmQDcPTChIBTAHQlhACkFCAlHNAhQ9RRmXrNQrWKwGjYNkA\u0026amp;debug=false\u0026amp;forceAllTransforms=true\u0026amp;shippedProposals=false\u0026amp;circleciRepo=\u0026amp;evaluate=true\u0026amp;fileSize=false\u0026amp;timeTravel=false\u0026amp;sourceType=module\u0026amp;lineWrap=true\u0026amp;presets=env%2Ces2015%2Ces2016%2Ces2017%2Ces2015-loose\u0026amp;prettier=true\u0026amp;targets=\u0026amp;version=7.12.9\u0026amp;externalPlugins=\" rel=\"nofollow noopener\" target=\"_blank\"\u003eBabelの実行結果\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"js\" data-sourcepos=\"105:1-111:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003eES20XX\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"c1\"\u003e// Array\u003c/span\u003e\n\u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003eanimals\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003e犬\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003e猫\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003edog\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003ecat\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003eanimals\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003edog\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e \u003cspan class=\"c1\"\u003e// 犬\u003c/span\u003e\n\u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ecat\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e \u003cspan class=\"c1\"\u003e// 猫\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"js\" data-sourcepos=\"113:1-120:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003eES5\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"c1\"\u003e// Array\u003c/span\u003e\n\u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003eanimals\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"dl\"\u003e\"\u003c/span\u003e\u003cspan class=\"s2\"\u003e犬\u003c/span\u003e\u003cspan class=\"dl\"\u003e\"\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e\"\u003c/span\u003e\u003cspan class=\"s2\"\u003e猫\u003c/span\u003e\u003cspan class=\"dl\"\u003e\"\u003c/span\u003e\u003cspan class=\"p\"\u003e];\u003c/span\u003e\n\u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003edog\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003eanimals\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e\n  \u003cspan class=\"nx\"\u003ecat\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003eanimals\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e];\u003c/span\u003e\n\u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003edog\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e \u003cspan class=\"c1\"\u003e// 犬\u003c/span\u003e\n\u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ecat\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e \u003cspan class=\"c1\"\u003e// 猫\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003ch2 data-sourcepos=\"122:1-122:24\"\u003e\n\u003cspan id=\"スプレッド構文\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%82%B9%E3%83%97%E3%83%AC%E3%83%83%E3%83%89%E6%A7%8B%E6%96%87\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eスプレッド構文\u003c/h2\u003e\n\u003cul data-sourcepos=\"124:1-125:0\"\u003e\n\u003cli data-sourcepos=\"124:1-125:0\"\u003e初めて見る人は \u003ccode\u003e...\u003c/code\u003e でもう意味不明ですね\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 data-sourcepos=\"126:1-126:30\"\u003e\n\u003cspan id=\"babel実行前後の比較-3\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#babel%E5%AE%9F%E8%A1%8C%E5%89%8D%E5%BE%8C%E3%81%AE%E6%AF%94%E8%BC%83-3\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eBabel実行前後の比較\u003c/h3\u003e\n\u003cul data-sourcepos=\"128:1-132:1\"\u003e\n\u003cli data-sourcepos=\"128:1-129:75\"\u003eObjectの方は長くてドン引きですね。実行結果から想像つくと思いますがObjectをコピーして新しいObjectに埋め込んでいます。これを \u003ccode\u003e...\u003c/code\u003e で表現できちゃうので便利な構文ですね（Babelの結果を見て理解を深めるという趣旨が・・・）\n\u003cul data-sourcepos=\"129:5-129:75\"\u003e\n\u003cli data-sourcepos=\"129:5-129:75\"\u003e同じkey名の値が複数回登場したら後勝ちになります\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"130:1-132:1\"\u003eArrayの方は短くてわかりやすいですね。\u003ccode\u003e[...xx]\u003c/code\u003eは\u003ca href=\"https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/concat\" rel=\"nofollow noopener\" target=\"_blank\"\u003econcat\u003c/a\u003eにそのまま置き換えられます。\u003ccode\u003e...\u003c/code\u003e で配列のコピーを展開して新しい配列に埋め込んでいます\n\u003cul data-sourcepos=\"131:5-132:1\"\u003e\n\u003cli data-sourcepos=\"131:5-132:1\"\u003econcatでも記述量は大して変わりませんが視覚的に新しい配列をイメージできるのがいいですね\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch4 data-sourcepos=\"133:1-133:11\"\u003e\n\u003cspan id=\"object-1\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#object-1\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eObject\u003c/h4\u003e\n\u003cul data-sourcepos=\"135:1-136:0\"\u003e\n\u003cli data-sourcepos=\"135:1-136:0\"\u003e\u003ca href=\"https://babeljs.io/repl#?browsers=defaults\u0026amp;build=\u0026amp;builtIns=false\u0026amp;spec=false\u0026amp;loose=false\u0026amp;code_lz=PTAEHkCMCsFMGMAuAoeB7AdgZ0aArlrAE6gC8oA3qBgIYC2sAXKAORoBeNA1gJYBMAVhYAaUDQDmTUAGYADKAC-AblSYc1WAHcAqoRLkq0NJGYsA6rEiAKhkDPDIA6GQNMMgIoYRoAHQeCxRSvTY0ADawbgFo4gAUGFq6xACUSqAglKBGJqwW1vbOrrQMphzc_EKiElJyishAA\u0026amp;debug=false\u0026amp;forceAllTransforms=true\u0026amp;shippedProposals=false\u0026amp;circleciRepo=\u0026amp;evaluate=true\u0026amp;fileSize=false\u0026amp;timeTravel=false\u0026amp;sourceType=module\u0026amp;lineWrap=true\u0026amp;presets=env%2Ces2015%2Ces2016%2Ces2017%2Ces2015-loose\u0026amp;prettier=true\u0026amp;targets=\u0026amp;version=7.12.9\u0026amp;externalPlugins=\" rel=\"nofollow noopener\" target=\"_blank\"\u003eBabelの実行結果\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"js\" data-sourcepos=\"137:1-142:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003eES20XX\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"c1\"\u003e// Object\u003c/span\u003e\n\u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003euser\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"na\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eozaki25\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"na\"\u003eage\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e30\u003c/span\u003e \u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003enewUser\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"na\"\u003ejob\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eWebエンジニア\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e...\u003c/span\u003e\u003cspan class=\"nx\"\u003euser\u003c/span\u003e \u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003enewUser\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e \u003cspan class=\"c1\"\u003e// { job: 'Webエンジニア', name: 'ozaki25', age: 30 }\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"js\" data-sourcepos=\"144:1-206:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003eES5\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"kd\"\u003efunction\u003c/span\u003e \u003cspan class=\"nf\"\u003eownKeys\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eobject\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eenumerableOnly\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n  \u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003ekeys\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003eObject\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003ekeys\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eobject\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n  \u003cspan class=\"k\"\u003eif \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nb\"\u003eObject\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003egetOwnPropertySymbols\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003esymbols\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003eObject\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003egetOwnPropertySymbols\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eobject\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n    \u003cspan class=\"k\"\u003eif \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eenumerableOnly\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n      \u003cspan class=\"nx\"\u003esymbols\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003esymbols\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003efilter\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nf\"\u003efunction \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003esym\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"nb\"\u003eObject\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003egetOwnPropertyDescriptor\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eobject\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003esym\u003c/span\u003e\u003cspan class=\"p\"\u003e).\u003c/span\u003e\u003cspan class=\"nx\"\u003eenumerable\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n      \u003cspan class=\"p\"\u003e});\u003c/span\u003e\n    \u003cspan class=\"nx\"\u003ekeys\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003epush\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eapply\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ekeys\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003esymbols\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n  \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"nx\"\u003ekeys\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\n\u003cspan class=\"kd\"\u003efunction\u003c/span\u003e \u003cspan class=\"nf\"\u003e_objectSpread\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003etarget\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n  \u003cspan class=\"k\"\u003efor \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003ei\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"nx\"\u003ei\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e \u003cspan class=\"nx\"\u003earguments\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003elength\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e \u003cspan class=\"nx\"\u003ei\u003c/span\u003e\u003cspan class=\"o\"\u003e++\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003esource\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003earguments\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003ei\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e!=\u003c/span\u003e \u003cspan class=\"kc\"\u003enull\u003c/span\u003e \u003cspan class=\"p\"\u003e?\u003c/span\u003e \u003cspan class=\"nx\"\u003earguments\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003ei\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{};\u003c/span\u003e\n    \u003cspan class=\"k\"\u003eif \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ei\u003c/span\u003e \u003cspan class=\"o\"\u003e%\u003c/span\u003e \u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n      \u003cspan class=\"nf\"\u003eownKeys\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nc\"\u003eObject\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003esource\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e).\u003c/span\u003e\u003cspan class=\"nf\"\u003eforEach\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nf\"\u003efunction \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ekey\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n        \u003cspan class=\"nf\"\u003e_defineProperty\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003etarget\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003ekey\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003esource\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003ekey\u003c/span\u003e\u003cspan class=\"p\"\u003e]);\u003c/span\u003e\n      \u003cspan class=\"p\"\u003e});\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"k\"\u003eelse\u003c/span\u003e \u003cspan class=\"k\"\u003eif \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nb\"\u003eObject\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003egetOwnPropertyDescriptors\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n      \u003cspan class=\"nb\"\u003eObject\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003edefineProperties\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003etarget\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nb\"\u003eObject\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003egetOwnPropertyDescriptors\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003esource\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"k\"\u003eelse\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n      \u003cspan class=\"nf\"\u003eownKeys\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nc\"\u003eObject\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003esource\u003c/span\u003e\u003cspan class=\"p\"\u003e)).\u003c/span\u003e\u003cspan class=\"nf\"\u003eforEach\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nf\"\u003efunction \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ekey\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n        \u003cspan class=\"nb\"\u003eObject\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003edefineProperty\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\n          \u003cspan class=\"nx\"\u003etarget\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n          \u003cspan class=\"nx\"\u003ekey\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n          \u003cspan class=\"nb\"\u003eObject\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003egetOwnPropertyDescriptor\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003esource\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003ekey\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n        \u003cspan class=\"p\"\u003e);\u003c/span\u003e\n      \u003cspan class=\"p\"\u003e});\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n  \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"nx\"\u003etarget\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\n\u003cspan class=\"kd\"\u003efunction\u003c/span\u003e \u003cspan class=\"nf\"\u003e_defineProperty\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eobj\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003ekey\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003evalue\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n  \u003cspan class=\"k\"\u003eif \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ekey\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e \u003cspan class=\"nx\"\u003eobj\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"nb\"\u003eObject\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003edefineProperty\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eobj\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003ekey\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n      \u003cspan class=\"na\"\u003evalue\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nx\"\u003evalue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n      \u003cspan class=\"na\"\u003eenumerable\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n      \u003cspan class=\"na\"\u003econfigurable\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n      \u003cspan class=\"na\"\u003ewritable\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e});\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"k\"\u003eelse\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"nx\"\u003eobj\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nx\"\u003ekey\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003evalue\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n  \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"nx\"\u003eobj\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\n\u003cspan class=\"c1\"\u003e// Object\u003c/span\u003e\n\u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003euser\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n  \u003cspan class=\"na\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e\"\u003c/span\u003e\u003cspan class=\"s2\"\u003eozaki25\u003c/span\u003e\u003cspan class=\"dl\"\u003e\"\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n  \u003cspan class=\"na\"\u003eage\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e30\u003c/span\u003e\n\u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\n\u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003enewUser\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nf\"\u003e_objectSpread\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"na\"\u003ejob\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e\"\u003c/span\u003e\u003cspan class=\"s2\"\u003eWebエンジニア\u003c/span\u003e\u003cspan class=\"dl\"\u003e\"\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n  \u003cspan class=\"nx\"\u003euser\u003c/span\u003e\n\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003ch4 data-sourcepos=\"208:1-208:10\"\u003e\n\u003cspan id=\"array-1\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#array-1\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eArray\u003c/h4\u003e\n\u003cul data-sourcepos=\"210:1-211:0\"\u003e\n\u003cli data-sourcepos=\"210:1-211:0\"\u003e\u003ca href=\"\"\u003eBabelの実行結果\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"js\" data-sourcepos=\"212:1-217:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003eES20XX\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"c1\"\u003e// Array\u003c/span\u003e\n\u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003eanimals\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003e犬\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003e猫\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003enewAnimals\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eパンダ\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eライオン\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e...\u003c/span\u003e\u003cspan class=\"nx\"\u003eanimals\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003enewAnimals\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e \u003cspan class=\"c1\"\u003e// ['パンダ', 'ライオン', '犬', '猫']\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"js\" data-sourcepos=\"219:1-224:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003eES5\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"c1\"\u003e// Array\u003c/span\u003e\n\u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003eanimals\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"dl\"\u003e\"\u003c/span\u003e\u003cspan class=\"s2\"\u003e犬\u003c/span\u003e\u003cspan class=\"dl\"\u003e\"\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e\"\u003c/span\u003e\u003cspan class=\"s2\"\u003e猫\u003c/span\u003e\u003cspan class=\"dl\"\u003e\"\u003c/span\u003e\u003cspan class=\"p\"\u003e];\u003c/span\u003e\n\u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003enewAnimals\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"dl\"\u003e\"\u003c/span\u003e\u003cspan class=\"s2\"\u003eパンダ\u003c/span\u003e\u003cspan class=\"dl\"\u003e\"\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e\"\u003c/span\u003e\u003cspan class=\"s2\"\u003eライオン\u003c/span\u003e\u003cspan class=\"dl\"\u003e\"\u003c/span\u003e\u003cspan class=\"p\"\u003e].\u003c/span\u003e\u003cspan class=\"nf\"\u003econcat\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eanimals\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003enewAnimals\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e \u003cspan class=\"c1\"\u003e// ['パンダ', 'ライオン', '犬', '猫']\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003ch2 data-sourcepos=\"226:1-226:39\"\u003e\n\u003cspan id=\"オプショナルチェイニング\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%82%AA%E3%83%97%E3%82%B7%E3%83%A7%E3%83%8A%E3%83%AB%E3%83%81%E3%82%A7%E3%82%A4%E3%83%8B%E3%83%B3%E3%82%B0\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eオプショナルチェイニング\u003c/h2\u003e\n\u003cul data-sourcepos=\"228:1-229:0\"\u003e\n\u003cli data-sourcepos=\"228:1-229:0\"\u003e他の言語で知ってる人は違和感ないと思いますが、知らない人だとこの \u003ccode\u003e?\u003c/code\u003e は何？となりがちです\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 data-sourcepos=\"230:1-230:30\"\u003e\n\u003cspan id=\"babel実行前後の比較-4\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#babel%E5%AE%9F%E8%A1%8C%E5%89%8D%E5%BE%8C%E3%81%AE%E6%AF%94%E8%BC%83-4\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eBabel実行前後の比較\u003c/h3\u003e\n\u003cul data-sourcepos=\"232:1-236:0\"\u003e\n\u003cli data-sourcepos=\"232:1-234:151\"\u003e\n\u003ccode\u003euser1.team?.name\u003c/code\u003e の部分は \u003ccode\u003e(_user1$team = user1.team) === null || _user1$team === void 0 ? void 0 : _user1$team.name;\u003c/code\u003e で表現されています\n\u003cul data-sourcepos=\"233:5-234:151\"\u003e\n\u003cli data-sourcepos=\"233:5-233:113\"\u003eもし \u003ccode\u003euser.team.name\u003c/code\u003e と書いてteamが \u003ccode\u003eundefined\u003c/code\u003e だった場合エラーで落ちてしまいます\u003c/li\u003e\n\u003cli data-sourcepos=\"234:5-234:151\"\u003e?をつけておくと \u003ccode\u003eteam\u003c/code\u003e が \u003ccode\u003enull\u003c/code\u003e や \u003ccode\u003eundefined\u003c/code\u003e だった場合エラーにさせずその場で \u003ccode\u003eundefined\u003c/code\u003e を返却してくれます\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"235:1-236:0\"\u003e\u003ca href=\"https://babeljs.io/repl#?browsers=defaults\u0026amp;build=\u0026amp;builtIns=false\u0026amp;spec=false\u0026amp;loose=false\u0026amp;code_lz=MYewdgzgLgBArhApgJwIwwLwwN4CgYxgCGAtogFwwDkIAXkQNYCWATAKxUA0-MUiplPAQLEylKgCU4YMCghceAX26LcuUJFgIULTDh6iK1Oo1YA2BarUbovfiQBypROiza0AOj6kA_B8Pq4BAgADaIHiEgAOYAFN6OzqgAlADcMAD06TBSMnLWQbDxTmS6bkjILF72fgE2oeGRsUXOLKkZWdIAJogAZkyynbhAA\u0026amp;debug=false\u0026amp;forceAllTransforms=true\u0026amp;shippedProposals=false\u0026amp;circleciRepo=\u0026amp;evaluate=true\u0026amp;fileSize=false\u0026amp;timeTravel=false\u0026amp;sourceType=module\u0026amp;lineWrap=true\u0026amp;presets=env%2Ces2015%2Ces2016%2Ces2017%2Ces2015-loose\u0026amp;prettier=true\u0026amp;targets=\u0026amp;version=7.12.9\u0026amp;externalPlugins=\" rel=\"nofollow noopener\" target=\"_blank\"\u003eBabelの実行結果\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"js\" data-sourcepos=\"237:1-255:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003eES20XX\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003euser1\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n  \u003cspan class=\"na\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eozaki25\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n  \u003cspan class=\"na\"\u003eteam\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"na\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eRunners\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\n\u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003euser2\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n  \u003cspan class=\"na\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eozaki26\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\n\n\u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003eteamName1\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003euser1\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eteam\u003c/span\u003e\u003cspan class=\"p\"\u003e?.\u003c/span\u003e\u003cspan class=\"nx\"\u003ename\u003c/span\u003e\n\u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eteamName1\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e \u003cspan class=\"c1\"\u003e// Runners\u003c/span\u003e\n\n\u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003eteamName2\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003euser2\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eteam\u003c/span\u003e\u003cspan class=\"p\"\u003e?.\u003c/span\u003e\u003cspan class=\"nx\"\u003ename\u003c/span\u003e\n\u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eteamName2\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e \u003cspan class=\"c1\"\u003e// undefined\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"js\" data-sourcepos=\"257:1-280:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003eES5\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003e_user1$team\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003e_user2$team\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\n\u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003euser1\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n  \u003cspan class=\"na\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e\"\u003c/span\u003e\u003cspan class=\"s2\"\u003eozaki25\u003c/span\u003e\u003cspan class=\"dl\"\u003e\"\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n  \u003cspan class=\"na\"\u003eteam\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"na\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e\"\u003c/span\u003e\u003cspan class=\"s2\"\u003eRunners\u003c/span\u003e\u003cspan class=\"dl\"\u003e\"\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003euser2\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n  \u003cspan class=\"na\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e\"\u003c/span\u003e\u003cspan class=\"s2\"\u003eozaki26\u003c/span\u003e\u003cspan class=\"dl\"\u003e\"\u003c/span\u003e\n\u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003eteamName1\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003e_user1$team\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003euser1\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eteam\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e===\u003c/span\u003e \u003cspan class=\"kc\"\u003enull\u003c/span\u003e \u003cspan class=\"o\"\u003e||\u003c/span\u003e \u003cspan class=\"nx\"\u003e_user1$team\u003c/span\u003e \u003cspan class=\"o\"\u003e===\u003c/span\u003e \u003cspan class=\"k\"\u003evoid\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e?\u003c/span\u003e \u003cspan class=\"k\"\u003evoid\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nx\"\u003e_user1$team\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eteamName1\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e \u003cspan class=\"c1\"\u003e// Runners\u003c/span\u003e\n\n\u003cspan class=\"kd\"\u003evar\u003c/span\u003e \u003cspan class=\"nx\"\u003eteamName2\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003e_user2$team\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003euser2\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eteam\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e===\u003c/span\u003e \u003cspan class=\"kc\"\u003enull\u003c/span\u003e \u003cspan class=\"o\"\u003e||\u003c/span\u003e \u003cspan class=\"nx\"\u003e_user2$team\u003c/span\u003e \u003cspan class=\"o\"\u003e===\u003c/span\u003e \u003cspan class=\"k\"\u003evoid\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e?\u003c/span\u003e \u003cspan class=\"k\"\u003evoid\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nx\"\u003e_user2$team\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ename\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eteamName2\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e \u003cspan class=\"c1\"\u003e// undefined\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003ch2 data-sourcepos=\"282:1-282:12\"\u003e\n\u003cspan id=\"まとめ\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%81%BE%E3%81%A8%E3%82%81\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eまとめ\u003c/h2\u003e\n\u003cul data-sourcepos=\"284:1-285:116\"\u003e\n\u003cli data-sourcepos=\"284:1-284:122\"\u003eとりあえず思いついたものだけ書きましたが他にも初見殺しの記法はいろいろありますね\u003c/li\u003e\n\u003cli data-sourcepos=\"285:1-285:116\"\u003eどれも慣れてしまえば便利な構文なので当たり前に使えるようになっていきましょう\u003c/li\u003e\n\u003c/ul\u003e\n","body":"- この記事は[MDCアドベントカレンダー](https://qiita.com/advent-calendar/2020/mdc)3日目の記事です\n- ちなみに1日目は[[PWA/A2HS]Webアプリをインストールしてホーム画面へ追加できるようにする](https://qiita.com/ozaki25/items/7994e2cf03b61592bb1d)を書きました\n\n## 概要\n\n- JavaScriptは言語仕様であるEcmaScriptのバージョンによって仕様が定義されています\n    - 参考：[ECMAScriptの使い方](https://azu.github.io/slide/2018/node/ecmascript39.html)\n- EcmaScriptのバージョン6(ES6/ES2015)で大きなアップデートが入って以降は様々な記法が追加され、ES5の時代しか知らない人からすると奇妙な書き方も増えてきました\n    - ES6からは毎年VUPされることとなりES2015のように年をバージョンとして扱うようになった\n- 今回はJavaScriptのコードを異なるEcmaScriptのバージョンにトランスパイルできるBabelを使って奇妙な記法をES5に変換し、従来の書き方でいうとどう書くか見ることで動作を理解したいと思います\n\n## アロー関数\n\n- アロー関数は比較的わかりやすい構文ですが、初見では `=\u003e` があったり一行だと省略できるものが多くて戸惑うこともあるかもしれません\n\n### Babel実行前後の比較\n\n- 大雑把にいうと `=\u003e` と `function` が置き換わっただけですね（厳密にはそれだけではないが）\n- [Babelの実行結果](https://babeljs.io/repl#?browsers=defaults\u0026build=\u0026builtIns=false\u0026spec=false\u0026loose=false\u0026code_lz=MYewdgzgLgBAhgEwTAvDAFHANDARgSlQD54YBqPAbgChRJYAnAUwgFcAbWNRBdARhwAmfDToQQ7JgDp2IAObpmbToQD0qmAGZqQA\u0026debug=false\u0026forceAllTransforms=true\u0026shippedProposals=false\u0026circleciRepo=\u0026evaluate=true\u0026fileSize=false\u0026timeTravel=false\u0026sourceType=module\u0026lineWrap=true\u0026presets=env%2Ces2015%2Ces2016%2Ces2017%2Ces2015-loose\u0026prettier=true\u0026targets=\u0026version=7.12.9\u0026externalPlugins=)\n\n```js:ES2015\nconst add = (a, b) =\u003e a + b;\nconst result = add(1, 2);\nconsole.log(result) // 3\n```\n\n```js:ES5\nvar add = function add(a, b) {\n  return a + b;\n};\n\nvar result = add(1, 2);\nconsole.log(result); // 3\n```\n\n## プロパティ名の省略\n\n- 省略記法なので初見だと何をしているのか一瞬迷いますね\n- 一度覚えてしまえば簡単だしコードがすっきり見えていいですね\n\n### Babel実行前後の比較\n\n- オブジェクトのkey名とvalueに設定する変数名が同様の場合省略できるというものです\n- [Babelの実行結果](https://babeljs.io/repl#?browsers=defaults\u0026build=\u0026builtIns=false\u0026spec=false\u0026loose=false\u0026code_lz=MYewdgzgLgBGCGBbApjAvDA5CAXvA1gJYBMArJgNwBQoks8A5qhgMwAM1N40MArhMgBO6GAG8qMOEmQAaCTEayqAX060IIADbIAdJpAMAFPyEBKCjAD0lsVJQAuLLgIlyMyYsfsYyqkA\u0026debug=false\u0026forceAllTransforms=true\u0026shippedProposals=false\u0026circleciRepo=\u0026evaluate=true\u0026fileSize=false\u0026timeTravel=false\u0026sourceType=module\u0026lineWrap=true\u0026presets=env%2Ces2015%2Ces2016%2Ces2017%2Ces2015-loose\u0026prettier=true\u0026targets=\u0026version=7.12.9\u0026externalPlugins=)\n\n```js:ES20XX\nconst name = 'ozaki25';\nconst age = 30;\n\nconst user = {\n  name,\n  age,\n};\n\nconsole.log(user); // { name: 'ozaki25',  age: 30 }\n```\n\n```js:ES5\nvar name = \"ozaki25\";\nvar age = 30;\nvar user = {\n  name: name,\n  age: age\n};\nconsole.log(user); // { name: 'ozaki25',  age: 30 }\n```\n\n## 分割代入\n\n- 分割代入はよく見ればなんとなく想像つきそうですが、左辺の書き方が見慣れないかもしれません。\n\n### Babel実行前後の比較\n\n- `const { name } = user;` が `var name = user.name;`の、`const [dog, cat] = animals;` が `var dog = animals[0], cat = animals[1];` のショートカットになってる感じですね\n\n#### Object\n\n- [Babelの実行結果](https://babeljs.io/repl#?browsers=defaults\u0026build=\u0026builtIns=false\u0026spec=false\u0026loose=false\u0026code_lz=PTAEHkCMCsFMGMAuAoeB7AdgZ0aArlrAE6gC8oA3sqKBgIYC2sAXKAORoBedA1gJYAmAKxsANNVB0A5i1ABmAAziAvgG5kqTDkq1GsUMrL5CRdemxoANrAB0ltFIAU9JgEpVoEKC69BQoA\u0026debug=false\u0026forceAllTransforms=true\u0026shippedProposals=false\u0026circleciRepo=\u0026evaluate=true\u0026fileSize=false\u0026timeTravel=false\u0026sourceType=module\u0026lineWrap=true\u0026presets=env%2Ces2015%2Ces2016%2Ces2017%2Ces2015-loose\u0026prettier=true\u0026targets=\u0026version=7.12.9\u0026externalPlugins=)\n\n```js:ES20XX\n// Object\nconst user = {\n  name: 'ozaki25',\n  age: 30,\n};\n\nconst { name } = user;\nconsole.log(name); // ozaki25\n```\n\n```js:ES5\n// Object\nvar user = {\n  name: \"ozaki25\",\n  age: 30\n};\nvar name = user.name;\nconsole.log(name); // ozaki25\n```\n\n#### Array\n\n- [Babelの実行結果](https://babeljs.io/repl#?browsers=defaults\u0026build=\u0026builtIns=false\u0026spec=false\u0026loose=false\u0026code_lz=PTAEEECdIQwTwFAGMD2A7AzgF1DNBLAWxgBsNQBeUAbQHJAap1oBpRbBqZ1oF1l1saATFAHMWSGFk6VcBYmQDcPTChIBTAHQlhACkFCAlHNAhQ9RRmXrNQrWKwGjYNkA\u0026debug=false\u0026forceAllTransforms=true\u0026shippedProposals=false\u0026circleciRepo=\u0026evaluate=true\u0026fileSize=false\u0026timeTravel=false\u0026sourceType=module\u0026lineWrap=true\u0026presets=env%2Ces2015%2Ces2016%2Ces2017%2Ces2015-loose\u0026prettier=true\u0026targets=\u0026version=7.12.9\u0026externalPlugins=)\n\n```js:ES20XX\n// Array\nconst animals = ['犬', '猫']\nconst [dog, cat] = animals;\nconsole.log(dog); // 犬\nconsole.log(cat); // 猫\n```\n\n```js:ES5\n// Array\nvar animals = [\"犬\", \"猫\"];\nvar dog = animals[0],\n  cat = animals[1];\nconsole.log(dog); // 犬\nconsole.log(cat); // 猫\n```\n\n## スプレッド構文\n\n- 初めて見る人は `...` でもう意味不明ですね\n\n### Babel実行前後の比較\n\n- Objectの方は長くてドン引きですね。実行結果から想像つくと思いますがObjectをコピーして新しいObjectに埋め込んでいます。これを `...` で表現できちゃうので便利な構文ですね（Babelの結果を見て理解を深めるという趣旨が・・・）\n    - 同じkey名の値が複数回登場したら後勝ちになります\n- Arrayの方は短くてわかりやすいですね。`[...xx]`は[concat](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/concat)にそのまま置き換えられます。`...` で配列のコピーを展開して新しい配列に埋め込んでいます\n    - concatでも記述量は大して変わりませんが視覚的に新しい配列をイメージできるのがいいですね\n \n#### Object\n\n- [Babelの実行結果](https://babeljs.io/repl#?browsers=defaults\u0026build=\u0026builtIns=false\u0026spec=false\u0026loose=false\u0026code_lz=PTAEHkCMCsFMGMAuAoeB7AdgZ0aArlrAE6gC8oA3qBgIYC2sAXKAORoBeNA1gJYBMAVhYAaUDQDmTUAGYADKAC-AblSYc1WAHcAqoRLkq0NJGYsA6rEiAKhkDPDIA6GQNMMgIoYRoAHQeCxRSvTY0ADawbgFo4gAUGFq6xACUSqAglKBGJqwW1vbOrrQMphzc_EKiElJyishAA\u0026debug=false\u0026forceAllTransforms=true\u0026shippedProposals=false\u0026circleciRepo=\u0026evaluate=true\u0026fileSize=false\u0026timeTravel=false\u0026sourceType=module\u0026lineWrap=true\u0026presets=env%2Ces2015%2Ces2016%2Ces2017%2Ces2015-loose\u0026prettier=true\u0026targets=\u0026version=7.12.9\u0026externalPlugins=)\n\n```js:ES20XX\n// Object\nconst user = { name: 'ozaki25', age: 30 };\nconst newUser = { job: 'Webエンジニア', ...user };\nconsole.log(newUser); // { job: 'Webエンジニア', name: 'ozaki25', age: 30 }\n```\n\n```js:ES5\nfunction ownKeys(object, enumerableOnly) {\n  var keys = Object.keys(object);\n  if (Object.getOwnPropertySymbols) {\n    var symbols = Object.getOwnPropertySymbols(object);\n    if (enumerableOnly)\n      symbols = symbols.filter(function (sym) {\n        return Object.getOwnPropertyDescriptor(object, sym).enumerable;\n      });\n    keys.push.apply(keys, symbols);\n  }\n  return keys;\n}\n\nfunction _objectSpread(target) {\n  for (var i = 1; i \u003c arguments.length; i++) {\n    var source = arguments[i] != null ? arguments[i] : {};\n    if (i % 2) {\n      ownKeys(Object(source), true).forEach(function (key) {\n        _defineProperty(target, key, source[key]);\n      });\n    } else if (Object.getOwnPropertyDescriptors) {\n      Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));\n    } else {\n      ownKeys(Object(source)).forEach(function (key) {\n        Object.defineProperty(\n          target,\n          key,\n          Object.getOwnPropertyDescriptor(source, key)\n        );\n      });\n    }\n  }\n  return target;\n}\n\nfunction _defineProperty(obj, key, value) {\n  if (key in obj) {\n    Object.defineProperty(obj, key, {\n      value: value,\n      enumerable: true,\n      configurable: true,\n      writable: true\n    });\n  } else {\n    obj[key] = value;\n  }\n  return obj;\n}\n\n// Object\nvar user = {\n  name: \"ozaki25\",\n  age: 30\n};\n\nvar newUser = _objectSpread(\n  {\n    job: \"Webエンジニア\"\n  },\n  user\n);\n```\n\n#### Array\n\n- [Babelの実行結果]()\n\n```js:ES20XX\n// Array\nconst animals = ['犬', '猫']\nconst newAnimals = ['パンダ', 'ライオン', ...animals]\nconsole.log(newAnimals); // ['パンダ', 'ライオン', '犬', '猫']\n```\n\n```js:ES5\n// Array\nvar animals = [\"犬\", \"猫\"];\nvar newAnimals = [\"パンダ\", \"ライオン\"].concat(animals);\nconsole.log(newAnimals); // ['パンダ', 'ライオン', '犬', '猫']\n```\n\n## オプショナルチェイニング\n\n- 他の言語で知ってる人は違和感ないと思いますが、知らない人だとこの `?` は何？となりがちです\n\n### Babel実行前後の比較\n\n- `user1.team?.name` の部分は `(_user1$team = user1.team) === null || _user1$team === void 0 ? void 0 : _user1$team.name;` で表現されています\n    - もし `user.team.name` と書いてteamが `undefined` だった場合エラーで落ちてしまいます\n    - ?をつけておくと `team` が `null` や `undefined` だった場合エラーにさせずその場で `undefined` を返却してくれます\n- [Babelの実行結果](https://babeljs.io/repl#?browsers=defaults\u0026build=\u0026builtIns=false\u0026spec=false\u0026loose=false\u0026code_lz=MYewdgzgLgBArhApgJwIwwLwwN4CgYxgCGAtogFwwDkIAXkQNYCWATAKxUA0-MUiplPAQLEylKgCU4YMCghceAX26LcuUJFgIULTDh6iK1Oo1YA2BarUbovfiQBypROiza0AOj6kA_B8Pq4BAgADaIHiEgAOYAFN6OzqgAlADcMAD06TBSMnLWQbDxTmS6bkjILF72fgE2oeGRsUXOLKkZWdIAJogAZkyynbhAA\u0026debug=false\u0026forceAllTransforms=true\u0026shippedProposals=false\u0026circleciRepo=\u0026evaluate=true\u0026fileSize=false\u0026timeTravel=false\u0026sourceType=module\u0026lineWrap=true\u0026presets=env%2Ces2015%2Ces2016%2Ces2017%2Ces2015-loose\u0026prettier=true\u0026targets=\u0026version=7.12.9\u0026externalPlugins=)\n\n```js:ES20XX\nconst user1 = {\n  name: 'ozaki25',\n  team: {\n    name: 'Runners',\n  },\n}\n\nconst user2 = {\n  name: 'ozaki26',\n}\n\n\nconst teamName1 = user1.team?.name\nconsole.log(teamName1); // Runners\n\nconst teamName2 = user2.team?.name\nconsole.log(teamName2); // undefined\n```\n\n```js:ES5\nvar _user1$team, _user2$team;\n\nvar user1 = {\n  name: \"ozaki25\",\n  team: {\n    name: \"Runners\"\n  }\n};\nvar user2 = {\n  name: \"ozaki26\"\n};\nvar teamName1 =\n  (_user1$team = user1.team) === null || _user1$team === void 0\n    ? void 0\n    : _user1$team.name;\nconsole.log(teamName1); // Runners\n\nvar teamName2 =\n  (_user2$team = user2.team) === null || _user2$team === void 0\n    ? void 0\n    : _user2$team.name;\nconsole.log(teamName2); // undefined\n```\n\n## まとめ\n\n- とりあえず思いついたものだけ書きましたが他にも初見殺しの記法はいろいろありますね\n- どれも慣れてしまえば便利な構文なので当たり前に使えるようになっていきましょう\n","coediting":false,"comments_count":0,"created_at":"2020-12-02T20:31:48+09:00","group":null,"id":"c8f24b5da35253ab2600","likes_count":2,"private":false,"reactions_count":0,"stocks_count":0,"tags":[{"name":"JavaScript","versions":[]},{"name":"ECMAScript","versions":[]},{"name":"babel","versions":[]}],"title":"JavaScriptの不思議な記法をBabelを通して解釈してみる","updated_at":"2021-10-26T18:50:28+09:00","url":"https://qiita.com/ozaki25/items/c8f24b5da35253ab2600","user":{"description":"","facebook_id":"","followees_count":0,"followers_count":79,"github_login_name":"ozaki25","id":"ozaki25","items_count":112,"linkedin_id":"","location":"","name":"","organization":"","permanent_id":175213,"profile_image_url":"https://qiita-image-store.s3.amazonaws.com/0/175213/profile-images/1491825881","team_only":false,"twitter_screen_name":"ozaki25rn","website_url":""},"page_views_count":null,"team_membership":null,"organization_url_name":null,"slide":false,"posting_campaign_uuid":null},{"rendered_body":"\u003cul data-sourcepos=\"1:1-2:0\"\u003e\n\u003cli data-sourcepos=\"1:1-2:0\"\u003eこの記事は\u003ca href=\"https://qiita.com/advent-calendar/2020/mdc\"\u003eMDCアドベントカレンダー\u003c/a\u003e1日目の記事です\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"3:1-3:15\"\u003e\n\u003cspan id=\"はじめに\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eはじめに\u003c/h2\u003e\n\u003cul data-sourcepos=\"5:1-7:0\"\u003e\n\u003cli data-sourcepos=\"5:1-5:188\"\u003ePWAと言われたときにホームスクリーンへ追加できるWebアプリのことと思う方も多いと思います（PWAの定義についてはここではふれません）\u003c/li\u003e\n\u003cli data-sourcepos=\"6:1-7:0\"\u003e最近だとライブラリやフレームワークに乗っかることで簡単にホームスクリーンへの追加を実現できるケースが多いですが今回は自前で実装する方法を整理します\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"8:1-8:18\"\u003e\n\u003cspan id=\"機能の概要\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E6%A9%9F%E8%83%BD%E3%81%AE%E6%A6%82%E8%A6%81\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e機能の概要\u003c/h2\u003e\n\u003cul data-sourcepos=\"10:1-12:0\"\u003e\n\u003cli data-sourcepos=\"10:1-10:128\"\u003eWebアプリをスマホアプリのように端末にインストールしホーム画面にアイコンを設置できます\u003c/li\u003e\n\u003cli data-sourcepos=\"11:1-12:0\"\u003eホーム画面のアイコンからアプリを開くとスプラッシュ画面が表示され画面上部にURLバーがない状態で全画面で表示することができます\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp data-sourcepos=\"13:1-13:120\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Ff6e8fac4-93a6-1105-54e4-dfac80a5f1a2.gif?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=3868dd32b373b9099b669893e3125083\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Ff6e8fac4-93a6-1105-54e4-dfac80a5f1a2.gif?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=3868dd32b373b9099b669893e3125083\" alt=\"demo.gif\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Ff6e8fac4-93a6-1105-54e4-dfac80a5f1a2.gif?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=a4fbaabde0c33b5720d81fd377703500 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/f6e8fac4-93a6-1105-54e4-dfac80a5f1a2.gif\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003ch2 data-sourcepos=\"16:1-16:9\"\u003e\n\u003cspan id=\"手順\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E6%89%8B%E9%A0%86\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e手順\u003c/h2\u003e\n\u003cul data-sourcepos=\"18:1-22:0\"\u003e\n\u003cli data-sourcepos=\"18:1-22:0\"\u003eホームスクリーンへ追加の仕組みを入れるためには以下の3つの要件があります\n\u003cul data-sourcepos=\"19:5-22:0\"\u003e\n\u003cli data-sourcepos=\"19:5-19:52\"\u003eServiceWorkerが有効化されていること\u003c/li\u003e\n\u003cli data-sourcepos=\"20:5-20:98\"\u003emanifestファイルが読み込まれていてそこに設定が記載されていること\u003c/li\u003e\n\u003cli data-sourcepos=\"21:5-22:0\"\u003eHTTPSで配信されていること\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 data-sourcepos=\"23:1-23:50\"\u003e\n\u003cspan id=\"serviceworkerが有効化されていること\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#serviceworker%E3%81%8C%E6%9C%89%E5%8A%B9%E5%8C%96%E3%81%95%E3%82%8C%E3%81%A6%E3%81%84%E3%82%8B%E3%81%93%E3%81%A8\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eServiceWorkerが有効化されていること\u003c/h3\u003e\n\u003cul data-sourcepos=\"25:1-27:0\"\u003e\n\u003cli data-sourcepos=\"25:1-25:126\"\u003eホームスクリーンへ追加の機能を使うためにはServiceWorkerが有効化されている必要があります\u003c/li\u003e\n\u003cli data-sourcepos=\"26:1-27:0\"\u003eオフラインキャッシュなど他の機能を使わないのであれば細かい実装は不要で読み込むだけでOKです\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"js\" data-sourcepos=\"28:1-30:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003esw.js\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"nb\"\u003eself\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eaddEventListener\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003efetch\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nf\"\u003efunction \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eevent\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{});\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"html\" data-sourcepos=\"32:1-54:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003eindex.html\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"cp\"\u003e\u0026lt;!DOCTYPE html\u0026gt;\u003c/span\u003e\n\u003cspan class=\"nt\"\u003e\u0026lt;html\u003c/span\u003e \u003cspan class=\"na\"\u003elang=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"ja\"\u003c/span\u003e\u003cspan class=\"nt\"\u003e\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"nt\"\u003e\u0026lt;head\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e\u0026lt;meta\u003c/span\u003e \u003cspan class=\"na\"\u003echarset=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"utf-8\"\u003c/span\u003e \u003cspan class=\"nt\"\u003e/\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e\u0026lt;meta\u003c/span\u003e \u003cspan class=\"na\"\u003ename=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"viewport\"\u003c/span\u003e \u003cspan class=\"na\"\u003econtent=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"width=device-width, initial-scale=1\"\u003c/span\u003e \u003cspan class=\"nt\"\u003e/\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e\u0026lt;title\u0026gt;\u003c/span\u003eHello PWA\u003cspan class=\"nt\"\u003e\u0026lt;/title\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"nt\"\u003e\u0026lt;/head\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"nt\"\u003e\u0026lt;body\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e\u0026lt;h1\u0026gt;\u003c/span\u003eHello PWA\u003cspan class=\"nt\"\u003e\u0026lt;/h1\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e\u0026lt;script\u0026gt;\u003c/span\u003e\n      \u003cspan class=\"nb\"\u003ewindow\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eaddEventListener\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eload\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n        \u003cspan class=\"k\"\u003eif \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eserviceWorker\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e \u003cspan class=\"nb\"\u003enavigator\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n          \u003cspan class=\"nb\"\u003enavigator\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eserviceWorker\u003c/span\u003e\n            \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eregister\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003esw.js\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n            \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003ethen\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eregistration\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eregistered\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eregistration\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n            \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"k\"\u003ecatch\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eerror\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eerror\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eerror\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n      \u003cspan class=\"p\"\u003e});\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e\u0026lt;/script\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"nt\"\u003e\u0026lt;/body\u0026gt;\u003c/span\u003e\n\u003cspan class=\"nt\"\u003e\u0026lt;/html\u0026gt;\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003ch3 data-sourcepos=\"57:1-57:96\"\u003e\n\u003cspan id=\"manifestファイルが読み込まれていてそこに設定が記載されていること\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#manifest%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%8C%E8%AA%AD%E3%81%BF%E8%BE%BC%E3%81%BE%E3%82%8C%E3%81%A6%E3%81%84%E3%81%A6%E3%81%9D%E3%81%93%E3%81%AB%E8%A8%AD%E5%AE%9A%E3%81%8C%E8%A8%98%E8%BC%89%E3%81%95%E3%82%8C%E3%81%A6%E3%81%84%E3%82%8B%E3%81%93%E3%81%A8\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003emanifestファイルが読み込まれていてそこに設定が記載されていること\u003c/h3\u003e\n\u003cul data-sourcepos=\"59:1-60:0\"\u003e\n\u003cli data-sourcepos=\"59:1-60:0\"\u003emanifest.webmanifestというファイルを作成してそこにホームスクリーンへ追加の設定をしていきます\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"json\" data-sourcepos=\"61:1-82:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003emanifest.webmanifest\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n  \u003c/span\u003e\u003cspan class=\"nl\"\u003e\"name\"\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\"Hello PWA\"\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e\n  \u003c/span\u003e\u003cspan class=\"nl\"\u003e\"short_name\"\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\"Hello\"\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e\n  \u003c/span\u003e\u003cspan class=\"nl\"\u003e\"start_url\"\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\".\"\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e\n  \u003c/span\u003e\u003cspan class=\"nl\"\u003e\"display\"\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\"standalone\"\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e\n  \u003c/span\u003e\u003cspan class=\"nl\"\u003e\"background_color\"\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\"#4E526F\"\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e\n  \u003c/span\u003e\u003cspan class=\"nl\"\u003e\"theme_color\"\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\"#4E526F\"\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e\n  \u003c/span\u003e\u003cspan class=\"nl\"\u003e\"icons\"\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"w\"\u003e\n    \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n      \u003c/span\u003e\u003cspan class=\"nl\"\u003e\"src\"\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\"images/icons-192.png\"\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e\n      \u003c/span\u003e\u003cspan class=\"nl\"\u003e\"sizes\"\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\"192x192\"\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e\n      \u003c/span\u003e\u003cspan class=\"nl\"\u003e\"type\"\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\"image/png\"\u003c/span\u003e\u003cspan class=\"w\"\u003e\n    \u003c/span\u003e\u003cspan class=\"p\"\u003e},\u003c/span\u003e\u003cspan class=\"w\"\u003e\n    \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n      \u003c/span\u003e\u003cspan class=\"nl\"\u003e\"src\"\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\"images/icons-512.png\"\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e\n      \u003c/span\u003e\u003cspan class=\"nl\"\u003e\"sizes\"\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\"512x512\"\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e\n      \u003c/span\u003e\u003cspan class=\"nl\"\u003e\"type\"\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\"image/png\"\u003c/span\u003e\u003cspan class=\"w\"\u003e\n    \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n  \u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cul data-sourcepos=\"84:1-96:0\"\u003e\n\u003cli data-sourcepos=\"84:1-85:133\"\u003enameとshort_nameはアプリ名を記載します\n\u003cul data-sourcepos=\"85:5-85:133\"\u003e\n\u003cli data-sourcepos=\"85:5-85:133\"\u003eホーム画面のアイコンの下に表示される名前などスペースが狭い場面ではshort_nameが使われます\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"86:1-86:101\"\u003estart_urlはホーム画面から起動したときにどのページを開くかを指定します\u003c/li\u003e\n\u003cli data-sourcepos=\"87:1-88:65\"\u003edisplayは表示モードの指定でfullscreen、standalone、minimal-uiの3つから選びます\n\u003cul data-sourcepos=\"88:5-88:65\"\u003e\n\u003cli data-sourcepos=\"88:5-88:65\"\u003e\u003ca href=\"https://web.dev/add-manifest/#display\" rel=\"nofollow noopener\" target=\"_blank\"\u003e詳細はこちら\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"89:1-89:113\"\u003ebackground_colorはスプラッシュ画面の背景色をtheme_colorはツールバーの色を指定します\u003c/li\u003e\n\u003cli data-sourcepos=\"90:1-94:169\"\u003eiconsにはアプリのアイコンを設定します\n\u003cul data-sourcepos=\"91:5-94:169\"\u003e\n\u003cli data-sourcepos=\"91:5-91:103\"\u003eスマホ用とデスクトップ用で192x192と512x512の画像を設定しておけばOKです\u003c/li\u003e\n\u003cli data-sourcepos=\"92:5-92:134\"\u003e192x192のアイコンは円形にしておかないと新し目のAndroidではアイコンが小さくなってしまいます\u003c/li\u003e\n\u003cli data-sourcepos=\"93:5-93:30\"\u003e小さくなってる例\u003c/li\u003e\n\u003cli data-sourcepos=\"94:5-94:169\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F44789bc5-c043-1864-8ee8-fd0fa4ab29f2.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=73aea4aaf87f4e1256a44a926a2e1868\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F44789bc5-c043-1864-8ee8-fd0fa4ab29f2.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=73aea4aaf87f4e1256a44a926a2e1868\" alt=\"スクリーンショット 2020-11-30 19.31.14.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F44789bc5-c043-1864-8ee8-fd0fa4ab29f2.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=c03bcae0806b72fa98c3a2196f54fadf 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/44789bc5-c043-1864-8ee8-fd0fa4ab29f2.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"95:1-96:0\"\u003e作成したmanifestファイルをhtml側で読み込めばOKです\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"html\" data-sourcepos=\"97:1-121:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003eindex.html\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"cp\"\u003e\u0026lt;!DOCTYPE html\u0026gt;\u003c/span\u003e\n\u003cspan class=\"nt\"\u003e\u0026lt;html\u003c/span\u003e \u003cspan class=\"na\"\u003elang=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"ja\"\u003c/span\u003e\u003cspan class=\"nt\"\u003e\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"nt\"\u003e\u0026lt;head\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e\u0026lt;meta\u003c/span\u003e \u003cspan class=\"na\"\u003echarset=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"utf-8\"\u003c/span\u003e \u003cspan class=\"nt\"\u003e/\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e\u0026lt;meta\u003c/span\u003e \u003cspan class=\"na\"\u003ename=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"viewport\"\u003c/span\u003e \u003cspan class=\"na\"\u003econtent=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"width=device-width, initial-scale=1\"\u003c/span\u003e \u003cspan class=\"nt\"\u003e/\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e\u0026lt;title\u0026gt;\u003c/span\u003eHello PWA\u003cspan class=\"nt\"\u003e\u0026lt;/title\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"c\"\u003e\u0026lt;!-- ↓manifestファイルの読み込みを追加↓ --\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e\u0026lt;link\u003c/span\u003e \u003cspan class=\"na\"\u003erel=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"manifest\"\u003c/span\u003e \u003cspan class=\"na\"\u003ehref=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"manifest.webmanifest\"\u003c/span\u003e \u003cspan class=\"nt\"\u003e/\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"nt\"\u003e\u0026lt;/head\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"nt\"\u003e\u0026lt;body\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e\u0026lt;h1\u0026gt;\u003c/span\u003eHello PWA\u003cspan class=\"nt\"\u003e\u0026lt;/h1\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e\u0026lt;script\u0026gt;\u003c/span\u003e\n      \u003cspan class=\"nb\"\u003ewindow\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eaddEventListener\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eload\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n        \u003cspan class=\"k\"\u003eif \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eserviceWorker\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e \u003cspan class=\"nb\"\u003enavigator\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n          \u003cspan class=\"nb\"\u003enavigator\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eserviceWorker\u003c/span\u003e\n            \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eregister\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003esw.js\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n            \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003ethen\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eregistration\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eregistered\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eregistration\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n            \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"k\"\u003ecatch\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eerror\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eerror\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eerror\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n      \u003cspan class=\"p\"\u003e});\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e\u0026lt;/script\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"nt\"\u003e\u0026lt;/body\u0026gt;\u003c/span\u003e\n\u003cspan class=\"nt\"\u003e\u0026lt;/html\u0026gt;\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003ch4 data-sourcepos=\"123:1-123:17\"\u003e\n\u003cspan id=\"iphone対応\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#iphone%E5%AF%BE%E5%BF%9C\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eiPhone対応\u003c/h4\u003e\n\u003cul data-sourcepos=\"125:1-127:0\"\u003e\n\u003cli data-sourcepos=\"125:1-125:102\"\u003eAndroidだけであればこれでいいのですがiPhone向けには専用の設定が必要です\u003c/li\u003e\n\u003cli data-sourcepos=\"126:1-127:0\"\u003eホーム画面に追加されるアイコンとスプラッシュ画面の画像を設定します\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"html\" data-sourcepos=\"128:1-196:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003eindex.html\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"cp\"\u003e\u0026lt;!DOCTYPE html\u0026gt;\u003c/span\u003e\n\u003cspan class=\"nt\"\u003e\u0026lt;html\u003c/span\u003e \u003cspan class=\"na\"\u003elang=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"ja\"\u003c/span\u003e\u003cspan class=\"nt\"\u003e\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"nt\"\u003e\u0026lt;head\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e\u0026lt;meta\u003c/span\u003e \u003cspan class=\"na\"\u003echarset=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"utf-8\"\u003c/span\u003e \u003cspan class=\"nt\"\u003e/\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e\u0026lt;meta\u003c/span\u003e \u003cspan class=\"na\"\u003ename=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"viewport\"\u003c/span\u003e \u003cspan class=\"na\"\u003econtent=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"width=device-width, initial-scale=1\"\u003c/span\u003e \u003cspan class=\"nt\"\u003e/\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"c\"\u003e\u0026lt;!-- ↓iPhone用にURLバーを表示しない設定を追加↓ --\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e\u0026lt;meta\u003c/span\u003e \u003cspan class=\"na\"\u003ename=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"apple-mobile-web-app-capable\"\u003c/span\u003e \u003cspan class=\"na\"\u003econtent=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"yes\"\u003c/span\u003e\u003cspan class=\"nt\"\u003e\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e\u0026lt;title\u0026gt;\u003c/span\u003eHello PWA\u003cspan class=\"nt\"\u003e\u0026lt;/title\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e\u0026lt;link\u003c/span\u003e \u003cspan class=\"na\"\u003erel=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"manifest\"\u003c/span\u003e \u003cspan class=\"na\"\u003ehref=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"manifest.webmanifest\"\u003c/span\u003e \u003cspan class=\"nt\"\u003e/\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"c\"\u003e\u0026lt;!-- ↓iPhone用にアイコン設定を追加↓ --\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e\u0026lt;link\u003c/span\u003e \u003cspan class=\"na\"\u003erel=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"apple-touch-icon\"\u003c/span\u003e \u003cspan class=\"na\"\u003ehref=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"images/icons-192.png\"\u003c/span\u003e \u003cspan class=\"nt\"\u003e/\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"c\"\u003e\u0026lt;!-- ↓iPhone用にスプラッシュ画面の設定を追加↓ --\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e\u0026lt;link\u003c/span\u003e\n      \u003cspan class=\"na\"\u003erel=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"apple-touch-startup-image\"\u003c/span\u003e\n      \u003cspan class=\"na\"\u003ehref=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"images/launch-640x1136.png\"\u003c/span\u003e\n      \u003cspan class=\"na\"\u003emedia=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)\"\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e/\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e\u0026lt;link\u003c/span\u003e\n      \u003cspan class=\"na\"\u003erel=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"apple-touch-startup-image\"\u003c/span\u003e\n      \u003cspan class=\"na\"\u003ehref=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"images/launch-750x1334.png\"\u003c/span\u003e\n      \u003cspan class=\"na\"\u003emedia=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)\"\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e/\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e\u0026lt;link\u003c/span\u003e\n      \u003cspan class=\"na\"\u003erel=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"apple-touch-startup-image\"\u003c/span\u003e\n      \u003cspan class=\"na\"\u003ehref=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"images/launch-1080x1920.png\"\u003c/span\u003e\n      \u003cspan class=\"na\"\u003emedia=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"(device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)\"\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e/\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e\u0026lt;link\u003c/span\u003e\n      \u003cspan class=\"na\"\u003erel=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"apple-touch-startup-image\"\u003c/span\u003e\n      \u003cspan class=\"na\"\u003ehref=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"images/launch-1125x2436.png\"\u003c/span\u003e\n      \u003cspan class=\"na\"\u003emedia=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)\"\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e/\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e\u0026lt;link\u003c/span\u003e\n      \u003cspan class=\"na\"\u003erel=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"apple-touch-startup-image\"\u003c/span\u003e\n      \u003cspan class=\"na\"\u003ehref=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"images/launch-828x1792.png\"\u003c/span\u003e\n      \u003cspan class=\"na\"\u003emedia=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)\"\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e/\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e\u0026lt;link\u003c/span\u003e\n      \u003cspan class=\"na\"\u003erel=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"apple-touch-startup-image\"\u003c/span\u003e\n      \u003cspan class=\"na\"\u003ehref=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"images/launch-1242x2688.png\"\u003c/span\u003e\n      \u003cspan class=\"na\"\u003emedia=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)\"\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e/\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e\u0026lt;link\u003c/span\u003e\n      \u003cspan class=\"na\"\u003erel=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"apple-touch-startup-image\"\u003c/span\u003e\n      \u003cspan class=\"na\"\u003ehref=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"images/launch-1170x2532.png\"\u003c/span\u003e\n      \u003cspan class=\"na\"\u003emedia=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"(device-width: 390px) and (device-height: 844px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)\"\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e/\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e\u0026lt;link\u003c/span\u003e\n      \u003cspan class=\"na\"\u003erel=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"apple-touch-startup-image\"\u003c/span\u003e\n      \u003cspan class=\"na\"\u003ehref=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"images/launch-1284x2778.png\"\u003c/span\u003e\n      \u003cspan class=\"na\"\u003emedia=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"(device-width: 428px) and (device-height: 926px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)\"\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e/\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"nt\"\u003e\u0026lt;/head\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"nt\"\u003e\u0026lt;body\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e\u0026lt;h1\u0026gt;\u003c/span\u003eHello PWA\u003cspan class=\"nt\"\u003e\u0026lt;/h1\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e\u0026lt;script\u0026gt;\u003c/span\u003e\n      \u003cspan class=\"nb\"\u003ewindow\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eaddEventListener\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eload\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n        \u003cspan class=\"k\"\u003eif \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eserviceWorker\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e \u003cspan class=\"k\"\u003ein\u003c/span\u003e \u003cspan class=\"nb\"\u003enavigator\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n          \u003cspan class=\"nb\"\u003enavigator\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eserviceWorker\u003c/span\u003e\n            \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eregister\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003esw.js\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n            \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003ethen\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eregistration\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eregistered\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eregistration\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n            \u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"k\"\u003ecatch\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eerror\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eerror\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eerror\u003c/span\u003e\u003cspan class=\"p\"\u003e));\u003c/span\u003e\n        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n      \u003cspan class=\"p\"\u003e});\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e\u0026lt;/script\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"nt\"\u003e\u0026lt;/body\u0026gt;\u003c/span\u003e\n\u003cspan class=\"nt\"\u003e\u0026lt;/html\u0026gt;\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cul data-sourcepos=\"198:1-199:0\"\u003e\n\u003cli data-sourcepos=\"198:1-199:0\"\u003eこれでコーディングは完了です！\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 data-sourcepos=\"200:1-200:39\"\u003e\n\u003cspan id=\"httpsで配信されていること\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#https%E3%81%A7%E9%85%8D%E4%BF%A1%E3%81%95%E3%82%8C%E3%81%A6%E3%81%84%E3%82%8B%E3%81%93%E3%81%A8\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eHTTPSで配信されていること\u003c/h3\u003e\n\u003cul data-sourcepos=\"202:1-204:0\"\u003e\n\u003cli data-sourcepos=\"202:1-202:53\"\u003e最後は実装ではなく配信方法の話です\u003c/li\u003e\n\u003cli data-sourcepos=\"203:1-204:0\"\u003e最近だと\u003ca href=\"https://www.netlify.com/\" rel=\"nofollow noopener\" target=\"_blank\"\u003eNetlify\u003c/a\u003eや\u003ca href=\"https://vercel.com\" rel=\"nofollow noopener\" target=\"_blank\"\u003eVercel\u003c/a\u003eなど無料でHTTPSでホスティングできるサービスがあるので趣味アプリであっても気軽に要件を満たせます\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"205:1-205:15\"\u003e\n\u003cspan id=\"動作確認\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E5%8B%95%E4%BD%9C%E7%A2%BA%E8%AA%8D\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e動作確認\u003c/h2\u003e\n\u003cul data-sourcepos=\"207:1-210:0\"\u003e\n\u003cli data-sourcepos=\"207:1-207:160\"\u003e今回紹介したコードは\u003ca href=\"https://github.com/ozaki25/advent-calendar-2020-pwa-sample\" rel=\"nofollow noopener\" target=\"_blank\"\u003eこちらのサンプルリポジトリ\u003c/a\u003eに公開してあります\u003c/li\u003e\n\u003cli data-sourcepos=\"208:1-210:0\"\u003eこのコードをGitHub Pagesで公開しています\n\u003cul data-sourcepos=\"209:5-210:0\"\u003e\n\u003cli data-sourcepos=\"209:5-210:0\"\u003e\u003ca href=\"https://ozaki25.github.io/advent-calendar-2020-pwa-sample/\" class=\"autolink\" rel=\"nofollow noopener\" target=\"_blank\"\u003ehttps://ozaki25.github.io/advent-calendar-2020-pwa-sample/\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 data-sourcepos=\"211:1-211:18\"\u003e\n\u003cspan id=\"androidchrome\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#androidchrome\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eAndroid/Chrome\u003c/h3\u003e\n\u003cp data-sourcepos=\"213:1-213:710\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fa761759c-55a7-92ed-d72a-5180dbf1df8e.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=5d37ec3923b502e5412bd341c4fd4f68\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fa761759c-55a7-92ed-d72a-5180dbf1df8e.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=5d37ec3923b502e5412bd341c4fd4f68\" alt=\"Screenshot_20201130-194441.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fa761759c-55a7-92ed-d72a-5180dbf1df8e.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=65839b8cb699e00bf912f6c1d85136f0 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/a761759c-55a7-92ed-d72a-5180dbf1df8e.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F94503c4d-1000-9c93-b16a-c7370ebcd2e8.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=e38cc154caf802da516a0b00955bf48e\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F94503c4d-1000-9c93-b16a-c7370ebcd2e8.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=e38cc154caf802da516a0b00955bf48e\" alt=\"Screenshot_20201130-194500.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F94503c4d-1000-9c93-b16a-c7370ebcd2e8.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=35d64de238ff0471f844909fc34ffc65 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/94503c4d-1000-9c93-b16a-c7370ebcd2e8.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F8ec1ce07-2037-6924-10a9-bfc0105b6fac.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=c5aba4ca34001be22fb03d874b485070\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F8ec1ce07-2037-6924-10a9-bfc0105b6fac.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=c5aba4ca34001be22fb03d874b485070\" alt=\"Screenshot_20201130-194527.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F8ec1ce07-2037-6924-10a9-bfc0105b6fac.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=9f8a5385116fd2d6308c126e4050d7c2 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/8ec1ce07-2037-6924-10a9-bfc0105b6fac.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fb07198b5-e774-66be-a911-b7f32b993b5d.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=f2f9a94904abfcd00c06d8a881279c0a\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fb07198b5-e774-66be-a911-b7f32b993b5d.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=f2f9a94904abfcd00c06d8a881279c0a\" alt=\"Screenshot_20201130-194539.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fb07198b5-e774-66be-a911-b7f32b993b5d.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=6ae1afa42dad997458e9225968e20611 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/b07198b5-e774-66be-a911-b7f32b993b5d.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F84772cd7-015a-d6e1-9c04-bd52863da4d7.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=9659cf7eafc2f856a809ad1de39d6ba8\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F84772cd7-015a-d6e1-9c04-bd52863da4d7.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=9659cf7eafc2f856a809ad1de39d6ba8\" alt=\"Screenshot_20201130-194548.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F84772cd7-015a-d6e1-9c04-bd52863da4d7.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=a6ca35b2a1fdc3eb8987b823ac977635 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/84772cd7-015a-d6e1-9c04-bd52863da4d7.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003ch3 data-sourcepos=\"215:1-215:17\"\u003e\n\u003cspan id=\"iphonesafari\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#iphonesafari\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eiPhone/Safari\u003c/h3\u003e\n\u003cp data-sourcepos=\"217:1-217:651\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fc4260086-7bc1-c09f-0ea3-041165ab64ba.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=13bb4855d562bfae974a9eaf5bbebf85\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fc4260086-7bc1-c09f-0ea3-041165ab64ba.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=13bb4855d562bfae974a9eaf5bbebf85\" alt=\"iOS の画像1.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fc4260086-7bc1-c09f-0ea3-041165ab64ba.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=db4cea3f86ffde5af425c9eb3fd8de0a 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/c4260086-7bc1-c09f-0ea3-041165ab64ba.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fcf18fb55-4ac1-950c-f108-a83a0ddb567d.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=73c38f6a49bd050fb71057debb06fa4e\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fcf18fb55-4ac1-950c-f108-a83a0ddb567d.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=73c38f6a49bd050fb71057debb06fa4e\" alt=\"iOS の画像2.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fcf18fb55-4ac1-950c-f108-a83a0ddb567d.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=0189a7e819ab696f4b7fe2e888c7019e 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/cf18fb55-4ac1-950c-f108-a83a0ddb567d.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F180b94af-6d55-66fb-dc57-2071aba2cf92.jpeg?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=230001ed1cf7f7710737fc45a2383785\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F180b94af-6d55-66fb-dc57-2071aba2cf92.jpeg?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=230001ed1cf7f7710737fc45a2383785\" alt=\"iOS の画像3.jpg\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F180b94af-6d55-66fb-dc57-2071aba2cf92.jpeg?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=b9ee624e6d54f717f1d685462817e381 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/180b94af-6d55-66fb-dc57-2071aba2cf92.jpeg\" loading=\"lazy\"\u003e\u003c/a\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F778b0475-14f1-614f-0150-698aca9f180d.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=c7922b93015840ec745cac6469bc1007\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F778b0475-14f1-614f-0150-698aca9f180d.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=c7922b93015840ec745cac6469bc1007\" alt=\"iOS の画像4.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F778b0475-14f1-614f-0150-698aca9f180d.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=f51bd203d0c9cb33cece2d8725131e83 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/778b0475-14f1-614f-0150-698aca9f180d.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F33f55ac6-585c-743b-23c8-f1dffc0963d8.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=4a2253d01ca093ffcadbd9ddf5e9c8f0\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F33f55ac6-585c-743b-23c8-f1dffc0963d8.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=4a2253d01ca093ffcadbd9ddf5e9c8f0\" alt=\"iOS の画像5.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F33f55ac6-585c-743b-23c8-f1dffc0963d8.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=2d26d413443fcadf721e04da70d2b8cd 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/33f55ac6-585c-743b-23c8-f1dffc0963d8.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003ch3 data-sourcepos=\"221:1-221:14\"\u003e\n\u003cspan id=\"macchrome\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#macchrome\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eMac/Chrome\u003c/h3\u003e\n\u003cp data-sourcepos=\"223:1-226:163\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F9765200e-1e80-d48e-4f1f-45f0d0bd9dc2.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=c87a57c2859b2cb00904bb6a6915d5eb\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F9765200e-1e80-d48e-4f1f-45f0d0bd9dc2.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=c87a57c2859b2cb00904bb6a6915d5eb\" alt=\"スクリーンショット 2020-11-30 20.04.55.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F9765200e-1e80-d48e-4f1f-45f0d0bd9dc2.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=a315aac148fa39c5b9c885f5d3e571cb 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/9765200e-1e80-d48e-4f1f-45f0d0bd9dc2.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003cbr\u003e\n\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F9f007a24-b99d-7c90-1d99-c5e084bdc20d.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=a31eb2de884ddb8d405f9e91aa4a79b3\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F9f007a24-b99d-7c90-1d99-c5e084bdc20d.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=a31eb2de884ddb8d405f9e91aa4a79b3\" alt=\"スクリーンショット 2020-11-30 20.05.03.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F9f007a24-b99d-7c90-1d99-c5e084bdc20d.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=b02d948c89920660bcf6cdf96c7dbac1 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/9f007a24-b99d-7c90-1d99-c5e084bdc20d.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003cbr\u003e\n\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F0dd81961-dcd8-eadb-69f7-a49ac0a654bf.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=942811951b2407f730677e3d65195218\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F0dd81961-dcd8-eadb-69f7-a49ac0a654bf.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=942811951b2407f730677e3d65195218\" alt=\"スクリーンショット 2020-11-30 20.05.36.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F0dd81961-dcd8-eadb-69f7-a49ac0a654bf.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=0fd661dccb59de22d1bf6ebc8205d8c7 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/0dd81961-dcd8-eadb-69f7-a49ac0a654bf.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003cbr\u003e\n\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fac331ec6-4aef-4730-6458-eca9e75aa9df.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=6691bd919f0d028ab7be4cc4ab209199\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fac331ec6-4aef-4730-6458-eca9e75aa9df.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=6691bd919f0d028ab7be4cc4ab209199\" alt=\"スクリーンショット 2020-11-30 20.05.44.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fac331ec6-4aef-4730-6458-eca9e75aa9df.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=2f09fa5e85d95ecabe0c8f8e3dfe67ed 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/ac331ec6-4aef-4730-6458-eca9e75aa9df.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003ch2 data-sourcepos=\"230:1-230:12\"\u003e\n\u003cspan id=\"まとめ\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%81%BE%E3%81%A8%E3%82%81\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eまとめ\u003c/h2\u003e\n\u003cul data-sourcepos=\"232:1-235:0\"\u003e\n\u003cli data-sourcepos=\"232:1-232:137\"\u003eホームスクリーンへ追加の機能がほしいだけであれば自前でも簡単に実装できることがわかりました\u003c/li\u003e\n\u003cli data-sourcepos=\"233:1-233:87\"\u003eAndroidとiPhoneで仕様が共通化されてないのがちょっと面倒ですね\u003c/li\u003e\n\u003cli data-sourcepos=\"234:1-235:0\"\u003eiPhoneのスプラッシュ画面はデバイスサイズこどに用意しないといけないのでそこが一番大変でした\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"236:1-236:18\"\u003e\n\u003cspan id=\"参考サイト\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E5%8F%82%E8%80%83%E3%82%B5%E3%82%A4%E3%83%88\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e参考サイト\u003c/h2\u003e\n\u003cul data-sourcepos=\"238:1-246:0\"\u003e\n\u003cli data-sourcepos=\"238:1-238:32\"\u003e\u003ca href=\"https://web.dev/app-shortcuts/\" class=\"autolink\" rel=\"nofollow noopener\" target=\"_blank\"\u003ehttps://web.dev/app-shortcuts/\u003c/a\u003e\u003c/li\u003e\n\u003cli data-sourcepos=\"239:1-239:35\"\u003e\u003ca href=\"https://web.dev/install-criteria/\" class=\"autolink\" rel=\"nofollow noopener\" target=\"_blank\"\u003ehttps://web.dev/install-criteria/\u003c/a\u003e\u003c/li\u003e\n\u003cli data-sourcepos=\"240:1-240:31\"\u003e\u003ca href=\"https://web.dev/add-manifest/\" class=\"autolink\" rel=\"nofollow noopener\" target=\"_blank\"\u003ehttps://web.dev/add-manifest/\u003c/a\u003e\u003c/li\u003e\n\u003cli data-sourcepos=\"241:1-241:83\"\u003e\u003ca href=\"https://developer.mozilla.org/ja/docs/Web/Progressive_web_apps/Add_to_home_screen\" class=\"autolink\" rel=\"nofollow noopener\" target=\"_blank\"\u003ehttps://developer.mozilla.org/ja/docs/Web/Progressive_web_apps/Add_to_home_screen\u003c/a\u003e\u003c/li\u003e\n\u003cli data-sourcepos=\"242:1-242:79\"\u003e\u003ca href=\"https://docs.microsoft.com/en-us/microsoft-edge/progressive-web-apps-chromium\" class=\"autolink\" rel=\"nofollow noopener\" target=\"_blank\"\u003ehttps://docs.microsoft.com/en-us/microsoft-edge/progressive-web-apps-chromium\u003c/a\u003e\u003c/li\u003e\n\u003cli data-sourcepos=\"243:1-243:163\"\u003e\u003ca href=\"https://developer.apple.com/library/archive/documentation/AppleApplications/Reference/SafariWebContent/ConfiguringWebApplications/ConfiguringWebApplications.html\" class=\"autolink\" rel=\"nofollow noopener\" target=\"_blank\"\u003ehttps://developer.apple.com/library/archive/documentation/AppleApplications/Reference/SafariWebContent/ConfiguringWebApplications/ConfiguringWebApplications.html\u003c/a\u003e\u003c/li\u003e\n\u003cli data-sourcepos=\"244:1-246:0\"\u003e\u003ca href=\"https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/adaptivity-and-layout/\" class=\"autolink\" rel=\"nofollow noopener\" target=\"_blank\"\u003ehttps://developer.apple.com/design/human-interface-guidelines/ios/visual-design/adaptivity-and-layout/\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n","body":"- この記事は[MDCアドベントカレンダー](https://qiita.com/advent-calendar/2020/mdc)1日目の記事です\n\n## はじめに\n\n- PWAと言われたときにホームスクリーンへ追加できるWebアプリのことと思う方も多いと思います（PWAの定義についてはここではふれません）\n- 最近だとライブラリやフレームワークに乗っかることで簡単にホームスクリーンへの追加を実現できるケースが多いですが今回は自前で実装する方法を整理します\n\n## 機能の概要\n\n- Webアプリをスマホアプリのように端末にインストールしホーム画面にアイコンを設置できます\n- ホーム画面のアイコンからアプリを開くとスプラッシュ画面が表示され画面上部にURLバーがない状態で全画面で表示することができます\n\n![demo.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/f6e8fac4-93a6-1105-54e4-dfac80a5f1a2.gif)\n\n\n## 手順\n\n- ホームスクリーンへ追加の仕組みを入れるためには以下の3つの要件があります\n    - ServiceWorkerが有効化されていること\n    - manifestファイルが読み込まれていてそこに設定が記載されていること\n    - HTTPSで配信されていること\n\n### ServiceWorkerが有効化されていること\n\n- ホームスクリーンへ追加の機能を使うためにはServiceWorkerが有効化されている必要があります\n- オフラインキャッシュなど他の機能を使わないのであれば細かい実装は不要で読み込むだけでOKです\n\n```js:sw.js\nself.addEventListener('fetch', function (event) {});\n```\n\n```html:index.html\n\u003c!DOCTYPE html\u003e\n\u003chtml lang=\"ja\"\u003e\n  \u003chead\u003e\n    \u003cmeta charset=\"utf-8\" /\u003e\n    \u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1\" /\u003e\n    \u003ctitle\u003eHello PWA\u003c/title\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n    \u003ch1\u003eHello PWA\u003c/h1\u003e\n    \u003cscript\u003e\n      window.addEventListener('load', () =\u003e {\n        if ('serviceWorker' in navigator) {\n          navigator.serviceWorker\n            .register('sw.js')\n            .then(registration =\u003e console.log('registered', registration))\n            .catch(error =\u003e console.log('error', error));\n        }\n      });\n    \u003c/script\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\n\n### manifestファイルが読み込まれていてそこに設定が記載されていること\n\n- manifest.webmanifestというファイルを作成してそこにホームスクリーンへ追加の設定をしていきます\n\n```json:manifest.webmanifest\n{\n  \"name\": \"Hello PWA\",\n  \"short_name\": \"Hello\",\n  \"start_url\": \".\",\n  \"display\": \"standalone\",\n  \"background_color\": \"#4E526F\",\n  \"theme_color\": \"#4E526F\",\n  \"icons\": [\n    {\n      \"src\": \"images/icons-192.png\",\n      \"sizes\": \"192x192\",\n      \"type\": \"image/png\"\n    },\n    {\n      \"src\": \"images/icons-512.png\",\n      \"sizes\": \"512x512\",\n      \"type\": \"image/png\"\n    }\n  ]\n}\n```\n\n- nameとshort_nameはアプリ名を記載します\n    - ホーム画面のアイコンの下に表示される名前などスペースが狭い場面ではshort_nameが使われます\n- start_urlはホーム画面から起動したときにどのページを開くかを指定します\n- displayは表示モードの指定でfullscreen、standalone、minimal-uiの3つから選びます\n    - [詳細はこちら](https://web.dev/add-manifest/#display)\n- background_colorはスプラッシュ画面の背景色をtheme_colorはツールバーの色を指定します\n- iconsにはアプリのアイコンを設定します\n    - スマホ用とデスクトップ用で192x192と512x512の画像を設定しておけばOKです\n    - 192x192のアイコンは円形にしておかないと新し目のAndroidではアイコンが小さくなってしまいます\n    - 小さくなってる例\n    - ![スクリーンショット 2020-11-30 19.31.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/44789bc5-c043-1864-8ee8-fd0fa4ab29f2.png)\n- 作成したmanifestファイルをhtml側で読み込めばOKです\n\n```html:index.html\n\u003c!DOCTYPE html\u003e\n\u003chtml lang=\"ja\"\u003e\n  \u003chead\u003e\n    \u003cmeta charset=\"utf-8\" /\u003e\n    \u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1\" /\u003e\n    \u003ctitle\u003eHello PWA\u003c/title\u003e\n    \u003c!-- ↓manifestファイルの読み込みを追加↓ --\u003e\n    \u003clink rel=\"manifest\" href=\"manifest.webmanifest\" /\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n    \u003ch1\u003eHello PWA\u003c/h1\u003e\n    \u003cscript\u003e\n      window.addEventListener('load', () =\u003e {\n        if ('serviceWorker' in navigator) {\n          navigator.serviceWorker\n            .register('sw.js')\n            .then(registration =\u003e console.log('registered', registration))\n            .catch(error =\u003e console.log('error', error));\n        }\n      });\n    \u003c/script\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\n#### iPhone対応\n\n- AndroidだけであればこれでいいのですがiPhone向けには専用の設定が必要です\n- ホーム画面に追加されるアイコンとスプラッシュ画面の画像を設定します\n\n```html:index.html\n\u003c!DOCTYPE html\u003e\n\u003chtml lang=\"ja\"\u003e\n  \u003chead\u003e\n    \u003cmeta charset=\"utf-8\" /\u003e\n    \u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1\" /\u003e\n    \u003c!-- ↓iPhone用にURLバーを表示しない設定を追加↓ --\u003e\n    \u003cmeta name=\"apple-mobile-web-app-capable\" content=\"yes\"\u003e\n    \u003ctitle\u003eHello PWA\u003c/title\u003e\n    \u003clink rel=\"manifest\" href=\"manifest.webmanifest\" /\u003e\n    \u003c!-- ↓iPhone用にアイコン設定を追加↓ --\u003e\n    \u003clink rel=\"apple-touch-icon\" href=\"images/icons-192.png\" /\u003e\n    \u003c!-- ↓iPhone用にスプラッシュ画面の設定を追加↓ --\u003e\n    \u003clink\n      rel=\"apple-touch-startup-image\"\n      href=\"images/launch-640x1136.png\"\n      media=\"(device-width: 320px) and (device-height: 568px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)\"\n    /\u003e\n    \u003clink\n      rel=\"apple-touch-startup-image\"\n      href=\"images/launch-750x1334.png\"\n      media=\"(device-width: 375px) and (device-height: 667px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)\"\n    /\u003e\n    \u003clink\n      rel=\"apple-touch-startup-image\"\n      href=\"images/launch-1080x1920.png\"\n      media=\"(device-width: 414px) and (device-height: 736px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)\"\n    /\u003e\n    \u003clink\n      rel=\"apple-touch-startup-image\"\n      href=\"images/launch-1125x2436.png\"\n      media=\"(device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)\"\n    /\u003e\n    \u003clink\n      rel=\"apple-touch-startup-image\"\n      href=\"images/launch-828x1792.png\"\n      media=\"(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 2) and (orientation: portrait)\"\n    /\u003e\n    \u003clink\n      rel=\"apple-touch-startup-image\"\n      href=\"images/launch-1242x2688.png\"\n      media=\"(device-width: 414px) and (device-height: 896px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)\"\n    /\u003e\n    \u003clink\n      rel=\"apple-touch-startup-image\"\n      href=\"images/launch-1170x2532.png\"\n      media=\"(device-width: 390px) and (device-height: 844px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)\"\n    /\u003e\n    \u003clink\n      rel=\"apple-touch-startup-image\"\n      href=\"images/launch-1284x2778.png\"\n      media=\"(device-width: 428px) and (device-height: 926px) and (-webkit-device-pixel-ratio: 3) and (orientation: portrait)\"\n    /\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n    \u003ch1\u003eHello PWA\u003c/h1\u003e\n    \u003cscript\u003e\n      window.addEventListener('load', () =\u003e {\n        if ('serviceWorker' in navigator) {\n          navigator.serviceWorker\n            .register('sw.js')\n            .then(registration =\u003e console.log('registered', registration))\n            .catch(error =\u003e console.log('error', error));\n        }\n      });\n    \u003c/script\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\n- これでコーディングは完了です！\n\n### HTTPSで配信されていること\n\n- 最後は実装ではなく配信方法の話です\n- 最近だと[Netlify](https://www.netlify.com/)や[Vercel](https://vercel.com)など無料でHTTPSでホスティングできるサービスがあるので趣味アプリであっても気軽に要件を満たせます\n\n## 動作確認\n\n- 今回紹介したコードは[こちらのサンプルリポジトリ](https://github.com/ozaki25/advent-calendar-2020-pwa-sample)に公開してあります\n- このコードをGitHub Pagesで公開しています\n    - https://ozaki25.github.io/advent-calendar-2020-pwa-sample/\n\n### Android/Chrome\n\n![Screenshot_20201130-194441.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/a761759c-55a7-92ed-d72a-5180dbf1df8e.png)![Screenshot_20201130-194500.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/94503c4d-1000-9c93-b16a-c7370ebcd2e8.png)![Screenshot_20201130-194527.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/8ec1ce07-2037-6924-10a9-bfc0105b6fac.png)![Screenshot_20201130-194539.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/b07198b5-e774-66be-a911-b7f32b993b5d.png)![Screenshot_20201130-194548.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/84772cd7-015a-d6e1-9c04-bd52863da4d7.png)\n\n### iPhone/Safari\n\n![iOS の画像1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/c4260086-7bc1-c09f-0ea3-041165ab64ba.png)![iOS の画像2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/cf18fb55-4ac1-950c-f108-a83a0ddb567d.png)![iOS の画像3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/180b94af-6d55-66fb-dc57-2071aba2cf92.jpeg)![iOS の画像4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/778b0475-14f1-614f-0150-698aca9f180d.png)![iOS の画像5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/33f55ac6-585c-743b-23c8-f1dffc0963d8.png)\n\n\n\n### Mac/Chrome\n\n![スクリーンショット 2020-11-30 20.04.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/9765200e-1e80-d48e-4f1f-45f0d0bd9dc2.png)\n![スクリーンショット 2020-11-30 20.05.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/9f007a24-b99d-7c90-1d99-c5e084bdc20d.png)\n![スクリーンショット 2020-11-30 20.05.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/0dd81961-dcd8-eadb-69f7-a49ac0a654bf.png)\n![スクリーンショット 2020-11-30 20.05.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/ac331ec6-4aef-4730-6458-eca9e75aa9df.png)\n\n\n\n## まとめ\n\n- ホームスクリーンへ追加の機能がほしいだけであれば自前でも簡単に実装できることがわかりました\n- AndroidとiPhoneで仕様が共通化されてないのがちょっと面倒ですね\n- iPhoneのスプラッシュ画面はデバイスサイズこどに用意しないといけないのでそこが一番大変でした\n\n## 参考サイト\n\n- https://web.dev/app-shortcuts/\n- https://web.dev/install-criteria/\n- https://web.dev/add-manifest/\n- https://developer.mozilla.org/ja/docs/Web/Progressive_web_apps/Add_to_home_screen\n- https://docs.microsoft.com/en-us/microsoft-edge/progressive-web-apps-chromium\n- https://developer.apple.com/library/archive/documentation/AppleApplications/Reference/SafariWebContent/ConfiguringWebApplications/ConfiguringWebApplications.html\n- https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/adaptivity-and-layout/\n\n\n","coediting":false,"comments_count":0,"created_at":"2020-11-30T20:18:44+09:00","group":null,"id":"7994e2cf03b61592bb1d","likes_count":21,"private":false,"reactions_count":0,"stocks_count":27,"tags":[{"name":"PWA","versions":[]}],"title":"[PWA/A2HS]Webアプリをインストールしてホーム画面へ追加できるようにする","updated_at":"2021-10-26T18:50:14+09:00","url":"https://qiita.com/ozaki25/items/7994e2cf03b61592bb1d","user":{"description":"","facebook_id":"","followees_count":0,"followers_count":79,"github_login_name":"ozaki25","id":"ozaki25","items_count":112,"linkedin_id":"","location":"","name":"","organization":"","permanent_id":175213,"profile_image_url":"https://qiita-image-store.s3.amazonaws.com/0/175213/profile-images/1491825881","team_only":false,"twitter_screen_name":"ozaki25rn","website_url":""},"page_views_count":null,"team_membership":null,"organization_url_name":null,"slide":false,"posting_campaign_uuid":null},{"rendered_body":"\u003ch2 data-sourcepos=\"1:1-1:9\"\u003e\n\u003cspan id=\"概要\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E6%A6%82%E8%A6%81\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e概要\u003c/h2\u003e\n\u003cul data-sourcepos=\"3:1-6:0\"\u003e\n\u003cli data-sourcepos=\"3:1-3:100\"\u003eServlet/JSPを使った古き良き技術スタックで画像のアップロードを試します\u003c/li\u003e\n\u003cli data-sourcepos=\"4:1-4:114\"\u003emultipart/form-dataで送ったファイルをサーバサイドでBase64化するのが今回やりたいこと\u003c/li\u003e\n\u003cli data-sourcepos=\"5:1-6:0\"\u003eServletもJSPもど素人です\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"7:1-7:12\"\u003e\n\u003cspan id=\"コード\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%82%B3%E3%83%BC%E3%83%89\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eコード\u003c/h2\u003e\n\u003ch3 data-sourcepos=\"9:1-9:28\"\u003e\n\u003cspan id=\"アップロード画面\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%82%A2%E3%83%83%E3%83%97%E3%83%AD%E3%83%BC%E3%83%89%E7%94%BB%E9%9D%A2\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eアップロード画面\u003c/h3\u003e\n\u003cul data-sourcepos=\"11:1-12:0\"\u003e\n\u003cli data-sourcepos=\"11:1-12:0\"\u003e最初に表示するアップロード画面のJSP\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"java\" data-sourcepos=\"13:1-30:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"o\"\u003e\u0026lt;%\u003c/span\u003e\u003cspan class=\"err\"\u003e@\u003c/span\u003e \u003cspan class=\"n\"\u003epage\u003c/span\u003e \u003cspan class=\"n\"\u003elanguage\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"java\"\u003c/span\u003e \u003cspan class=\"n\"\u003econtentType\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"text/html; charset=UTF-8\"\u003c/span\u003e \u003cspan class=\"n\"\u003epageEncoding\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"UTF-8\"\u003c/span\u003e\u003cspan class=\"o\"\u003e%\u0026gt;\u003c/span\u003e\n\u003cspan class=\"o\"\u003e\u0026lt;!\u003c/span\u003e\u003cspan class=\"no\"\u003eDOCTYPE\u003c/span\u003e \u003cspan class=\"n\"\u003ehtml\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\n\u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003ehtml\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003ehead\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003emeta\u003c/span\u003e \u003cspan class=\"n\"\u003echarset\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"UTF-8\"\u003c/span\u003e \u003cspan class=\"o\"\u003e/\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003etitle\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"nc\"\u003eJava\u003c/span\u003e \u003cspan class=\"nc\"\u003eSample\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"n\"\u003etitle\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"o\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"n\"\u003eheader\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003ebody\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eh1\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"nc\"\u003eHello\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"n\"\u003eh1\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eform\u003c/span\u003e \u003cspan class=\"n\"\u003eaction\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"upload\"\u003c/span\u003e \u003cspan class=\"n\"\u003emethod\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"post\"\u003c/span\u003e \u003cspan class=\"n\"\u003eenctype\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"multipart/form-data\"\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\n      \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003einput\u003c/span\u003e \u003cspan class=\"n\"\u003etype\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"hidden\"\u003c/span\u003e \u003cspan class=\"n\"\u003ename\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"action_name\"\u003c/span\u003e \u003cspan class=\"n\"\u003evalue\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"upload\"\u003c/span\u003e \u003cspan class=\"o\"\u003e/\u0026gt;\u003c/span\u003e\n      \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003einput\u003c/span\u003e \u003cspan class=\"n\"\u003etype\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"file\"\u003c/span\u003e \u003cspan class=\"n\"\u003ename\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"file\"\u003c/span\u003e \u003cspan class=\"o\"\u003e/\u0026gt;\u003c/span\u003e\n      \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003ebutton\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"nc\"\u003eUpload\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"n\"\u003ebutton\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"o\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"n\"\u003eform\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"o\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"n\"\u003ebody\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\n\u003cspan class=\"o\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"n\"\u003ehtml\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cul data-sourcepos=\"32:1-33:0\"\u003e\n\u003cli data-sourcepos=\"32:1-33:0\"\u003e\n\u003ccode\u003e/upload\u003c/code\u003e に対してmultipart/form-dataで画像を投げます\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 data-sourcepos=\"34:1-34:25\"\u003e\n\u003cspan id=\"画像の取得処理\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E7%94%BB%E5%83%8F%E3%81%AE%E5%8F%96%E5%BE%97%E5%87%A6%E7%90%86\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e画像の取得処理\u003c/h3\u003e\n\u003cul data-sourcepos=\"36:1-39:0\"\u003e\n\u003cli data-sourcepos=\"36:1-39:0\"\u003eアップロードのリクエストを受け取るコントローラ\n\u003cul data-sourcepos=\"37:5-39:0\"\u003e\n\u003cli data-sourcepos=\"37:5-37:148\"\u003eベースとなっているサンプルそのままですが \u003ccode\u003eaction_name\u003c/code\u003e が\u003ccode\u003eupload\u003c/code\u003e のときは \u003ccode\u003eUploadAction\u003c/code\u003e を呼び出してる感じ\u003c/li\u003e\n\u003cli data-sourcepos=\"38:5-39:0\"\u003e今回はmultipart/form-dataを受け取るので \u003ccode\u003e@MultipartConfig\u003c/code\u003e をつけてるところがポイント\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"java\" data-sourcepos=\"40:1-81:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"kn\"\u003epackage\u003c/span\u003e \u003cspan class=\"nn\"\u003ecom.example.controller\u003c/span\u003e\u003cspan class=\"o\"\u003e;\u003c/span\u003e\n\n\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003ejava.io.IOException\u003c/span\u003e\u003cspan class=\"o\"\u003e;\u003c/span\u003e\n\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003ejavax.servlet.RequestDispatcher\u003c/span\u003e\u003cspan class=\"o\"\u003e;\u003c/span\u003e\n\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003ejavax.servlet.ServletException\u003c/span\u003e\u003cspan class=\"o\"\u003e;\u003c/span\u003e\n\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003ejavax.servlet.annotation.MultipartConfig\u003c/span\u003e\u003cspan class=\"o\"\u003e;\u003c/span\u003e\n\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003ejavax.servlet.annotation.WebServlet\u003c/span\u003e\u003cspan class=\"o\"\u003e;\u003c/span\u003e\n\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003ejavax.servlet.http.HttpServlet\u003c/span\u003e\u003cspan class=\"o\"\u003e;\u003c/span\u003e\n\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003ejavax.servlet.http.HttpServletRequest\u003c/span\u003e\u003cspan class=\"o\"\u003e;\u003c/span\u003e\n\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003ejavax.servlet.http.HttpServletResponse\u003c/span\u003e\u003cspan class=\"o\"\u003e;\u003c/span\u003e\n\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003ecom.example.action.Action\u003c/span\u003e\u003cspan class=\"o\"\u003e;\u003c/span\u003e\n\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003ecom.example.action.UploadAction\u003c/span\u003e\u003cspan class=\"o\"\u003e;\u003c/span\u003e\n\n\u003cspan class=\"nd\"\u003e@WebServlet\u003c/span\u003e\u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eurlPatterns\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"o\"\u003e{\u003c/span\u003e \u003cspan class=\"s\"\u003e\"/upload\"\u003c/span\u003e \u003cspan class=\"o\"\u003e})\u003c/span\u003e\n\u003cspan class=\"nd\"\u003e@MultipartConfig\u003c/span\u003e\n\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eUploadController\u003c/span\u003e \u003cspan class=\"kd\"\u003eextends\u003c/span\u003e \u003cspan class=\"nc\"\u003eHttpServlet\u003c/span\u003e \u003cspan class=\"o\"\u003e{\u003c/span\u003e\n\n  \u003cspan class=\"nd\"\u003e@Override\u003c/span\u003e\n  \u003cspan class=\"kd\"\u003eprotected\u003c/span\u003e \u003cspan class=\"kt\"\u003evoid\u003c/span\u003e \u003cspan class=\"nf\"\u003edoPost\u003c/span\u003e\u003cspan class=\"o\"\u003e(\u003c/span\u003e\n    \u003cspan class=\"nc\"\u003eHttpServletRequest\u003c/span\u003e \u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"o\"\u003e,\u003c/span\u003e\n    \u003cspan class=\"nc\"\u003eHttpServletResponse\u003c/span\u003e \u003cspan class=\"n\"\u003eresponse\u003c/span\u003e\n  \u003cspan class=\"o\"\u003e)\u003c/span\u003e\n    \u003cspan class=\"kd\"\u003ethrows\u003c/span\u003e \u003cspan class=\"nc\"\u003eServletException\u003c/span\u003e\u003cspan class=\"o\"\u003e,\u003c/span\u003e \u003cspan class=\"nc\"\u003eIOException\u003c/span\u003e \u003cspan class=\"o\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"nc\"\u003eString\u003c/span\u003e \u003cspan class=\"n\"\u003estrActionName\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003egetParameter\u003c/span\u003e\u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\"action_name\"\u003c/span\u003e\u003cspan class=\"o\"\u003e);\u003c/span\u003e\n    \u003cspan class=\"nc\"\u003eAction\u003c/span\u003e \u003cspan class=\"n\"\u003eaction\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003egetInstance\u003c/span\u003e\u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003estrActionName\u003c/span\u003e\u003cspan class=\"o\"\u003e);\u003c/span\u003e\n    \u003cspan class=\"nc\"\u003eString\u003c/span\u003e \u003cspan class=\"n\"\u003eforwardPath\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eaction\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eexecute\u003c/span\u003e\u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"o\"\u003e);\u003c/span\u003e\n    \u003cspan class=\"nc\"\u003eRequestDispatcher\u003c/span\u003e \u003cspan class=\"n\"\u003erd\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003egetRequestDispatcher\u003c/span\u003e\u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eforwardPath\u003c/span\u003e\u003cspan class=\"o\"\u003e);\u003c/span\u003e\n\n    \u003cspan class=\"n\"\u003erd\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eforward\u003c/span\u003e\u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"o\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eresponse\u003c/span\u003e\u003cspan class=\"o\"\u003e);\u003c/span\u003e\n  \u003cspan class=\"o\"\u003e}\u003c/span\u003e\n  \n  \u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e \u003cspan class=\"kd\"\u003estatic\u003c/span\u003e \u003cspan class=\"nc\"\u003eAction\u003c/span\u003e \u003cspan class=\"nf\"\u003egetInstance\u003c/span\u003e\u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"nc\"\u003eString\u003c/span\u003e \u003cspan class=\"n\"\u003eactionName\u003c/span\u003e\u003cspan class=\"o\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"k\"\u003eswitch\u003c/span\u003e \u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eactionName\u003c/span\u003e\u003cspan class=\"o\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e{\u003c/span\u003e\n      \u003cspan class=\"k\"\u003ecase\u003c/span\u003e \u003cspan class=\"s\"\u003e\"upload\"\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e\n        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"nf\"\u003eUploadAction\u003c/span\u003e\u003cspan class=\"o\"\u003e();\u003c/span\u003e\n      \u003cspan class=\"k\"\u003edefault\u003c/span\u003e\u003cspan class=\"o\"\u003e:\u003c/span\u003e\n        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003enull\u003c/span\u003e\u003cspan class=\"o\"\u003e;\u003c/span\u003e\n    \u003cspan class=\"o\"\u003e}\u003c/span\u003e\n  \u003cspan class=\"o\"\u003e}\u003c/span\u003e\n\u003cspan class=\"o\"\u003e}\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cul data-sourcepos=\"83:1-84:0\"\u003e\n\u003cli data-sourcepos=\"83:1-84:0\"\u003e画像を受け取って処理するAction\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"java\" data-sourcepos=\"85:1-134:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"kn\"\u003epackage\u003c/span\u003e \u003cspan class=\"nn\"\u003ecom.example.action\u003c/span\u003e\u003cspan class=\"o\"\u003e;\u003c/span\u003e\n\n\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003ejava.io.BufferedOutputStream\u003c/span\u003e\u003cspan class=\"o\"\u003e;\u003c/span\u003e\n\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003ejava.io.ByteArrayOutputStream\u003c/span\u003e\u003cspan class=\"o\"\u003e;\u003c/span\u003e\n\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003ejava.io.InputStream\u003c/span\u003e\u003cspan class=\"o\"\u003e;\u003c/span\u003e\n\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003ejava.util.Base64\u003c/span\u003e\u003cspan class=\"o\"\u003e;\u003c/span\u003e\n\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003ejavax.servlet.http.HttpServletRequest\u003c/span\u003e\u003cspan class=\"o\"\u003e;\u003c/span\u003e\n\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003ejavax.servlet.http.Part\u003c/span\u003e\u003cspan class=\"o\"\u003e;\u003c/span\u003e\n\n\u003cspan class=\"kd\"\u003epublic\u003c/span\u003e \u003cspan class=\"kd\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eUploadAction\u003c/span\u003e \u003cspan class=\"kd\"\u003eextends\u003c/span\u003e \u003cspan class=\"nc\"\u003eAction\u003c/span\u003e \u003cspan class=\"o\"\u003e{\u003c/span\u003e\n\n  \u003cspan class=\"nd\"\u003e@Override\u003c/span\u003e\n  \u003cspan class=\"kd\"\u003eprotected\u003c/span\u003e \u003cspan class=\"nc\"\u003eString\u003c/span\u003e \u003cspan class=\"nf\"\u003eprocessRequest\u003c/span\u003e\u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"nc\"\u003eHttpServletRequest\u003c/span\u003e \u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"o\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"k\"\u003etry\u003c/span\u003e \u003cspan class=\"o\"\u003e{\u003c/span\u003e\n      \u003cspan class=\"nc\"\u003ePart\u003c/span\u003e \u003cspan class=\"n\"\u003efilePart\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003egetPart\u003c/span\u003e\u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\"file\"\u003c/span\u003e\u003cspan class=\"o\"\u003e);\u003c/span\u003e\n      \u003cspan class=\"nc\"\u003eString\u003c/span\u003e \u003cspan class=\"n\"\u003econtentType\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003efilePart\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003egetContentType\u003c/span\u003e\u003cspan class=\"o\"\u003e();\u003c/span\u003e\n      \u003cspan class=\"nc\"\u003eInputStream\u003c/span\u003e \u003cspan class=\"n\"\u003efileContent\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003efilePart\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003egetInputStream\u003c/span\u003e\u003cspan class=\"o\"\u003e();\u003c/span\u003e\n      \u003cspan class=\"kt\"\u003ebyte\u003c/span\u003e\u003cspan class=\"o\"\u003e[]\u003c/span\u003e \u003cspan class=\"n\"\u003ebyteArray\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003egetByteArray\u003c/span\u003e\u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003efileContent\u003c/span\u003e\u003cspan class=\"o\"\u003e);\u003c/span\u003e\n      \u003cspan class=\"nc\"\u003eString\u003c/span\u003e \u003cspan class=\"n\"\u003ebase64String\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nc\"\u003eBase64\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003egetEncoder\u003c/span\u003e\u003cspan class=\"o\"\u003e().\u003c/span\u003e\u003cspan class=\"na\"\u003eencodeToString\u003c/span\u003e\u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ebyteArray\u003c/span\u003e\u003cspan class=\"o\"\u003e);\u003c/span\u003e\n\n      \u003cspan class=\"nc\"\u003eSystem\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eout\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eprintln\u003c/span\u003e\u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\"filePart: \"\u003c/span\u003e \u003cspan class=\"o\"\u003e+\u003c/span\u003e \u003cspan class=\"n\"\u003efilePart\u003c/span\u003e\u003cspan class=\"o\"\u003e);\u003c/span\u003e\n      \u003cspan class=\"nc\"\u003eSystem\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eout\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eprintln\u003c/span\u003e\u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\"contentType: \"\u003c/span\u003e \u003cspan class=\"o\"\u003e+\u003c/span\u003e \u003cspan class=\"n\"\u003econtentType\u003c/span\u003e\u003cspan class=\"o\"\u003e);\u003c/span\u003e\n      \u003cspan class=\"nc\"\u003eSystem\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eout\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eprintln\u003c/span\u003e\u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\"base64String: \"\u003c/span\u003e \u003cspan class=\"o\"\u003e+\u003c/span\u003e \u003cspan class=\"n\"\u003ebase64String\u003c/span\u003e\u003cspan class=\"o\"\u003e);\u003c/span\u003e\n\n      \u003cspan class=\"c1\"\u003e// 必要に応じてcontentTypeとbase64化した画像情報をDBに保存などする\u003c/span\u003e\n\n      \u003cspan class=\"c1\"\u003e// 表示用にcontentTypeとbase64化した画像を組み合わせる\u003c/span\u003e\n      \u003cspan class=\"nc\"\u003eString\u003c/span\u003e \u003cspan class=\"n\"\u003eimage\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s\"\u003e\"data:\"\u003c/span\u003e \u003cspan class=\"o\"\u003e+\u003c/span\u003e \u003cspan class=\"n\"\u003econtentType\u003c/span\u003e \u003cspan class=\"o\"\u003e+\u003c/span\u003e \u003cspan class=\"s\"\u003e\";base64,\"\u003c/span\u003e \u003cspan class=\"o\"\u003e+\u003c/span\u003e \u003cspan class=\"n\"\u003ebase64String\u003c/span\u003e\u003cspan class=\"o\"\u003e;\u003c/span\u003e\n      \u003cspan class=\"n\"\u003erequest\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003esetAttribute\u003c/span\u003e\u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"s\"\u003e\"image\"\u003c/span\u003e\u003cspan class=\"o\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eimage\u003c/span\u003e\u003cspan class=\"o\"\u003e);\u003c/span\u003e\n    \u003cspan class=\"o\"\u003e}\u003c/span\u003e \u003cspan class=\"k\"\u003ecatch\u003c/span\u003e \u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"nc\"\u003eException\u003c/span\u003e \u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"o\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e{\u003c/span\u003e\n      \u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eprintStackTrace\u003c/span\u003e\u003cspan class=\"o\"\u003e();\u003c/span\u003e\n    \u003cspan class=\"o\"\u003e}\u003c/span\u003e\n    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"s\"\u003e\"image.jsp\"\u003c/span\u003e\u003cspan class=\"o\"\u003e;\u003c/span\u003e\n  \u003cspan class=\"o\"\u003e}\u003c/span\u003e\n\n  \u003cspan class=\"kd\"\u003eprivate\u003c/span\u003e \u003cspan class=\"kd\"\u003estatic\u003c/span\u003e \u003cspan class=\"kt\"\u003ebyte\u003c/span\u003e\u003cspan class=\"o\"\u003e[]\u003c/span\u003e \u003cspan class=\"nf\"\u003egetByteArray\u003c/span\u003e\u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"nc\"\u003eInputStream\u003c/span\u003e \u003cspan class=\"n\"\u003eis\u003c/span\u003e\u003cspan class=\"o\"\u003e)\u003c/span\u003e \u003cspan class=\"kd\"\u003ethrows\u003c/span\u003e \u003cspan class=\"nc\"\u003eException\u003c/span\u003e \u003cspan class=\"o\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"nc\"\u003eByteArrayOutputStream\u003c/span\u003e \u003cspan class=\"n\"\u003eb\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"nc\"\u003eByteArrayOutputStream\u003c/span\u003e\u003cspan class=\"o\"\u003e();\u003c/span\u003e\n    \u003cspan class=\"nc\"\u003eBufferedOutputStream\u003c/span\u003e \u003cspan class=\"n\"\u003eos\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"nc\"\u003eBufferedOutputStream\u003c/span\u003e\u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eb\u003c/span\u003e\u003cspan class=\"o\"\u003e);\u003c/span\u003e\n    \u003cspan class=\"k\"\u003ewhile\u003c/span\u003e \u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"o\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e{\u003c/span\u003e\n      \u003cspan class=\"kt\"\u003eint\u003c/span\u003e \u003cspan class=\"n\"\u003ei\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eis\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eread\u003c/span\u003e\u003cspan class=\"o\"\u003e();\u003c/span\u003e\n      \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ei\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"o\"\u003e)\u003c/span\u003e \u003cspan class=\"k\"\u003ebreak\u003c/span\u003e\u003cspan class=\"o\"\u003e;\u003c/span\u003e\n      \u003cspan class=\"n\"\u003eos\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003ewrite\u003c/span\u003e\u003cspan class=\"o\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ei\u003c/span\u003e\u003cspan class=\"o\"\u003e);\u003c/span\u003e\n    \u003cspan class=\"o\"\u003e}\u003c/span\u003e\n    \u003cspan class=\"n\"\u003eos\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eflush\u003c/span\u003e\u003cspan class=\"o\"\u003e();\u003c/span\u003e\n    \u003cspan class=\"n\"\u003eos\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003eclose\u003c/span\u003e\u003cspan class=\"o\"\u003e();\u003c/span\u003e\n    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eb\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"na\"\u003etoByteArray\u003c/span\u003e\u003cspan class=\"o\"\u003e();\u003c/span\u003e\n  \u003cspan class=\"o\"\u003e}\u003c/span\u003e\n\u003cspan class=\"o\"\u003e}\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cul data-sourcepos=\"136:1-139:0\"\u003e\n\u003cli data-sourcepos=\"136:1-136:158\"\u003e通常のパラメータは \u003ccode\u003erequest.getParameter(\"xxx\")\u003c/code\u003e で取得しますが今回はファイルなので \u003ccode\u003erequest.getPart(\"xxx\")\u003c/code\u003e で取得してます\u003c/li\u003e\n\u003cli data-sourcepos=\"137:1-137:96\"\u003eその後はInputStream → byte → Base64って感じでBase64までたどり着きました\u003c/li\u003e\n\u003cli data-sourcepos=\"138:1-139:0\"\u003eせっかくなのでJSPにBase64化した値を渡して画面に出すところまで作ってます\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 data-sourcepos=\"140:1-140:19\"\u003e\n\u003cspan id=\"画像の表示\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E7%94%BB%E5%83%8F%E3%81%AE%E8%A1%A8%E7%A4%BA\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e画像の表示\u003c/h3\u003e\n\u003cul data-sourcepos=\"142:1-143:0\"\u003e\n\u003cli data-sourcepos=\"142:1-143:0\"\u003e画像を表示するJSP\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"java\" data-sourcepos=\"144:1-158:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"o\"\u003e\u0026lt;%\u003c/span\u003e\u003cspan class=\"err\"\u003e@\u003c/span\u003e \u003cspan class=\"n\"\u003epage\u003c/span\u003e \u003cspan class=\"n\"\u003elanguage\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"java\"\u003c/span\u003e \u003cspan class=\"n\"\u003econtentType\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"text/html; charset=UTF-8\"\u003c/span\u003e \u003cspan class=\"n\"\u003epageEncoding\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"UTF-8\"\u003c/span\u003e\u003cspan class=\"o\"\u003e%\u0026gt;\u003c/span\u003e\n\u003cspan class=\"o\"\u003e\u0026lt;!\u003c/span\u003e\u003cspan class=\"no\"\u003eDOCTYPE\u003c/span\u003e \u003cspan class=\"n\"\u003ehtml\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\n\u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nl\"\u003ejsp:\u003c/span\u003e\u003cspan class=\"n\"\u003euseBean\u003c/span\u003e \u003cspan class=\"n\"\u003eid\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"image\"\u003c/span\u003e \u003cspan class=\"n\"\u003escope\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"request\"\u003c/span\u003e \u003cspan class=\"n\"\u003etype\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"java.lang.String\"\u003c/span\u003e \u003cspan class=\"o\"\u003e/\u0026gt;\u003c/span\u003e\n\u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003ehtml\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003ehead\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003emeta\u003c/span\u003e \u003cspan class=\"n\"\u003echarset\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"UTF-8\"\u003c/span\u003e \u003cspan class=\"o\"\u003e/\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003etitle\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"nc\"\u003eJava\u003c/span\u003e \u003cspan class=\"nc\"\u003eSample\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"n\"\u003etitle\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"o\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"n\"\u003eheader\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003ebody\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eh1\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\u003cspan class=\"nc\"\u003eHello\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"n\"\u003eh1\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"n\"\u003eimg\u003c/span\u003e \u003cspan class=\"n\"\u003esrc\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"\u0026lt;%= image %\u0026gt;\"\u003c/span\u003e \u003cspan class=\"o\"\u003e/\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"o\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"n\"\u003ebody\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\n\u003cspan class=\"o\"\u003e\u0026lt;/\u003c/span\u003e\u003cspan class=\"n\"\u003ehtml\u003c/span\u003e\u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cul data-sourcepos=\"160:1-162:0\"\u003e\n\u003cli data-sourcepos=\"160:1-160:80\"\u003eimgタグのsrc属性にBase64もセットできるので埋め込んでます\u003c/li\u003e\n\u003cli data-sourcepos=\"161:1-162:0\"\u003eここまでできると画像をアップロードするとプレビューする画面が表示されるという一連の流れができました\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"163:1-163:12\"\u003e\n\u003cspan id=\"まとめ\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%81%BE%E3%81%A8%E3%82%81\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eまとめ\u003c/h2\u003e\n\u003cul data-sourcepos=\"165:1-166:52\"\u003e\n\u003cli data-sourcepos=\"165:1-165:102\"\u003eServlet/JSP使ってるとはいえfileのを受け取ったあとの処理は純粋なJavaですね\u003c/li\u003e\n\u003cli data-sourcepos=\"166:1-166:52\"\u003eServlet/JSP力が無駄にあがってしまった\u003c/li\u003e\n\u003c/ul\u003e\n","body":"## 概要\n\n- Servlet/JSPを使った古き良き技術スタックで画像のアップロードを試します\n- multipart/form-dataで送ったファイルをサーバサイドでBase64化するのが今回やりたいこと\n- ServletもJSPもど素人です\n\n## コード\n\n### アップロード画面\n\n- 最初に表示するアップロード画面のJSP\n\n```java\n\u003c%@ page language=\"java\" contentType=\"text/html; charset=UTF-8\" pageEncoding=\"UTF-8\"%\u003e\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n  \u003chead\u003e\n    \u003cmeta charset=\"UTF-8\" /\u003e\n    \u003ctitle\u003eJava Sample\u003c/title\u003e\n  \u003c/header\u003e\n  \u003cbody\u003e\n    \u003ch1\u003eHello\u003c/h1\u003e\n    \u003cform action=\"upload\" method=\"post\" enctype=\"multipart/form-data\"\u003e\n      \u003cinput type=\"hidden\" name=\"action_name\" value=\"upload\" /\u003e\n      \u003cinput type=\"file\" name=\"file\" /\u003e\n      \u003cbutton\u003eUpload\u003c/button\u003e\n    \u003c/form\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\n- `/upload` に対してmultipart/form-dataで画像を投げます\n\n### 画像の取得処理\n\n- アップロードのリクエストを受け取るコントローラ\n    - ベースとなっているサンプルそのままですが `action_name` が`upload` のときは `UploadAction` を呼び出してる感じ\n    - 今回はmultipart/form-dataを受け取るので `@MultipartConfig` をつけてるところがポイント\n\n```java\npackage com.example.controller;\n\nimport java.io.IOException;\nimport javax.servlet.RequestDispatcher;\nimport javax.servlet.ServletException;\nimport javax.servlet.annotation.MultipartConfig;\nimport javax.servlet.annotation.WebServlet;\nimport javax.servlet.http.HttpServlet;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.HttpServletResponse;\nimport com.example.action.Action;\nimport com.example.action.UploadAction;\n\n@WebServlet(urlPatterns = { \"/upload\" })\n@MultipartConfig\npublic class UploadController extends HttpServlet {\n\n  @Override\n  protected void doPost(\n    HttpServletRequest request,\n    HttpServletResponse response\n  )\n    throws ServletException, IOException {\n    String strActionName = request.getParameter(\"action_name\");\n    Action action = getInstance(strActionName);\n    String forwardPath = action.execute(request);\n    RequestDispatcher rd = request.getRequestDispatcher(forwardPath);\n\n    rd.forward(request, response);\n  }\n  \n  private static Action getInstance(String actionName) {\n    switch (actionName) {\n      case \"upload\":\n        return new UploadAction();\n      default:\n        return null;\n    }\n  }\n}\n```\n\n- 画像を受け取って処理するAction\n\n```java\npackage com.example.action;\n\nimport java.io.BufferedOutputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.InputStream;\nimport java.util.Base64;\nimport javax.servlet.http.HttpServletRequest;\nimport javax.servlet.http.Part;\n\npublic class UploadAction extends Action {\n\n  @Override\n  protected String processRequest(HttpServletRequest request) {\n    try {\n      Part filePart = request.getPart(\"file\");\n      String contentType = filePart.getContentType();\n      InputStream fileContent = filePart.getInputStream();\n      byte[] byteArray = getByteArray(fileContent);\n      String base64String = Base64.getEncoder().encodeToString(byteArray);\n\n      System.out.println(\"filePart: \" + filePart);\n      System.out.println(\"contentType: \" + contentType);\n      System.out.println(\"base64String: \" + base64String);\n\n      // 必要に応じてcontentTypeとbase64化した画像情報をDBに保存などする\n\n      // 表示用にcontentTypeとbase64化した画像を組み合わせる\n      String image = \"data:\" + contentType + \";base64,\" + base64String;\n      request.setAttribute(\"image\", image);\n    } catch (Exception e) {\n      e.printStackTrace();\n    }\n    return \"image.jsp\";\n  }\n\n  private static byte[] getByteArray(InputStream is) throws Exception {\n    ByteArrayOutputStream b = new ByteArrayOutputStream();\n    BufferedOutputStream os = new BufferedOutputStream(b);\n    while (true) {\n      int i = is.read();\n      if (i == -1) break;\n      os.write(i);\n    }\n    os.flush();\n    os.close();\n    return b.toByteArray();\n  }\n}\n```\n\n- 通常のパラメータは `request.getParameter(\"xxx\")` で取得しますが今回はファイルなので `request.getPart(\"xxx\")` で取得してます\n- その後はInputStream → byte → Base64って感じでBase64までたどり着きました\n- せっかくなのでJSPにBase64化した値を渡して画面に出すところまで作ってます\n\n### 画像の表示\n\n- 画像を表示するJSP\n\n```java\n\u003c%@ page language=\"java\" contentType=\"text/html; charset=UTF-8\" pageEncoding=\"UTF-8\"%\u003e\n\u003c!DOCTYPE html\u003e\n\u003cjsp:useBean id=\"image\" scope=\"request\" type=\"java.lang.String\" /\u003e\n\u003chtml\u003e\n  \u003chead\u003e\n    \u003cmeta charset=\"UTF-8\" /\u003e\n    \u003ctitle\u003eJava Sample\u003c/title\u003e\n  \u003c/header\u003e\n  \u003cbody\u003e\n    \u003ch1\u003eHello\u003c/h1\u003e\n    \u003cimg src=\"\u003c%= image %\u003e\" /\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\n- imgタグのsrc属性にBase64もセットできるので埋め込んでます\n- ここまでできると画像をアップロードするとプレビューする画面が表示されるという一連の流れができました\n\n## まとめ\n\n- Servlet/JSP使ってるとはいえfileのを受け取ったあとの処理は純粋なJavaですね\n- Servlet/JSP力が無駄にあがってしまった\n","coediting":false,"comments_count":0,"created_at":"2020-09-03T01:19:34+09:00","group":null,"id":"d7e85e1ed93d35caa286","likes_count":8,"private":false,"reactions_count":0,"stocks_count":2,"tags":[{"name":"Java","versions":[]},{"name":"JSP","versions":[]},{"name":"servlet","versions":[]}],"title":"[Java]画像をアップロードしてBase64化する","updated_at":"2021-10-26T18:50:08+09:00","url":"https://qiita.com/ozaki25/items/d7e85e1ed93d35caa286","user":{"description":"","facebook_id":"","followees_count":0,"followers_count":79,"github_login_name":"ozaki25","id":"ozaki25","items_count":112,"linkedin_id":"","location":"","name":"","organization":"","permanent_id":175213,"profile_image_url":"https://qiita-image-store.s3.amazonaws.com/0/175213/profile-images/1491825881","team_only":false,"twitter_screen_name":"ozaki25rn","website_url":""},"page_views_count":null,"team_membership":null,"organization_url_name":null,"slide":false,"posting_campaign_uuid":null},{"rendered_body":"\u003ch2 data-sourcepos=\"1:1-1:21\"\u003e\n\u003cspan id=\"やりたいこと\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%82%84%E3%82%8A%E3%81%9F%E3%81%84%E3%81%93%E3%81%A8\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eやりたいこと\u003c/h2\u003e\n\u003cul data-sourcepos=\"3:1-4:0\"\u003e\n\u003cli data-sourcepos=\"3:1-4:0\"\u003e下図のようなグラフでラベル部分(Januaryなど)をクリックすると詳細ページに遷移するといった動きを実現したかった\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp data-sourcepos=\"5:1-5:163\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F760956a3-4533-385d-e302-e7fa608e2e53.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=c6d1a7cece9129f0ced62076aa7629b8\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F760956a3-4533-385d-e302-e7fa608e2e53.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=c6d1a7cece9129f0ced62076aa7629b8\" alt=\"スクリーンショット 2020-09-01 23.33.06.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F760956a3-4533-385d-e302-e7fa608e2e53.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=b2e3a4b551d9f0ce413abcad030f962d 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/760956a3-4533-385d-e302-e7fa608e2e53.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003ch2 data-sourcepos=\"7:1-7:9\"\u003e\n\u003cspan id=\"結論\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E7%B5%90%E8%AB%96\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e結論\u003c/h2\u003e\n\u003cul data-sourcepos=\"9:1-11:0\"\u003e\n\u003cli data-sourcepos=\"9:1-9:47\"\u003eきれいに実装するのは無理だった\u003c/li\u003e\n\u003cli data-sourcepos=\"10:1-11:0\"\u003eグラフのクリックを検知してクリックしたのがどの項目か取得することはできた\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"12:1-12:12\"\u003e\n\u003cspan id=\"やり方\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%82%84%E3%82%8A%E6%96%B9\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eやり方\u003c/h2\u003e\n\u003ch3 data-sourcepos=\"14:1-14:45\"\u003e\n\u003cspan id=\"chartjsをそのまま使うパターン\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#chartjs%E3%82%92%E3%81%9D%E3%81%AE%E3%81%BE%E3%81%BE%E4%BD%BF%E3%81%86%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eChart.jsをそのまま使うパターン\u003c/h3\u003e\n\u003cul data-sourcepos=\"16:1-17:0\"\u003e\n\u003cli data-sourcepos=\"16:1-17:0\"\u003eこんなグラフがあったとする\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"html\" data-sourcepos=\"18:1-42:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"nt\"\u003e\u0026lt;html\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"nt\"\u003e\u0026lt;head\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e\u0026lt;meta\u003c/span\u003e \u003cspan class=\"na\"\u003echarset=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"utf-8\"\u003c/span\u003e \u003cspan class=\"nt\"\u003e/\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e\u0026lt;title\u0026gt;\u003c/span\u003echartjs sample\u003cspan class=\"nt\"\u003e\u0026lt;/title\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"nt\"\u003e\u0026lt;/head\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"nt\"\u003e\u0026lt;canvas\u003c/span\u003e \u003cspan class=\"na\"\u003eid=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"myChart\"\u003c/span\u003e\u003cspan class=\"nt\"\u003e\u0026gt;\u0026lt;/canvas\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"nt\"\u003e\u0026lt;script \u003c/span\u003e\u003cspan class=\"na\"\u003esrc=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"https://cdn.jsdelivr.net/npm/chart.js@2.8.0\"\u003c/span\u003e\u003cspan class=\"nt\"\u003e\u0026gt;\u0026lt;/script\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"nt\"\u003e\u0026lt;script\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003ectx\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003edocument\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003egetElementById\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003emyChart\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e).\u003c/span\u003e\u003cspan class=\"nf\"\u003egetContext\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003e2d\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n    \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003echart\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"nc\"\u003eChart\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ectx\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n      \u003cspan class=\"na\"\u003etype\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003ehorizontalBar\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n      \u003cspan class=\"na\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n        \u003cspan class=\"na\"\u003elabels\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eJanuary\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eFebruary\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eMarch\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eApril\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eMay\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eJune\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eJuly\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e\n        \u003cspan class=\"na\"\u003edatasets\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\n          \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n            \u003cspan class=\"na\"\u003elabel\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eMy First dataset\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n            \u003cspan class=\"na\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e10\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e5\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e20\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e30\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e45\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e\n          \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n        \u003cspan class=\"p\"\u003e],\u003c/span\u003e\n      \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e});\u003c/span\u003e\n  \u003cspan class=\"nt\"\u003e\u0026lt;/script\u0026gt;\u003c/span\u003e\n\u003cspan class=\"nt\"\u003e\u0026lt;/html\u0026gt;\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cul data-sourcepos=\"44:1-46:0\"\u003e\n\u003cli data-sourcepos=\"44:1-46:0\"\u003e見た目はこんな感じ\n\u003cul data-sourcepos=\"45:5-46:0\"\u003e\n\u003cli data-sourcepos=\"45:5-46:0\"\u003e棒グラフの部分にホバーするとツールチップが出る\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp data-sourcepos=\"47:1-47:163\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F10989365-50c3-5e3f-70d1-794f47623158.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=b091098618b11010c9f9887e9052933d\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F10989365-50c3-5e3f-70d1-794f47623158.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=b091098618b11010c9f9887e9052933d\" alt=\"スクリーンショット 2020-09-01 23.58.38.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F10989365-50c3-5e3f-70d1-794f47623158.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=14911c8b41fb694161c9be2dc415960d 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/10989365-50c3-5e3f-70d1-794f47623158.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cul data-sourcepos=\"50:1-51:0\"\u003e\n\u003cli data-sourcepos=\"50:1-51:0\"\u003eクリックイベントを検知\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"html\" data-sourcepos=\"52:1-83:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"nt\"\u003e\u0026lt;html\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"nt\"\u003e\u0026lt;head\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e\u0026lt;meta\u003c/span\u003e \u003cspan class=\"na\"\u003echarset=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"utf-8\"\u003c/span\u003e \u003cspan class=\"nt\"\u003e/\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e\u0026lt;title\u0026gt;\u003c/span\u003echartjs sample\u003cspan class=\"nt\"\u003e\u0026lt;/title\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"nt\"\u003e\u0026lt;/head\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"nt\"\u003e\u0026lt;canvas\u003c/span\u003e \u003cspan class=\"na\"\u003eid=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"myChart\"\u003c/span\u003e\u003cspan class=\"nt\"\u003e\u0026gt;\u0026lt;/canvas\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"nt\"\u003e\u0026lt;script \u003c/span\u003e\u003cspan class=\"na\"\u003esrc=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"https://cdn.jsdelivr.net/npm/chart.js@2.8.0\"\u003c/span\u003e\u003cspan class=\"nt\"\u003e\u0026gt;\u0026lt;/script\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"nt\"\u003e\u0026lt;script\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003ectx\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003edocument\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003egetElementById\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003emyChart\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e).\u003c/span\u003e\u003cspan class=\"nf\"\u003egetContext\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003e2d\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n    \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003echart\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"nc\"\u003eChart\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ectx\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n      \u003cspan class=\"na\"\u003etype\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003ehorizontalBar\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n      \u003cspan class=\"na\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n        \u003cspan class=\"na\"\u003elabels\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eJanuary\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eFebruary\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eMarch\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eApril\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eMay\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eJune\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eJuly\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e\n        \u003cspan class=\"na\"\u003edatasets\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\n          \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n            \u003cspan class=\"na\"\u003elabel\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eMy First dataset\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n            \u003cspan class=\"na\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e10\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e5\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e20\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e30\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e45\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e\n          \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n        \u003cspan class=\"p\"\u003e],\u003c/span\u003e\n      \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e});\u003c/span\u003e\n    \u003cspan class=\"c1\"\u003e// ここから追加\u003c/span\u003e\n    \u003cspan class=\"nb\"\u003edocument\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003egetElementById\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003emyChart\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e).\u003c/span\u003e\u003cspan class=\"nf\"\u003eaddEventListener\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eclick\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003ee\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n      \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003eelements\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003echart\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003egetElementAtEvent\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ee\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n      \u003cspan class=\"k\"\u003eif \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eelements\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003elength\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n        \u003cspan class=\"nf\"\u003ealert\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e`\u003c/span\u003e\u003cspan class=\"p\"\u003e${\u003c/span\u003e\u003cspan class=\"nx\"\u003eelements\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e].\u003c/span\u003e\u003cspan class=\"nx\"\u003e_index\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e: \u003c/span\u003e\u003cspan class=\"p\"\u003e${\u003c/span\u003e\u003cspan class=\"nx\"\u003eelements\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e].\u003c/span\u003e\u003cspan class=\"nx\"\u003e_model\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003elabel\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e`\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n      \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e});\u003c/span\u003e\n  \u003cspan class=\"nt\"\u003e\u0026lt;/script\u0026gt;\u003c/span\u003e\n\u003cspan class=\"nt\"\u003e\u0026lt;/html\u0026gt;\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cul data-sourcepos=\"85:1-89:0\"\u003e\n\u003cli data-sourcepos=\"85:1-85:44\"\u003e最後の方に処理を追加しました\u003c/li\u003e\n\u003cli data-sourcepos=\"86:1-86:134\"\u003echart.jsのcanvas全体に対するクリックを検知して\u003ccode\u003egetElementAtEvent\u003c/code\u003eを使うことでいろいろ情報がとれます\u003c/li\u003e\n\u003cli data-sourcepos=\"87:1-89:0\"\u003e\n\u003ccode\u003e_index\u003c/code\u003eで何番目の項目\u003ccode\u003e_model.label\u003c/code\u003eでラベルの値などとることができます\n\u003cul data-sourcepos=\"88:5-89:0\"\u003e\n\u003cli data-sourcepos=\"88:5-89:0\"\u003eこのindexなどを使うことで冒頭に書いたやりたいことで言う遷移先などを判別することができます\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp data-sourcepos=\"90:1-90:163\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fa1c19ce1-9dc6-b985-5e75-2149e40f273d.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=ed6065c6cc23bd84578bdb6f2b99fc09\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fa1c19ce1-9dc6-b985-5e75-2149e40f273d.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=ed6065c6cc23bd84578bdb6f2b99fc09\" alt=\"スクリーンショット 2020-09-01 23.59.02.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fa1c19ce1-9dc6-b985-5e75-2149e40f273d.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=297f3df0fd714010fc1583b10a1d6052 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/a1c19ce1-9dc6-b985-5e75-2149e40f273d.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cul data-sourcepos=\"92:1-95:0\"\u003e\n\u003cli data-sourcepos=\"92:1-92:101\"\u003eこれではラベルのクリックは検知できず棒グラフの部分だけ検知できます\u003c/li\u003e\n\u003cli data-sourcepos=\"93:1-95:0\"\u003eただ、軸の最小値を1以上にするとラベルのクリックも反応するようになりました\n\u003cul data-sourcepos=\"94:5-95:0\"\u003e\n\u003cli data-sourcepos=\"94:5-95:0\"\u003eこの辺はたまたまこれでいける要件ならって感じですね\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"html\" data-sourcepos=\"96:1-138:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"nt\"\u003e\u0026lt;html\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"nt\"\u003e\u0026lt;head\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e\u0026lt;meta\u003c/span\u003e \u003cspan class=\"na\"\u003echarset=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"utf-8\"\u003c/span\u003e \u003cspan class=\"nt\"\u003e/\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"nt\"\u003e\u0026lt;title\u0026gt;\u003c/span\u003echartjs sample\u003cspan class=\"nt\"\u003e\u0026lt;/title\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"nt\"\u003e\u0026lt;/head\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"nt\"\u003e\u0026lt;canvas\u003c/span\u003e \u003cspan class=\"na\"\u003eid=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"myChart\"\u003c/span\u003e\u003cspan class=\"nt\"\u003e\u0026gt;\u0026lt;/canvas\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"nt\"\u003e\u0026lt;script \u003c/span\u003e\u003cspan class=\"na\"\u003esrc=\u003c/span\u003e\u003cspan class=\"s\"\u003e\"https://cdn.jsdelivr.net/npm/chart.js@2.8.0\"\u003c/span\u003e\u003cspan class=\"nt\"\u003e\u0026gt;\u0026lt;/script\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"nt\"\u003e\u0026lt;script\u0026gt;\u003c/span\u003e\n    \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003ectx\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003edocument\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003egetElementById\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003emyChart\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e).\u003c/span\u003e\u003cspan class=\"nf\"\u003egetContext\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003e2d\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n    \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003echart\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"nc\"\u003eChart\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ectx\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n      \u003cspan class=\"na\"\u003etype\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003ehorizontalBar\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n      \u003cspan class=\"na\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n        \u003cspan class=\"na\"\u003elabels\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eJanuary\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eFebruary\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eMarch\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eApril\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eMay\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eJune\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eJuly\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e\n        \u003cspan class=\"na\"\u003edatasets\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\n          \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n            \u003cspan class=\"na\"\u003elabel\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eMy First dataset\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n            \u003cspan class=\"na\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e10\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e5\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e20\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e30\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e45\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e\n          \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n        \u003cspan class=\"p\"\u003e],\u003c/span\u003e\n      \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n      \u003cspan class=\"c1\"\u003e// 追加した部分\u003c/span\u003e\n      \u003cspan class=\"na\"\u003eoptions\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n        \u003cspan class=\"na\"\u003escales\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n          \u003cspan class=\"na\"\u003exAxes\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\n            \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n              \u003cspan class=\"na\"\u003eticks\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n                \u003cspan class=\"na\"\u003emin\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n              \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n            \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n          \u003cspan class=\"p\"\u003e],\u003c/span\u003e\n        \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n      \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e});\u003c/span\u003e\n    \u003cspan class=\"nb\"\u003edocument\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003egetElementById\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003emyChart\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e).\u003c/span\u003e\u003cspan class=\"nf\"\u003eaddEventListener\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eclick\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003ee\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n      \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003eelements\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003echart\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003egetElementAtEvent\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ee\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n      \u003cspan class=\"k\"\u003eif \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eelements\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003elength\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n        \u003cspan class=\"nf\"\u003ealert\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e`\u003c/span\u003e\u003cspan class=\"p\"\u003e${\u003c/span\u003e\u003cspan class=\"nx\"\u003eelements\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e].\u003c/span\u003e\u003cspan class=\"nx\"\u003e_index\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e: \u003c/span\u003e\u003cspan class=\"p\"\u003e${\u003c/span\u003e\u003cspan class=\"nx\"\u003eelements\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e].\u003c/span\u003e\u003cspan class=\"nx\"\u003e_model\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003elabel\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e`\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n      \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e});\u003c/span\u003e\n  \u003cspan class=\"nt\"\u003e\u0026lt;/script\u0026gt;\u003c/span\u003e\n\u003cspan class=\"nt\"\u003e\u0026lt;/html\u0026gt;\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003ch2 data-sourcepos=\"140:1-140:32\"\u003e\n\u003cspan id=\"reactを使ったパターン\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#react%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%9F%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eReactを使ったパターン\u003c/h2\u003e\n\u003cul data-sourcepos=\"142:1-144:0\"\u003e\n\u003cli data-sourcepos=\"142:1-142:53\"\u003ereact-chartjs-2を使った場合のやり方です\u003c/li\u003e\n\u003cli data-sourcepos=\"143:1-144:0\"\u003e以下のコマンドでライブラリを事前に追加しておく\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"sh\" data-sourcepos=\"145:1-147:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003eyarn add chart.js react-chartjs-2\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cul data-sourcepos=\"149:1-150:0\"\u003e\n\u003cli data-sourcepos=\"149:1-150:0\"\u003eまずはグラフを出すだけだとこんな感じ\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"jsx\" data-sourcepos=\"151:1-179:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003eApp.js\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"k\"\u003eimport\u003c/span\u003e \u003cspan class=\"nx\"\u003eReact\u003c/span\u003e \u003cspan class=\"k\"\u003efrom\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003ereact\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003cspan class=\"k\"\u003eimport\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"nx\"\u003eHorizontalBar\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"k\"\u003efrom\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003ereact-chartjs-2\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\n\u003cspan class=\"kd\"\u003efunction\u003c/span\u003e \u003cspan class=\"nf\"\u003eApp\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n  \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003edata\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"na\"\u003elabels\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eJanuary\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eFebruary\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eMarch\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eApril\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eMay\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eJune\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eJuly\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e\n    \u003cspan class=\"na\"\u003edatasets\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\n      \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n        \u003cspan class=\"na\"\u003elabel\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eMy First dataset\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n        \u003cspan class=\"na\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e10\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e5\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e20\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e30\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e45\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e\n      \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e],\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\n  \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003eonElementsClick\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003eelements\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eelements\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n    \u003cspan class=\"k\"\u003eif \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eelements\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003elength\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n        \u003cspan class=\"nf\"\u003ealert\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e`\u003c/span\u003e\u003cspan class=\"p\"\u003e${\u003c/span\u003e\u003cspan class=\"nx\"\u003eelements\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e].\u003c/span\u003e\u003cspan class=\"nx\"\u003e_index\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e: \u003c/span\u003e\u003cspan class=\"p\"\u003e${\u003c/span\u003e\u003cspan class=\"nx\"\u003eelements\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e].\u003c/span\u003e\u003cspan class=\"nx\"\u003e_model\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003elabel\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e`\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\n  \u003cspan class=\"k\"\u003ereturn \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nc\"\u003eHorizontalBar\u003c/span\u003e \u003cspan class=\"na\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"nx\"\u003edata\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e \u003cspan class=\"na\"\u003eonElementsClick\u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"nx\"\u003eonElementsClick\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e \u003cspan class=\"p\"\u003e/\u0026gt;\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\n\u003cspan class=\"k\"\u003eexport\u003c/span\u003e \u003cspan class=\"k\"\u003edefault\u003c/span\u003e \u003cspan class=\"nx\"\u003eApp\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cul data-sourcepos=\"181:1-183:0\"\u003e\n\u003cli data-sourcepos=\"181:1-181:43\"\u003e見た目はReactじゃない時と同じ\u003c/li\u003e\n\u003cli data-sourcepos=\"182:1-183:0\"\u003eクリックの検知を入れるとこんな感じ\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"jsx\" data-sourcepos=\"184:1-209:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003eApp.js\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"k\"\u003eimport\u003c/span\u003e \u003cspan class=\"nx\"\u003eReact\u003c/span\u003e \u003cspan class=\"k\"\u003efrom\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003ereact\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003cspan class=\"k\"\u003eimport\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"nx\"\u003eHorizontalBar\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"k\"\u003efrom\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003ereact-chartjs-2\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\n\u003cspan class=\"kd\"\u003efunction\u003c/span\u003e \u003cspan class=\"nf\"\u003eApp\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n  \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003edata\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"na\"\u003elabels\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eJanuary\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eFebruary\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eMarch\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eApril\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eMay\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eJune\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eJuly\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e\n    \u003cspan class=\"na\"\u003edatasets\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\n      \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n        \u003cspan class=\"na\"\u003elabel\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eMy First dataset\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n        \u003cspan class=\"na\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e10\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e5\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e20\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e30\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e45\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e\n      \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e],\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e};\u003c/span\u003e\n  \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003eonElementsClick\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003eelements\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eonElementsClick\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n    \u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eelements\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n    \u003cspan class=\"k\"\u003eif \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eelements\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003elength\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n      \u003cspan class=\"nf\"\u003ealert\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e`\u003c/span\u003e\u003cspan class=\"p\"\u003e${\u003c/span\u003e\u003cspan class=\"nx\"\u003eelements\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e].\u003c/span\u003e\u003cspan class=\"nx\"\u003e_index\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e: \u003c/span\u003e\u003cspan class=\"p\"\u003e${\u003c/span\u003e\u003cspan class=\"nx\"\u003eelements\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e].\u003c/span\u003e\u003cspan class=\"nx\"\u003e_model\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003elabel\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e`\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e};\u003c/span\u003e\n  \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"p\"\u003e\u0026lt;\u003c/span\u003e\u003cspan class=\"nc\"\u003eHorizontalBar\u003c/span\u003e \u003cspan class=\"na\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"nx\"\u003edata\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e \u003cspan class=\"na\"\u003eonElementsClick\u003c/span\u003e\u003cspan class=\"p\"\u003e=\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"nx\"\u003eonElementsClick\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e \u003cspan class=\"p\"\u003e/\u0026gt;\u003c/span\u003e\n\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\n\u003cspan class=\"k\"\u003eexport\u003c/span\u003e \u003cspan class=\"k\"\u003edefault\u003c/span\u003e \u003cspan class=\"nx\"\u003eApp\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cul data-sourcepos=\"211:1-213:0\"\u003e\n\u003cli data-sourcepos=\"211:1-213:0\"\u003eこれでどの項目をクリックしたか検知することができる\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"214:1-214:12\"\u003e\n\u003cspan id=\"最後に\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E6%9C%80%E5%BE%8C%E3%81%AB\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e最後に\u003c/h2\u003e\n\u003cul data-sourcepos=\"216:1-217:44\"\u003e\n\u003cli data-sourcepos=\"216:1-216:97\"\u003eChart.jsがcanvasで描画されるのでこまかいことやろうとすると大変ですね\u003c/li\u003e\n\u003cli data-sourcepos=\"217:1-217:44\"\u003e最低限やりたいことはできそう\u003c/li\u003e\n\u003c/ul\u003e\n","body":"## やりたいこと\n\n- 下図のようなグラフでラベル部分(Januaryなど)をクリックすると詳細ページに遷移するといった動きを実現したかった\n\n![スクリーンショット 2020-09-01 23.33.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/760956a3-4533-385d-e302-e7fa608e2e53.png)\n\n## 結論\n\n- きれいに実装するのは無理だった\n- グラフのクリックを検知してクリックしたのがどの項目か取得することはできた\n\n## やり方\n\n### Chart.jsをそのまま使うパターン\n\n- こんなグラフがあったとする\n\n```html\n\u003chtml\u003e\n  \u003chead\u003e\n    \u003cmeta charset=\"utf-8\" /\u003e\n    \u003ctitle\u003echartjs sample\u003c/title\u003e\n  \u003c/head\u003e\n  \u003ccanvas id=\"myChart\"\u003e\u003c/canvas\u003e\n  \u003cscript src=\"https://cdn.jsdelivr.net/npm/chart.js@2.8.0\"\u003e\u003c/script\u003e\n  \u003cscript\u003e\n    const ctx = document.getElementById('myChart').getContext('2d');\n    const chart = new Chart(ctx, {\n      type: 'horizontalBar',\n      data: {\n        labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],\n        datasets: [\n          {\n            label: 'My First dataset',\n            data: [3, 10, 5, 2, 20, 30, 45],\n          },\n        ],\n      },\n    });\n  \u003c/script\u003e\n\u003c/html\u003e\n```\n\n- 見た目はこんな感じ\n    - 棒グラフの部分にホバーするとツールチップが出る\n\n![スクリーンショット 2020-09-01 23.58.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/10989365-50c3-5e3f-70d1-794f47623158.png)\n\n\n- クリックイベントを検知\n\n```html\n\u003chtml\u003e\n  \u003chead\u003e\n    \u003cmeta charset=\"utf-8\" /\u003e\n    \u003ctitle\u003echartjs sample\u003c/title\u003e\n  \u003c/head\u003e\n  \u003ccanvas id=\"myChart\"\u003e\u003c/canvas\u003e\n  \u003cscript src=\"https://cdn.jsdelivr.net/npm/chart.js@2.8.0\"\u003e\u003c/script\u003e\n  \u003cscript\u003e\n    const ctx = document.getElementById('myChart').getContext('2d');\n    const chart = new Chart(ctx, {\n      type: 'horizontalBar',\n      data: {\n        labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],\n        datasets: [\n          {\n            label: 'My First dataset',\n            data: [3, 10, 5, 2, 20, 30, 45],\n          },\n        ],\n      },\n    });\n    // ここから追加\n    document.getElementById('myChart').addEventListener('click', e =\u003e {\n      const elements = chart.getElementAtEvent(e);\n      if (elements.length) {\n        alert(`${elements[0]._index}: ${elements[0]._model.label}`);\n      }\n    });\n  \u003c/script\u003e\n\u003c/html\u003e\n```\n\n- 最後の方に処理を追加しました\n- chart.jsのcanvas全体に対するクリックを検知して`getElementAtEvent`を使うことでいろいろ情報がとれます\n- `_index`で何番目の項目`_model.label`でラベルの値などとることができます\n    - このindexなどを使うことで冒頭に書いたやりたいことで言う遷移先などを判別することができます\n\n![スクリーンショット 2020-09-01 23.59.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/a1c19ce1-9dc6-b985-5e75-2149e40f273d.png)\n\n- これではラベルのクリックは検知できず棒グラフの部分だけ検知できます\n- ただ、軸の最小値を1以上にするとラベルのクリックも反応するようになりました\n    - この辺はたまたまこれでいける要件ならって感じですね\n\n```html\n\u003chtml\u003e\n  \u003chead\u003e\n    \u003cmeta charset=\"utf-8\" /\u003e\n    \u003ctitle\u003echartjs sample\u003c/title\u003e\n  \u003c/head\u003e\n  \u003ccanvas id=\"myChart\"\u003e\u003c/canvas\u003e\n  \u003cscript src=\"https://cdn.jsdelivr.net/npm/chart.js@2.8.0\"\u003e\u003c/script\u003e\n  \u003cscript\u003e\n    const ctx = document.getElementById('myChart').getContext('2d');\n    const chart = new Chart(ctx, {\n      type: 'horizontalBar',\n      data: {\n        labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],\n        datasets: [\n          {\n            label: 'My First dataset',\n            data: [3, 10, 5, 2, 20, 30, 45],\n          },\n        ],\n      },\n      // 追加した部分\n      options: {\n        scales: {\n          xAxes: [\n            {\n              ticks: {\n                min: 1,\n              },\n            },\n          ],\n        },\n      },\n    });\n    document.getElementById('myChart').addEventListener('click', e =\u003e {\n      const elements = chart.getElementAtEvent(e);\n      if (elements.length) {\n        alert(`${elements[0]._index}: ${elements[0]._model.label}`);\n      }\n    });\n  \u003c/script\u003e\n\u003c/html\u003e\n```\n\n## Reactを使ったパターン\n\n- react-chartjs-2を使った場合のやり方です\n- 以下のコマンドでライブラリを事前に追加しておく\n\n```sh\nyarn add chart.js react-chartjs-2\n```\n\n- まずはグラフを出すだけだとこんな感じ\n\n```jsx:App.js\nimport React from 'react';\nimport { HorizontalBar } from 'react-chartjs-2';\n\nfunction App() {\n  const data = {\n    labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],\n    datasets: [\n      {\n        label: 'My First dataset',\n        data: [3, 10, 5, 2, 20, 30, 45],\n      },\n    ],\n  };\n\n  const onElementsClick = elements =\u003e {\n    console.log(elements);\n    if (elements.length) {\n        alert(`${elements[0]._index}: ${elements[0]._model.label}`);\n    }\n  };\n\n  return (\n    \u003cHorizontalBar data={data} onElementsClick={onElementsClick} /\u003e\n  );\n}\n\nexport default App;\n```\n\n- 見た目はReactじゃない時と同じ\n- クリックの検知を入れるとこんな感じ\n\n```jsx:App.js\nimport React from 'react';\nimport { HorizontalBar } from 'react-chartjs-2';\n\nfunction App() {\n  const data = {\n    labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'],\n    datasets: [\n      {\n        label: 'My First dataset',\n        data: [3, 10, 5, 2, 20, 30, 45],\n      },\n    ],\n  };\n  const onElementsClick = elements =\u003e {\n    console.log('onElementsClick');\n    console.log(elements);\n    if (elements.length) {\n      alert(`${elements[0]._index}: ${elements[0]._model.label}`);\n    }\n  };\n  return \u003cHorizontalBar data={data} onElementsClick={onElementsClick} /\u003e\n}\n\nexport default App;\n```\n\n- これでどの項目をクリックしたか検知することができる\n\n\n## 最後に\n\n- Chart.jsがcanvasで描画されるのでこまかいことやろうとすると大変ですね\n- 最低限やりたいことはできそう\n","coediting":false,"comments_count":0,"created_at":"2020-09-02T00:00:35+09:00","group":null,"id":"448d4b1ff8549ccd0e83","likes_count":15,"private":false,"reactions_count":0,"stocks_count":6,"tags":[{"name":"chart.js","versions":[]},{"name":"React","versions":[]}],"title":"[Chart.js]グラフのクリックを検知したい","updated_at":"2021-10-26T18:50:02+09:00","url":"https://qiita.com/ozaki25/items/448d4b1ff8549ccd0e83","user":{"description":"","facebook_id":"","followees_count":0,"followers_count":79,"github_login_name":"ozaki25","id":"ozaki25","items_count":112,"linkedin_id":"","location":"","name":"","organization":"","permanent_id":175213,"profile_image_url":"https://qiita-image-store.s3.amazonaws.com/0/175213/profile-images/1491825881","team_only":false,"twitter_screen_name":"ozaki25rn","website_url":""},"page_views_count":null,"team_membership":null,"organization_url_name":null,"slide":false,"posting_campaign_uuid":null},{"rendered_body":"\u003ch2 data-sourcepos=\"1:1-1:9\"\u003e\n\u003cspan id=\"概要\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E6%A6%82%E8%A6%81\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e概要\u003c/h2\u003e\n\u003cul data-sourcepos=\"3:1-8:0\"\u003e\n\u003cli data-sourcepos=\"3:1-4:132\"\u003e以前にQiitaの前日のいいね数を通知してくれるLINE Botを作りました\n\u003cul data-sourcepos=\"4:5-4:132\"\u003e\n\u003cli data-sourcepos=\"4:5-4:132\"\u003e\u003ca href=\"https://qiita.com/ozaki25/items/03b834de8c0c175d955d\" id=\"reference-abd45f84a9ebfd0bf3b3\"\u003eQiitaのいいね数を通知してくれるLINE Botを作ってみた\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"5:1-8:0\"\u003eこの延長でいいね数の推移をグラフで表示するWebページを作ってみたというのが今回の内容です\n\u003cul data-sourcepos=\"6:5-8:0\"\u003e\n\u003cli data-sourcepos=\"6:5-6:92\"\u003eせっかくなので技術検証も兼ねてJamstackな構成で作ってみました\u003c/li\u003e\n\u003cli data-sourcepos=\"7:5-8:0\"\u003e前述の記事の通りデータはDynamoDBにデータを蓄積しているのでHeadlessCMSなどは使わずにチャレンジしてみます\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"9:1-9:18\"\u003e\n\u003cspan id=\"作ったもの\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E4%BD%9C%E3%81%A3%E3%81%9F%E3%82%82%E3%81%AE\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e作ったもの\u003c/h2\u003e\n\u003cul data-sourcepos=\"11:1-12:0\"\u003e\n\u003cli data-sourcepos=\"11:1-12:0\"\u003e直近1週間のいいね数をグラフで表示するWebアプリをLIFFアプリとして表示しています\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp data-sourcepos=\"13:1-13:142\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fa656b608-38c1-afa4-52ac-1428c92e63a8.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=0fa7048e13bbf212645f3aeaa9f8686d\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fa656b608-38c1-afa4-52ac-1428c92e63a8.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=0fa7048e13bbf212645f3aeaa9f8686d\" alt=\"Screenshot_20200726-014511.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fa656b608-38c1-afa4-52ac-1428c92e63a8.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=7648f8358f0d37a73ef8f1f5c6d8557e 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/a656b608-38c1-afa4-52ac-1428c92e63a8.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003ch2 data-sourcepos=\"15:1-15:9\"\u003e\n\u003cspan id=\"構成\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E6%A7%8B%E6%88%90\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e構成\u003c/h2\u003e\n\u003cul data-sourcepos=\"17:1-19:0\"\u003e\n\u003cli data-sourcepos=\"17:1-19:0\"\u003eJamstackな構成とするためにいろいろ使っているので少々複雑です\n\u003cul data-sourcepos=\"18:5-19:0\"\u003e\n\u003cli data-sourcepos=\"18:5-19:0\"\u003e要素ごとに何をしているのか説明していきます\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F28b07683-d3cc-c289-2f83-3088c5f0b064.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=c1400157ba95abd6cd1e9a9b2c84e1a4\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg width=\"861\" alt=\"スクリーンショット 2020-07-26 1.33.56.png\" src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F28b07683-d3cc-c289-2f83-3088c5f0b064.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=c1400157ba95abd6cd1e9a9b2c84e1a4\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F28b07683-d3cc-c289-2f83-3088c5f0b064.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=c1d45fc238f1bc326487867a9d2a86fd 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/28b07683-d3cc-c289-2f83-3088c5f0b064.png\" loading=\"lazy\"\u003e\u003c/a\u003e\n\u003ch2 data-sourcepos=\"22:1-22:24\"\u003e\n\u003cspan id=\"実装済みの機能\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E5%AE%9F%E8%A3%85%E6%B8%88%E3%81%BF%E3%81%AE%E6%A9%9F%E8%83%BD\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e実装済みの機能\u003c/h2\u003e\n\u003cul data-sourcepos=\"24:1-25:0\"\u003e\n\u003cli data-sourcepos=\"24:1-25:0\"\u003eQiitaのいいね数を蓄積していく処理は前述の記事で紹介済みです\u003c/li\u003e\n\u003c/ul\u003e\n\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F7d2f814c-1658-16bf-c840-40baec2f6821.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=02ea685ee2d11cc0ce3bddc2889989f8\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg width=\"859\" alt=\"スクリーンショット 2020-07-26 2.10.04.png\" src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F7d2f814c-1658-16bf-c840-40baec2f6821.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=02ea685ee2d11cc0ce3bddc2889989f8\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F7d2f814c-1658-16bf-c840-40baec2f6821.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=f52efbab7664ede208e74ce4f5b4dd92 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/7d2f814c-1658-16bf-c840-40baec2f6821.png\" loading=\"lazy\"\u003e\u003c/a\u003e\n\u003cul data-sourcepos=\"28:1-30:0\"\u003e\n\u003cli data-sourcepos=\"28:1-28:87\"\u003eLambdaでQiitaのAPIを叩き取得したデータをDynamoDBに保存しています\u003c/li\u003e\n\u003cli data-sourcepos=\"29:1-30:0\"\u003eLambdaのバッチ実行で毎日0時に実行しています\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"31:1-31:51\"\u003e\n\u003cspan id=\"nextjsを使ったjamstackな仕組みの構築\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#nextjs%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%9Fjamstack%E3%81%AA%E4%BB%95%E7%B5%84%E3%81%BF%E3%81%AE%E6%A7%8B%E7%AF%89\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eNext.jsを使ったJamstackな仕組みの構築\u003c/h2\u003e\n\u003cul data-sourcepos=\"33:1-34:0\"\u003e\n\u003cli data-sourcepos=\"33:1-34:0\"\u003eまずはビルドデプロイ自動化の仕組みは置いておいてアプリの部分からです\u003c/li\u003e\n\u003c/ul\u003e\n\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F90750e74-6d60-3372-701c-d391ef63f642.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=54944fe1bebe5c2e6b4caed8fb8faf1e\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg width=\"859\" alt=\"スクリーンショット 2020-07-26 1.53.02.png\" src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F90750e74-6d60-3372-701c-d391ef63f642.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=54944fe1bebe5c2e6b4caed8fb8faf1e\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F90750e74-6d60-3372-701c-d391ef63f642.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=6fdb1114aa87f3e6cc7db12f9376575f 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/90750e74-6d60-3372-701c-d391ef63f642.png\" loading=\"lazy\"\u003e\u003c/a\u003e\n\u003cul data-sourcepos=\"37:1-43:0\"\u003e\n\u003cli data-sourcepos=\"37:1-37:137\"\u003eJamstackの最大の特徴であるビルド時にAPIアクセスしてデータ埋め込んだHTMLを生成するという部分です\u003c/li\u003e\n\u003cli data-sourcepos=\"38:1-40:114\"\u003eビルド時にAPIアクセスできるライブラリはいくつかありますが今回はReactベースのNext.jsを使いました\n\u003cul data-sourcepos=\"39:5-40:114\"\u003e\n\u003cli data-sourcepos=\"39:5-39:257\"\u003e\n\u003ca href=\"https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation\" rel=\"nofollow noopener\" target=\"_blank\"\u003egetStaticProps\u003c/a\u003eと\u003ca href=\"https://nextjs.org/docs/basic-features/data-fetching#getstaticpaths-static-generation\" rel=\"nofollow noopener\" target=\"_blank\"\u003egetStaticPaths\u003c/a\u003eを使えれば簡単に実現できます\u003c/li\u003e\n\u003cli data-sourcepos=\"40:5-40:114\"\u003e\n\u003ca href=\"https://jamstack-handson.ozaki25.now.sh/page2.html\" rel=\"nofollow noopener\" target=\"_blank\"\u003eこんな資料\u003c/a\u003eも過去に作ったのでご参考で\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"41:1-43:0\"\u003eこの記事はJamstackにフォーカスしているので深く触れませんがLIFF SDKを入れてLIFFアプリ化しています\n\u003cul data-sourcepos=\"42:5-43:0\"\u003e\n\u003cli data-sourcepos=\"42:5-43:0\"\u003eLINE IDを取得できるのでログイン機能いらず！\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"44:1-44:47\"\u003e\n\u003cspan id=\"github-actionsでvercelに自動デプロイ\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#github-actions%E3%81%A7vercel%E3%81%AB%E8%87%AA%E5%8B%95%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eGitHub ActionsでVercelに自動デプロイ\u003c/h2\u003e\n\u003cul data-sourcepos=\"46:1-47:0\"\u003e\n\u003cli data-sourcepos=\"46:1-47:0\"\u003e次はNext.jsで作ったアプリのデプロイ周りです\u003c/li\u003e\n\u003c/ul\u003e\n\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Ffda0475a-0f75-6769-8601-3629c7b3ab31.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=e3e4cdb4023833a229845ff25f1d7523\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg width=\"859\" alt=\"スクリーンショット 2020-07-26 1.59.32.png\" src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Ffda0475a-0f75-6769-8601-3629c7b3ab31.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=e3e4cdb4023833a229845ff25f1d7523\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Ffda0475a-0f75-6769-8601-3629c7b3ab31.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=63a43dce9851410d32289851a0049a51 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/fda0475a-0f75-6769-8601-3629c7b3ab31.png\" loading=\"lazy\"\u003e\u003c/a\u003e\n\u003cul data-sourcepos=\"50:1-55:0\"\u003e\n\u003cli data-sourcepos=\"50:1-50:74\"\u003e今回はホスティングサービスとしてVercelを使いました\u003c/li\u003e\n\u003cli data-sourcepos=\"51:1-53:62\"\u003eさらにGitHub Actionsで自動的にデプロイできる仕組みを作りました\n\u003cul data-sourcepos=\"52:5-53:62\"\u003e\n\u003cli data-sourcepos=\"52:5-53:62\"\u003eVercel用のActionが公式であったのでそれを使っています\n\u003cul data-sourcepos=\"53:9-53:62\"\u003e\n\u003cli data-sourcepos=\"53:9-53:62\"\u003e\u003ca href=\"https://github.com/marketplace/actions/vercel-action\" class=\"autolink\" rel=\"nofollow noopener\" target=\"_blank\"\u003ehttps://github.com/marketplace/actions/vercel-action\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"54:1-55:0\"\u003eGitHub Actions起動のトリガーはmasterへのプッシュとAPIアクセスを設定しています\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"yml\" data-sourcepos=\"56:1-81:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003e.github/workflows/deploy.yml\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"na\"\u003ename\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003edeploy\u003c/span\u003e\n\u003cspan class=\"na\"\u003eon\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n  \u003cspan class=\"na\"\u003epush\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n    \u003cspan class=\"na\"\u003ebranches\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n      \u003cspan class=\"pi\"\u003e-\u003c/span\u003e \u003cspan class=\"s\"\u003emaster\u003c/span\u003e\n  \u003cspan class=\"na\"\u003erepository_dispatch\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n    \u003cspan class=\"na\"\u003ebranches\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n      \u003cspan class=\"pi\"\u003e-\u003c/span\u003e \u003cspan class=\"s\"\u003emaster\u003c/span\u003e\n\u003cspan class=\"na\"\u003ejobs\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n  \u003cspan class=\"na\"\u003edeploy\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n    \u003cspan class=\"na\"\u003eruns-on\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003eubuntu-latest\u003c/span\u003e\n    \u003cspan class=\"na\"\u003esteps\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n      \u003cspan class=\"pi\"\u003e-\u003c/span\u003e \u003cspan class=\"na\"\u003euses\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003eactions/checkout@v2\u003c/span\u003e\n      \u003cspan class=\"pi\"\u003e-\u003c/span\u003e \u003cspan class=\"na\"\u003ename\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003eUse Node.js \u003c/span\u003e\u003cspan class=\"m\"\u003e14\u003c/span\u003e\n        \u003cspan class=\"na\"\u003euses\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003eactions/setup-node@v1\u003c/span\u003e\n        \u003cspan class=\"na\"\u003ewith\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n          \u003cspan class=\"na\"\u003enode-version\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"m\"\u003e14\u003c/span\u003e\n      \u003cspan class=\"pi\"\u003e-\u003c/span\u003e \u003cspan class=\"na\"\u003euses\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003eamondnet/vercel-action@v19\u003c/span\u003e\n        \u003cspan class=\"na\"\u003ewith\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n          \u003cspan class=\"na\"\u003evercel-token\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003e${{ secrets.VERCEL_TOKEN }}\u003c/span\u003e\n          \u003cspan class=\"na\"\u003egithub-token\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003e${{ secrets.GITHUB_TOKEN }}\u003c/span\u003e\n          \u003cspan class=\"na\"\u003evercel-args\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s1\"\u003e'\u003c/span\u003e\u003cspan class=\"s\"\u003e--prod\u003c/span\u003e\u003cspan class=\"nv\"\u003e \u003c/span\u003e\u003cspan class=\"s\"\u003e-f\u003c/span\u003e\u003cspan class=\"nv\"\u003e \u003c/span\u003e\u003cspan class=\"s\"\u003e-b\u003c/span\u003e\u003cspan class=\"nv\"\u003e \u003c/span\u003e\u003cspan class=\"s\"\u003eTZ=Asia/Tokyo\u003c/span\u003e\u003cspan class=\"nv\"\u003e \u003c/span\u003e\u003cspan class=\"s\"\u003e-b\u003c/span\u003e\u003cspan class=\"nv\"\u003e \u003c/span\u003e\u003cspan class=\"s\"\u003eREACT_APP_LIFF_ID=${{\u003c/span\u003e\u003cspan class=\"nv\"\u003e \u003c/span\u003e\u003cspan class=\"s\"\u003esecrets.REACT_APP_LIFF_ID\u003c/span\u003e\u003cspan class=\"nv\"\u003e \u003c/span\u003e\u003cspan class=\"s\"\u003e}}\u003c/span\u003e\u003cspan class=\"nv\"\u003e \u003c/span\u003e\u003cspan class=\"s\"\u003e-b\u003c/span\u003e\u003cspan class=\"nv\"\u003e \u003c/span\u003e\u003cspan class=\"s\"\u003eREACT_APP_API_URL=${{\u003c/span\u003e\u003cspan class=\"nv\"\u003e \u003c/span\u003e\u003cspan class=\"s\"\u003esecrets.REACT_APP_API_URL\u003c/span\u003e\u003cspan class=\"nv\"\u003e \u003c/span\u003e\u003cspan class=\"s\"\u003e}}'\u003c/span\u003e\n          \u003cspan class=\"na\"\u003evercel-org-id\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003e${{ secrets.ORG_ID}}\u003c/span\u003e\n          \u003cspan class=\"na\"\u003evercel-project-id\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003e${{ secrets.PROJECT_ID}}\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003ch2 data-sourcepos=\"83:1-83:82\"\u003e\n\u003cspan id=\"dynamodbの更新をトリガーにlambdaを起動してgithub-actionsを叩く\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#dynamodb%E3%81%AE%E6%9B%B4%E6%96%B0%E3%82%92%E3%83%88%E3%83%AA%E3%82%AC%E3%83%BC%E3%81%ABlambda%E3%82%92%E8%B5%B7%E5%8B%95%E3%81%97%E3%81%A6github-actions%E3%82%92%E5%8F%A9%E3%81%8F\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eDynamoDBの更新をトリガーにLambdaを起動してGitHub Actionsを叩く\u003c/h2\u003e\n\u003cul data-sourcepos=\"85:1-86:0\"\u003e\n\u003cli data-sourcepos=\"85:1-86:0\"\u003e最後にJamstack自動化の肝となるデータ更新をトリガーにビルドデプロイを走らせる仕組みです\u003c/li\u003e\n\u003c/ul\u003e\n\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F7ae7a5f2-67dc-ac75-bd98-2ecbea5880fd.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=c491a195e29e16edb63233bae6ec2eb5\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg width=\"860\" alt=\"スクリーンショット 2020-07-26 2.07.57.png\" src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F7ae7a5f2-67dc-ac75-bd98-2ecbea5880fd.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=c491a195e29e16edb63233bae6ec2eb5\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F7ae7a5f2-67dc-ac75-bd98-2ecbea5880fd.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=1347b68e4810e0e05419b0a982e0b72b 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/7ae7a5f2-67dc-ac75-bd98-2ecbea5880fd.png\" loading=\"lazy\"\u003e\u003c/a\u003e\n\u003cul data-sourcepos=\"89:1-95:0\"\u003e\n\u003cli data-sourcepos=\"89:1-89:126\"\u003eDynamoDB Streamsを使うことで特定のテーブルの更新をトリガーにLambdaを起動することができます\u003c/li\u003e\n\u003cli data-sourcepos=\"90:1-92:78\"\u003eそしてGitHub ActionsをAPIアクセスで起動できるようにしているためLambdaからそのAPIを叩いています\n\u003cul data-sourcepos=\"91:5-92:78\"\u003e\n\u003cli data-sourcepos=\"91:5-91:148\"\u003eちょっとまわりくどいですがDynamoDBの更新をトリガーにGitHub Actionsを起動するために試行錯誤した結果です\u003c/li\u003e\n\u003cli data-sourcepos=\"92:5-92:78\"\u003e他のサービスを使ったりすればもう少し違ったかな？\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"93:1-95:0\"\u003e今回はLambdaやDynamoDB周りはServerlessFrameworkを使っています\n\u003cul data-sourcepos=\"94:5-95:0\"\u003e\n\u003cli data-sourcepos=\"94:5-95:0\"\u003eserverless.ymlはこんな感じです\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"yml\" data-sourcepos=\"96:1-153:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003eserverless.yml(抜粋)\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"na\"\u003eservice\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n  \u003cspan class=\"na\"\u003ename\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003eline-qiita-bot\u003c/span\u003e\n\u003cspan class=\"na\"\u003eprovider\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n  \u003cspan class=\"na\"\u003ename\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003eaws\u003c/span\u003e\n  \u003cspan class=\"na\"\u003eruntime\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003enodejs12.x\u003c/span\u003e\n  \u003cspan class=\"na\"\u003eregion\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003eap-northeast-1\u003c/span\u003e\n  \u003cspan class=\"na\"\u003eenvironment\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n    \u003cspan class=\"na\"\u003eDYNAMODB_TABLE\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003e${self:service.name}-${self:provider.stage}\u003c/span\u003e\n    \u003cspan class=\"na\"\u003eQIITA_HISTORY_TABLE\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003e${self:provider.environment.DYNAMODB_TABLE}-QiitaHistory\u003c/span\u003e\n  \u003cspan class=\"na\"\u003eiamRoleStatements\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n    \u003cspan class=\"pi\"\u003e-\u003c/span\u003e \u003cspan class=\"na\"\u003eEffect\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003eAllow\u003c/span\u003e\n      \u003cspan class=\"na\"\u003eAction\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n        \u003cspan class=\"pi\"\u003e-\u003c/span\u003e \u003cspan class=\"s1\"\u003e'\u003c/span\u003e\u003cspan class=\"s\"\u003edynamodb:Query'\u003c/span\u003e\n        \u003cspan class=\"pi\"\u003e-\u003c/span\u003e \u003cspan class=\"s1\"\u003e'\u003c/span\u003e\u003cspan class=\"s\"\u003edynamodb:Scan'\u003c/span\u003e\n        \u003cspan class=\"pi\"\u003e-\u003c/span\u003e \u003cspan class=\"s1\"\u003e'\u003c/span\u003e\u003cspan class=\"s\"\u003edynamodb:GetItem'\u003c/span\u003e\n        \u003cspan class=\"pi\"\u003e-\u003c/span\u003e \u003cspan class=\"s1\"\u003e'\u003c/span\u003e\u003cspan class=\"s\"\u003edynamodb:PutItem'\u003c/span\u003e\n        \u003cspan class=\"pi\"\u003e-\u003c/span\u003e \u003cspan class=\"s1\"\u003e'\u003c/span\u003e\u003cspan class=\"s\"\u003edynamodb:UpdateItem'\u003c/span\u003e\n        \u003cspan class=\"pi\"\u003e-\u003c/span\u003e \u003cspan class=\"s1\"\u003e'\u003c/span\u003e\u003cspan class=\"s\"\u003edynamodb:DeleteItem'\u003c/span\u003e\n        \u003cspan class=\"pi\"\u003e-\u003c/span\u003e \u003cspan class=\"s1\"\u003e'\u003c/span\u003e\u003cspan class=\"s\"\u003edynamodb:ListStreams'\u003c/span\u003e\n        \u003cspan class=\"pi\"\u003e-\u003c/span\u003e \u003cspan class=\"s1\"\u003e'\u003c/span\u003e\u003cspan class=\"s\"\u003edynamodb:DescribeTable'\u003c/span\u003e\n      \u003cspan class=\"na\"\u003eResource\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s1\"\u003e'\u003c/span\u003e\u003cspan class=\"s\"\u003earn:aws:dynamodb:${self:provider.region}:*:table/${self:provider.environment.DYNAMODB_TABLE}*'\u003c/span\u003e\n\u003cspan class=\"na\"\u003efunctions\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n  \u003cspan class=\"na\"\u003etriggerGitHubActions\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n    \u003cspan class=\"na\"\u003ehandler\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003ehandler.triggerGitHubActions\u003c/span\u003e\n    \u003cspan class=\"na\"\u003eevents\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n      \u003cspan class=\"pi\"\u003e-\u003c/span\u003e \u003cspan class=\"na\"\u003estream\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n          \u003cspan class=\"na\"\u003etype\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003edynamodb\u003c/span\u003e\n          \u003cspan class=\"na\"\u003ebatchSize\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"m\"\u003e1\u003c/span\u003e\n          \u003cspan class=\"na\"\u003estartingPosition\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003eLATEST\u003c/span\u003e\n          \u003cspan class=\"na\"\u003emaximumRetryAttempts\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"m\"\u003e1\u003c/span\u003e\n          \u003cspan class=\"na\"\u003eenabled\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\n          \u003cspan class=\"na\"\u003earn\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n            \u003cspan class=\"na\"\u003eFn::GetAtt\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n              \u003cspan class=\"pi\"\u003e-\u003c/span\u003e \u003cspan class=\"s\"\u003eQiitaHistory\u003c/span\u003e\n              \u003cspan class=\"pi\"\u003e-\u003c/span\u003e \u003cspan class=\"s\"\u003eStreamArn\u003c/span\u003e\n\u003cspan class=\"na\"\u003eresources\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n  \u003cspan class=\"na\"\u003eResources\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n    \u003cspan class=\"na\"\u003eQiitaHistory\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n      \u003cspan class=\"na\"\u003eType\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003eAWS::DynamoDB::Table\u003c/span\u003e\n      \u003cspan class=\"na\"\u003eProperties\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n        \u003cspan class=\"na\"\u003eTableName\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003e${self:provider.environment.QIITA_HISTORY_TABLE}\u003c/span\u003e\n        \u003cspan class=\"na\"\u003eAttributeDefinitions\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n          \u003cspan class=\"pi\"\u003e-\u003c/span\u003e \u003cspan class=\"na\"\u003eAttributeName\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003euserId\u003c/span\u003e\n            \u003cspan class=\"na\"\u003eAttributeType\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003eS\u003c/span\u003e\n          \u003cspan class=\"pi\"\u003e-\u003c/span\u003e \u003cspan class=\"na\"\u003eAttributeName\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003edate\u003c/span\u003e\n            \u003cspan class=\"na\"\u003eAttributeType\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003eS\u003c/span\u003e\n        \u003cspan class=\"na\"\u003eKeySchema\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n          \u003cspan class=\"pi\"\u003e-\u003c/span\u003e \u003cspan class=\"na\"\u003eAttributeName\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003euserId\u003c/span\u003e\n            \u003cspan class=\"na\"\u003eKeyType\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003eHASH\u003c/span\u003e\n          \u003cspan class=\"pi\"\u003e-\u003c/span\u003e \u003cspan class=\"na\"\u003eAttributeName\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003edate\u003c/span\u003e\n            \u003cspan class=\"na\"\u003eKeyType\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003eRANGE\u003c/span\u003e\n        \u003cspan class=\"na\"\u003eProvisionedThroughput\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n          \u003cspan class=\"na\"\u003eReadCapacityUnits\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"m\"\u003e1\u003c/span\u003e\n          \u003cspan class=\"na\"\u003eWriteCapacityUnits\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"m\"\u003e1\u003c/span\u003e\n        \u003cspan class=\"na\"\u003eStreamSpecification\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n          \u003cspan class=\"na\"\u003eStreamViewType\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003eNEW_AND_OLD_IMAGES\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cul data-sourcepos=\"155:1-159:0\"\u003e\n\u003cli data-sourcepos=\"155:1-159:0\"\u003eLambdaでの処理はAPIにアクセスしているだけです\n\u003cul data-sourcepos=\"156:5-159:0\"\u003e\n\u003cli data-sourcepos=\"156:5-156:160\"\u003eGitHub Actionsを起動するAPIはPOSTで\u003ccode\u003ehttps://api.github.com/repos/アカウント名/リポジトリ名/dispatches\u003c/code\u003eにアクセスする感じです\u003c/li\u003e\n\u003cli data-sourcepos=\"157:5-159:0\"\u003e詳細はドキュメントをご確認ください\n\u003cul data-sourcepos=\"158:9-159:0\"\u003e\n\u003cli data-sourcepos=\"158:9-159:0\"\u003e\u003ca href=\"https://docs.github.com/en/rest/reference/repos#create-a-repository-dispatch-event\" class=\"autolink\" rel=\"nofollow noopener\" target=\"_blank\"\u003ehttps://docs.github.com/en/rest/reference/repos#create-a-repository-dispatch-event\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"160:1-160:18\"\u003e\n\u003cspan id=\"全体の流れ\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E5%85%A8%E4%BD%93%E3%81%AE%E6%B5%81%E3%82%8C\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e全体の流れ\u003c/h2\u003e\n\u003cul data-sourcepos=\"162:1-164:0\"\u003e\n\u003cli data-sourcepos=\"162:1-162:77\"\u003e少々複雑な構成でしたがこれで全部説明し終わりました\u003c/li\u003e\n\u003cli data-sourcepos=\"163:1-164:0\"\u003e改めて全体の流れを振り返ります\u003c/li\u003e\n\u003c/ul\u003e\n\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F28b07683-d3cc-c289-2f83-3088c5f0b064.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=c1400157ba95abd6cd1e9a9b2c84e1a4\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg width=\"861\" alt=\"スクリーンショット 2020-07-26 1.33.56.png\" src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F28b07683-d3cc-c289-2f83-3088c5f0b064.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=c1400157ba95abd6cd1e9a9b2c84e1a4\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F28b07683-d3cc-c289-2f83-3088c5f0b064.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=c1d45fc238f1bc326487867a9d2a86fd 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/28b07683-d3cc-c289-2f83-3088c5f0b064.png\" loading=\"lazy\"\u003e\u003c/a\u003e\n\u003cul data-sourcepos=\"167:1-175:0\"\u003e\n\u003cli data-sourcepos=\"167:1-167:95\"\u003e①毎日0時にLambdaがバッチ実行されQiitaのAPIからいいね数を取得します\u003c/li\u003e\n\u003cli data-sourcepos=\"168:1-168:55\"\u003e②取得したデータはDynamoDBに保存します\u003c/li\u003e\n\u003cli data-sourcepos=\"169:1-169:94\"\u003e③DynamoDBにデータが保存されるとそれをトリガーにLambdaが起動します\u003c/li\u003e\n\u003cli data-sourcepos=\"170:1-170:94\"\u003e④そのLambdaではGitHub Actionsを起動するAPIへのアクセスのみを行います\u003c/li\u003e\n\u003cli data-sourcepos=\"171:1-171:149\"\u003e⑤APIアクセスにより起動されたGitHub ActionsではNext.jsで作られたアプリをビルドしVercelへのデプロイを行います\u003c/li\u003e\n\u003cli data-sourcepos=\"172:1-172:152\"\u003e⑥〜⑨ビルド時にNext.jsのStatic Site Generationの機能によってAPIからいいね数のデータを取得してHTMLに埋め込みます\u003c/li\u003e\n\u003cli data-sourcepos=\"173:1-173:71\"\u003e⑩生成された静的ファイルをVercelへデプロイします\u003c/li\u003e\n\u003cli data-sourcepos=\"174:1-175:0\"\u003e=\u0026gt;ユーザがページにアクセスすると最新データが反映されたグラフページが表示されます！\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"176:1-176:12\"\u003e\n\u003cspan id=\"まとめ\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%81%BE%E3%81%A8%E3%82%81\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eまとめ\u003c/h2\u003e\n\u003cul data-sourcepos=\"178:1-181:0\"\u003e\n\u003cli data-sourcepos=\"178:1-178:156\"\u003eもともとDynamoDBにデータが蓄積されていたところからスタートしましたがなんとかJamstackな構成を実現できました！\u003c/li\u003e\n\u003cli data-sourcepos=\"179:1-181:0\"\u003eどこか一部分でも何かの参考になれば嬉しいです！\u003c/li\u003e\n\u003c/ul\u003e\n","body":"## 概要\n\n- 以前にQiitaの前日のいいね数を通知してくれるLINE Botを作りました\n    - [Qiitaのいいね数を通知してくれるLINE Botを作ってみた](https://qiita.com/ozaki25/items/03b834de8c0c175d955d)\n- この延長でいいね数の推移をグラフで表示するWebページを作ってみたというのが今回の内容です\n    - せっかくなので技術検証も兼ねてJamstackな構成で作ってみました\n    - 前述の記事の通りデータはDynamoDBにデータを蓄積しているのでHeadlessCMSなどは使わずにチャレンジしてみます\n\n## 作ったもの\n\n- 直近1週間のいいね数をグラフで表示するWebアプリをLIFFアプリとして表示しています\n\n![Screenshot_20200726-014511.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/a656b608-38c1-afa4-52ac-1428c92e63a8.png)\n\n## 構成\n\n- Jamstackな構成とするためにいろいろ使っているので少々複雑です\n    - 要素ごとに何をしているのか説明していきます\n\n\u003cimg width=\"861\" alt=\"スクリーンショット 2020-07-26 1.33.56.png\" src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/28b07683-d3cc-c289-2f83-3088c5f0b064.png\"\u003e\n\n## 実装済みの機能\n\n- Qiitaのいいね数を蓄積していく処理は前述の記事で紹介済みです\n\n\u003cimg width=\"859\" alt=\"スクリーンショット 2020-07-26 2.10.04.png\" src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/7d2f814c-1658-16bf-c840-40baec2f6821.png\"\u003e\n\n- LambdaでQiitaのAPIを叩き取得したデータをDynamoDBに保存しています\n- Lambdaのバッチ実行で毎日0時に実行しています\n\n## Next.jsを使ったJamstackな仕組みの構築\n\n- まずはビルドデプロイ自動化の仕組みは置いておいてアプリの部分からです\n\n\u003cimg width=\"859\" alt=\"スクリーンショット 2020-07-26 1.53.02.png\" src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/90750e74-6d60-3372-701c-d391ef63f642.png\"\u003e\n\n- Jamstackの最大の特徴であるビルド時にAPIアクセスしてデータ埋め込んだHTMLを生成するという部分です\n- ビルド時にAPIアクセスできるライブラリはいくつかありますが今回はReactベースのNext.jsを使いました\n    - [getStaticProps](https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation)と[getStaticPaths](https://nextjs.org/docs/basic-features/data-fetching#getstaticpaths-static-generation)を使えれば簡単に実現できます\n    - [こんな資料](https://jamstack-handson.ozaki25.now.sh/page2.html)も過去に作ったのでご参考で\n- この記事はJamstackにフォーカスしているので深く触れませんがLIFF SDKを入れてLIFFアプリ化しています\n    - LINE IDを取得できるのでログイン機能いらず！\n\n## GitHub ActionsでVercelに自動デプロイ\n\n- 次はNext.jsで作ったアプリのデプロイ周りです\n\n\u003cimg width=\"859\" alt=\"スクリーンショット 2020-07-26 1.59.32.png\" src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/fda0475a-0f75-6769-8601-3629c7b3ab31.png\"\u003e\n\n- 今回はホスティングサービスとしてVercelを使いました\n- さらにGitHub Actionsで自動的にデプロイできる仕組みを作りました\n    - Vercel用のActionが公式であったのでそれを使っています\n        - https://github.com/marketplace/actions/vercel-action\n- GitHub Actions起動のトリガーはmasterへのプッシュとAPIアクセスを設定しています\n\n```yml:.github/workflows/deploy.yml\nname: deploy\non:\n  push:\n    branches:\n      - master\n  repository_dispatch:\n    branches:\n      - master\njobs:\n  deploy:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n      - name: Use Node.js 14\n        uses: actions/setup-node@v1\n        with:\n          node-version: 14\n      - uses: amondnet/vercel-action@v19\n        with:\n          vercel-token: ${{ secrets.VERCEL_TOKEN }}\n          github-token: ${{ secrets.GITHUB_TOKEN }}\n          vercel-args: '--prod -f -b TZ=Asia/Tokyo -b REACT_APP_LIFF_ID=${{ secrets.REACT_APP_LIFF_ID }} -b REACT_APP_API_URL=${{ secrets.REACT_APP_API_URL }}'\n          vercel-org-id: ${{ secrets.ORG_ID}}\n          vercel-project-id: ${{ secrets.PROJECT_ID}}\n```\n\n## DynamoDBの更新をトリガーにLambdaを起動してGitHub Actionsを叩く\n\n- 最後にJamstack自動化の肝となるデータ更新をトリガーにビルドデプロイを走らせる仕組みです\n\n\u003cimg width=\"860\" alt=\"スクリーンショット 2020-07-26 2.07.57.png\" src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/7ae7a5f2-67dc-ac75-bd98-2ecbea5880fd.png\"\u003e\n\n- DynamoDB Streamsを使うことで特定のテーブルの更新をトリガーにLambdaを起動することができます\n- そしてGitHub ActionsをAPIアクセスで起動できるようにしているためLambdaからそのAPIを叩いています\n    - ちょっとまわりくどいですがDynamoDBの更新をトリガーにGitHub Actionsを起動するために試行錯誤した結果です\n    - 他のサービスを使ったりすればもう少し違ったかな？\n- 今回はLambdaやDynamoDB周りはServerlessFrameworkを使っています\n    - serverless.ymlはこんな感じです\n\n```yml:serverless.yml(抜粋)\nservice:\n  name: line-qiita-bot\nprovider:\n  name: aws\n  runtime: nodejs12.x\n  region: ap-northeast-1\n  environment:\n    DYNAMODB_TABLE: ${self:service.name}-${self:provider.stage}\n    QIITA_HISTORY_TABLE: ${self:provider.environment.DYNAMODB_TABLE}-QiitaHistory\n  iamRoleStatements:\n    - Effect: Allow\n      Action:\n        - 'dynamodb:Query'\n        - 'dynamodb:Scan'\n        - 'dynamodb:GetItem'\n        - 'dynamodb:PutItem'\n        - 'dynamodb:UpdateItem'\n        - 'dynamodb:DeleteItem'\n        - 'dynamodb:ListStreams'\n        - 'dynamodb:DescribeTable'\n      Resource: 'arn:aws:dynamodb:${self:provider.region}:*:table/${self:provider.environment.DYNAMODB_TABLE}*'\nfunctions:\n  triggerGitHubActions:\n    handler: handler.triggerGitHubActions\n    events:\n      - stream:\n          type: dynamodb\n          batchSize: 1\n          startingPosition: LATEST\n          maximumRetryAttempts: 1\n          enabled: true\n          arn:\n            Fn::GetAtt:\n              - QiitaHistory\n              - StreamArn\nresources:\n  Resources:\n    QiitaHistory:\n      Type: AWS::DynamoDB::Table\n      Properties:\n        TableName: ${self:provider.environment.QIITA_HISTORY_TABLE}\n        AttributeDefinitions:\n          - AttributeName: userId\n            AttributeType: S\n          - AttributeName: date\n            AttributeType: S\n        KeySchema:\n          - AttributeName: userId\n            KeyType: HASH\n          - AttributeName: date\n            KeyType: RANGE\n        ProvisionedThroughput:\n          ReadCapacityUnits: 1\n          WriteCapacityUnits: 1\n        StreamSpecification:\n          StreamViewType: NEW_AND_OLD_IMAGES\n```\n\n- Lambdaでの処理はAPIにアクセスしているだけです\n    - GitHub Actionsを起動するAPIはPOSTで`https://api.github.com/repos/アカウント名/リポジトリ名/dispatches`にアクセスする感じです\n    - 詳細はドキュメントをご確認ください\n        - https://docs.github.com/en/rest/reference/repos#create-a-repository-dispatch-event\n\n## 全体の流れ\n\n- 少々複雑な構成でしたがこれで全部説明し終わりました\n- 改めて全体の流れを振り返ります\n\n\u003cimg width=\"861\" alt=\"スクリーンショット 2020-07-26 1.33.56.png\" src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/28b07683-d3cc-c289-2f83-3088c5f0b064.png\"\u003e\n\n- ①毎日0時にLambdaがバッチ実行されQiitaのAPIからいいね数を取得します\n- ②取得したデータはDynamoDBに保存します\n- ③DynamoDBにデータが保存されるとそれをトリガーにLambdaが起動します\n- ④そのLambdaではGitHub Actionsを起動するAPIへのアクセスのみを行います\n- ⑤APIアクセスにより起動されたGitHub ActionsではNext.jsで作られたアプリをビルドしVercelへのデプロイを行います\n- ⑥〜⑨ビルド時にNext.jsのStatic Site Generationの機能によってAPIからいいね数のデータを取得してHTMLに埋め込みます\n- ⑩生成された静的ファイルをVercelへデプロイします\n- =\u003eユーザがページにアクセスすると最新データが反映されたグラフページが表示されます！\n\n## まとめ\n\n- もともとDynamoDBにデータが蓄積されていたところからスタートしましたがなんとかJamstackな構成を実現できました！\n- どこか一部分でも何かの参考になれば嬉しいです！\n\n\n","coediting":false,"comments_count":0,"created_at":"2020-07-26T02:42:29+09:00","group":null,"id":"04c880bdd7ad46ae5c07","likes_count":7,"private":false,"reactions_count":0,"stocks_count":8,"tags":[{"name":"DynamoDB","versions":[]},{"name":"lambda","versions":[]},{"name":"Next.js","versions":[]},{"name":"JAMstack","versions":[]},{"name":"GitHubActions","versions":[]}],"title":"[Next.js/Vercel]LambdaとDynamoDBでJamstackなアプリを作ってみた","updated_at":"2021-10-26T18:49:55+09:00","url":"https://qiita.com/ozaki25/items/04c880bdd7ad46ae5c07","user":{"description":"","facebook_id":"","followees_count":0,"followers_count":79,"github_login_name":"ozaki25","id":"ozaki25","items_count":112,"linkedin_id":"","location":"","name":"","organization":"","permanent_id":175213,"profile_image_url":"https://qiita-image-store.s3.amazonaws.com/0/175213/profile-images/1491825881","team_only":false,"twitter_screen_name":"ozaki25rn","website_url":""},"page_views_count":null,"team_membership":null,"organization_url_name":null,"slide":false,"posting_campaign_uuid":null},{"rendered_body":"\u003ch2 data-sourcepos=\"1:1-1:9\"\u003e\n\u003cspan id=\"背景\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E8%83%8C%E6%99%AF\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e背景\u003c/h2\u003e\n\u003cul data-sourcepos=\"3:1-6:0\"\u003e\n\u003cli data-sourcepos=\"3:1-3:151\"\u003e\n\u003ca href=\"https://vercel.com/\" rel=\"nofollow noopener\" target=\"_blank\"\u003eVercel\u003c/a\u003eでアプリをホスティングしている場合、基本的にVercel側でアプリをビルドすることになる\u003c/li\u003e\n\u003cli data-sourcepos=\"4:1-4:112\"\u003eJamstackなアプリを作っていて、ビルド時に日付を取得しAPIを叩く処理を書いていた\u003c/li\u003e\n\u003cli data-sourcepos=\"5:1-6:0\"\u003eしかし、ビルドしている環境のタイムゾーンが日本時間ではないようで意図した動きになっていなかった\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"7:1-7:12\"\u003e\n\u003cspan id=\"解決策\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E8%A7%A3%E6%B1%BA%E7%AD%96\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e解決策\u003c/h2\u003e\n\u003cul data-sourcepos=\"9:1-11:0\"\u003e\n\u003cli data-sourcepos=\"9:1-9:143\"\u003eビルド時の環境変数としてタイムゾーンを設定するとそのタイムゾーンでビルドが実行されるようになる\u003c/li\u003e\n\u003cli data-sourcepos=\"10:1-11:0\"\u003evcコマンドを叩いているのであれば\u003ccode\u003e-b\u003c/code\u003eオプションでビルド時の環境変数を設定できる\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"text\" data-sourcepos=\"12:1-14:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003evc -b TZ=Asia/Tokyo\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cul data-sourcepos=\"16:1-16:65\"\u003e\n\u003cli data-sourcepos=\"16:1-16:65\"\u003eこれで無事日本時間でビルドすることができた\u003c/li\u003e\n\u003c/ul\u003e\n","body":"## 背景\n\n- [Vercel](https://vercel.com/)でアプリをホスティングしている場合、基本的にVercel側でアプリをビルドすることになる\n- Jamstackなアプリを作っていて、ビルド時に日付を取得しAPIを叩く処理を書いていた\n- しかし、ビルドしている環境のタイムゾーンが日本時間ではないようで意図した動きになっていなかった\n\n## 解決策\n\n- ビルド時の環境変数としてタイムゾーンを設定するとそのタイムゾーンでビルドが実行されるようになる\n- vcコマンドを叩いているのであれば`-b`オプションでビルド時の環境変数を設定できる\n\n```\nvc -b TZ=Asia/Tokyo\n```\n\n- これで無事日本時間でビルドすることができた\n","coediting":false,"comments_count":0,"created_at":"2020-07-25T03:17:27+09:00","group":null,"id":"260cc8ca68c81ae67287","likes_count":2,"private":false,"reactions_count":0,"stocks_count":0,"tags":[{"name":"Vercel","versions":[]}],"title":"［Vercel］build時のタイムゾーンを設定する","updated_at":"2021-10-26T18:49:46+09:00","url":"https://qiita.com/ozaki25/items/260cc8ca68c81ae67287","user":{"description":"","facebook_id":"","followees_count":0,"followers_count":79,"github_login_name":"ozaki25","id":"ozaki25","items_count":112,"linkedin_id":"","location":"","name":"","organization":"","permanent_id":175213,"profile_image_url":"https://qiita-image-store.s3.amazonaws.com/0/175213/profile-images/1491825881","team_only":false,"twitter_screen_name":"ozaki25rn","website_url":""},"page_views_count":null,"team_membership":null,"organization_url_name":null,"slide":false,"posting_campaign_uuid":null},{"rendered_body":"\u003ch2 data-sourcepos=\"1:1-1:9\"\u003e\n\u003cspan id=\"背景\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E8%83%8C%E6%99%AF\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e背景\u003c/h2\u003e\n\u003cul data-sourcepos=\"3:1-6:0\"\u003e\n\u003cli data-sourcepos=\"3:1-4:191\"\u003eこんな記事を読みました\n\u003cul data-sourcepos=\"4:5-4:191\"\u003e\n\u003cli data-sourcepos=\"4:5-4:191\"\u003e\u003ca href=\"https://qiita.com/aion0721/items/a75c6aed91691b5014c9\" id=\"reference-3af21451428ca5c4e9dd\"\u003e\u0026lt;Powershell\u0026gt;特定の文字列で区切られたファイル名をそのファイル名の一部で一括変換するクイズ\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"5:1-6:0\"\u003eJavaScriptでやれと言われているような気がしたのでやってみます\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"7:1-7:21\"\u003e\n\u003cspan id=\"やりたいこと\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%82%84%E3%82%8A%E3%81%9F%E3%81%84%E3%81%93%E3%81%A8\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eやりたいこと\u003c/h2\u003e\n\u003cul data-sourcepos=\"9:1-13:0\"\u003e\n\u003cli data-sourcepos=\"9:1-9:115\"\u003eフォルダの中に\u003ccode\u003eabc_test_ok_1_this.csv\u003c/code\u003e・・・\u003ccode\u003eabc_test_ok_100_this.csv\u003c/code\u003eのファイルが入っている\u003c/li\u003e\n\u003cli data-sourcepos=\"10:1-10:63\"\u003eこれらを\u003ccode\u003e1.csv\u003c/code\u003e・・・\u003ccode\u003e100.csv\u003c/code\u003eにリネームしたい\u003c/li\u003e\n\u003cli data-sourcepos=\"11:1-11:71\"\u003eということだと思う（斜め読みしたから違うかも）\u003c/li\u003e\n\u003cli data-sourcepos=\"12:1-13:0\"\u003eこれらをコマンドラインでワンライナーで実現するというもの\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"14:1-14:9\"\u003e\n\u003cspan id=\"環境\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E7%92%B0%E5%A2%83\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e環境\u003c/h2\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"bash\" data-sourcepos=\"16:1-19:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003enode \u003cspan class=\"nt\"\u003e-v\u003c/span\u003e\n\u003cspan class=\"c\"\u003e# v14.5.0\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003ch2 data-sourcepos=\"21:1-21:15\"\u003e\n\u003cspan id=\"事前準備\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E4%BA%8B%E5%89%8D%E6%BA%96%E5%82%99\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e事前準備\u003c/h2\u003e\n\u003cul data-sourcepos=\"23:1-24:0\"\u003e\n\u003cli data-sourcepos=\"23:1-24:0\"\u003e元記事は\u003ccode\u003eabc_test_ok_1_this.csv\u003c/code\u003e・・・\u003ccode\u003eabc_test_ok_100_this.csv\u003c/code\u003eのファイルを用意するところからワンライナーでやってるのでそこから\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"bash\" data-sourcepos=\"25:1-27:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003enode \u003cspan class=\"nt\"\u003e-e\u003c/span\u003e \u003cspan class=\"s2\"\u003e\"const fs=require('fs');[...Array(100).keys()].forEach(i=\u0026gt;fs.writeFile('abc_test_ok_'+(i+1)+'_this.csv','',()=\u0026gt;{}));\"\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cul data-sourcepos=\"29:1-31:0\"\u003e\n\u003cli data-sourcepos=\"29:1-29:249\"\u003enodeコマンドは\u003ccode\u003enode main.js\u003c/code\u003eのようにしてファイルを実行できるが、\u003ccode\u003enode -e \"console.log('Hello')\"\u003c/code\u003eのように\u003ccode\u003e-e\u003c/code\u003eオプションをつけるとその後をEvalするのでファイルに書かなくても直接実行できる\u003c/li\u003e\n\u003cli data-sourcepos=\"30:1-31:0\"\u003eちゃんと改行するとこんな感じ\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"js\" data-sourcepos=\"32:1-38:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003efs\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nf\"\u003erequire\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003efs\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\n\u003cspan class=\"p\"\u003e[...\u003c/span\u003e\u003cspan class=\"nc\"\u003eArray\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e100\u003c/span\u003e\u003cspan class=\"p\"\u003e).\u003c/span\u003e\u003cspan class=\"nf\"\u003ekeys\u003c/span\u003e\u003cspan class=\"p\"\u003e()].\u003c/span\u003e\u003cspan class=\"nf\"\u003eforEach\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ei\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n  \u003cspan class=\"nx\"\u003efs\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003ewriteFile\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e`abc_test_ok_\u003c/span\u003e\u003cspan class=\"p\"\u003e${\u003c/span\u003e\u003cspan class=\"nx\"\u003ei\u003c/span\u003e \u003cspan class=\"o\"\u003e+\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e_this.csv`\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e''\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{});\u003c/span\u003e\n\u003cspan class=\"p\"\u003e});\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cul data-sourcepos=\"40:1-49:0\"\u003e\n\u003cli data-sourcepos=\"40:1-42:47\"\u003e\n\u003ccode\u003e[...Array(100).keys()]\u003c/code\u003eで要素数100個の配列を作っています\n\u003cul data-sourcepos=\"41:5-42:47\"\u003e\n\u003cli data-sourcepos=\"41:5-41:90\"\u003e\n\u003ccode\u003e.kyes()\u003c/code\u003eとしてるので配列の各要素には0〜99の値が格納されます\u003c/li\u003e\n\u003cli data-sourcepos=\"42:5-42:47\"\u003e\n\u003ccode\u003e[0, 1, 2, ... 98, 99]\u003c/code\u003eといった具合\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"43:1-43:66\"\u003eこれをforEachしてるので100回ループが回ってます\u003c/li\u003e\n\u003cli data-sourcepos=\"44:1-47:81\"\u003eファイルの作成には\u003ccode\u003efs.writeFile('ファイル名', 'ファイルの内容', callback)\u003c/code\u003eを使っています\n\u003cul data-sourcepos=\"45:5-47:81\"\u003e\n\u003cli data-sourcepos=\"45:5-45:77\"\u003eファイル名はお題の名前にしてindexを埋め込んでいる\u003c/li\u003e\n\u003cli data-sourcepos=\"46:5-46:69\"\u003eファイルの中身は指定がなかったので空っぽに\u003c/li\u003e\n\u003cli data-sourcepos=\"47:5-47:81\"\u003eコールバックはとりあえず今回はいらないので空の関数\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"48:1-49:0\"\u003eという感じで実行すると\u003ccode\u003eabc_test_ok_1_this.csv\u003c/code\u003e・・・\u003ccode\u003eabc_test_ok_100_this.csv\u003c/code\u003eが作成されました\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"50:1-50:30\"\u003e\n\u003cspan id=\"ファイルのリネーム\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E3%83%AA%E3%83%8D%E3%83%BC%E3%83%A0\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eファイルのリネーム\u003c/h2\u003e\n\u003cul data-sourcepos=\"52:1-54:0\"\u003e\n\u003cli data-sourcepos=\"52:1-52:32\"\u003eここが本題でしょうか\u003c/li\u003e\n\u003cli data-sourcepos=\"53:1-54:0\"\u003eフォルダの中にある\u003ccode\u003eabc_test_ok_1_this.csv\u003c/code\u003e・・・\u003ccode\u003eabc_test_ok_100_this.csv\u003c/code\u003eのファイルを\u003ccode\u003e1.csv\u003c/code\u003e・・・\u003ccode\u003e100.csv\u003c/code\u003eにリネームします\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"bash\" data-sourcepos=\"55:1-57:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003enode \u003cspan class=\"nt\"\u003e-e\u003c/span\u003e \u003cspan class=\"s2\"\u003e\"const fs=require('fs');fs.readdir(__dirname,(error,files)=\u0026gt;{files.forEach(file=\u0026gt;{fs.rename(file,file.match(/abc_test_ok_(.*)_this.csv/)[1]+'.csv',() =\u0026gt; {},);});});\"\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cul data-sourcepos=\"59:1-61:0\"\u003e\n\u003cli data-sourcepos=\"59:1-59:115\"\u003eさっきと同じくnodeコマンドの\u003ccode\u003e-e\u003c/code\u003eオプションを使ってワンライナーで実現しています\u003c/li\u003e\n\u003cli data-sourcepos=\"60:1-61:0\"\u003e改行するとこんな感じ\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"js\" data-sourcepos=\"62:1-70:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003efs\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nf\"\u003erequire\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003efs\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\n\u003cspan class=\"nx\"\u003efs\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003ereaddir\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003e__dirname\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eerror\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003efiles\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n  \u003cspan class=\"nx\"\u003efiles\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eforEach\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003efile\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"nx\"\u003efs\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003erename\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003efile\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003efile\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003ematch\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sr\"\u003e/abc_test_ok_\u003c/span\u003e\u003cspan class=\"se\"\u003e(\u003c/span\u003e\u003cspan class=\"sr\"\u003e.*\u003c/span\u003e\u003cspan class=\"se\"\u003e)\u003c/span\u003e\u003cspan class=\"sr\"\u003e_this.csv/\u003c/span\u003e\u003cspan class=\"p\"\u003e)[\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e+\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003e.csv\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{});\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e});\u003c/span\u003e\n\u003cspan class=\"p\"\u003e});\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cul data-sourcepos=\"72:1-82:0\"\u003e\n\u003cli data-sourcepos=\"72:1-74:73\"\u003e\n\u003ccode\u003efs.readdir('ディレクトリ', callback)\u003c/code\u003eを使ってディレクトリの中のファイル一覧を取得しています\n\u003cul data-sourcepos=\"73:5-74:73\"\u003e\n\u003cli data-sourcepos=\"73:5-73:84\"\u003e第1引数はカレントディレクトリでいいので\u003ccode\u003e__dirname\u003c/code\u003eを定義\u003c/li\u003e\n\u003cli data-sourcepos=\"74:5-74:73\"\u003e第2引数はファイル名の一覧を処理する関数を定義\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"75:1-75:122\"\u003efilesにファイル名の一覧が入っているので\u003ccode\u003efiles.forEach(file =\u0026gt; { ... }\u003c/code\u003eでループを回しています\u003c/li\u003e\n\u003cli data-sourcepos=\"76:1-79:53\"\u003e\n\u003ccode\u003efs.rename('変更前のファイル名', '変更後のファイル名', callback)\u003c/code\u003eを使ってリネームしています\n\u003cul data-sourcepos=\"77:5-79:53\"\u003e\n\u003cli data-sourcepos=\"77:5-77:66\"\u003e変更前のファイルはループで回ってくる\u003ccode\u003efile\u003c/code\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"78:5-78:93\"\u003e変更後のファイル名は正規表現で必要な部分を引き抜いています\u003c/li\u003e\n\u003cli data-sourcepos=\"79:5-79:53\"\u003ecallbackは今回は不要なので空の関数\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"80:1-82:0\"\u003eこれでフォルダの中にある\u003ccode\u003eabc_test_ok_1_this.csv\u003c/code\u003e・・・\u003ccode\u003eabc_test_ok_100_this.csv\u003c/code\u003eのファイルを\u003ccode\u003e1.csv\u003c/code\u003e・・・\u003ccode\u003e100.csv\u003c/code\u003eにリネームできました\n\u003cul data-sourcepos=\"81:5-82:0\"\u003e\n\u003cli data-sourcepos=\"81:5-82:0\"\u003e余計なファイルがあったときの処理とかはいったん無視しました\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"83:1-83:15\"\u003e\n\u003cspan id=\"終わりに\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E7%B5%82%E3%82%8F%E3%82%8A%E3%81%AB\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e終わりに\u003c/h2\u003e\n\u003cul data-sourcepos=\"85:1-88:0\"\u003e\n\u003cli data-sourcepos=\"85:1-85:95\"\u003eちょっとしたスクリプトを書くのは息抜きの暇つぶしにいいですね。\u003c/li\u003e\n\u003cli data-sourcepos=\"86:1-86:168\"\u003eNodeでファイルの読み書きなんてほとんどやったことないのでググりながらやりました。もっとかっこいい書き方あるかも。\u003c/li\u003e\n\u003cli data-sourcepos=\"87:1-88:0\"\u003e次は誰かPythonとかRubyとかでお願いします。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"89:1-89:9\"\u003e\n\u003cspan id=\"参考\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E5%8F%82%E8%80%83\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e参考\u003c/h2\u003e\n\u003cul data-sourcepos=\"91:1-91:79\"\u003e\n\u003cli data-sourcepos=\"91:1-91:79\"\u003e\u003ca href=\"https://nodejs.org/api/fs.html\" rel=\"nofollow noopener\" target=\"_blank\"\u003eFile System | Node.js v14.5.0 Documentation\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n","body":"## 背景\n\n- こんな記事を読みました\n    - [\\\u003cPowershell\\\u003e特定の文字列で区切られたファイル名をそのファイル名の一部で一括変換するクイズ](https://qiita.com/aion0721/items/a75c6aed91691b5014c9)\n- JavaScriptでやれと言われているような気がしたのでやってみます\n\n## やりたいこと\n\n- フォルダの中に`abc_test_ok_1_this.csv`・・・`abc_test_ok_100_this.csv`のファイルが入っている\n- これらを`1.csv`・・・`100.csv`にリネームしたい\n- ということだと思う（斜め読みしたから違うかも）\n- これらをコマンドラインでワンライナーで実現するというもの\n\n## 環境\n\n```zsh\nnode -v\n# v14.5.0\n```\n\n## 事前準備\n\n- 元記事は`abc_test_ok_1_this.csv`・・・`abc_test_ok_100_this.csv`のファイルを用意するところからワンライナーでやってるのでそこから\n\n```zsh\nnode -e \"const fs=require('fs');[...Array(100).keys()].forEach(i=\u003efs.writeFile('abc_test_ok_'+(i+1)+'_this.csv','',()=\u003e{}));\"\n```\n\n- nodeコマンドは`node main.js`のようにしてファイルを実行できるが、`node -e \"console.log('Hello')\"`のように`-e`オプションをつけるとその後をEvalするのでファイルに書かなくても直接実行できる\n- ちゃんと改行するとこんな感じ\n\n```js\nconst fs = require('fs');\n\n[...Array(100).keys()].forEach(i =\u003e {\n  fs.writeFile(`abc_test_ok_${i + 1}_this.csv`, '', () =\u003e {});\n});\n```\n\n- `[...Array(100).keys()]`で要素数100個の配列を作っています\n    - `.kyes()`としてるので配列の各要素には0〜99の値が格納されます\n    - `[0, 1, 2, ... 98, 99]`といった具合\n- これをforEachしてるので100回ループが回ってます\n- ファイルの作成には`fs.writeFile('ファイル名', 'ファイルの内容', callback)`を使っています\n    - ファイル名はお題の名前にしてindexを埋め込んでいる\n    - ファイルの中身は指定がなかったので空っぽに\n    - コールバックはとりあえず今回はいらないので空の関数\n- という感じで実行すると`abc_test_ok_1_this.csv`・・・`abc_test_ok_100_this.csv`が作成されました\n\n## ファイルのリネーム\n\n- ここが本題でしょうか\n- フォルダの中にある`abc_test_ok_1_this.csv`・・・`abc_test_ok_100_this.csv`のファイルを`1.csv`・・・`100.csv`にリネームします\n\n```zsh\nnode -e \"const fs=require('fs');fs.readdir(__dirname,(error,files)=\u003e{files.forEach(file=\u003e{fs.rename(file,file.match(/abc_test_ok_(.*)_this.csv/)[1]+'.csv',() =\u003e {},);});});\"\n```\n\n- さっきと同じくnodeコマンドの`-e`オプションを使ってワンライナーで実現しています\n- 改行するとこんな感じ\n\n```js\nconst fs = require('fs');\n\nfs.readdir(__dirname, (error, files) =\u003e {\n  files.forEach(file =\u003e {\n    fs.rename(file, file.match(/abc_test_ok_(.*)_this.csv/)[1] + '.csv', () =\u003e {});\n  });\n});\n```\n\n- `fs.readdir('ディレクトリ', callback)`を使ってディレクトリの中のファイル一覧を取得しています\n    - 第1引数はカレントディレクトリでいいので`__dirname`を定義\n    - 第2引数はファイル名の一覧を処理する関数を定義\n- filesにファイル名の一覧が入っているので`files.forEach(file =\u003e { ... }`でループを回しています\n- `fs.rename('変更前のファイル名', '変更後のファイル名', callback)`を使ってリネームしています\n    - 変更前のファイルはループで回ってくる`file`\n    - 変更後のファイル名は正規表現で必要な部分を引き抜いています\n    - callbackは今回は不要なので空の関数\n- これでフォルダの中にある`abc_test_ok_1_this.csv`・・・`abc_test_ok_100_this.csv`のファイルを`1.csv`・・・`100.csv`にリネームできました\n    - 余計なファイルがあったときの処理とかはいったん無視しました\n\n## 終わりに\n\n- ちょっとしたスクリプトを書くのは息抜きの暇つぶしにいいですね。\n- Nodeでファイルの読み書きなんてほとんどやったことないのでググりながらやりました。もっとかっこいい書き方あるかも。\n- 次は誰かPythonとかRubyとかでお願いします。\n\n## 参考\n\n- [File System | Node.js v14.5.0 Documentation](https://nodejs.org/api/fs.html)\n","coediting":false,"comments_count":0,"created_at":"2020-07-14T01:06:54+09:00","group":null,"id":"33a57ad7eea55822c764","likes_count":3,"private":false,"reactions_count":0,"stocks_count":0,"tags":[{"name":"JavaScript","versions":[]},{"name":"Node.js","versions":[]},{"name":"パズル","versions":[]},{"name":"クイズ","versions":[]}],"title":"\u003cJavaScript\u003e特定の文字列で区切られたファイル名をそのファイル名の一部で一括変換するクイズ","updated_at":"2021-10-26T18:49:36+09:00","url":"https://qiita.com/ozaki25/items/33a57ad7eea55822c764","user":{"description":"","facebook_id":"","followees_count":0,"followers_count":79,"github_login_name":"ozaki25","id":"ozaki25","items_count":112,"linkedin_id":"","location":"","name":"","organization":"","permanent_id":175213,"profile_image_url":"https://qiita-image-store.s3.amazonaws.com/0/175213/profile-images/1491825881","team_only":false,"twitter_screen_name":"ozaki25rn","website_url":""},"page_views_count":null,"team_membership":null,"organization_url_name":null,"slide":false,"posting_campaign_uuid":null},{"rendered_body":"\u003ch2 data-sourcepos=\"1:1-1:9\"\u003e\n\u003cspan id=\"概要\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E6%A6%82%E8%A6%81\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e概要\u003c/h2\u003e\n\u003cul data-sourcepos=\"3:1-8:0\"\u003e\n\u003cli data-sourcepos=\"3:1-3:161\"\u003e電話番号でメッセージを送るSMSを使いたいなと思って調べてみたらAmazonSNSで実現できると分かったので試してみました\u003c/li\u003e\n\u003cli data-sourcepos=\"4:1-4:74\"\u003eAPI Gateway - Lambda - SNS を連携させてAPI化させてみました\u003c/li\u003e\n\u003cli data-sourcepos=\"5:1-5:148\"\u003e当然ですが指定した電話番号に本当にSMSを送れてしまうので、\u003cstrong\u003e悪用しない/されないようにご注意ください\u003c/strong\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"6:1-8:0\"\u003eちなみに日本の場合は一通辺り$0.07451のようです\n\u003cul data-sourcepos=\"7:5-8:0\"\u003e\n\u003cli data-sourcepos=\"7:5-8:0\"\u003e\u003ca href=\"https://aws.amazon.com/jp/sns/sms-pricing/\" class=\"autolink\" rel=\"nofollow noopener\" target=\"_blank\"\u003ehttps://aws.amazon.com/jp/sns/sms-pricing/\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"9:1-9:9\"\u003e\n\u003cspan id=\"構成\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E6%A7%8B%E6%88%90\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e構成\u003c/h2\u003e\n\u003cul data-sourcepos=\"11:1-12:0\"\u003e\n\u003cli data-sourcepos=\"11:1-12:0\"\u003e今回作る構成です\u003c/li\u003e\n\u003c/ul\u003e\n\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F5ad3dcea-7e4c-eb69-79ef-31809ef3d46e.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=8c9e9136ff716c89949279932b349077\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg width=\"799\" alt=\"スクリーンショット 2020-06-03 0.02.25.png\" src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F5ad3dcea-7e4c-eb69-79ef-31809ef3d46e.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=8c9e9136ff716c89949279932b349077\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F5ad3dcea-7e4c-eb69-79ef-31809ef3d46e.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=eadd9915f5a4088954c9b9bcb0f25dc2 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/5ad3dcea-7e4c-eb69-79ef-31809ef3d46e.png\" loading=\"lazy\"\u003e\u003c/a\u003e\n\u003ch2 data-sourcepos=\"15:1-15:15\"\u003e\n\u003cspan id=\"雛形生成\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E9%9B%9B%E5%BD%A2%E7%94%9F%E6%88%90\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e雛形生成\u003c/h2\u003e\n\u003cul data-sourcepos=\"17:1-19:0\"\u003e\n\u003cli data-sourcepos=\"17:1-17:45\"\u003e今回はServerlessFrameworkを使います\u003c/li\u003e\n\u003cli data-sourcepos=\"18:1-19:0\"\u003eLambdaのコードだけ見たい人はスキップしてください\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"bash\" data-sourcepos=\"20:1-25:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"nb\"\u003emkdir \u003c/span\u003esms-sample\n\u003cspan class=\"nb\"\u003ecd \u003c/span\u003esms-sample\nsls create \u003cspan class=\"nt\"\u003e-t\u003c/span\u003e aws-nodejs-typescript\nnpm i\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cul data-sourcepos=\"27:1-28:0\"\u003e\n\u003cli data-sourcepos=\"27:1-28:0\"\u003e一式作成できました\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"text\" data-sourcepos=\"29:1-40:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003etree -I node_modules\n.\n├── handler.ts\n├── package-lock.json\n├── package.json\n├── serverless.yml\n├── tsconfig.json\n├── vscode\n│   └── launch.json\n└── webpack.config.js\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003ch2 data-sourcepos=\"42:1-42:24\"\u003e\n\u003cspan id=\"lambda関数の作成\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#lambda%E9%96%A2%E6%95%B0%E3%81%AE%E4%BD%9C%E6%88%90\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eLambda関数の作成\u003c/h2\u003e\n\u003cul data-sourcepos=\"44:1-45:0\"\u003e\n\u003cli data-sourcepos=\"44:1-45:0\"\u003eAWS SDKを使うのでインストールしておきます\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"bash\" data-sourcepos=\"46:1-48:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003enpm i \u003cspan class=\"nt\"\u003e-D\u003c/span\u003e aws-sdk\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cul data-sourcepos=\"50:1-53:0\"\u003e\n\u003cli data-sourcepos=\"50:1-53:0\"\u003ehandler.tsを修正します\n\u003cul data-sourcepos=\"51:5-53:0\"\u003e\n\u003cli data-sourcepos=\"51:5-51:73\"\u003eLambda関数の中でSNSのpublishを呼んでSMSを送信します\u003c/li\u003e\n\u003cli data-sourcepos=\"52:5-53:0\"\u003e電話番号とメッセージはパラメータとして受け取ります\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"ts\" data-sourcepos=\"54:1-78:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003ehandler.ts\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"k\"\u003eimport\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"nx\"\u003eSNS\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"k\"\u003efrom\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eaws-sdk\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003cspan class=\"k\"\u003eimport\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"nx\"\u003eAPIGatewayProxyHandler\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"k\"\u003efrom\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eaws-lambda\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\n\u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003esns\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"nc\"\u003eSNS\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\n\u003cspan class=\"k\"\u003eexport\u003c/span\u003e \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003esendSMS\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nx\"\u003eAPIGatewayProxyHandler\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003easync\u003c/span\u003e \u003cspan class=\"nx\"\u003eevent\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n  \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003ebody\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003eJSON\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eparse\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eevent\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ebody\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n  \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"nx\"\u003emessage\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003ephoneNumber\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003ebody\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n  \u003cspan class=\"k\"\u003etry\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"c1\"\u003e// 電話番号とメッセージを設定\u003c/span\u003e\n    \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"na\"\u003eparams\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nx\"\u003eSNS\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eTypes\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003ePublishInput\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n      \u003cspan class=\"c1\"\u003e// 先頭の0を削って+81をつける\u003c/span\u003e\n      \u003cspan class=\"na\"\u003ePhoneNumber\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nx\"\u003ephoneNumber\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003ereplace\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sr\"\u003e/^0*/\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003e+81\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e\n      \u003cspan class=\"na\"\u003eMessage\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nx\"\u003emessage\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e};\u003c/span\u003e\n    \u003cspan class=\"c1\"\u003e// メッセージを送信\u003c/span\u003e\n    \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"nx\"\u003esns\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003epublish\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eparams\u003c/span\u003e\u003cspan class=\"p\"\u003e).\u003c/span\u003e\u003cspan class=\"nf\"\u003epromise\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"na\"\u003estatusCode\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e200\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"na\"\u003ebody\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003esuccess\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e \u003cspan class=\"p\"\u003e};\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"k\"\u003ecatch \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ee\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ee\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"na\"\u003estatusCode\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e500\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"na\"\u003ebody\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nx\"\u003eJSON\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003estringify\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003ee\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e};\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003ch2 data-sourcepos=\"80:1-80:26\"\u003e\n\u003cspan id=\"serverlessymlの設定\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#serverlessyml%E3%81%AE%E8%A8%AD%E5%AE%9A\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eServerless.ymlの設定\u003c/h2\u003e\n\u003cul data-sourcepos=\"82:1-83:0\"\u003e\n\u003cli data-sourcepos=\"82:1-83:0\"\u003eAPI Gatewayの設定やiamの設定などしていきます\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"yml\" data-sourcepos=\"84:1-115:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003eserverless.yml\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"na\"\u003eservice\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n  \u003cspan class=\"na\"\u003ename\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003esms-sample\u003c/span\u003e\n\u003cspan class=\"na\"\u003ecustom\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n  \u003cspan class=\"na\"\u003ewebpack\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n    \u003cspan class=\"na\"\u003ewebpackConfig\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003e./webpack.config.js\u003c/span\u003e\n    \u003cspan class=\"na\"\u003eincludeModules\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\n\u003cspan class=\"na\"\u003eplugins\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n  \u003cspan class=\"pi\"\u003e-\u003c/span\u003e \u003cspan class=\"s\"\u003eserverless-webpack\u003c/span\u003e\n\u003cspan class=\"na\"\u003eprovider\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n  \u003cspan class=\"na\"\u003ename\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003eaws\u003c/span\u003e\n  \u003cspan class=\"na\"\u003eruntime\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003enodejs12.x\u003c/span\u003e\n  \u003cspan class=\"na\"\u003eregion\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003eap-northeast-1\u003c/span\u003e\n  \u003cspan class=\"na\"\u003eapiGateway\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n    \u003cspan class=\"na\"\u003eminimumCompressionSize\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"m\"\u003e1024\u003c/span\u003e\n  \u003cspan class=\"na\"\u003eenvironment\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n    \u003cspan class=\"na\"\u003eAWS_NODEJS_CONNECTION_REUSE_ENABLED\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"m\"\u003e1\u003c/span\u003e\n  \u003cspan class=\"c1\"\u003e# iamの設定\u003c/span\u003e\n  \u003cspan class=\"na\"\u003eiamRoleStatements\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n    \u003cspan class=\"pi\"\u003e-\u003c/span\u003e \u003cspan class=\"na\"\u003eEffect\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003eAllow\u003c/span\u003e\n      \u003cspan class=\"na\"\u003eAction\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n        \u003cspan class=\"pi\"\u003e-\u003c/span\u003e \u003cspan class=\"s1\"\u003e'\u003c/span\u003e\u003cspan class=\"s\"\u003esns:Publish'\u003c/span\u003e \u003cspan class=\"c1\"\u003e# SNSのPublishの権限を付与\u003c/span\u003e\n      \u003cspan class=\"na\"\u003eResource\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s1\"\u003e'\u003c/span\u003e\u003cspan class=\"s\"\u003e*'\u003c/span\u003e\n\u003cspan class=\"na\"\u003efunctions\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n  \u003cspan class=\"na\"\u003esendSMS\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n    \u003cspan class=\"na\"\u003ehandler\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003ehandler.sendSMS\u003c/span\u003e\n    \u003cspan class=\"c1\"\u003e# API Gatewayの設定(POST:/sendSMS)\u003c/span\u003e\n    \u003cspan class=\"na\"\u003eevents\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n      \u003cspan class=\"pi\"\u003e-\u003c/span\u003e \u003cspan class=\"na\"\u003ehttp\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n          \u003cspan class=\"na\"\u003emethod\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003epost\u003c/span\u003e\n          \u003cspan class=\"na\"\u003epath\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003esendSMS\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003ch2 data-sourcepos=\"117:1-117:15\"\u003e\n\u003cspan id=\"動作確認\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E5%8B%95%E4%BD%9C%E7%A2%BA%E8%AA%8D\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e動作確認\u003c/h2\u003e\n\u003cul data-sourcepos=\"119:1-120:0\"\u003e\n\u003cli data-sourcepos=\"119:1-120:0\"\u003eデプロイします\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"bash\" data-sourcepos=\"121:1-123:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003esls deploy\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cul data-sourcepos=\"125:1-126:0\"\u003e\n\u003cli data-sourcepos=\"125:1-126:0\"\u003eエンドポイントにアクセスしてみます\u003c/li\u003e\n\u003c/ul\u003e\n\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fd3c97ef0-90bb-68eb-382b-e2efd3af5038.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=2aa952af403ab76cd555072b1a0db7de\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg width=\"831\" alt=\"スクリーンショット 2020-06-03 0.24.26.png\" src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fd3c97ef0-90bb-68eb-382b-e2efd3af5038.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=2aa952af403ab76cd555072b1a0db7de\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fd3c97ef0-90bb-68eb-382b-e2efd3af5038.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=285c53a5f0fb655f38ff8cbfe2a79fbd 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/d3c97ef0-90bb-68eb-382b-e2efd3af5038.png\" loading=\"lazy\"\u003e\u003c/a\u003e\n\u003cul data-sourcepos=\"129:1-130:0\"\u003e\n\u003cli data-sourcepos=\"129:1-130:0\"\u003eちゃんと届きました！\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp data-sourcepos=\"131:1-131:142\"\u003e\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F19d4294b-b7cf-97e8-6b1b-033c8482f2ec.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=78415e00cea00beb8ba2befb0d6d14a4\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F19d4294b-b7cf-97e8-6b1b-033c8482f2ec.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=78415e00cea00beb8ba2befb0d6d14a4\" alt=\"Screenshot_20200603-002459.png\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F19d4294b-b7cf-97e8-6b1b-033c8482f2ec.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=ce1720e819637e5802166769d85a4bbe 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/19d4294b-b7cf-97e8-6b1b-033c8482f2ec.png\" loading=\"lazy\"\u003e\u003c/a\u003e\u003c/p\u003e\n\u003ch2 data-sourcepos=\"133:1-133:12\"\u003e\n\u003cspan id=\"まとめ\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%81%BE%E3%81%A8%E3%82%81\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eまとめ\u003c/h2\u003e\n\u003cul data-sourcepos=\"135:1-136:81\"\u003e\n\u003cli data-sourcepos=\"135:1-135:62\"\u003e思ったよりも簡単にSMS送信を実現できました\u003c/li\u003e\n\u003cli data-sourcepos=\"136:1-136:81\"\u003eServerlessFrameworkでどこでもすぐに再現できるのがいいですね\u003c/li\u003e\n\u003c/ul\u003e\n","body":"## 概要\n\n- 電話番号でメッセージを送るSMSを使いたいなと思って調べてみたらAmazonSNSで実現できると分かったので試してみました\n- API Gateway - Lambda - SNS を連携させてAPI化させてみました\n- 当然ですが指定した電話番号に本当にSMSを送れてしまうので、**悪用しない/されないようにご注意ください**\n- ちなみに日本の場合は一通辺り$0.07451のようです\n    - https://aws.amazon.com/jp/sns/sms-pricing/\n\n## 構成\n\n- 今回作る構成です\n\n\u003cimg width=\"799\" alt=\"スクリーンショット 2020-06-03 0.02.25.png\" src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/5ad3dcea-7e4c-eb69-79ef-31809ef3d46e.png\"\u003e\n\n## 雛形生成\n\n- 今回はServerlessFrameworkを使います\n- Lambdaのコードだけ見たい人はスキップしてください\n\n```zsh\nmkdir sms-sample\ncd sms-sample\nsls create -t aws-nodejs-typescript\nnpm i\n```\n\n- 一式作成できました\n\n```\ntree -I node_modules\n.\n├── handler.ts\n├── package-lock.json\n├── package.json\n├── serverless.yml\n├── tsconfig.json\n├── vscode\n│   └── launch.json\n└── webpack.config.js\n```\n\n## Lambda関数の作成\n\n- AWS SDKを使うのでインストールしておきます\n\n```zsh\nnpm i -D aws-sdk\n```\n\n- handler.tsを修正します\n    - Lambda関数の中でSNSのpublishを呼んでSMSを送信します\n    - 電話番号とメッセージはパラメータとして受け取ります\n\n```ts:handler.ts\nimport { SNS } from 'aws-sdk';\nimport { APIGatewayProxyHandler } from 'aws-lambda';\n\nconst sns = new SNS();\n\nexport const sendSMS: APIGatewayProxyHandler = async event =\u003e {\n  const body = JSON.parse(event.body);\n  const { message, phoneNumber } = body;\n  try {\n    // 電話番号とメッセージを設定\n    const params: SNS.Types.PublishInput = {\n      // 先頭の0を削って+81をつける\n      PhoneNumber: phoneNumber.replace(/^0*/, '+81'),\n      Message: message,\n    };\n    // メッセージを送信\n    await sns.publish(params).promise();\n    return { statusCode: 200, body: 'success' };\n  } catch (e) {\n    console.log(e);\n    return { statusCode: 500, body: JSON.stringify(e) };\n  }\n};\n```\n\n## Serverless.ymlの設定\n\n- API Gatewayの設定やiamの設定などしていきます\n\n```yml:serverless.yml\nservice:\n  name: sms-sample\ncustom:\n  webpack:\n    webpackConfig: ./webpack.config.js\n    includeModules: true\nplugins:\n  - serverless-webpack\nprovider:\n  name: aws\n  runtime: nodejs12.x\n  region: ap-northeast-1\n  apiGateway:\n    minimumCompressionSize: 1024\n  environment:\n    AWS_NODEJS_CONNECTION_REUSE_ENABLED: 1\n  # iamの設定\n  iamRoleStatements:\n    - Effect: Allow\n      Action:\n        - 'sns:Publish' # SNSのPublishの権限を付与\n      Resource: '*'\nfunctions:\n  sendSMS:\n    handler: handler.sendSMS\n    # API Gatewayの設定(POST:/sendSMS)\n    events:\n      - http:\n          method: post\n          path: sendSMS\n```\n\n## 動作確認\n\n- デプロイします\n\n```zsh\nsls deploy\n```\n\n- エンドポイントにアクセスしてみます\n\n\u003cimg width=\"831\" alt=\"スクリーンショット 2020-06-03 0.24.26.png\" src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/d3c97ef0-90bb-68eb-382b-e2efd3af5038.png\"\u003e\n\n- ちゃんと届きました！\n\n![Screenshot_20200603-002459.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/19d4294b-b7cf-97e8-6b1b-033c8482f2ec.png)\n\n## まとめ\n\n- 思ったよりも簡単にSMS送信を実現できました\n- ServerlessFrameworkでどこでもすぐに再現できるのがいいですね\n","coediting":false,"comments_count":0,"created_at":"2020-06-03T00:29:43+09:00","group":null,"id":"80953bf4a592abef2389","likes_count":2,"private":false,"reactions_count":0,"stocks_count":0,"tags":[{"name":"AWS","versions":[]},{"name":"lambda","versions":[]},{"name":"ServerlessFramework","versions":[]}],"title":"【AWS】LambdaとSNSでスマホにSMSを送る","updated_at":"2021-10-26T18:49:29+09:00","url":"https://qiita.com/ozaki25/items/80953bf4a592abef2389","user":{"description":"","facebook_id":"","followees_count":0,"followers_count":79,"github_login_name":"ozaki25","id":"ozaki25","items_count":112,"linkedin_id":"","location":"","name":"","organization":"","permanent_id":175213,"profile_image_url":"https://qiita-image-store.s3.amazonaws.com/0/175213/profile-images/1491825881","team_only":false,"twitter_screen_name":"ozaki25rn","website_url":""},"page_views_count":null,"team_membership":null,"organization_url_name":null,"slide":false,"posting_campaign_uuid":null},{"rendered_body":"\u003ch2 data-sourcepos=\"1:1-1:9\"\u003e\n\u003cspan id=\"概要\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E6%A6%82%E8%A6%81\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e概要\u003c/h2\u003e\n\u003cul data-sourcepos=\"3:1-7:0\"\u003e\n\u003cli data-sourcepos=\"3:1-3:85\"\u003eLambdaなどをAPI Gateway経由で公開することはよくあると思います\u003c/li\u003e\n\u003cli data-sourcepos=\"4:1-4:98\"\u003eですが公開したエンドポイントが誰でも叩けると困るケースもあります\u003c/li\u003e\n\u003cli data-sourcepos=\"5:1-5:102\"\u003eそんな時にAPI GatewayのCustom Authorizerを使うと簡単に認証機能を付与できます\u003c/li\u003e\n\u003cli data-sourcepos=\"6:1-7:0\"\u003e今回はServerlessFrameworkを使った手順を紹介します\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"8:1-8:9\"\u003e\n\u003cspan id=\"手順\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E6%89%8B%E9%A0%86\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e手順\u003c/h2\u003e\n\u003ch3 data-sourcepos=\"10:1-10:16\"\u003e\n\u003cspan id=\"雛形生成\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E9%9B%9B%E5%BD%A2%E7%94%9F%E6%88%90\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e雛形生成\u003c/h3\u003e\n\u003cul data-sourcepos=\"12:1-13:0\"\u003e\n\u003cli data-sourcepos=\"12:1-13:0\"\u003eServerlessFrameworkで雛形を作るところからいきます\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"bash\" data-sourcepos=\"14:1-19:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"nb\"\u003emkdir \u003c/span\u003ecustom-auth-sample\n\u003cspan class=\"nb\"\u003ecd \u003c/span\u003ecustom-auth-sample\nsls create \u003cspan class=\"nt\"\u003e-t\u003c/span\u003e aws-nodejs-typescript\nnpm i\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cul data-sourcepos=\"21:1-22:0\"\u003e\n\u003cli data-sourcepos=\"21:1-22:0\"\u003e一式作成できました\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"text\" data-sourcepos=\"23:1-34:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003etree -I node_modules\n.\n├── handler.ts\n├── package-lock.json\n├── package.json\n├── serverless.yml\n├── tsconfig.json\n├── vscode\n│   └── launch.json\n└── webpack.config.js\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003ch3 data-sourcepos=\"36:1-36:30\"\u003e\n\u003cspan id=\"hello関数の動作確認\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#hello%E9%96%A2%E6%95%B0%E3%81%AE%E5%8B%95%E4%BD%9C%E7%A2%BA%E8%AA%8D\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eHello関数の動作確認\u003c/h3\u003e\n\u003cul data-sourcepos=\"38:1-41:0\"\u003e\n\u003cli data-sourcepos=\"38:1-39:101\"\u003eデフォルトでHello関数があるのでそれを確認しておきます\n\u003cul data-sourcepos=\"39:5-39:101\"\u003e\n\u003cli data-sourcepos=\"39:5-39:101\"\u003eHello関数を認証突破しないと叩けないようにする流れで進めていきます\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"40:1-41:0\"\u003ehandler.tsではhello関数は\u003ccode\u003emessage\u003c/code\u003eなどをreturnするだけの処理として定義してあります\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"ts\" data-sourcepos=\"42:1-53:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003ehandler.ts\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"c1\"\u003e// handler.ts抜粋\u003c/span\u003e\n\u003cspan class=\"k\"\u003eexport\u003c/span\u003e \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003ehello\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nx\"\u003eAPIGatewayProxyHandler\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003easync \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eevent\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003e_context\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n  \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"na\"\u003estatusCode\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e200\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n    \u003cspan class=\"na\"\u003ebody\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nx\"\u003eJSON\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003estringify\u003c/span\u003e\u003cspan class=\"p\"\u003e({\u003c/span\u003e\n      \u003cspan class=\"na\"\u003emessage\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eGo Serverless Webpack (Typescript) v1.0! Your function executed successfully!\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n      \u003cspan class=\"na\"\u003einput\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nx\"\u003eevent\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e},\u003c/span\u003e \u003cspan class=\"kc\"\u003enull\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cul data-sourcepos=\"55:1-56:0\"\u003e\n\u003cli data-sourcepos=\"55:1-56:0\"\u003eserverless.ymlでhello関数には\u003ccode\u003eGET:/hello\u003c/code\u003eでアクセスできるように定義してあります\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"yml\" data-sourcepos=\"57:1-67:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003eserverless.yml\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"c1\"\u003e# serverless.yml抜粋\u003c/span\u003e\n\n\u003cspan class=\"na\"\u003efunctions\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n  \u003cspan class=\"na\"\u003ehello\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n    \u003cspan class=\"na\"\u003ehandler\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003ehandler.hello\u003c/span\u003e\n    \u003cspan class=\"na\"\u003eevents\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n      \u003cspan class=\"pi\"\u003e-\u003c/span\u003e \u003cspan class=\"na\"\u003ehttp\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n          \u003cspan class=\"na\"\u003emethod\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003eget\u003c/span\u003e\n          \u003cspan class=\"na\"\u003epath\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003ehello\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cul data-sourcepos=\"69:1-70:0\"\u003e\n\u003cli data-sourcepos=\"69:1-70:0\"\u003eデプロイしましょう\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"bash\" data-sourcepos=\"71:1-73:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003esls deploy\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fcf2c5a94-3923-27f4-9e91-e057a1f675d7.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=fa65a4a733a898b6ce4026e39aa5686f\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg width=\"740\" alt=\"スクリーンショット 2020-06-02 1.31.20.png\" src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fcf2c5a94-3923-27f4-9e91-e057a1f675d7.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=fa65a4a733a898b6ce4026e39aa5686f\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fcf2c5a94-3923-27f4-9e91-e057a1f675d7.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=1547fe6f6b4c327e3f557b85ac0b4617 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/cf2c5a94-3923-27f4-9e91-e057a1f675d7.png\" loading=\"lazy\"\u003e\u003c/a\u003e\n\u003cul data-sourcepos=\"77:1-78:0\"\u003e\n\u003cli data-sourcepos=\"77:1-78:0\"\u003eログに出てくるURLにアクセスするmessageが返ってきます\u003c/li\u003e\n\u003c/ul\u003e\n\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fc5c96238-4509-885d-1cd2-34b2186f3878.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=94ac6f94c629fd9896dfdf675a6e42d8\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg width=\"1000\" alt=\"スクリーンショット 2020-06-02 1.32.23.png\" src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fc5c96238-4509-885d-1cd2-34b2186f3878.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=94ac6f94c629fd9896dfdf675a6e42d8\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fc5c96238-4509-885d-1cd2-34b2186f3878.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=b0fbdabaef02ab426673404a69b09bd7 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/c5c96238-4509-885d-1cd2-34b2186f3878.png\" loading=\"lazy\"\u003e\u003c/a\u003e\n\u003ch3 data-sourcepos=\"81:1-81:23\"\u003e\n\u003cspan id=\"auth関数の作成\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#auth%E9%96%A2%E6%95%B0%E3%81%AE%E4%BD%9C%E6%88%90\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eAuth関数の作成\u003c/h3\u003e\n\u003cul data-sourcepos=\"83:1-87:0\"\u003e\n\u003cli data-sourcepos=\"83:1-83:110\"\u003eユーザから送信されたトークンが正しいかどうかチェックする関数を作成します\u003c/li\u003e\n\u003cli data-sourcepos=\"84:1-87:0\"\u003ehandler.tsの一番下に以下の内容を追記してください\n\u003cul data-sourcepos=\"85:5-87:0\"\u003e\n\u003cli data-sourcepos=\"85:5-85:142\"\u003eheaderのAuthorizationにセットされた値と、事前に定義しておいたキーが一致してるかチェックしています\u003c/li\u003e\n\u003cli data-sourcepos=\"86:5-87:0\"\u003e一致した場合は後続の処理を実行する権限が付与され実行されます\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"ts\" data-sourcepos=\"88:1-112:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003ehandler.ts\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"c1\"\u003e// 省略\u003c/span\u003e\n\n\u003cspan class=\"k\"\u003eexport\u003c/span\u003e \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003eauth\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nx\"\u003eCustomAuthorizerHandler\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eevent\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003econtext\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n  \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"nx\"\u003eauthorizationToken\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003eevent\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n  \u003cspan class=\"k\"\u003eif \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eauthorizationToken\u003c/span\u003e \u003cspan class=\"o\"\u003e===\u003c/span\u003e \u003cspan class=\"nx\"\u003eprocess\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eenv\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eAUTH_KEY\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003epolicy\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n      \u003cspan class=\"na\"\u003eprincipalId\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003euser\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n      \u003cspan class=\"na\"\u003epolicyDocument\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n        \u003cspan class=\"na\"\u003eVersion\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003e2012-10-17\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n        \u003cspan class=\"na\"\u003eStatement\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\n          \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n            \u003cspan class=\"na\"\u003eAction\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eexecute-api:Invoke\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n            \u003cspan class=\"na\"\u003eEffect\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eAllow\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n            \u003cspan class=\"na\"\u003eResource\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nx\"\u003eevent\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003emethodArn\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n          \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n        \u003cspan class=\"p\"\u003e],\u003c/span\u003e\n      \u003cspan class=\"p\"\u003e},\u003c/span\u003e\n    \u003cspan class=\"p\"\u003e};\u003c/span\u003e\n    \u003cspan class=\"nx\"\u003econtext\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003esucceed\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003epolicy\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"k\"\u003eelse\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"nx\"\u003econtext\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003efail\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eUnauthorized\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cul data-sourcepos=\"114:1-118:0\"\u003e\n\u003cli data-sourcepos=\"114:1-118:0\"\u003eserverless.ymlにCustomAuthorizerとして登録する設定をします\n\u003cul data-sourcepos=\"115:5-118:0\"\u003e\n\u003cli data-sourcepos=\"115:5-115:105\"\u003e全量載せていませんがコメントで書いている部分を追記しているだけです\u003c/li\u003e\n\u003cli data-sourcepos=\"116:5-118:0\"\u003eAUTH_KEYを直接埋め込むのが嫌なケースはdotenvなど使うといいでしょう\n\u003cul data-sourcepos=\"117:9-118:0\"\u003e\n\u003cli data-sourcepos=\"117:9-118:0\"\u003e参考: \u003ca href=\"https://qiita.com/ozaki25/items/c855c3c7bcb45515539f\" id=\"reference-a6735f260e08039d3b1c\"\u003eServerlessFrameworkでdotenvを使う\u003c/a\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"yml\" data-sourcepos=\"119:1-138:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003eserverless.yml\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"c1\"\u003e# 省略\u003c/span\u003e\n\u003cspan class=\"na\"\u003eprovider\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n  \u003cspan class=\"c1\"\u003e# 省略\u003c/span\u003e\n  \u003cspan class=\"na\"\u003eenvironment\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n    \u003cspan class=\"na\"\u003eAWS_NODEJS_CONNECTION_REUSE_ENABLED\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"m\"\u003e1\u003c/span\u003e\n    \u003cspan class=\"na\"\u003eAUTH_KEY\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003epassword@12345\u003c/span\u003e \u003cspan class=\"c1\"\u003e# キーを環境変数として設定\u003c/span\u003e\n\n\u003cspan class=\"na\"\u003efunctions\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n  \u003cspan class=\"na\"\u003ehello\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n    \u003cspan class=\"na\"\u003ehandler\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003ehandler.hello\u003c/span\u003e\n    \u003cspan class=\"na\"\u003eevents\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n      \u003cspan class=\"pi\"\u003e-\u003c/span\u003e \u003cspan class=\"na\"\u003ehttp\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n          \u003cspan class=\"na\"\u003emethod\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003eget\u003c/span\u003e\n          \u003cspan class=\"na\"\u003epath\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003ehello\u003c/span\u003e\n          \u003cspan class=\"na\"\u003eauthorizer\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003eauthorizerFunc\u003c/span\u003e \u003cspan class=\"c1\"\u003e# CustomAuthorizerが適用されるように設定\u003c/span\u003e\n  \u003cspan class=\"c1\"\u003e# 追加したLambda関数を登録\u003c/span\u003e\n  \u003cspan class=\"na\"\u003eauthorizerFunc\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n    \u003cspan class=\"na\"\u003ehandler\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003ehandler.auth\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cul data-sourcepos=\"140:1-141:0\"\u003e\n\u003cli data-sourcepos=\"140:1-141:0\"\u003eデプロイしましょう\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"bash\" data-sourcepos=\"142:1-144:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003esls deploy\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cul data-sourcepos=\"146:1-147:0\"\u003e\n\u003cli data-sourcepos=\"146:1-147:0\"\u003eデプロイできたら再びHello関数にアクセスしてみましょう\u003c/li\u003e\n\u003c/ul\u003e\n\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F2c00a15c-89c7-7aab-f1a8-385ae2eb0d43.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=0c57f316f934bdf26cc8a48625ffbd95\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg width=\"871\" alt=\"スクリーンショット 2020-06-02 1.42.53.png\" src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F2c00a15c-89c7-7aab-f1a8-385ae2eb0d43.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=0c57f316f934bdf26cc8a48625ffbd95\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F2c00a15c-89c7-7aab-f1a8-385ae2eb0d43.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=0161970521b617276c1d7071438b4f1d 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/2c00a15c-89c7-7aab-f1a8-385ae2eb0d43.png\" loading=\"lazy\"\u003e\u003c/a\u003e\n\u003cul data-sourcepos=\"150:1-152:0\"\u003e\n\u003cli data-sourcepos=\"150:1-150:71\"\u003e設定が適用されていれば401で弾かれているはずです\u003c/li\u003e\n\u003cli data-sourcepos=\"151:1-152:0\"\u003eheaderのAuthorizationにキーを設定して再度アクセスしてみます\u003c/li\u003e\n\u003c/ul\u003e\n\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F19d29e8f-528a-4df8-ba92-c00af67a5347.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=d6640f0449cf382f3216ff4fe575e35d\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg width=\"913\" alt=\"スクリーンショット 2020-06-02 1.44.14.png\" src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F19d29e8f-528a-4df8-ba92-c00af67a5347.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=d6640f0449cf382f3216ff4fe575e35d\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F19d29e8f-528a-4df8-ba92-c00af67a5347.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=a2687ebece4777bfa718dfb354c5e051 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/19d29e8f-528a-4df8-ba92-c00af67a5347.png\" loading=\"lazy\"\u003e\u003c/a\u003e\n\u003cul data-sourcepos=\"155:1-156:0\"\u003e\n\u003cli data-sourcepos=\"155:1-156:0\"\u003eキーが正しいのでちゃんとアクセスできました！簡単ですね！\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"157:1-157:12\"\u003e\n\u003cspan id=\"まとめ\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%81%BE%E3%81%A8%E3%82%81\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eまとめ\u003c/h2\u003e\n\u003cul data-sourcepos=\"159:1-159:118\"\u003e\n\u003cli data-sourcepos=\"159:1-159:118\"\u003e簡単なCustomAuthorizerの設定はServerlessFrameworkを使うと簡単に実現することができました！\u003c/li\u003e\n\u003c/ul\u003e\n","body":"## 概要\n\n- LambdaなどをAPI Gateway経由で公開することはよくあると思います\n- ですが公開したエンドポイントが誰でも叩けると困るケースもあります\n- そんな時にAPI GatewayのCustom Authorizerを使うと簡単に認証機能を付与できます\n- 今回はServerlessFrameworkを使った手順を紹介します\n\n## 手順\n\n### 雛形生成\n\n- ServerlessFrameworkで雛形を作るところからいきます\n\n```zsh\nmkdir custom-auth-sample\ncd custom-auth-sample\nsls create -t aws-nodejs-typescript\nnpm i\n```\n\n- 一式作成できました\n\n```\ntree -I node_modules\n.\n├── handler.ts\n├── package-lock.json\n├── package.json\n├── serverless.yml\n├── tsconfig.json\n├── vscode\n│   └── launch.json\n└── webpack.config.js\n```\n\n### Hello関数の動作確認\n\n- デフォルトでHello関数があるのでそれを確認しておきます\n    - Hello関数を認証突破しないと叩けないようにする流れで進めていきます\n- handler.tsではhello関数は`message`などをreturnするだけの処理として定義してあります\n\n```ts:handler.ts\n// handler.ts抜粋\nexport const hello: APIGatewayProxyHandler = async (event, _context) =\u003e {\n  return {\n    statusCode: 200,\n    body: JSON.stringify({\n      message: 'Go Serverless Webpack (Typescript) v1.0! Your function executed successfully!',\n      input: event,\n    }, null, 2),\n  };\n}\n```\n\n- serverless.ymlでhello関数には`GET:/hello`でアクセスできるように定義してあります\n\n```yml:serverless.yml\n# serverless.yml抜粋\n\nfunctions:\n  hello:\n    handler: handler.hello\n    events:\n      - http:\n          method: get\n          path: hello\n```\n\n- デプロイしましょう\n\n```zsh\nsls deploy\n```\n\n\u003cimg width=\"740\" alt=\"スクリーンショット 2020-06-02 1.31.20.png\" src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/cf2c5a94-3923-27f4-9e91-e057a1f675d7.png\"\u003e\n\n- ログに出てくるURLにアクセスするmessageが返ってきます\n\n\u003cimg width=\"1000\" alt=\"スクリーンショット 2020-06-02 1.32.23.png\" src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/c5c96238-4509-885d-1cd2-34b2186f3878.png\"\u003e\n\n### Auth関数の作成\n\n- ユーザから送信されたトークンが正しいかどうかチェックする関数を作成します\n- handler.tsの一番下に以下の内容を追記してください\n    - headerのAuthorizationにセットされた値と、事前に定義しておいたキーが一致してるかチェックしています\n    - 一致した場合は後続の処理を実行する権限が付与され実行されます\n\n```ts:handler.ts\n// 省略\n\nexport const auth: CustomAuthorizerHandler = (event, context) =\u003e {\n  const { authorizationToken } = event;\n  if (authorizationToken === process.env.AUTH_KEY) {\n    const policy = {\n      principalId: 'user',\n      policyDocument: {\n        Version: '2012-10-17',\n        Statement: [\n          {\n            Action: 'execute-api:Invoke',\n            Effect: 'Allow',\n            Resource: event.methodArn,\n          },\n        ],\n      },\n    };\n    context.succeed(policy);\n  } else {\n    context.fail('Unauthorized');\n  }\n};\n```\n\n- serverless.ymlにCustomAuthorizerとして登録する設定をします\n    - 全量載せていませんがコメントで書いている部分を追記しているだけです\n    - AUTH_KEYを直接埋め込むのが嫌なケースはdotenvなど使うといいでしょう\n        - 参考: [ServerlessFrameworkでdotenvを使う](https://qiita.com/ozaki25/items/c855c3c7bcb45515539f)\n\n```yml:serverless.yml\n# 省略\nprovider:\n  # 省略\n  environment:\n    AWS_NODEJS_CONNECTION_REUSE_ENABLED: 1\n    AUTH_KEY: password@12345 # キーを環境変数として設定\n\nfunctions:\n  hello:\n    handler: handler.hello\n    events:\n      - http:\n          method: get\n          path: hello\n          authorizer: authorizerFunc # CustomAuthorizerが適用されるように設定\n  # 追加したLambda関数を登録\n  authorizerFunc:\n    handler: handler.auth\n```\n\n- デプロイしましょう\n\n```zsh\nsls deploy\n```\n\n- デプロイできたら再びHello関数にアクセスしてみましょう\n\n\u003cimg width=\"871\" alt=\"スクリーンショット 2020-06-02 1.42.53.png\" src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/2c00a15c-89c7-7aab-f1a8-385ae2eb0d43.png\"\u003e\n\n- 設定が適用されていれば401で弾かれているはずです\n- headerのAuthorizationにキーを設定して再度アクセスしてみます\n\n\u003cimg width=\"913\" alt=\"スクリーンショット 2020-06-02 1.44.14.png\" src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/19d29e8f-528a-4df8-ba92-c00af67a5347.png\"\u003e\n\n- キーが正しいのでちゃんとアクセスできました！簡単ですね！\n\n## まとめ\n\n- 簡単なCustomAuthorizerの設定はServerlessFrameworkを使うと簡単に実現することができました！\n","coediting":false,"comments_count":0,"created_at":"2020-06-02T01:48:25+09:00","group":null,"id":"dcec85d7357ddd8417c7","likes_count":2,"private":false,"reactions_count":0,"stocks_count":0,"tags":[{"name":"AWS","versions":[]},{"name":"ServerlessFramework","versions":[]}],"title":"【AWS】API GatewayのCustomAuthorizerを設定する","updated_at":"2021-10-26T18:49:21+09:00","url":"https://qiita.com/ozaki25/items/dcec85d7357ddd8417c7","user":{"description":"","facebook_id":"","followees_count":0,"followers_count":79,"github_login_name":"ozaki25","id":"ozaki25","items_count":112,"linkedin_id":"","location":"","name":"","organization":"","permanent_id":175213,"profile_image_url":"https://qiita-image-store.s3.amazonaws.com/0/175213/profile-images/1491825881","team_only":false,"twitter_screen_name":"ozaki25rn","website_url":""},"page_views_count":null,"team_membership":null,"organization_url_name":null,"slide":false,"posting_campaign_uuid":null},{"rendered_body":"\u003ch2 data-sourcepos=\"1:1-1:9\"\u003e\n\u003cspan id=\"概要\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E6%A6%82%E8%A6%81\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e概要\u003c/h2\u003e\n\u003cul data-sourcepos=\"3:1-4:0\"\u003e\n\u003cli data-sourcepos=\"3:1-4:0\"\u003eCLIなどからAWSにアクセスするためのアクセスキーの発行手順です\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"5:1-5:30\"\u003e\n\u003cspan id=\"アクセスキーの発行\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E3%82%AD%E3%83%BC%E3%81%AE%E7%99%BA%E8%A1%8C\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eアクセスキーの発行\u003c/h2\u003e\n\u003cul data-sourcepos=\"7:1-10:0\"\u003e\n\u003cli data-sourcepos=\"7:1-8:75\"\u003eAWSにログインしキャプチャの通り進めていきます\n\u003cul data-sourcepos=\"8:5-8:75\"\u003e\n\u003cli data-sourcepos=\"8:5-8:75\"\u003eユーザ作成の権限があるユーザで実行してください\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"9:1-10:0\"\u003eIAMのメニューを開く\u003c/li\u003e\n\u003c/ul\u003e\n\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Faf531cd2-4250-eb34-dd9a-5a919275ba4c.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=9d762078d7c7c34fa4195cdcc33b82f4\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg width=\"733\" alt=\"スクリーンショット 2020-05-25 15.41.55.png\" src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Faf531cd2-4250-eb34-dd9a-5a919275ba4c.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=9d762078d7c7c34fa4195cdcc33b82f4\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Faf531cd2-4250-eb34-dd9a-5a919275ba4c.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=b22d76f3016aeddece58210b2e6b5811 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/af531cd2-4250-eb34-dd9a-5a919275ba4c.png\" loading=\"lazy\"\u003e\u003c/a\u003e\n\u003cul data-sourcepos=\"13:1-14:0\"\u003e\n\u003cli data-sourcepos=\"13:1-14:0\"\u003eユーザタブからユーザ追加を選択\u003c/li\u003e\n\u003c/ul\u003e\n\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F3674fe84-13ae-61bd-f1ea-b4bb684bee0c.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=fa3eea29a1031f1eb4a066093e39b34b\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg width=\"732\" alt=\"スクリーンショット 2020-05-25 15.43.49.png\" src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F3674fe84-13ae-61bd-f1ea-b4bb684bee0c.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=fa3eea29a1031f1eb4a066093e39b34b\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F3674fe84-13ae-61bd-f1ea-b4bb684bee0c.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=d85c00f5b239f689574ec9c8c5a04e7d 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/3674fe84-13ae-61bd-f1ea-b4bb684bee0c.png\" loading=\"lazy\"\u003e\u003c/a\u003e\n\u003cul data-sourcepos=\"17:1-20:0\"\u003e\n\u003cli data-sourcepos=\"17:1-17:38\"\u003e適当なユーザ名を入れます\u003c/li\u003e\n\u003cli data-sourcepos=\"18:1-20:0\"\u003eプログラムによるアクセスのみチェックを入れます\n\u003cul data-sourcepos=\"19:5-20:0\"\u003e\n\u003cli data-sourcepos=\"19:5-20:0\"\u003eAWSマネジメントコンソールへのアクセスにチェックを入れるとWebコンソールにログインできるユーザになってしまいます\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fb00f1f17-c40d-416e-4eb3-dc47e7fb1d13.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=6aeda86efd0c96dcb3c8cb6489588ecf\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg width=\"733\" alt=\"スクリーンショット 2020-05-25 15.44.25.png\" src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fb00f1f17-c40d-416e-4eb3-dc47e7fb1d13.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=6aeda86efd0c96dcb3c8cb6489588ecf\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fb00f1f17-c40d-416e-4eb3-dc47e7fb1d13.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=ef2a524d0f6a8e4ead5d9e270bd89236 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/b00f1f17-c40d-416e-4eb3-dc47e7fb1d13.png\" loading=\"lazy\"\u003e\u003c/a\u003e\n\u003cul data-sourcepos=\"23:1-25:0\"\u003e\n\u003cli data-sourcepos=\"23:1-25:0\"\u003e権限を設定します\n\u003cul data-sourcepos=\"24:5-25:0\"\u003e\n\u003cli data-sourcepos=\"24:5-25:0\"\u003eここではAdmin権限を付与しています\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fb90f8634-4171-d6cb-1961-a80473be01a5.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=97267b65f311f0866c484f56fb3ac3bf\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg width=\"732\" alt=\"スクリーンショット 2020-05-25 15.44.49.png\" src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fb90f8634-4171-d6cb-1961-a80473be01a5.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=97267b65f311f0866c484f56fb3ac3bf\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fb90f8634-4171-d6cb-1961-a80473be01a5.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=66e8bb74065160f59ef2ea707b092088 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/b90f8634-4171-d6cb-1961-a80473be01a5.png\" loading=\"lazy\"\u003e\u003c/a\u003e\n\u003cul data-sourcepos=\"28:1-29:0\"\u003e\n\u003cli data-sourcepos=\"28:1-29:0\"\u003eタグは何も設定しなくて大丈夫です\u003c/li\u003e\n\u003c/ul\u003e\n\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F1bd8c1e0-7f6d-dd00-90f4-b0c4a5480ff3.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=5c17aefb776b6b95381ad571a7343e5b\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg width=\"733\" alt=\"スクリーンショット 2020-05-25 15.45.05.png\" src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F1bd8c1e0-7f6d-dd00-90f4-b0c4a5480ff3.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=5c17aefb776b6b95381ad571a7343e5b\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F1bd8c1e0-7f6d-dd00-90f4-b0c4a5480ff3.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=a5582cd3f60ba05140e320c0a2c8767f 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/1bd8c1e0-7f6d-dd00-90f4-b0c4a5480ff3.png\" loading=\"lazy\"\u003e\u003c/a\u003e\n\u003cul data-sourcepos=\"32:1-33:0\"\u003e\n\u003cli data-sourcepos=\"32:1-33:0\"\u003eユーザーの作成ボタンを押して送信します\u003c/li\u003e\n\u003c/ul\u003e\n\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F85352afa-b1b2-3c1e-da4e-bbd3d4c0a006.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=e44179f93bffac903537394ce2e49673\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg width=\"732\" alt=\"スクリーンショット 2020-05-25 15.45.18.png\" src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F85352afa-b1b2-3c1e-da4e-bbd3d4c0a006.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=e44179f93bffac903537394ce2e49673\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F85352afa-b1b2-3c1e-da4e-bbd3d4c0a006.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=2e119d03092288a37232237c2ffd8800 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/85352afa-b1b2-3c1e-da4e-bbd3d4c0a006.png\" loading=\"lazy\"\u003e\u003c/a\u003e\n\u003cul data-sourcepos=\"36:1-38:0\"\u003e\n\u003cli data-sourcepos=\"36:1-38:0\"\u003eアクセスキーIDとシークレットアクセスキーが表示されます\n\u003cul data-sourcepos=\"37:5-38:0\"\u003e\n\u003cli data-sourcepos=\"37:5-38:0\"\u003e漏洩することがないよう厳重に管理しましょう\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F92ad9e70-4dc4-863f-7fd0-a5e610e949be.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=4d6ee1e9253b10385b3f8081d1847f31\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg width=\"797\" alt=\"スクリーンショット 2020-05-25 15.45.43.png\" src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F92ad9e70-4dc4-863f-7fd0-a5e610e949be.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=4d6ee1e9253b10385b3f8081d1847f31\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F92ad9e70-4dc4-863f-7fd0-a5e610e949be.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=6412a182bee78246d7146bd4ad0d1897 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/92ad9e70-4dc4-863f-7fd0-a5e610e949be.png\" loading=\"lazy\"\u003e\u003c/a\u003e\n\u003ch2 data-sourcepos=\"41:1-41:30\"\u003e\n\u003cspan id=\"アクセスキーの登録\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E3%82%AD%E3%83%BC%E3%81%AE%E7%99%BB%E9%8C%B2\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eアクセスキーの登録\u003c/h2\u003e\n\u003cul data-sourcepos=\"43:1-46:0\"\u003e\n\u003cli data-sourcepos=\"43:1-43:62\"\u003eローカル端末上にアクセスキーを登録します\u003c/li\u003e\n\u003cli data-sourcepos=\"44:1-44:141\"\u003eホームディレクトリ直下に\u003ccode\u003e.aws\u003c/code\u003eフォルダを作成しその中に\u003ccode\u003ecredentials\u003c/code\u003eという名前のファイルを作成します\u003c/li\u003e\n\u003cli data-sourcepos=\"45:1-46:0\"\u003e発行したアクセスキーとシークレットキーを登録します\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"config\" data-sourcepos=\"47:1-53:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003e~/.aws/credentials\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e[\u003cspan class=\"n\"\u003edefault\u003c/span\u003e]\n\u003cspan class=\"n\"\u003eaws_access_key_id\u003c/span\u003e=\u003cspan class=\"n\"\u003eXXXXXXXXXXXXXXXXXXXX\u003c/span\u003e\n\u003cspan class=\"n\"\u003eaws_secret_access_key\u003c/span\u003e=\u003cspan class=\"n\"\u003eXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\u003c/span\u003e\n\u003cspan class=\"n\"\u003eregion\u003c/span\u003e=\u003cspan class=\"n\"\u003eap\u003c/span\u003e-\u003cspan class=\"n\"\u003enortheast\u003c/span\u003e-\u003cspan class=\"m\"\u003e1\u003c/span\u003e\n\u003cspan class=\"n\"\u003eoutput\u003c/span\u003e=\u003cspan class=\"n\"\u003ejson\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cul data-sourcepos=\"55:1-56:0\"\u003e\n\u003cli data-sourcepos=\"55:1-56:0\"\u003e複数のキーを登録したい場合は\u003ccode\u003e[default]\u003c/code\u003eの部分を他の名前にして登録します\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"config\" data-sourcepos=\"57:1-69:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003e~/.aws/credentials\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e[\u003cspan class=\"n\"\u003edefault\u003c/span\u003e]\n\u003cspan class=\"n\"\u003eaws_access_key_id\u003c/span\u003e=\u003cspan class=\"n\"\u003eXXXXXXXXXXXXXXXXXXXX\u003c/span\u003e\n\u003cspan class=\"n\"\u003eaws_secret_access_key\u003c/span\u003e=\u003cspan class=\"n\"\u003eXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\u003c/span\u003e\n\u003cspan class=\"n\"\u003eregion\u003c/span\u003e=\u003cspan class=\"n\"\u003eap\u003c/span\u003e-\u003cspan class=\"n\"\u003enortheast\u003c/span\u003e-\u003cspan class=\"m\"\u003e1\u003c/span\u003e\n\u003cspan class=\"n\"\u003eoutput\u003c/span\u003e=\u003cspan class=\"n\"\u003ejson\u003c/span\u003e\n\n[\u003cspan class=\"n\"\u003emy\u003c/span\u003e-\u003cspan class=\"n\"\u003eaccount\u003c/span\u003e]\n\u003cspan class=\"n\"\u003eaws_access_key_id\u003c/span\u003e=\u003cspan class=\"n\"\u003eXXXXXXXXXXXXXXXXXXXX\u003c/span\u003e\n\u003cspan class=\"n\"\u003eaws_secret_access_key\u003c/span\u003e=\u003cspan class=\"n\"\u003eXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\u003c/span\u003e\n\u003cspan class=\"n\"\u003eregion\u003c/span\u003e=\u003cspan class=\"n\"\u003eap\u003c/span\u003e-\u003cspan class=\"n\"\u003enortheast\u003c/span\u003e-\u003cspan class=\"m\"\u003e1\u003c/span\u003e\n\u003cspan class=\"n\"\u003eoutput\u003c/span\u003e=\u003cspan class=\"n\"\u003ejson\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n","body":"## 概要\n\n- CLIなどからAWSにアクセスするためのアクセスキーの発行手順です\n\n## アクセスキーの発行\n\n- AWSにログインしキャプチャの通り進めていきます\n    - ユーザ作成の権限があるユーザで実行してください\n- IAMのメニューを開く\n\n\u003cimg width=\"733\" alt=\"スクリーンショット 2020-05-25 15.41.55.png\" src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/af531cd2-4250-eb34-dd9a-5a919275ba4c.png\"\u003e\n\n- ユーザタブからユーザ追加を選択\n\n\u003cimg width=\"732\" alt=\"スクリーンショット 2020-05-25 15.43.49.png\" src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/3674fe84-13ae-61bd-f1ea-b4bb684bee0c.png\"\u003e\n\n- 適当なユーザ名を入れます\n- プログラムによるアクセスのみチェックを入れます\n    - AWSマネジメントコンソールへのアクセスにチェックを入れるとWebコンソールにログインできるユーザになってしまいます\n\n\u003cimg width=\"733\" alt=\"スクリーンショット 2020-05-25 15.44.25.png\" src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/b00f1f17-c40d-416e-4eb3-dc47e7fb1d13.png\"\u003e\n\n- 権限を設定します\n    - ここではAdmin権限を付与しています\n\n\u003cimg width=\"732\" alt=\"スクリーンショット 2020-05-25 15.44.49.png\" src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/b90f8634-4171-d6cb-1961-a80473be01a5.png\"\u003e\n\n- タグは何も設定しなくて大丈夫です\n\n\u003cimg width=\"733\" alt=\"スクリーンショット 2020-05-25 15.45.05.png\" src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/1bd8c1e0-7f6d-dd00-90f4-b0c4a5480ff3.png\"\u003e\n\n- ユーザーの作成ボタンを押して送信します\n\n\u003cimg width=\"732\" alt=\"スクリーンショット 2020-05-25 15.45.18.png\" src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/85352afa-b1b2-3c1e-da4e-bbd3d4c0a006.png\"\u003e\n\n- アクセスキーIDとシークレットアクセスキーが表示されます\n    - 漏洩することがないよう厳重に管理しましょう\n\n\u003cimg width=\"797\" alt=\"スクリーンショット 2020-05-25 15.45.43.png\" src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/92ad9e70-4dc4-863f-7fd0-a5e610e949be.png\"\u003e\n\n## アクセスキーの登録\n\n- ローカル端末上にアクセスキーを登録します\n- ホームディレクトリ直下に`.aws`フォルダを作成しその中に`credentials`という名前のファイルを作成します\n- 発行したアクセスキーとシークレットキーを登録します\n\n```config:~/.aws/credentials\n[default]\naws_access_key_id=XXXXXXXXXXXXXXXXXXXX\naws_secret_access_key=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nregion=ap-northeast-1\noutput=json\n```\n\n- 複数のキーを登録したい場合は`[default]`の部分を他の名前にして登録します\n\n```config:~/.aws/credentials\n[default]\naws_access_key_id=XXXXXXXXXXXXXXXXXXXX\naws_secret_access_key=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nregion=ap-northeast-1\noutput=json\n\n[my-account]\naws_access_key_id=XXXXXXXXXXXXXXXXXXXX\naws_secret_access_key=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\nregion=ap-northeast-1\noutput=json\n```\n\n","coediting":false,"comments_count":0,"created_at":"2020-05-25T16:11:03+09:00","group":null,"id":"034f7f8e8ad69adceea7","likes_count":15,"private":false,"reactions_count":0,"stocks_count":12,"tags":[{"name":"AWS","versions":[]}],"title":"【AWS】アクセスキーの作成/登録","updated_at":"2021-10-26T18:49:14+09:00","url":"https://qiita.com/ozaki25/items/034f7f8e8ad69adceea7","user":{"description":"","facebook_id":"","followees_count":0,"followers_count":79,"github_login_name":"ozaki25","id":"ozaki25","items_count":112,"linkedin_id":"","location":"","name":"","organization":"","permanent_id":175213,"profile_image_url":"https://qiita-image-store.s3.amazonaws.com/0/175213/profile-images/1491825881","team_only":false,"twitter_screen_name":"ozaki25rn","website_url":""},"page_views_count":null,"team_membership":null,"organization_url_name":null,"slide":false,"posting_campaign_uuid":null},{"rendered_body":"\u003ch2 data-sourcepos=\"1:1-1:15\"\u003e\n\u003cspan id=\"きっかけ\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%81%8D%E3%81%A3%E3%81%8B%E3%81%91\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eきっかけ\u003c/h2\u003e\n\u003cul data-sourcepos=\"3:1-5:0\"\u003e\n\u003cli data-sourcepos=\"3:1-3:83\"\u003e昨今の事情もあり在宅勤務を行っている方は多いと思います\u003c/li\u003e\n\u003cli data-sourcepos=\"4:1-5:0\"\u003eそんな中でチームのSlackでこんなやりとりがありました\u003c/li\u003e\n\u003c/ul\u003e\n\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fe0139cd0-92ef-1636-8c95-0d8f988aa770.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=25e6c2d1ec6996656f9c3e9d28f7c427\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg width=\"601\" alt=\"スクリーンショット 2020-04-29 0.18.53.png\" src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fe0139cd0-92ef-1636-8c95-0d8f988aa770.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=25e6c2d1ec6996656f9c3e9d28f7c427\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2Fe0139cd0-92ef-1636-8c95-0d8f988aa770.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=28cfa0d77f742aeb0d392bfd73ee19a1 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/e0139cd0-92ef-1636-8c95-0d8f988aa770.png\" loading=\"lazy\"\u003e\u003c/a\u003e\n\u003cul data-sourcepos=\"8:1-10:0\"\u003e\n\u003cli data-sourcepos=\"8:1-8:71\"\u003e在宅でリモートだと曜日感覚なくなってきますよね\u003c/li\u003e\n\u003cli data-sourcepos=\"9:1-10:0\"\u003e明日が休みだと思うことで生まれる\u003cstrong\u003e生産性爆上げ日\u003c/strong\u003eを逃してしまうのはもったいない！ということでStayHomeの暇つぶしも兼ねて次の日が休みの時にSlackに通知してくれるBotを作ってみました\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"11:1-11:15\"\u003e\n\u003cspan id=\"登場人物\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E7%99%BB%E5%A0%B4%E4%BA%BA%E7%89%A9\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e登場人物\u003c/h2\u003e\n\u003cul data-sourcepos=\"13:1-17:0\"\u003e\n\u003cli data-sourcepos=\"13:1-13:18\"\u003eAWS Lambda(Node)\u003c/li\u003e\n\u003cli data-sourcepos=\"14:1-14:51\"\u003e\u003ca href=\"https://holidays-jp.github.io/\" rel=\"nofollow noopener\" target=\"_blank\"\u003eHolidays JP API\u003c/a\u003e\u003c/li\u003e\n\u003cli data-sourcepos=\"15:1-15:59\"\u003e\u003ca href=\"https://slack.dev/node-slack-sdk/web-api\" rel=\"nofollow noopener\" target=\"_blank\"\u003eSlack Web API\u003c/a\u003e\u003c/li\u003e\n\u003cli data-sourcepos=\"16:1-17:0\"\u003e\u003ca href=\"https://serverless.com/\" rel=\"nofollow noopener\" target=\"_blank\"\u003eServerlessFramework\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"18:1-18:18\"\u003e\n\u003cspan id=\"できたもの\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%81%A7%E3%81%8D%E3%81%9F%E3%82%82%E3%81%AE\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eできたもの\u003c/h2\u003e\n\u003cul data-sourcepos=\"20:1-21:0\"\u003e\n\u003cli data-sourcepos=\"20:1-21:0\"\u003e当日が平日で、翌日が土日祝日の場合、昼の12時にSlackに以下の投稿がされるようにしてみました\u003c/li\u003e\n\u003c/ul\u003e\n\u003ca href=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F985d2e0c-3a80-e7b2-53fe-497454e41970.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=5072de395cea9a6cfcd2d19d085e8c27\" target=\"_blank\" rel=\"nofollow noopener\"\u003e\u003cimg width=\"312\" alt=\"スクリーンショット 2020-04-29 0.36.19.png\" src=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F985d2e0c-3a80-e7b2-53fe-497454e41970.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;s=5072de395cea9a6cfcd2d19d085e8c27\" srcset=\"https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F175213%2F985d2e0c-3a80-e7b2-53fe-497454e41970.png?ixlib=rb-4.0.0\u0026amp;auto=format\u0026amp;gif-q=60\u0026amp;q=75\u0026amp;w=1400\u0026amp;fit=max\u0026amp;s=b543ad12b803693941ecc243196d8966 1x\" data-canonical-src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/985d2e0c-3a80-e7b2-53fe-497454e41970.png\" loading=\"lazy\"\u003e\u003c/a\u003e\n\u003cul data-sourcepos=\"24:1-26:0\"\u003e\n\u003cli data-sourcepos=\"24:1-26:0\"\u003eコードはこちらです\n\u003cul data-sourcepos=\"25:5-26:0\"\u003e\n\u003cli data-sourcepos=\"25:5-26:0\"\u003e\u003ca href=\"https://github.com/ozaki25/slack-push-day-before-holiday\" class=\"autolink\" rel=\"nofollow noopener\" target=\"_blank\"\u003ehttps://github.com/ozaki25/slack-push-day-before-holiday\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"27:1-27:15\"\u003e\n\u003cspan id=\"実装内容\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E5%AE%9F%E8%A3%85%E5%86%85%E5%AE%B9\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e実装内容\u003c/h2\u003e\n\u003cul data-sourcepos=\"29:1-34:0\"\u003e\n\u003cli data-sourcepos=\"29:1-29:37\"\u003e\u003ca href=\"#lambda%E3%81%AE%E5%AE%9F%E8%A3%85\"\u003eLambdaの実装\u003c/a\u003e\u003c/li\u003e\n\u003cli data-sourcepos=\"30:1-30:43\"\u003e\u003ca href=\"#%E5%9C%9F%E6%97%A5%E7%A5%9D%E6%97%A5%E5%88%A4%E5%AE%9A\"\u003e土日祝日判定\u003c/a\u003e\u003c/li\u003e\n\u003cli data-sourcepos=\"31:1-31:41\"\u003e\u003ca href=\"#slack%E3%81%B8%E3%81%AE%E9%80%9A%E7%9F%A5\"\u003eSlackへの通知\u003c/a\u003e\u003c/li\u003e\n\u003cli data-sourcepos=\"32:1-32:63\"\u003e\u003ca href=\"#serverlessframework%E3%81%AE%E8%A8%AD%E5%AE%9A\"\u003eServerlessFrameworkの設定\u003c/a\u003e\u003c/li\u003e\n\u003cli data-sourcepos=\"33:1-34:0\"\u003e\u003ca href=\"#localdevprod%E3%81%AE%E4%BD%BF%E3%81%84%E5%88%86%E3%81%91\"\u003eLocal/Dev/Prodの使い分け\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 data-sourcepos=\"35:1-35:19\"\u003e\n\u003cspan id=\"lambdaの実装\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#lambda%E3%81%AE%E5%AE%9F%E8%A3%85\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eLambdaの実装\u003c/h3\u003e\n\u003cul data-sourcepos=\"37:1-39:0\"\u003e\n\u003cli data-sourcepos=\"37:1-39:0\"\u003eLambdaにデプロイするベースとなる\u003ccode\u003ehandler.ts\u003c/code\u003eはこんな感じです\n\u003cul data-sourcepos=\"38:5-39:0\"\u003e\n\u003cli data-sourcepos=\"38:5-39:0\"\u003e今日と明日が平日かどうか判定し、今日が平日で明日が土日祝日の場合はSlackにメッセージを送ります\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"ts\" data-sourcepos=\"40:1-61:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003ehandler.ts\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"k\"\u003eimport\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e \u003cspan class=\"kd\"\u003eas \u003c/span\u003e\u003cspan class=\"nx\"\u003edayjs\u003c/span\u003e \u003cspan class=\"k\"\u003efrom\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003edayjs\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003cspan class=\"k\"\u003eimport\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"nx\"\u003eAPIGatewayProxyHandler\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"k\"\u003efrom\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eaws-lambda\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\n\u003cspan class=\"k\"\u003eimport\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"nx\"\u003eisHoliday\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"k\"\u003efrom\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003e./holidayUtil\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003cspan class=\"k\"\u003eimport\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"nx\"\u003epushMessage\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"k\"\u003efrom\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003e./slackApi\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\n\u003cspan class=\"k\"\u003eexport\u003c/span\u003e \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003enoticeDayBeforeHoliday\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nx\"\u003eAPIGatewayProxyHandler\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003easync \u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n  \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003etoday\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nf\"\u003edayjs\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n  \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003etomorrow\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003etoday\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eadd\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eday\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\n  \u003cspan class=\"k\"\u003eif \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"nf\"\u003eisHoliday\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003etoday\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"na\"\u003estatusCode\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e200\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"na\"\u003ebody\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eToday is Holiday!!!\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e \u003cspan class=\"p\"\u003e};\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n  \u003cspan class=\"k\"\u003eif \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"o\"\u003e!\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"nf\"\u003eisHoliday\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003etomorrow\u003c/span\u003e\u003cspan class=\"p\"\u003e)))\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"na\"\u003estatusCode\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e200\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"na\"\u003ebody\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e\"\u003c/span\u003e\u003cspan class=\"s2\"\u003eLet's work tomorrow\u003c/span\u003e\u003cspan class=\"dl\"\u003e\"\u003c/span\u003e \u003cspan class=\"p\"\u003e};\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\n  \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"nf\"\u003epushMessage\u003c/span\u003e\u003cspan class=\"p\"\u003e({\u003c/span\u003e \u003cspan class=\"na\"\u003etext\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003e明日は休みだよ！残りの仕事も頑張ろう！\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e \u003cspan class=\"p\"\u003e});\u003c/span\u003e\n  \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"na\"\u003estatusCode\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e200\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"na\"\u003ebody\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eTomorrow is Holiday!!!\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e \u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003ch3 data-sourcepos=\"63:1-63:22\"\u003e\n\u003cspan id=\"土日祝日判定\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E5%9C%9F%E6%97%A5%E7%A5%9D%E6%97%A5%E5%88%A4%E5%AE%9A\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003e土日祝日判定\u003c/h3\u003e\n\u003cul data-sourcepos=\"65:1-67:0\"\u003e\n\u003cli data-sourcepos=\"65:1-67:0\"\u003e日本の祝日情報を取得するAPIがないかと検索したら\u003ca href=\"https://holidays-jp.github.io/\" rel=\"nofollow noopener\" target=\"_blank\"\u003eHolidays JP API\u003c/a\u003eを見つけたので使ってみました\n\u003cul data-sourcepos=\"66:5-67:0\"\u003e\n\u003cli data-sourcepos=\"66:5-67:0\"\u003e以下のような形式で祝日一覧を返してくれるAPIです\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"json\" data-sourcepos=\"68:1-74:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n  \u003c/span\u003e\u003cspan class=\"nl\"\u003e\"2017-01-01\"\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\"元日\"\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e\n  \u003c/span\u003e\u003cspan class=\"nl\"\u003e\"2017-01-02\"\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\"元日 振替休日\"\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e\n  \u003c/span\u003e\u003cspan class=\"nl\"\u003e\"2017-01-09\"\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\"成人の日\"\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cul data-sourcepos=\"76:1-78:0\"\u003e\n\u003cli data-sourcepos=\"76:1-78:0\"\u003e祝日判定をしている\u003ccode\u003eholidayUtil.ts\u003c/code\u003eのコードはこんな感じです\n\u003cul data-sourcepos=\"77:5-78:0\"\u003e\n\u003cli data-sourcepos=\"77:5-78:0\"\u003e引数でもらった日付を土曜/日曜か判定し、どちらでもなければ Holidays JP API から取得した祝日に該当するかチェックしています\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"ts\" data-sourcepos=\"79:1-111:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003eholidayUtil.ts\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"k\"\u003eimport\u003c/span\u003e \u003cspan class=\"nx\"\u003efetch\u003c/span\u003e \u003cspan class=\"k\"\u003efrom\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003enode-fetch\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003cspan class=\"k\"\u003eimport\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e \u003cspan class=\"kd\"\u003eas \u003c/span\u003e\u003cspan class=\"nx\"\u003edayjs\u003c/span\u003e \u003cspan class=\"k\"\u003efrom\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003edayjs\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\n\u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003egetHolidays\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003easync \u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n  \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003eres\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"nf\"\u003efetch\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003ehttps://holidays-jp.github.io/api/v1/date.json\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n  \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"nx\"\u003eres\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003ejson\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\n\u003cspan class=\"k\"\u003eexport\u003c/span\u003e \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003eisHoliday\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003easync \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003edate\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nx\"\u003edayjs\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eDayjs\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n  \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003eformattedDate\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003edate\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eformat\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003eYYYY-MM-DD\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\n  \u003cspan class=\"k\"\u003eif \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003edate\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eday\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"o\"\u003e===\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e`\u003c/span\u003e\u003cspan class=\"p\"\u003e${\u003c/span\u003e\u003cspan class=\"nx\"\u003eformattedDate\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e is Sunday`\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\n  \u003cspan class=\"k\"\u003eif \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003edate\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003eday\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"o\"\u003e===\u003c/span\u003e \u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e`\u003c/span\u003e\u003cspan class=\"p\"\u003e${\u003c/span\u003e\u003cspan class=\"nx\"\u003eformattedDate\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e is Saturday`\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\n  \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003eholidays\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"nf\"\u003egetHolidays\u003c/span\u003e\u003cspan class=\"p\"\u003e();\u003c/span\u003e\n\n  \u003cspan class=\"k\"\u003eif \u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nb\"\u003eObject\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003ekeys\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eholidays\u003c/span\u003e\u003cspan class=\"p\"\u003e).\u003c/span\u003e\u003cspan class=\"nf\"\u003eincludes\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eformattedDate\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n    \u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e`\u003c/span\u003e\u003cspan class=\"p\"\u003e${\u003c/span\u003e\u003cspan class=\"nx\"\u003eformattedDate\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e is Holiday`\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\n  \u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e`\u003c/span\u003e\u003cspan class=\"p\"\u003e${\u003c/span\u003e\u003cspan class=\"nx\"\u003eformattedDate\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e is Weekday`\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n  \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003efalse\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003ch3 data-sourcepos=\"113:1-113:21\"\u003e\n\u003cspan id=\"slackへの通知\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#slack%E3%81%B8%E3%81%AE%E9%80%9A%E7%9F%A5\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eSlackへの通知\u003c/h3\u003e\n\u003cul data-sourcepos=\"115:1-119:0\"\u003e\n\u003cli data-sourcepos=\"115:1-115:123\"\u003eSlackへの通知は公式が提供する\u003ca href=\"https://www.npmjs.com/package/@slack/web-api\" rel=\"nofollow noopener\" target=\"_blank\"\u003e@slack/web-api\u003c/a\u003eを使いました\u003c/li\u003e\n\u003cli data-sourcepos=\"116:1-119:0\"\u003eSlackへ通知を送る\u003ccode\u003eslackApi.ts\u003c/code\u003eはこんな感じです\n\u003cul data-sourcepos=\"117:5-119:0\"\u003e\n\u003cli data-sourcepos=\"117:5-119:0\"\u003eTokenはリポジトリに含めたくない、投稿先チャンネルはdevとprodで差し替えたいといった事情から、それらの情報は環境変数から取得しています\n\u003cul data-sourcepos=\"118:9-119:0\"\u003e\n\u003cli data-sourcepos=\"118:9-119:0\"\u003e詳しいやり方は\u003ca href=\"https://qiita.com/ozaki25/items/c855c3c7bcb45515539f\" id=\"reference-146620cb701d69e70535\"\u003eこちらの記事\u003c/a\u003eで\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"ts\" data-sourcepos=\"120:1-137:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003eslackApi.ts\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e  \n\u003cspan class=\"k\"\u003eimport\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"nx\"\u003eWebClient\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"k\"\u003efrom\u003c/span\u003e \u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"s1\"\u003e@slack/web-api\u003c/span\u003e\u003cspan class=\"dl\"\u003e'\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\n\u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e \u003cspan class=\"nx\"\u003eSLACK_API_TOKEN\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eSLACK_CHANNEL\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nx\"\u003eSLACK_USERNAME\u003c/span\u003e \u003cspan class=\"p\"\u003e}\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nx\"\u003eprocess\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003eenv\u003c/span\u003e\u003cspan class=\"p\"\u003e;\u003c/span\u003e\n\n\u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003eslackClient\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003enew\u003c/span\u003e \u003cspan class=\"nc\"\u003eWebClient\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nx\"\u003eSLACK_API_TOKEN\u003c/span\u003e\u003cspan class=\"p\"\u003e);\u003c/span\u003e\n\n\u003cspan class=\"k\"\u003eexport\u003c/span\u003e \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003epushMessage\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003easync \u003c/span\u003e\u003cspan class=\"p\"\u003e({\u003c/span\u003e \u003cspan class=\"nx\"\u003etext\u003c/span\u003e \u003cspan class=\"p\"\u003e})\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u0026gt;\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n  \u003cspan class=\"kd\"\u003econst\u003c/span\u003e \u003cspan class=\"nx\"\u003eresult\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"nx\"\u003eslackClient\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nx\"\u003echat\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003epostMessage\u003c/span\u003e\u003cspan class=\"p\"\u003e({\u003c/span\u003e\n    \u003cspan class=\"nx\"\u003etext\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n    \u003cspan class=\"na\"\u003echannel\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nx\"\u003eSLACK_CHANNEL\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n    \u003cspan class=\"na\"\u003eusername\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nx\"\u003eSLACK_USERNAME\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n    \u003cspan class=\"na\"\u003eunfurl_links\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n  \u003cspan class=\"p\"\u003e});\u003c/span\u003e\n  \u003cspan class=\"nx\"\u003econsole\u003c/span\u003e\u003cspan class=\"p\"\u003e.\u003c/span\u003e\u003cspan class=\"nf\"\u003elog\u003c/span\u003e\u003cspan class=\"p\"\u003e({\u003c/span\u003e \u003cspan class=\"nx\"\u003eresult\u003c/span\u003e \u003cspan class=\"p\"\u003e});\u003c/span\u003e\n\u003cspan class=\"p\"\u003e};\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003ch3 data-sourcepos=\"139:1-139:32\"\u003e\n\u003cspan id=\"serverlessframeworkの設定\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#serverlessframework%E3%81%AE%E8%A8%AD%E5%AE%9A\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eServerlessFrameworkの設定\u003c/h3\u003e\n\u003cul data-sourcepos=\"141:1-143:0\"\u003e\n\u003cli data-sourcepos=\"141:1-143:0\"\u003eServerlessFrameworkの設定ファイルである\u003ccode\u003eserverless.yml\u003c/code\u003eはこんな感じです\n\u003cul data-sourcepos=\"142:5-143:0\"\u003e\n\u003cli data-sourcepos=\"142:5-143:0\"\u003e上の説明でも出てきた環境変数周りの設定やLambdaの定期実行のためのschedule設定などをしています\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"yml\" data-sourcepos=\"144:1-184:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003eserverless.yml\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"na\"\u003eservice\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n  \u003cspan class=\"na\"\u003ename\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003eday-before-holiday\u003c/span\u003e\n\u003cspan class=\"na\"\u003ecustom\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n  \u003cspan class=\"na\"\u003ewebpack\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n    \u003cspan class=\"na\"\u003ewebpackConfig\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003e./webpack.config.js\u003c/span\u003e\n    \u003cspan class=\"na\"\u003eincludeModules\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003etrue\u003c/span\u003e\n  \u003cspan class=\"na\"\u003edefaultStage\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003edev\u003c/span\u003e\n  \u003cspan class=\"na\"\u003eslack\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n    \u003cspan class=\"na\"\u003edev\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n      \u003cspan class=\"na\"\u003etoken\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003e${env:SLACK_API_TOKEN_DEV}\u003c/span\u003e\n      \u003cspan class=\"na\"\u003echannel\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003e${env:SLACK_CHANNEL_DEV}\u003c/span\u003e\n      \u003cspan class=\"na\"\u003eusername\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003e${env:SLACK_USERNAME_DEV}\u003c/span\u003e\n    \u003cspan class=\"na\"\u003eproduction\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n      \u003cspan class=\"na\"\u003etoken\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003e${env:SLACK_API_TOKEN_PROD}\u003c/span\u003e\n      \u003cspan class=\"na\"\u003echannel\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003e${env:SLACK_CHANNEL_PROD}\u003c/span\u003e\n      \u003cspan class=\"na\"\u003eusername\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003e${env:SLACK_USERNAME_PROD}\u003c/span\u003e\n  \u003cspan class=\"na\"\u003eschedule\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n    \u003cspan class=\"na\"\u003enoticeDayBeforeHoliday\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n      \u003cspan class=\"na\"\u003edev\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"c1\"\u003e# スケジュールなし\u003c/span\u003e\n      \u003cspan class=\"na\"\u003eproduction\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003ecron(0 3 * * ? *)\u003c/span\u003e \u003cspan class=\"c1\"\u003e# 日本時間で毎日昼12時\u003c/span\u003e\n\u003cspan class=\"na\"\u003eplugins\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n  \u003cspan class=\"pi\"\u003e-\u003c/span\u003e \u003cspan class=\"s\"\u003eserverless-webpack\u003c/span\u003e\n  \u003cspan class=\"pi\"\u003e-\u003c/span\u003e \u003cspan class=\"s\"\u003eserverless-dotenv-plugin\u003c/span\u003e\n\u003cspan class=\"na\"\u003eprovider\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n  \u003cspan class=\"na\"\u003ename\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003eaws\u003c/span\u003e\n  \u003cspan class=\"na\"\u003eruntime\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003enodejs12.x\u003c/span\u003e\n  \u003cspan class=\"na\"\u003estage\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003e${opt:stage, self:custom.defaultStage}\u003c/span\u003e\n  \u003cspan class=\"na\"\u003eregion\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003eap-northeast-1\u003c/span\u003e\n  \u003cspan class=\"na\"\u003eenvironment\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n    \u003cspan class=\"na\"\u003eAWS_NODEJS_CONNECTION_REUSE_ENABLED\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"m\"\u003e1\u003c/span\u003e\n    \u003cspan class=\"na\"\u003eTZ\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003eAsia/Tokyo\u003c/span\u003e\n    \u003cspan class=\"na\"\u003eSLACK_API_TOKEN\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003e${self:custom.slack.${self:provider.stage}.token}\u003c/span\u003e\n    \u003cspan class=\"na\"\u003eSLACK_CHANNEL\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003e${self:custom.slack.${self:provider.stage}.channel}\u003c/span\u003e\n    \u003cspan class=\"na\"\u003eSLACK_USERNAME\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003e${self:custom.slack.${self:provider.stage}.username}\u003c/span\u003e\n\u003cspan class=\"na\"\u003efunctions\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n  \u003cspan class=\"na\"\u003enoticeDayBeforeHoliday\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n    \u003cspan class=\"na\"\u003ehandler\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003esrc/handler.noticeDayBeforeHoliday\u003c/span\u003e\n    \u003cspan class=\"na\"\u003eevents\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e\n      \u003cspan class=\"pi\"\u003e-\u003c/span\u003e \u003cspan class=\"na\"\u003eschedule\u003c/span\u003e\u003cspan class=\"pi\"\u003e:\u003c/span\u003e \u003cspan class=\"s\"\u003e${self:custom.schedule.noticeDayBeforeHoliday.${self:provider.stage}}\u003c/span\u003e\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003ch3 data-sourcepos=\"186:1-186:33\"\u003e\n\u003cspan id=\"localdevprodの使い分け\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#localdevprod%E3%81%AE%E4%BD%BF%E3%81%84%E5%88%86%E3%81%91\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eLocal/Dev/Prodの使い分け\u003c/h3\u003e\n\u003cul data-sourcepos=\"188:1-194:0\"\u003e\n\u003cli data-sourcepos=\"188:1-188:102\"\u003eちょっとしたLambdaの開発ですが本番環境Onlyで突っ走るのは抵抗があります\u003c/li\u003e\n\u003cli data-sourcepos=\"189:1-192:33\"\u003e今回はローカルとAWS(Dev環境)とAWS(Prod環境)に分けて動作確認をしていきました\n\u003cul data-sourcepos=\"190:5-192:33\"\u003e\n\u003cli data-sourcepos=\"190:5-190:48\"\u003e基本はローカルで開発を進める\u003c/li\u003e\n\u003cli data-sourcepos=\"191:5-191:36\"\u003e完成したらDevでテスト\u003c/li\u003e\n\u003cli data-sourcepos=\"192:5-192:33\"\u003eOKならProdへデプロイ\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli data-sourcepos=\"193:1-194:0\"\u003e\n\u003ccode\u003epackage.json\u003c/code\u003eに定義した\u003ccode\u003esrcipts\u003c/code\u003eはこんな感じです\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"json\" data-sourcepos=\"195:1-202:3\"\u003e\n\u003cdiv class=\"code-lang\"\u003e\u003cspan class=\"bold\"\u003epackage.json\u003c/span\u003e\u003c/div\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\u003cspan class=\"w\"\u003e  \u003c/span\u003e\u003cspan class=\"nl\"\u003e\"scripts\"\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"w\"\u003e\n    \u003c/span\u003e\u003cspan class=\"nl\"\u003e\"invoke\"\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\"sls invoke local -f noticeDayBeforeHoliday\"\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e\n    \u003c/span\u003e\u003cspan class=\"nl\"\u003e\"invoke:aws\"\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\"sls invoke -f noticeDayBeforeHoliday\"\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e\n    \u003c/span\u003e\u003cspan class=\"nl\"\u003e\"deploy\"\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\"sls deploy\"\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\u003cspan class=\"w\"\u003e\n    \u003c/span\u003e\u003cspan class=\"nl\"\u003e\"deploy:prod\"\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"w\"\u003e \u003c/span\u003e\u003cspan class=\"s2\"\u003e\"sls deploy --stage production\"\u003c/span\u003e\u003cspan class=\"w\"\u003e\n  \u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\u003cspan class=\"w\"\u003e\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003ch4 data-sourcepos=\"204:1-204:10\"\u003e\n\u003cspan id=\"local\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#local\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eLocal\u003c/h4\u003e\n\u003cul data-sourcepos=\"206:1-207:0\"\u003e\n\u003cli data-sourcepos=\"206:1-207:0\"\u003eローカルでは以下のコマンドで実行することができます\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"sh\" data-sourcepos=\"208:1-210:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003eyarn invoke\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003ch4 data-sourcepos=\"212:1-212:8\"\u003e\n\u003cspan id=\"dev\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#dev\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eDev\u003c/h4\u003e\n\u003cul data-sourcepos=\"214:1-215:0\"\u003e\n\u003cli data-sourcepos=\"214:1-215:0\"\u003eAWSにDevモードでデプロイする時は以下のコマンドです\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"sh\" data-sourcepos=\"216:1-219:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\nyarn deploy\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cul data-sourcepos=\"221:1-222:0\"\u003e\n\u003cli data-sourcepos=\"221:1-222:0\"\u003eAWSにデプロイしたLambdaを実行する時は以下のコマンドです\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"sh\" data-sourcepos=\"223:1-225:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003eyarn invoke:aws\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003ch4 data-sourcepos=\"227:1-227:9\"\u003e\n\u003cspan id=\"prod\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#prod\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eProd\u003c/h4\u003e\n\u003cul data-sourcepos=\"229:1-230:0\"\u003e\n\u003cli data-sourcepos=\"229:1-230:0\"\u003eAWSにProdモードでデプロイする時は以下のコマンドです\u003c/li\u003e\n\u003c/ul\u003e\n\u003cdiv class=\"code-frame\" data-lang=\"sh\" data-sourcepos=\"231:1-234:3\"\u003e\u003cdiv class=\"highlight\"\u003e\u003cpre\u003e\u003ccode\u003e\nyarn deploy:prod\n\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003c/div\u003e\n\u003cul data-sourcepos=\"236:1-238:0\"\u003e\n\u003cli data-sourcepos=\"236:1-238:0\"\u003eProdを暴発させるのは良くないのでProdのLambdaを実行するコマンドは入れませんでした\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 data-sourcepos=\"239:1-239:12\"\u003e\n\u003cspan id=\"まとめ\" class=\"fragment\"\u003e\u003c/span\u003e\u003ca href=\"#%E3%81%BE%E3%81%A8%E3%82%81\"\u003e\u003ci class=\"fa fa-link\"\u003e\u003c/i\u003e\u003c/a\u003eまとめ\u003c/h2\u003e\n\u003cul data-sourcepos=\"241:1-244:0\"\u003e\n\u003cli data-sourcepos=\"241:1-241:123\"\u003eちょっとしたLambda関数の開発でしたがServerlessFrameworkが快適なので作ってて楽しかったです\u003c/li\u003e\n\u003cli data-sourcepos=\"242:1-242:92\"\u003eこれで在宅勤務でも祝前日の生産性爆上げ日を確保できそうですね\u003c/li\u003e\n\u003cli data-sourcepos=\"243:1-244:0\"\u003eStayHomeが続きますが工夫して乗り切りましょう！\u003c/li\u003e\n\u003c/ul\u003e\n","body":"## きっかけ\n\n- 昨今の事情もあり在宅勤務を行っている方は多いと思います\n- そんな中でチームのSlackでこんなやりとりがありました\n\n\u003cimg width=\"601\" alt=\"スクリーンショット 2020-04-29 0.18.53.png\" src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/e0139cd0-92ef-1636-8c95-0d8f988aa770.png\"\u003e\n\n- 在宅でリモートだと曜日感覚なくなってきますよね\n- 明日が休みだと思うことで生まれる**生産性爆上げ日**を逃してしまうのはもったいない！ということでStayHomeの暇つぶしも兼ねて次の日が休みの時にSlackに通知してくれるBotを作ってみました\n\n## 登場人物\n\n- AWS Lambda(Node)\n- [Holidays JP API](https://holidays-jp.github.io/)\n- [Slack Web API](https://slack.dev/node-slack-sdk/web-api)\n- [ServerlessFramework](https://serverless.com/)\n\n## できたもの\n\n- 当日が平日で、翌日が土日祝日の場合、昼の12時にSlackに以下の投稿がされるようにしてみました\n\n\u003cimg width=\"312\" alt=\"スクリーンショット 2020-04-29 0.36.19.png\" src=\"https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/175213/985d2e0c-3a80-e7b2-53fe-497454e41970.png\"\u003e\n\n- コードはこちらです\n    - https://github.com/ozaki25/slack-push-day-before-holiday\n\n## 実装内容\n\n- [Lambdaの実装](#lambdaの実装)\n- [土日祝日判定](#土日祝日判定)\n- [Slackへの通知](#slackへの通知)\n- [ServerlessFrameworkの設定](#serverlessframeworkの設定)\n- [Local/Dev/Prodの使い分け](#localdevprodの使い分け)\n\n### Lambdaの実装\n\n- Lambdaにデプロイするベースとなる`handler.ts`はこんな感じです\n    - 今日と明日が平日かどうか判定し、今日が平日で明日が土日祝日の場合はSlackにメッセージを送ります\n\n```ts:handler.ts\nimport * as dayjs from 'dayjs';\nimport { APIGatewayProxyHandler } from 'aws-lambda';\n\nimport { isHoliday } from './holidayUtil';\nimport { pushMessage } from './slackApi';\n\nexport const noticeDayBeforeHoliday: APIGatewayProxyHandler = async () =\u003e {\n  const today = dayjs();\n  const tomorrow = today.add(1, 'day');\n\n  if (await isHoliday(today)) {\n    return { statusCode: 200, body: 'Today is Holiday!!!' };\n  }\n  if (!(await isHoliday(tomorrow))) {\n    return { statusCode: 200, body: \"Let's work tomorrow\" };\n  }\n\n  await pushMessage({ text: '明日は休みだよ！残りの仕事も頑張ろう！' });\n  return { statusCode: 200, body: 'Tomorrow is Holiday!!!' };\n};\n```\n\n### 土日祝日判定\n\n- 日本の祝日情報を取得するAPIがないかと検索したら[Holidays JP API](https://holidays-jp.github.io/)を見つけたので使ってみました\n    - 以下のような形式で祝日一覧を返してくれるAPIです\n\n```json\n{\n  \"2017-01-01\": \"元日\",\n  \"2017-01-02\": \"元日 振替休日\",\n  \"2017-01-09\": \"成人の日\"\n}\n```\n\n- 祝日判定をしている`holidayUtil.ts`のコードはこんな感じです\n    - 引数でもらった日付を土曜/日曜か判定し、どちらでもなければ Holidays JP API から取得した祝日に該当するかチェックしています\n\n```ts:holidayUtil.ts\nimport fetch from 'node-fetch';\nimport * as dayjs from 'dayjs';\n\nconst getHolidays = async () =\u003e {\n  const res = await fetch('https://holidays-jp.github.io/api/v1/date.json');\n  return res.json();\n};\n\nexport const isHoliday = async (date: dayjs.Dayjs) =\u003e {\n  const formattedDate = date.format('YYYY-MM-DD');\n\n  if (date.day() === 0) {\n    console.log(`${formattedDate} is Sunday`);\n    return true;\n  }\n\n  if (date.day() === 6) {\n    console.log(`${formattedDate} is Saturday`);\n    return true;\n  }\n\n  const holidays = await getHolidays();\n\n  if (Object.keys(holidays).includes(formattedDate)) {\n    console.log(`${formattedDate} is Holiday`);\n    return true;\n  }\n\n  console.log(`${formattedDate} is Weekday`);\n  return false;\n};\n```\n\n### Slackへの通知\n\n- Slackへの通知は公式が提供する[@slack/web-api](https://www.npmjs.com/package/@slack/web-api)を使いました\n- Slackへ通知を送る`slackApi.ts`はこんな感じです\n    - Tokenはリポジトリに含めたくない、投稿先チャンネルはdevとprodで差し替えたいといった事情から、それらの情報は環境変数から取得しています\n        - 詳しいやり方は[こちらの記事](https://qiita.com/ozaki25/items/c855c3c7bcb45515539f)で\n\n```ts:slackApi.ts\n  \nimport { WebClient } from '@slack/web-api';\n\nconst { SLACK_API_TOKEN, SLACK_CHANNEL, SLACK_USERNAME } = process.env;\n\nconst slackClient = new WebClient(SLACK_API_TOKEN);\n\nexport const pushMessage = async ({ text }) =\u003e {\n  const result = await slackClient.chat.postMessage({\n    text,\n    channel: SLACK_CHANNEL,\n    username: SLACK_USERNAME,\n    unfurl_links: true,\n  });\n  console.log({ result });\n};\n```\n\n### ServerlessFrameworkの設定\n\n- ServerlessFrameworkの設定ファイルである`serverless.yml`はこんな感じです\n    - 上の説明でも出てきた環境変数周りの設定やLambdaの定期実行のためのschedule設定などをしています\n\n```yml:serverless.yml\nservice:\n  name: day-before-holiday\ncustom:\n  webpack:\n    webpackConfig: ./webpack.config.js\n    includeModules: true\n  defaultStage: dev\n  slack:\n    dev:\n      token: ${env:SLACK_API_TOKEN_DEV}\n      channel: ${env:SLACK_CHANNEL_DEV}\n      username: ${env:SLACK_USERNAME_DEV}\n    production:\n      token: ${env:SLACK_API_TOKEN_PROD}\n      channel: ${env:SLACK_CHANNEL_PROD}\n      username: ${env:SLACK_USERNAME_PROD}\n  schedule:\n    noticeDayBeforeHoliday:\n      dev: # スケジュールなし\n      production: cron(0 3 * * ? *) # 日本時間で毎日昼12時\nplugins:\n  - serverless-webpack\n  - serverless-dotenv-plugin\nprovider:\n  name: aws\n  runtime: nodejs12.x\n  stage: ${opt:stage, self:custom.defaultStage}\n  region: ap-northeast-1\n  environment:\n    AWS_NODEJS_CONNECTION_REUSE_ENABLED: 1\n    TZ: Asia/Tokyo\n    SLACK_API_TOKEN: ${self:custom.slack.${self:provider.stage}.token}\n    SLACK_CHANNEL: ${self:custom.slack.${self:provider.stage}.channel}\n    SLACK_USERNAME: ${self:custom.slack.${self:provider.stage}.username}\nfunctions:\n  noticeDayBeforeHoliday:\n    handler: src/handler.noticeDayBeforeHoliday\n    events:\n      - schedule: ${self:custom.schedule.noticeDayBeforeHoliday.${self:provider.stage}}\n```\n\n### Local/Dev/Prodの使い分け\n\n- ちょっとしたLambdaの開発ですが本番環境Onlyで突っ走るのは抵抗があります\n- 今回はローカルとAWS(Dev環境)とAWS(Prod環境)に分けて動作確認をしていきました\n    - 基本はローカルで開発を進める\n    - 完成したらDevでテスト\n    - OKならProdへデプロイ\n- `package.json`に定義した`srcipts`はこんな感じです\n\n```json:package.json\n  \"scripts\": {\n    \"invoke\": \"sls invoke local -f noticeDayBeforeHoliday\",\n    \"invoke:aws\": \"sls invoke -f noticeDayBeforeHoliday\",\n    \"deploy\": \"sls deploy\",\n    \"deploy:prod\": \"sls deploy --stage production\"\n  }\n```\n\n#### Local\n\n- ローカルでは以下のコマンドで実行することができます\n\n```sh\nyarn invoke\n```\n\n#### Dev\n\n- AWSにDevモードでデプロイする時は以下のコマンドです\n\n```sh\n\nyarn deploy\n```\n\n- AWSにデプロイしたLambdaを実行する時は以下のコマンドです\n\n```sh\nyarn invoke:aws\n```\n\n#### Prod\n\n- AWSにProdモードでデプロイする時は以下のコマンドです\n\n```sh\n\nyarn deploy:prod\n```\n\n- Prodを暴発させるのは良くないのでProdのLambdaを実行するコマンドは入れませんでした\n\n\n## まとめ\n\n- ちょっとしたLambda関数の開発でしたがServerlessFrameworkが快適なので作ってて楽しかったです\n- これで在宅勤務でも祝前日の生産性爆上げ日を確保できそうですね\n- StayHomeが続きますが工夫して乗り切りましょう！\n\n","coediting":false,"comments_count":0,"created_at":"2020-04-29T01:20:38+09:00","group":null,"id":"30a1d161931fa1fb4278","likes_count":4,"private":false,"reactions_count":0,"stocks_count":0,"tags":[{"name":"lambda","versions":[]},{"name":"slackbot","versions":[]},{"name":"ServerlessFramework","versions":[]}],"title":"【SlackBot】明日は休みだよBotを作ってみた","updated_at":"2021-10-26T18:49:08+09:00","url":"https://qiita.com/ozaki25/items/30a1d161931fa1fb4278","user":{"description":"","facebook_id":"","followees_count":0,"followers_count":79,"github_login_name":"ozaki25","id":"ozaki25","items_count":112,"linkedin_id":"","location":"","name":"","organization":"","permanent_id":175213,"profile_image_url":"https://qiita-image-store.s3.amazonaws.com/0/175213/profile-images/1491825881","team_only":false,"twitter_screen_name":"ozaki25rn","website_url":""},"page_views_count":null,"team_membership":null,"organization_url_name":null,"slide":false,"posting_campaign_uuid":null}]