mirror of
https://github.com/kou029w/intro-to-graphql.git
synced 2025-01-19 08:28:05 +00:00
335 lines
83 KiB
HTML
335 lines
83 KiB
HTML
|
<!DOCTYPE html><html lang="ja-JP"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,height=device-height,initial-scale=1.0"><meta name="apple-mobile-web-app-capable" content="yes"><meta http-equiv="X-UA-Compatible" content="ie=edge"><meta property="og:type" content="website"><meta name="twitter:card" content="summary"><style>@media screen{body[data-bespoke-view=""] .bespoke-marp-parent>.bespoke-marp-osc>button,body[data-bespoke-view=next] .bespoke-marp-parent>.bespoke-marp-osc>button,body[data-bespoke-view=presenter] .bespoke-marp-presenter-container .bespoke-marp-presenter-info-container button{-webkit-appearance:none;-moz-appearance:none;appearance:none;-webkit-tap-highlight-color:transparent;background-color:transparent;border:0;color:inherit;cursor:pointer;font-size:inherit;opacity:.8;outline:none;padding:0;transition:opacity .2s linear}body[data-bespoke-view=""] .bespoke-marp-parent>.bespoke-marp-osc>button:disabled,body[data-bespoke-view=next] .bespoke-marp-parent>.bespoke-marp-osc>button:disabled,body[data-bespoke-view=presenter] .bespoke-marp-presenter-container .bespoke-marp-presenter-info-container button:disabled{cursor:not-allowed;opacity:.15!important}body[data-bespoke-view=""] .bespoke-marp-parent>.bespoke-marp-osc>button:hover,body[data-bespoke-view=next] .bespoke-marp-parent>.bespoke-marp-osc>button:hover,body[data-bespoke-view=presenter] .bespoke-marp-presenter-container .bespoke-marp-presenter-info-container button:hover{opacity:1}body[data-bespoke-view=""] .bespoke-marp-parent>.bespoke-marp-osc>button:hover:active,body[data-bespoke-view=next] .bespoke-marp-parent>.bespoke-marp-osc>button:hover:active,body[data-bespoke-view=presenter] .bespoke-marp-presenter-container .bespoke-marp-presenter-info-container button:hover:active{opacity:.6}body[data-bespoke-view=""] .bespoke-marp-parent>.bespoke-marp-osc>button:hover:not(:disabled),body[data-bespoke-view=next] .bespoke-marp-parent>.bespoke-marp-osc>button:hover:not(:disabled),body[data-bespoke-view=presenter] .bespoke-marp-presenter-container .bespoke-marp-presenter-info-container button:hover:not(:disabled){transition:none}body[data-bespoke-view=""] .bespoke-marp-parent>.bespoke-marp-osc>button[data-bespoke-marp-osc=prev],body[data-bespoke-view=next] .bespoke-marp-parent>.bespoke-marp-osc>button[data-bespoke-marp-osc=prev],body[data-bespoke-view=presenter] .bespoke-marp-presenter-container .bespoke-marp-presenter-info-container button.bespoke-marp-presenter-info-page-prev{background:transparent url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj48cGF0aCBmaWxsPSJub25lIiBzdHJva2U9IiNmZmYiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSI1IiBkPSJNNjggOTAgMjggNTBsNDAtNDAiLz48L3N2Zz4=") no-repeat 50%;background-size:contain;overflow:hidden;text-indent:100%;white-space:nowrap}body[data-bespoke-view=""] .bespoke-marp-parent>.bespoke-marp-osc>button[data-bespoke-marp-osc=next],body[data-bespoke-view=next] .bespoke-marp-parent>.bespoke-marp-osc>button[data-bespoke-marp-osc=next],body[data-bespoke-view=presenter] .bespoke-marp-presenter-container .bespoke-marp-presenter-info-container button.bespoke-marp-presenter-info-page-next{background:transparent url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj48cGF0aCBmaWxsPSJub25lIiBzdHJva2U9IiNmZmYiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSI1IiBkPSJtMzIgOTAgNDAtNDAtNDAtNDAiLz48L3N2Zz4=") no-repeat 50%;background-size:contain;overflow:hidden;text-indent:100%;white-space:nowrap}body[data-bespoke-view=""] .bespoke-marp-parent>.bespoke-marp-osc>button[data-bespoke-marp-osc=fullscreen],body[data-bespoke-view=next] .bespoke-marp-parent>.bespoke-marp-osc>button[data-bespoke-marp-osc=fullscreen]{background:transparent url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIj48ZGVmcz48c3R5bGU+LmF7ZmlsbDpub25lO3N0cm9rZTojZmZmO3N0cm9rZS1saW5lY2FwOnJvdW5kO
|
||
|
/*!
|
||
|
* Marp default theme.
|
||
|
*
|
||
|
* @theme default
|
||
|
* @author Yuki Hattori
|
||
|
*
|
||
|
* @auto-scaling true
|
||
|
* @size 4:3 960px 720px
|
||
|
*/div#p>svg>foreignObject>section .octicon{fill:currentColor;display:inline-block;vertical-align:text-bottom}div#p>svg>foreignObject>section .anchor{float:left;line-height:1;margin-left:-20px;padding-right:4px}div#p>svg>foreignObject>section .anchor:focus{outline:none}div#p>svg>foreignObject>section h1 .octicon-link,div#p>svg>foreignObject>section h2 .octicon-link,div#p>svg>foreignObject>section h3 .octicon-link,div#p>svg>foreignObject>section h4 .octicon-link,div#p>svg>foreignObject>section h5 .octicon-link,div#p>svg>foreignObject>section h6 .octicon-link{color:#1b1f23;vertical-align:middle;visibility:hidden}div#p>svg>foreignObject>section h1:hover .anchor,div#p>svg>foreignObject>section h2:hover .anchor,div#p>svg>foreignObject>section h3:hover .anchor,div#p>svg>foreignObject>section h4:hover .anchor,div#p>svg>foreignObject>section h5:hover .anchor,div#p>svg>foreignObject>section h6:hover .anchor{text-decoration:none}div#p>svg>foreignObject>section h1:hover .anchor .octicon-link,div#p>svg>foreignObject>section h2:hover .anchor .octicon-link,div#p>svg>foreignObject>section h3:hover .anchor .octicon-link,div#p>svg>foreignObject>section h4:hover .anchor .octicon-link,div#p>svg>foreignObject>section h5:hover .anchor .octicon-link,div#p>svg>foreignObject>section h6:hover .anchor .octicon-link{visibility:visible}div#p>svg>foreignObject>section h1:hover .anchor .octicon-link:before,div#p>svg>foreignObject>section h2:hover .anchor .octicon-link:before,div#p>svg>foreignObject>section h3:hover .anchor .octicon-link:before,div#p>svg>foreignObject>section h4:hover .anchor .octicon-link:before,div#p>svg>foreignObject>section h5:hover .anchor .octicon-link:before,div#p>svg>foreignObject>section h6:hover .anchor .octicon-link:before{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' aria-hidden='true'%3E%3Cpath fill-rule='evenodd' d='M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z'/%3E%3C/svg%3E");content:" ";display:inline-block;height:16px;width:16px}div#p>svg>foreignObject>section{word-wrap:break-word;color:#24292e;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji;font-size:16px;line-height:1.5;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}div#p>svg>foreignObject>section{--marpit-root-font-size:16px}div#p>svg>foreignObject>section details{display:block}div#p>svg>foreignObject>section summary{display:list-item}div#p>svg>foreignObject>section a{background-color:initial}div#p>svg>foreignObject>section a:active,div#p>svg>foreignObject>section a:hover{outline-width:0}div#p>svg>foreignObject>section strong{font-weight:inherit;font-weight:bolder}div#p>svg>foreignObject>section h1{margin:.67em 0}div#p>svg>foreignObject>section img{border-style:none}div#p>svg>foreignObject>section code,div#p>svg>foreignObject>section kbd,div#p>svg>foreignObject>section pre{font-family:monospace,monospace;font-size:1em}div#p>svg>foreignObject>section hr{box-sizing:initial;overflow:visible}div#p>svg>foreignObject>section input{font:inherit;margin:0;overflow:visible}div#p>svg>foreignObject>section [type=checkbox]{padding:0}div#p>svg>foreignObject>section *,div#p>svg>foreignObject>section [type=checkbox]{box-sizing:border-box}div#p>svg>foreignObject>section input{font-family:inherit;font-size:inherit;line-height:inherit}div#p>svg>foreignObject>section a{color:#0366d6;text-decoration:none}div#p>svg>foreignObject>section a:hover{text-decoration:underline}div#p>svg>foreignObject>section strong{font-weight:600}div#p>svg>foreignObject>section hr{background:transparent;border-bottom:1px solid #dfe2e5;height:0;margin:15px 0;overflow:hidden}div#p>svg>foreignObject>section hr:after,div#p>svg>foreignObject>section hr:before{content:"";display:table
|
||
|
/* content:""; */display:table}div#p>svg>foreignObject>section:after{clear:both}div#p>svg>foreignObject>section>:first-child{margin-top:0!important}div#p>svg>foreignObject>section>:last-child{margin-bottom:0!important}div#p>svg>foreignObject>section a:not([href]){color:inherit;text-decoration:none}div#p>svg>foreignObject>section blockquote,div#p>svg>foreignObject>section details,div#p>svg>foreignObject>section dl,div#p>svg>foreignObject>section ol,div#p>svg>foreignObject>section p,div#p>svg>foreignObject>section pre,div#p>svg>foreignObject>section table,div#p>svg>foreignObject>section ul{margin-bottom:16px;margin-top:0}div#p>svg>foreignObject>section hr{background-color:#e1e4e8;border:0;height:.25em;margin:24px 0;padding:0}div#p>svg>foreignObject>section blockquote{border-left:.25em solid #dfe2e5;color:#6a737d;padding:0 1em}div#p>svg>foreignObject>section blockquote>:first-child{margin-top:0}div#p>svg>foreignObject>section blockquote>:last-child{margin-bottom:0}div#p>svg>foreignObject>section h1,div#p>svg>foreignObject>section h2,div#p>svg>foreignObject>section h3,div#p>svg>foreignObject>section h4,div#p>svg>foreignObject>section h5,div#p>svg>foreignObject>section h6{font-weight:600;line-height:1.25;margin-bottom:16px;margin-top:24px}div#p>svg>foreignObject>section h1{font-size:2em}div#p>svg>foreignObject>section h1,div#p>svg>foreignObject>section h2{border-bottom:1px solid #eaecef;padding-bottom:.3em}div#p>svg>foreignObject>section h2{font-size:1.5em}div#p>svg>foreignObject>section h3{font-size:1.25em}div#p>svg>foreignObject>section h4{font-size:1em}div#p>svg>foreignObject>section h5{font-size:.875em}div#p>svg>foreignObject>section h6{color:#6a737d;font-size:.85em}div#p>svg>foreignObject>section ol,div#p>svg>foreignObject>section ul{padding-left:2em}div#p>svg>foreignObject>section ol ol,div#p>svg>foreignObject>section ol ul,div#p>svg>foreignObject>section ul ol,div#p>svg>foreignObject>section ul ul{margin-bottom:0;margin-top:0}div#p>svg>foreignObject>section li{word-wrap:break-all}div#p>svg>foreignObject>section li>p{margin-top:16px}div#p>svg>foreignObject>section li+li{margin-top:.25em}div#p>svg>foreignObject>section dl{padding:0}div#p>svg>foreignObject>section dl dt{font-size:1em;font-style:italic;font-weight:600;margin-top:16px;padding:0}div#p>svg>foreignObject>section dl dd{margin-bottom:16px;padding:0 16px}div#p>svg>foreignObject>section table{display:block;overflow:auto;width:100%}div#p>svg>foreignObject>section table th{font-weight:600}div#p>svg>foreignObject>section table td,div#p>svg>foreignObject>section table th{border:1px solid #dfe2e5;padding:6px 13px}div#p>svg>foreignObject>section table tr{background-color:#fff;border-top:1px solid #c6cbd1}div#p>svg>foreignObject>section table tr:nth-child(2n){background-color:#f6f8fa}div#p>svg>foreignObject>section img{background-color:#fff;box-sizing:initial;max-width:100%}div#p>svg>foreignObject>section img[align=right]{padding-left:20px}div#p>svg>foreignObject>section img[align=left]{padding-right:20px}div#p>svg>foreignObject>section code{background-color:rgba(27,31,35,.05);border-radius:3px;font-size:85%;margin:0;padding:.2em .4em}div#p>svg>foreignObject>section pre{word-wrap:normal}div#p>svg>foreignObject>section pre>code{background:transparent;border:0;font-size:100%;margin:0;padding:0;white-space:pre;word-break:normal}div#p>svg>foreignObject>section .highlight{margin-bottom:16px}div#p>svg>foreignObject>section .highlight pre{margin-bottom:0;word-break:normal}div#p>svg>foreignObject>section pre{background-color:#f6f8fa;border-radius:3px;font-size:85%;line-height:1.45;overflow:auto;padding:16px}div#p>svg>foreignObject>section pre code{word-wrap:normal;background-color:initial;border:0;display:inline;line-height:inherit;margin:0;max-width:auto;overflow:visible;padding:0}div#p>svg>foreignObject>section .commit-tease-sha{color:#444d56;display:inline-block;font-family:SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;font-size:90%}div#p>svg>foreignObject>section div#p>svg>foreignObject>section section.commit-tease-sha{--marpit-root-font-size:90%}div#p>svg>forei
|
||
|
<h1>GraphQL概論</h1>
|
||
|
<p>WebDINO Japan エンジニア<br />
|
||
|
<a href="https://github.com/kou029w">渡邉浩平</a></p>
|
||
|
<p><img src="https://github.com/kou029w.png" alt="w:200" style="width:200px;" /></p>
|
||
|
</section>
|
||
|
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="2" data-paginate="true" data-marpit-pagination="2" data-marpit-pagination-total="28" style="--paginate:true;">
|
||
|
<h2><a href="https://graphql.org">GraphQL</a>とは</h2>
|
||
|
<p>サーバーへの問い合わせ (Query)</p>
|
||
|
<pre><code class="language-graphql"><svg data-marp-fitting="svg" data-marp-fitting-code><foreignObject><span data-marp-fitting-svg-content><span data-marp-fitting-svg-content-wrap>{
|
||
|
pokemon(name: "Pikachu") {
|
||
|
classification
|
||
|
}
|
||
|
}
|
||
|
</span></span></foreignObject></svg></code></pre>
|
||
|
<p>サーバーからの応答 (Response, JSON形式)</p>
|
||
|
<pre><code class="language-json"><svg data-marp-fitting="svg" data-marp-fitting-code><foreignObject><span data-marp-fitting-svg-content><span data-marp-fitting-svg-content-wrap>{
|
||
|
<span class="hljs-attr">"data"</span>: {
|
||
|
<span class="hljs-attr">"pokemon"</span>: {
|
||
|
<span class="hljs-attr">"classification"</span>: <span class="hljs-string">"Mouse Pokémon"</span>
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
</span></span></foreignObject></svg></code></pre>
|
||
|
<p><a href="https://graphql-pokemon2.vercel.app">https://graphql-pokemon2.vercel.app</a></p>
|
||
|
<p>GraphQLとはAPIのクエリ言語</p>
|
||
|
</section>
|
||
|
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="3" data-paginate="true" data-marpit-pagination="3" data-marpit-pagination-total="28" style="--paginate:true;">
|
||
|
<h2>GraphQLとは</h2>
|
||
|
<ul>
|
||
|
<li>APIのクエリ言語</li>
|
||
|
<li>クエリ言語の型を宣言するGraphQLスキーマ言語</li>
|
||
|
<li>Webクライアントとサーバーのためのアプリケーション層の仕様</li>
|
||
|
</ul>
|
||
|
<p>:::info<br />
|
||
|
GraphQL以外の身近な言語の例:</p>
|
||
|
<ul>
|
||
|
<li>クエリ言語: SQL</li>
|
||
|
<li>スキーマ言語: <a href="https://json-schema.org/">JSON Schema</a>, <a href="http://www.w3.org/TR/xmlschema11-1/">XSD</a><br />
|
||
|
:::</li>
|
||
|
</ul>
|
||
|
</section>
|
||
|
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="4" data-paginate="true" data-marpit-pagination="4" data-marpit-pagination-total="28" style="--paginate:true;">
|
||
|
<h2>歴史</h2>
|
||
|
<ul>
|
||
|
<li>2012年 Facebookによる開発</li>
|
||
|
<li>2015年 オープンソース化</li>
|
||
|
<li>2019年 <a href="https://graphql.org/foundation/">GraphQL Foundation</a>に移管</li>
|
||
|
</ul>
|
||
|
<p>オープンソースな仕様になっており、自由に<a href="https://github.com/graphql/graphql-spec/blob/main/CONTRIBUTING.md">貢献可能</a></p>
|
||
|
</section>
|
||
|
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="5" data-paginate="true" data-marpit-pagination="5" data-marpit-pagination-total="28" style="--paginate:true;">
|
||
|
<h2>主な仕様</h2>
|
||
|
<ol>
|
||
|
<li>サーバー: <a href="https://graphql.org/learn/introspection/">使用可能なデータの構造とその操作を宣言するための言語 (スキーマ言語)</a></li>
|
||
|
<li>クライアント: <a href="https://graphql.org/learn/queries/">サーバーにデータを要求するための言語 (クエリ言語)</a></li>
|
||
|
<li>サーバー: <a href="https://graphql.org/learn/execution/">クエリの実行方法 (Resolvers)</a></li>
|
||
|
<li>クライアント: <a href="https://graphql.org/learn/serving-over-http/#response">受け取るデータ形式 (JSON)</a></li>
|
||
|
</ol>
|
||
|
</section>
|
||
|
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="6" data-paginate="true" data-marpit-pagination="6" data-marpit-pagination-total="28" style="--paginate:true;">
|
||
|
<h2>何でないか</h2>
|
||
|
<ul>
|
||
|
<li>データベースではない</li>
|
||
|
<li>JavaScriptではない</li>
|
||
|
</ul>
|
||
|
</section>
|
||
|
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="7" data-paginate="true" data-marpit-pagination="7" data-marpit-pagination-total="28" style="--paginate:true;">
|
||
|
<h2>なぜGraphQLを使うのか</h2>
|
||
|
<ol>
|
||
|
<li>単一リクエスト</li>
|
||
|
<li>型システム</li>
|
||
|
<li>便利な開発ツール</li>
|
||
|
</ol>
|
||
|
</section>
|
||
|
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="8" data-paginate="true" data-marpit-pagination="8" data-marpit-pagination-total="28" style="--paginate:true;">
|
||
|
<h3>1. 単一リクエスト</h3>
|
||
|
<p>効率的なデータ読み込み</p>
|
||
|
<p>オーバーフェッチを最小限に抑え、サーバーへのラウンドトリップを少なくする<br />
|
||
|
FacebookがGraphQLを開発した理由は、<a href="https://reactjs.org/blog/2015/05/01/graphql-introduction.html">モバイルネイティブアプリへの移行のため</a><br />
|
||
|
スマホの普及に伴う低速、省電力なデバイスの利用の増加が背景</p>
|
||
|
</section>
|
||
|
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="9" data-paginate="true" data-marpit-pagination="9" data-marpit-pagination-total="28" style="--paginate:true;">
|
||
|
<p>REST</p>
|
||
|
<p><img src="https://imgur.com/VRyV7Jh.png" alt="REST" /></p>
|
||
|
<p><a href="https://www.howtographql.com/basics/1-graphql-is-the-better-rest/">https://www.howtographql.com/basics/1-graphql-is-the-better-rest/</a></p>
|
||
|
</section>
|
||
|
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="10" data-paginate="true" data-marpit-pagination="10" data-marpit-pagination-total="28" style="--paginate:true;">
|
||
|
<p>GraphQL</p>
|
||
|
<p><img src="https://imgur.com/z9VKnHs.png" alt="GraphQL" /></p>
|
||
|
<p><a href="https://www.howtographql.com/basics/1-graphql-is-the-better-rest/">https://www.howtographql.com/basics/1-graphql-is-the-better-rest/</a></p>
|
||
|
</section>
|
||
|
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="11" data-paginate="true" data-marpit-pagination="11" data-marpit-pagination-total="28" style="--paginate:true;">
|
||
|
<p>REST</p>
|
||
|
<ol>
|
||
|
<li><code>GET /users/<id></code></li>
|
||
|
<li><code>GET /users/<id>/posts</code></li>
|
||
|
<li><code>GET /users/<id>/followers</code></li>
|
||
|
</ol>
|
||
|
<p>GraphQL</p>
|
||
|
<ol>
|
||
|
<li><code>GET /?query={user(id:<id>){name,posts{title},followers(last:<count>){name}}}</code></li>
|
||
|
</ol>
|
||
|
</section>
|
||
|
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="12" data-paginate="true" data-marpit-pagination="12" data-marpit-pagination-total="28" style="--paginate:true;">
|
||
|
<h3>2. 型システム</h3>
|
||
|
<p>さまざまなフロントエンド環境のサポート</p>
|
||
|
<p>単一APIの構築と正確なデータ構造の維持<br />
|
||
|
クライアントアプリケーションを実行するフロントエンドフレームワークとプラットフォームの多様化が背景</p>
|
||
|
</section>
|
||
|
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="13" data-paginate="true" data-marpit-pagination="13" data-marpit-pagination-total="28" style="--paginate:true;">
|
||
|
<pre><code class="language-graphql="><svg data-marp-fitting="svg" data-marp-fitting-code><foreignObject><span data-marp-fitting-svg-content><span data-marp-fitting-svg-content-wrap>"""ポケモンを表します"""
|
||
|
type Pokemon {
|
||
|
"""このオブジェクトのID"""
|
||
|
id: ID!
|
||
|
|
||
|
"""このポケモンの全国図鑑No."""
|
||
|
number: String
|
||
|
|
||
|
"""このポケモンの名前"""
|
||
|
name: String
|
||
|
|
||
|
"""このポケモンの重さの最大と最小"""
|
||
|
weight: PokemonDimension
|
||
|
|
||
|
"""このポケモンの高さの最大と最小"""
|
||
|
height: PokemonDimension
|
||
|
|
||
|
"""このポケモンの分類"""
|
||
|
classification: String
|
||
|
|
||
|
# ...
|
||
|
}
|
||
|
</span></span></foreignObject></svg></code></pre>
|
||
|
<p>特定のプログラミング言語に依存しないGraphQLスキーマ言語</p>
|
||
|
</section>
|
||
|
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="14" data-paginate="true" data-marpit-pagination="14" data-marpit-pagination-total="28" style="--paginate:true;">
|
||
|
<pre><code class="language-graphql"><svg data-marp-fitting="svg" data-marp-fitting-code><foreignObject><span data-marp-fitting-svg-content><span data-marp-fitting-svg-content-wrap>"""ポケモンを表します"""
|
||
|
type Pokemon {
|
||
|
}
|
||
|
|
||
|
"""ポケモンの寸法を表します"""
|
||
|
type PokemonDimension {
|
||
|
}
|
||
|
</span></span></foreignObject></svg></code></pre>
|
||
|
<p>オブジェクトの種類</p>
|
||
|
</section>
|
||
|
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="15" data-paginate="true" data-marpit-pagination="15" data-marpit-pagination-total="28" style="--paginate:true;">
|
||
|
<pre><code class="language-graphql"><svg data-marp-fitting="svg" data-marp-fitting-code><foreignObject><span data-marp-fitting-svg-content><span data-marp-fitting-svg-content-wrap>"""ポケモンを表します"""
|
||
|
type Pokemon {
|
||
|
"""このオブジェクトのID"""
|
||
|
id: ID!
|
||
|
|
||
|
"""このポケモンの名前"""
|
||
|
name: String
|
||
|
|
||
|
"""このポケモンの分類"""
|
||
|
classification: String
|
||
|
|
||
|
"""このポケモンの高さの最大と最小"""
|
||
|
height: PokemonDimension
|
||
|
}
|
||
|
</span></span></foreignObject></svg></code></pre>
|
||
|
<p>オブジェクトに含まれるフィールド</p>
|
||
|
</section>
|
||
|
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="16" data-paginate="true" data-marpit-pagination="16" data-marpit-pagination-total="28" style="--paginate:true;">
|
||
|
<pre><code class="language-javascript"><svg data-marp-fitting="svg" data-marp-fitting-code><foreignObject><span data-marp-fitting-svg-content><span data-marp-fitting-svg-content-wrap><span class="hljs-comment">// JavaScript</span>
|
||
|
<span class="hljs-keyword">const</span> pokemonQuery = <span class="hljs-string">`{
|
||
|
pokemon(name: "Pikachu") {
|
||
|
classification
|
||
|
}
|
||
|
}`</span>;
|
||
|
|
||
|
fetch(<span class="hljs-string">`http://example/?<span class="hljs-subst">${<span class="hljs-keyword">new</span> URLSearchParams({ query: pokemonQuery })}</span>`</span>)
|
||
|
.then(<span class="hljs-function"><span class="hljs-params">r</span> =></span> r.json())
|
||
|
.then(<span class="hljs-function">(<span class="hljs-params">{ data }</span>) =></span> <span class="hljs-built_in">console</span>.log(data?.pokemon?.classification));
|
||
|
<span class="hljs-comment">// => "Mouse Pokémon"</span>
|
||
|
</span></span></foreignObject></svg></code></pre>
|
||
|
<pre><code class="language-kotlin"><svg data-marp-fitting="svg" data-marp-fitting-code><foreignObject><span data-marp-fitting-svg-content><span data-marp-fitting-svg-content-wrap><span class="hljs-comment">// Kotlin</span>
|
||
|
<span class="hljs-keyword">val</span> response = apolloClient.query(pokemonQuery).await()
|
||
|
Log.d(response?.<span class="hljs-keyword">data</span>?.pokemon?.classification)
|
||
|
</span></span></foreignObject></svg></code></pre>
|
||
|
<pre><code class="language-swift"><svg data-marp-fitting="svg" data-marp-fitting-code><foreignObject><span data-marp-fitting-svg-content><span data-marp-fitting-svg-content-wrap><span class="hljs-comment">// Swift</span>
|
||
|
apollo.fetch(query: pokemonQuery) { result <span class="hljs-keyword">in</span>
|
||
|
<span class="hljs-keyword">guard</span> <span class="hljs-keyword">let</span> data <span class="hljs-operator">=</span> <span class="hljs-keyword">try?</span> result.get().data <span class="hljs-keyword">else</span> { <span class="hljs-keyword">return</span> }
|
||
|
<span class="hljs-built_in">print</span>(data.pokemon<span class="hljs-operator">?</span>.classification)
|
||
|
}
|
||
|
</span></span></foreignObject></svg></code></pre>
|
||
|
<p><a href="https://www.apollographql.com">https://www.apollographql.com</a></p>
|
||
|
</section>
|
||
|
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="17" data-paginate="true" data-marpit-pagination="17" data-marpit-pagination-total="28" style="--paginate:true;">
|
||
|
<h3>3. 便利な開発ツール</h3>
|
||
|
<p>短期間での開発</p>
|
||
|
<p>クライアントアプリケーションの設計変更に対応するためのツールが提供されている<br />
|
||
|
継続的デプロイ (CD) が背景</p>
|
||
|
</section>
|
||
|
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="18" data-paginate="true" data-marpit-pagination="18" data-marpit-pagination-total="28" style="--paginate:true;">
|
||
|
<p>IDE</p>
|
||
|
<p><img src="https://raw.githubusercontent.com/graphql/graphiql/main/packages/graphiql/resources/graphiql.jpg" alt="GraphiQL" /></p>
|
||
|
<p><a href="https://github.com/graphql/graphiql">GraphiQL</a>, <a href="https://github.com/graphql/graphql-playground">GraphQL Playground</a>, etc.</p>
|
||
|
</section>
|
||
|
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="19" data-paginate="true" data-marpit-pagination="19" data-marpit-pagination-total="28" style="--paginate:true;">
|
||
|
<p>コードの生成</p>
|
||
|
<pre><code class="language-console"><svg data-marp-fitting="svg" data-marp-fitting-code><foreignObject><span data-marp-fitting-svg-content><span data-marp-fitting-svg-content-wrap><span class="hljs-meta">$</span><span class="bash"> graphql-codegen</span>
|
||
|
</span></span></foreignObject></svg></code></pre>
|
||
|
<p>使う</p>
|
||
|
<pre><code class="language-typescript"><svg data-marp-fitting="svg" data-marp-fitting-code><foreignObject><span data-marp-fitting-svg-content><span data-marp-fitting-svg-content-wrap><span class="hljs-keyword">import</span> { usePokemonQuery } <span class="hljs-keyword">from</span> <span class="hljs-string">"./generated"</span>;
|
||
|
|
||
|
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> () => {
|
||
|
<span class="hljs-keyword">const</span> { data } = usePokemonQuery();
|
||
|
<span class="hljs-keyword">return</span> data?.pokemon?.classification;
|
||
|
};
|
||
|
</span></span></foreignObject></svg></code></pre>
|
||
|
<p><a href="https://www.graphql-code-generator.com/">GraphQL Code Generator</a><br />
|
||
|
<a href="https://www.graphql-code-generator.com/docs/plugins/typescript-react-query">React</a>, <a href="https://www.graphql-code-generator.com/docs/plugins/typescript-vue-apollo">Vue</a>, <a href="https://www.graphql-code-generator.com/docs/plugins/kotlin">Kotlin</a>, etc.</p>
|
||
|
</section>
|
||
|
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="20" data-paginate="true" data-marpit-pagination="20" data-marpit-pagination-total="28" style="--paginate:true;">
|
||
|
<h2>まとめ</h2>
|
||
|
<ul>
|
||
|
<li>GraphQLとはデータを問い合わせるクエリ言語仕様と周辺技術</li>
|
||
|
<li>単一リクエスト/型システム/便利な開発ツール</li>
|
||
|
</ul>
|
||
|
</section>
|
||
|
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="21" data-paginate="true" data-marpit-pagination="21" data-marpit-pagination-total="28" style="--paginate:true;">
|
||
|
<h2>後付</h2>
|
||
|
</section>
|
||
|
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="22" data-paginate="true" data-marpit-pagination="22" data-marpit-pagination-total="28" style="--paginate:true;">
|
||
|
<h3>より理解を深めるための知識</h3>
|
||
|
<ul>
|
||
|
<li><a href="https://gihyo.jp/book/2010/978-4-7741-4204-3">山本陽平「Webを支える技術」</a> - HTTPの基礎知識、REST</li>
|
||
|
<li><a href="https://graphql.org/">GraphQL | A query language for your API</a></li>
|
||
|
<li><a href="https://www.howtographql.com/">How to GraphQL - The Fullstack Tutorial for GraphQL</a></li>
|
||
|
</ul>
|
||
|
</section>
|
||
|
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="23" data-paginate="true" data-marpit-pagination="23" data-marpit-pagination-total="28" style="--paginate:true;">
|
||
|
<h3>何を話していないか</h3>
|
||
|
<ul>
|
||
|
<li><a href="https://spec.graphql.org/">言語仕様</a></li>
|
||
|
<li><a href="http://spec.graphql.org/June2018/#sec-Errors">エラーレスポンス</a></li>
|
||
|
<li><a href="https://graphql.org/learn/validation/">Validation</a></li>
|
||
|
<li><a href="https://graphql.org/learn/execution/">Execution</a></li>
|
||
|
<li><a href="https://spec.graphql.org/June2018/#sec-Subscription">Subscription</a></li>
|
||
|
<li><a href="https://graphql.org/learn/introspection/">Introspection</a></li>
|
||
|
</ul>
|
||
|
</section>
|
||
|
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="24" data-paginate="true" data-marpit-pagination="24" data-marpit-pagination-total="28" style="--paginate:true;">
|
||
|
<h3>JSON Serialization</h3>
|
||
|
<table>
|
||
|
<thead>
|
||
|
<tr>
|
||
|
<th>GraphQL Value</th>
|
||
|
<th>JSON Value</th>
|
||
|
</tr>
|
||
|
</thead>
|
||
|
<tbody>
|
||
|
<tr>
|
||
|
<td>Map</td>
|
||
|
<td>Object</td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td>List</td>
|
||
|
<td>Array</td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td>Null</td>
|
||
|
<td>null</td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td>String</td>
|
||
|
<td>String</td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td>Boolean</td>
|
||
|
<td>true or false</td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td>Int</td>
|
||
|
<td>Number</td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td>Float</td>
|
||
|
<td>Number</td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td>Enum Value</td>
|
||
|
<td>String</td>
|
||
|
</tr>
|
||
|
</tbody>
|
||
|
</table>
|
||
|
<p><a href="https://spec.graphql.org/June2018/#sec-JSON-Serialization">https://spec.graphql.org/June2018/#sec-JSON-Serialization</a></p>
|
||
|
</section>
|
||
|
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="25" data-paginate="true" data-marpit-pagination="25" data-marpit-pagination-total="28" style="--paginate:true;">
|
||
|
<h3>セキュリティ</h3>
|
||
|
<p>セキュリティの懸念事項は一般的なWebサービスと同様に存在</p>
|
||
|
<ul>
|
||
|
<li><a href="https://owasp.org/www-project-top-ten/">OWASP Top Ten</a></li>
|
||
|
<li><a href="https://cheatsheetseries.owasp.org/cheatsheets/GraphQL_Cheat_Sheet.html">GraphQL - OWASP Cheat Sheet Series</a></li>
|
||
|
</ul>
|
||
|
</section>
|
||
|
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="26" data-paginate="true" data-marpit-pagination="26" data-marpit-pagination-total="28" style="--paginate:true;">
|
||
|
<h3>認証・認可</h3>
|
||
|
<p>GraphQL仕様に含まないので一般的なWebの認証・認可の設計と同様に行う</p>
|
||
|
<ul>
|
||
|
<li><a href="https://openid.net/">OpenID</a></li>
|
||
|
<li><a href="https://oauth.net/">OAuth</a></li>
|
||
|
<li><a href="https://jwt.io/">JWT</a></li>
|
||
|
</ul>
|
||
|
</section>
|
||
|
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="27" data-paginate="true" data-marpit-pagination="27" data-marpit-pagination-total="28" style="--paginate:true;">
|
||
|
<h3>キャッシュ</h3>
|
||
|
<p>HTTP GETメソッドによる一般的な<a href="https://developer.mozilla.org/ja/docs/Web/HTTP/Caching">HTTP キャッシュ</a>に加え、GraphQLでは<a href="https://graphql.org/learn/global-object-identification/">グローバルなオブジェクトの識別子の宣言</a>による<a href="https://graphql.org/learn/caching/">キャッシュ</a>が存在</p>
|
||
|
</section>
|
||
|
</foreignObject></svg><svg data-marpit-svg="" viewBox="0 0 1280 720"><foreignObject width="1280" height="720"><section id="28" data-paginate="true" data-marpit-pagination="28" data-marpit-pagination-total="28" style="--paginate:true;">
|
||
|
<h3>(参考程度) 導入するか迷ったら…</h3>
|
||
|
<pre><code class="language-graphviz"><svg data-marp-fitting="svg" data-marp-fitting-code><foreignObject><span data-marp-fitting-svg-content><span data-marp-fitting-svg-content-wrap>digraph {
|
||
|
apps[shape=diamond,label="モバイルアプリ?"]
|
||
|
apps->GraphQL[label=Yes]
|
||
|
apps->REST[label=No]
|
||
|
{rank=same;apps;GraphQL}
|
||
|
}
|
||
|
</span></span></foreignObject></svg></code></pre>
|
||
|
</section>
|
||
|
<script>!function(){"use strict";const t="marpitSVGPolyfill:setZoomFactor,",e=Symbol();let r,o;function n(n){const i="object"==typeof n&&n.target||document,a="object"==typeof n?n.zoom:n;window[e]||(Object.defineProperty(window,e,{configurable:!0,value:!0}),window.addEventListener("message",(({data:e,origin:r})=>{if(r===window.origin)try{if(e&&"string"==typeof e&&e.startsWith(t)){const[,t]=e.split(","),r=Number.parseFloat(t);Number.isNaN(r)||(o=r)}}catch(t){console.error(t)}})));let l=!1;Array.from(i.querySelectorAll("svg[data-marpit-svg]"),(t=>{var e,n,i,s;t.style.transform||(t.style.transform="translateZ(0)");const c=a||o||t.currentScale||1;r!==c&&(r=c,l=c);const d=t.getBoundingClientRect(),{length:u}=t.children;for(let r=0;r<u;r+=1){const o=t.children[r],a=o.getScreenCTM();if(a){const t=null!==(n=null===(e=o.x)||void 0===e?void 0:e.baseVal.value)&&void 0!==n?n:0,r=null!==(s=null===(i=o.y)||void 0===i?void 0:i.baseVal.value)&&void 0!==s?s:0,l=o.firstElementChild,{style:u}=l;u.transformOrigin||(u.transformOrigin=`${-t}px ${-r}px`),u.transform=`scale(${c}) matrix(${a.a}, ${a.b}, ${a.c}, ${a.d}, ${a.e-d.left}, ${a.f-d.top}) translateZ(0.0001px)`}}})),!1!==l&&Array.from(i.querySelectorAll("iframe"),(({contentWindow:e})=>{null==e||e.postMessage(`${t}${l}`,"null"===window.origin?"*":window.origin)}))}r=1,o=void 0;const i=(t,e,r)=>{if(t.getAttribute(e)!==r)return t.setAttribute(e,r),!0};function a({once:t=!1,target:e=document}={}){const r="Apple Computer, Inc."===navigator.vendor?[n]:[];let o=!t;const a=()=>{for(const t of r)t({target:e});!function(t=document){Array.from(t.querySelectorAll('svg[data-marp-fitting="svg"]'),(t=>{var e;const r=t.firstChild,o=r.firstChild,{scrollWidth:n,scrollHeight:a}=o;let l,s=1;if(t.hasAttribute("data-marp-fitting-code")&&(l=null===(e=t.parentElement)||void 0===e?void 0:e.parentElement),t.hasAttribute("data-marp-fitting-math")&&(l=t.parentElement),l){const t=getComputedStyle(l),e=Math.ceil(l.clientWidth-parseFloat(t.paddingLeft||"0")-parseFloat(t.paddingRight||"0"));e&&(s=e)}const c=Math.max(n,s),d=Math.max(a,1),u=`0 0 ${c} ${d}`;i(r,"width",`${c}`),i(r,"height",`${d}`),i(t,"preserveAspectRatio",getComputedStyle(t).getPropertyValue("--preserve-aspect-ratio")||"xMinYMin meet"),i(t,"viewBox",u)&&t.classList.toggle("__reflow__")}))}(e),o&&window.requestAnimationFrame(a)};return a(),()=>{o=!1}}const l=Symbol(),s=document.currentScript;((t=document)=>{if("undefined"==typeof window)throw new Error("Marp Core's browser script is valid only in browser context.");if(t[l])return t[l];const e=a({target:t}),r=()=>{e(),delete t[l]};Object.defineProperty(t,l,{configurable:!0,value:r})})(s?s.getRootNode():document)}();
|
||
|
</script></foreignObject></svg></div><script>/*!! License: https://unpkg.com/@marp-team/marp-cli@1.1.1/lib/bespoke.js.LICENSE.txt */
|
||
|
!function(){"use strict";var e=function(e,t){var n,r=1===(e.parent||e).nodeType?e.parent||e:document.querySelector(e.parent||e),a=[].filter.call("string"==typeof e.slides?r.querySelectorAll(e.slides):e.slides||r.children,(function(e){return"SCRIPT"!==e.nodeName})),s={},i=function(e,t){return(t=t||{}).index=a.indexOf(e),t.slide=e,t},o=function(e,t){s[e]=(s[e]||[]).filter((function(e){return e!==t}))},l=function(e,t){return(s[e]||[]).reduce((function(e,n){return e&&!1!==n(t)}),!0)},c=function(e,t){a[e]&&(n&&l("deactivate",i(n,t)),n=a[e],l("activate",i(n,t)))},d=function(e,t){var r=a.indexOf(n)+e;l(e>0?"next":"prev",i(n,t))&&c(r,t)},u={off:o,on:function(e,t){return(s[e]||(s[e]=[])).push(t),o.bind(null,e,t)},fire:l,slide:function(e,t){if(!arguments.length)return a.indexOf(n);l("slide",i(a[e],t))&&c(e,t)},next:d.bind(null,1),prev:d.bind(null,-1),parent:r,slides:a,destroy:function(e){l("destroy",i(n,e)),s={}}};return(t||[]).forEach((function(e){e(u)})),n||c(0),u};function t(e){e.parent.classList.add("bespoke-marp-parent"),e.slides.forEach((e=>e.classList.add("bespoke-marp-slide"))),e.on("activate",(t=>{const n=t.slide,r=!n.classList.contains("bespoke-marp-active");e.slides.forEach((e=>{e.classList.remove("bespoke-marp-active"),e.setAttribute("aria-hidden","true")})),n.classList.add("bespoke-marp-active"),n.removeAttribute("aria-hidden"),r&&(n.classList.add("bespoke-marp-active-ready"),document.body.clientHeight,n.classList.remove("bespoke-marp-active-ready"))}))}function n(e){let t=0,n=0;Object.defineProperty(e,"fragments",{enumerable:!0,value:e.slides.map((e=>[null,...e.querySelectorAll("[data-marpit-fragment]")]))});const r=r=>void 0!==e.fragments[t][n+r],a=(r,a)=>{t=r,n=a,e.fragments.forEach(((e,t)=>{e.forEach(((e,n)=>{if(null==e)return;const s=t<r||t===r&&n<=a;e.setAttribute("data-bespoke-marp-fragment",s?"active":"inactive"),t===r&&n===a?e.setAttribute("data-bespoke-marp-current-fragment","current"):e.removeAttribute("data-bespoke-marp-current-fragment")}))})),e.fragmentIndex=a;const s={slide:e.slides[r],index:r,fragments:e.fragments[r],fragmentIndex:a};e.fire("fragment",s)};e.on("next",(({fragment:s=!0})=>{if(s){if(r(1))return a(t,n+1),!1;const s=t+1;e.fragments[s]&&a(s,0)}else{const r=e.fragments[t].length;if(n+1<r)return a(t,r-1),!1;const s=e.fragments[t+1];s&&a(t+1,s.length-1)}})),e.on("prev",(({fragment:s=!0})=>{if(r(-1)&&s)return a(t,n-1),!1;const i=t-1;e.fragments[i]&&a(i,e.fragments[i].length-1)})),e.on("slide",(({index:t,fragment:n})=>{let r=0;if(void 0!==n){const a=e.fragments[t];if(a){const{length:e}=a;r=-1===n?e-1:Math.min(Math.max(n,0),e-1)}}a(t,r)})),a(0,0)}var r,a={exports:{}};r=a,function(){var e="undefined"!=typeof window&&void 0!==window.document?window.document:{},t=r.exports,n=function(){for(var t,n=[["requestFullscreen","exitFullscreen","fullscreenElement","fullscreenEnabled","fullscreenchange","fullscreenerror"],["webkitRequestFullscreen","webkitExitFullscreen","webkitFullscreenElement","webkitFullscreenEnabled","webkitfullscreenchange","webkitfullscreenerror"],["webkitRequestFullScreen","webkitCancelFullScreen","webkitCurrentFullScreenElement","webkitCancelFullScreen","webkitfullscreenchange","webkitfullscreenerror"],["mozRequestFullScreen","mozCancelFullScreen","mozFullScreenElement","mozFullScreenEnabled","mozfullscreenchange","mozfullscreenerror"],["msRequestFullscreen","msExitFullscreen","msFullscreenElement","msFullscreenEnabled","MSFullscreenChange","MSFullscreenError"]],r=0,a=n.length,s={};r<a;r++)if((t=n[r])&&t[1]in e){for(r=0;r<t.length;r++)s[n[0][r]]=t[r];return s}return!1}(),a={change:n.fullscreenchange,error:n.fullscreenerror},s={request:function(t,r){return new Promise(function(a,s){var i=function(){this.off("change",i),a()}.bind(this);this.on("change",i);var o=(t=t||e.documentElement)[n.requestFullscreen](r);o instanceof Promise&&o.then(i).catch(s)}.bind(this))},exit:function(){return new Promise(function(t,r){if(this.isFullscreen){var a=function(){this.off("change",a),t()}.bind(this);this.on("change",a);var s=e[n.exitFullscreen]();s instanceof Promise&&s.then(a).catch(r)}else t()}.bi
|