Upgrade to Pro — share decks privately, control downloads, hide ads and more …

はてなリモートインターンシップ2023 フロントエンドブートキャンプ講義資料

Hatena
October 18, 2023

はてなリモートインターンシップ2023 フロントエンドブートキャンプ講義資料

Hatena

October 18, 2023
Tweet

More Decks by Hatena

Other Decks in Programming

Transcript

  1. このブートキャンプのゴール • 皆さんが主に後半のチーム開発でフロントエンドに関わるようなタスクに取り組む際 に困らない最低限の知識をつけてもらう • この講義で扱う"フロントエンド"の領域は JavaScript/TypeScript/React • Web 開発において年々重要度を増しているフロントエンドを⾼速にキャッチアップ

    してもらう • 講義のボリュームが⼤きいこともあり、周辺技術に関する説明を省略する場合もあ る インターンが終わった後も React や TypeScript など、フロントエンド技術について知る 際の⼿助けになれば良いと思っています。 #hatenaintern)*)+
  2. 様々な動作環境の⼀例 それぞれの環境には、これらの技術によって備えることができます。 • 古いスマホやパソコン(IE や古いバージョンのブラウザ) • Web 標準 • アクセシビリティ機能を使っているユーザ

    • アクセシビリティ (accessibility, a88y) • (利⽤制限がかかっている)遅い‧不安定なインターネット • パフォーマンス対策 それぞれ、どのように対策するのか⾒ていきます。 #hatenaintern)*)+
  3. 例: React アプリをユーザに表 ⽰するには? 1. (開発者)実装 • TypeScript の型チェック;、最新の JavaScript

    構⽂での開発 2. (開発者)ビルド • Web 標準 • パフォーマンス対策 3. (ユーザー)Web ページへのリクエスト • html を読み込み、それに付随するリソースを読み込むf 4. (ユーザー)ページを表⽰ • 読み込んだリソースを元にページを表⽰する • アクセシビリティ (accessibility, aAAy) ! 読み込むファイルが⼤きい‧多いほど、次の段階であるページ表⽰が遅くなる ! なぜ TypeScript を使うのか? - TypeScript Deep Dive ⽇本語版 #hatenaintern)*)+
  4. 例: React アプリをユーザに表 ⽰するには? • Web 標準 • パフォーマンス対策 •

    アクセシビリティ (accessibility, aAAy) 具体的にはどうのように対策していくの か、開発の流れと共に⾒ていきます。 #hatenaintern)*)+
  5. 例: React アプリをユーザに表⽰するには? しかし、React/TypeScript で開発していた場合はどうでしょうか? <!-- index.html --> <html> <head>

    <script type="module" src="main.tsx"></script> </head> <body> <div id="root"></div> </body> </html> // main.tsx const App = () => { const user: User | null = useUser() return ( <div> Α͏ͦ͜ɺ{user?.name ?? 'ήετ'}͞Μɻ </div> ) } const container = document.getElementById('root') const root = createRoot(container!) root.render(<App />) #hatenaintern)*)+
  6. 例: React アプリをユーザに表⽰するには? • TypeScript をブラウザが解釈できない • JavaScript なら const

    user = useUser() • TypeScript なら const user: User | null = useUser() • JSX をブラウザが解釈できない • JSX とは const label = () => (<div>...</div>) のように、 HTML タグを書いてあるものN • 新しい構⽂を解釈できないような、(未アップデートの)古いブラウザが存在する • ESabab で追加された構⽂ ?. h や ?? jなど => JSX や TypeScript、新しい JavaScript 構⽂をトランスパイル@によってブラウザが解釈できるように書き換える。 ! 未対応の構⽂をターゲットが解釈可能な構⽂へ書き換えることによって、その環境でもコードが動くようにすること。 ! Null 合体演算⼦ (??) - JavaScript | MDN ! オプショナルチェーン (?.) - JavaScript | MDN ! JSX の導⼊ ‒ React #hatenaintern)*)+
  7. トランスパイル アロー関数式とテンプレートリテラルは ES4567(ES9) から、Null 合体演 算⼦ (??)は ES4545 から導⼊されたもので、それ以降アップデートしてい ないブラウザでは動きません。

    そのため、トランスパイルによって次のように変換します。 var hello = function hello(name) { return "hello, " + (name !== null && name !== void 0 ? name : "ήετ") + "!" } hello("hatena") // > hello, hatena! hello() // > hello, ήετ! #hatenaintern)*)+
  8. ツールチェイン( • 前スライドで説明したトランスパイルを⾏うツールはトランスパイラと呼ばれ、代表的なものは 3 つあります。 • バンドラはトランスパイルしたモジュール群をできるだけ最適にバンドルしてくれるツールです。 • 最近はこれらのツールを結合する統合ツールのようなもの (Romec,

    Denog) が登場したりもしています。 トランスパイラ • Babel (JavaScript) • SWC (Rust) • esbuild (Golang) バンドラ • Webpack • Rollup • Parcel (SWC) • Snowpack, Vite (esbuild) ! https://deno.land/ ! https://rome.tools #hatenaintern)*)+
  9. Web 標準 Chrome, IE, Safari, Firefox など世の中には多くのブラウザがあり、そのバージョンもユーザーによって 異なっています。 • Web

    標準()という活動によって、すべてのブラウザで構⽂や API が同じ振る舞いをすることを⽬指して いる(( • JavaScript(ECMAScript)、HTML、DOM API、CSS の対応バージョンもブラウザによって異なる • もし古いブラウザでの動作をサポートするなら? • API が⾜りない場合は polyfill を⽤いて補う • 新しい JavaScript トランスパイルによって古い構⽂に変換する !! 2021 年のウェブ標準とブラウザ | gihyo.jp !" Web standards (ウェブ標準) - MDN Web Docs ⽤語集: ウェブ関連⽤語の定義 | MDN #hatenaintern)*)+
  10. Web 標準 ブラウザごとのサポート状況は以下のサイトで確認できます。 • MDN%& • https://developer.mozilla.org/ja/docs/Web • ウェブ上には有象無象のあらゆる情報が転がっているので、フロントエンドやウェブ についてのことで困ったらまずは

    MDN を⾒るのがオススメ • Can I use • https://caniuse.com !" mozilla のドメイン下にあるが、Google やマイクロソフトなどが Open Web Docs という団体を通じて⽀援をしている https://opencollective.com/open-web-docs/updates/ introducing-open-web-docs #hatenaintern)*)+
  11. アクセシビリティ (accessibility, a55y) アクセシビリティ | MDN ウェブ開発におけるアクセシビリティとは、何らかの理由により能⼒に制約がある場合でも、可能な限り多く の⼈々がウェブサイトを使⽤できるようにすることを意味します。アクセシビリティとは、個⼈の⾝体的‧認 知的能⼒やウェブへのアクセス⽅法に関わらず、可能な限りアクセス可能なコンテンツを開発することです。 W"C

    - Accessibility 「ハードウェア、ソフトウェア、⾔語、⽂化、所在地、物理的/精神的能⼒にかかわらず、ウェブは基本的に すべての⼈に向けて設計されています。ウェブがこの⽬的を達成できると、さまざまな聴⼒、視⼒、認知能⼒ をもつ⼈々がウェブにアクセスできるようになります。」 #hatenaintern)*)+
  12. アクセシビリティ (accessibility, a55y) • セマンティックマークアップ,- (意味的 HTML) • リストには <ul>

    や <ol> を使うなど • これによって、スクリーンリーダー などが適切に解釈できるようになる。 • 代替テキスト,V • ネットワークエラーなどで画像の読み込みに失敗した際は、alt にいれた⽂字を表⽰するなど • キーボード操作のサポート • キーボード操作 (Tab キーでのフォーカス移動など) のサポートなど !" HTML の画像 - ウェブ開発を学ぶ | MDN !" Semantics (セマンティクス) - MDN Web Docs ⽤語集: ウェブ関連⽤語の定義 | MDN #hatenaintern)*)+
  13. パフォーマンス対策 (利⽤制限がかかっている)遅い‧不安定なインターネットを使っているユーザーが 主に対象となる。 • ユーザーの転送量や計算資源を浪費させるケース • 無駄な js や css、画像などのアセットのダウンロード

    • アプリケーションを 1 つにバンドルした巨⼤なファイル • 遅れて表⽰された要素によってページのレイアウトが変わってしまうkl => 最初のページに必要なものだけダウンロードして素早く表⽰するべき !" Cumulative Layout Shift (CLS) https://web.dev/i!=n/ja/cls/ #hatenaintern)*)+
  14. パフォーマンス対策 (利⽤制限がかかっている)遅い‧不安定なインターネットを使っているユーザーが主に対象とな る。 • 世界中のユーザーからのリクエスト • 例:⽇本にあるサーバーより前段、よりユーザーに近い位置(CDN, Edge)でリクエストを処理する • 複数回訪れる可能性のあるアプリケーション

    • CDN やブラウザキャッシュを⽤いて、何度も同じファイルをダウンロードする必要性を抑える • 1 度に巨⼤なファイルのダウンロードによって、ページの表⽰速度が遅い • コンテンツの表⽰タイミングにあわせてリソースを取得する • モジュール分割 (Code Splitting)、事前‧遅延読み込み (preload, defer, lazy) #hatenaintern)*)+
  15. 例: React アプリをユーザに表 ⽰するには? 1. (開発者)実装 • TypeScript の型チェック、最新の JavaScript

    構⽂での開発 2. (開発者)ビルド • 古い環境をサポートするための、Transpile, Polyfill • ページごとに必要最低限の Bundle, Code Splitting 3. (ユーザー)Web ページへのリクエスト • html を読み込み、それに付随するリソースを読み込む 4. (ユーザー)ページを表⽰ • 読み込んだリソースを元にページを表⽰する • スクリーンリーダーやキーボード操作に対応する #hatenaintern)*)+
  16. きほんのき 1/2 // ม਺ͱએݴ let a = "a" // let

    ͸্ॻ͖Մೳ const b = "b" // ্ॻ͖ෆՄೳ a = "A" // OK b = "B" // Cannot assign to "b" because it is a constant JavaScript における変数宣⾔は基本的に const を優先して使うこと を推奨します。変数が変更されないことがわかっている場合、コード を読む際の理解が容易になります。 #hatenaintern)*)+
  17. きほんのき 1/2 const id = "1234" // string const name

    = null // null const age = 2022 // number const isAdmin = false // boolean // object const user = { id, username: name age, isAdmin, memo, } user.age // 2022 // ະఆٛϓϩύςΟͩͱ undefined ͕ฦΔ user.abc #hatenaintern)*)+
  18. きほんのき 2/2 // ؔ਺ function getUser(id) { return db.users.find(id) }

    const getBlog = (id) => { return db.blogs.find(id) } // ഑ྻ const array1 = [1, 2] // ഑ྻॲཧ for (elm of array1) { console.log(elm) } array2.forEach((elm) => { console.log(elm) }) array1.map((elm) => elm * 2) // [2, 4] array1.at(0) // 1 #hatenaintern)*)+
  19. Arrow Function • function や return を使わずに関数を⽣成できる • 基本的にはこの⽅法で関数を⽣成しておけばハマらないBC const

    hello = name => `Hello, ${name}` hello("world") // hello, world • 引数が 1 つのときは引数を囲う () も省略可 • Tips: 返り値がオブジェクトのときは () で囲う必要がある const getProps = () => ({ a: "foo", b: "bar" }) !" 主に this が関わってくるとハマるようになる。詳細は付録「Arrow Function について」を読んでください。 #hatenaintern)*)+
  20. Arrow Function • 引数が複数ある場合は () で囲う • 関数内の記述が複数⾏に渡るときは {} と

    return を⽤いる const hello = (name, lang) => { if (lang === "ja") return `͜Μʹͪ͸ɺ${name}` if (lang === "es") return `Hola, ${name}` return `Hello, ${name}` } #hatenaintern)*)+
  21. Promiseについて • ⾮同期処理を抽象化したオブジェクト • Pending(成功も失敗もしていない状態)な状態を 表現できる • 成功(resolve)すれば Fulfilled、失敗(reject) すれば

    Rejected という状態になります • Fulfilled か Rejected のどちらかになる(つ まり処理が完了した)状態を Settled と呼びま す。 • 成功すれば then メソッド、失敗すれば catch メソッド、それぞれのコールバック関数に処 理が⾮同期に引き継がれる #hatenaintern)*)+
  22. const sleep = (ms) => new Promise((resolve, reject) => {

    setTimeout(() => { if (math.random() > 0.5) { return reject(new Error("error")) } else { return resolve(ms) } }, ms) }) sleep(1000) .then((ms /* resolveͨ͠஋Λड͚औΕΔ */) => console.log(`sleep: ${ms}ms`)) .catch((e) => { console.error(e) }) #hatenaintern)*)+
  23. fetch を使った例 • fetch は HTTP 通信をするための API で Promise

    を返す • Promise は⾮同期なので、① が console に出⼒された後に成功/失敗それぞれの場合のメッセージが表⽰される /* Ϣʔβʔ৘ใΛऔಘ͢ΔAPIͷྫɻҎԼͷΑ͏ͳJSON͕ฦͬͯ͘Δͱ͍͏૝ఆɻ { user: {id: 1, name: 'hatena'} } */ const promise = fetch("https://api.example.com/user/1") .then((res) => res.json()) .then((json) => { const user = json.user console.log(`${user.name}ͷid͸${user.id}`) }) .catch(console.error) console.log(promise) // Promise {<pending>} ɾɾɾ ᶃ #hatenaintern)*)+
  24. 複数の Promise を扱う⽅法 Promise の配列を作成し、それを Promise.all() に渡すことで、複数の⾮同期処理を待ち合わせる。 const promises =

    urls.map((url) => fetch(url)) // ෳ਺ͷAPIͳͲΛฒྻʹୟ͘ Promise.all(promises) .then((responses) => { // ͢΂ͯͷ Promise ͕੒ޭͨ͠৔߹͜͜ʹདྷΔ }) .catch(console.error) • Promise.all() • 全てが成功すれば、Resolve(全ての結果の配列)を得る。1 つでも失敗すると Reject になる。 • Promise.allSettled() • 全てが完了した後に Resolve する。基本的には Reject しない。ここの Promise の実⾏結果を配列で得る ことが出来る。 #hatenaintern)*)+
  25. async/await • async function の中であれば、await を⽤いて Promise を返す ⾮同期関数を同期関数のように扱えるIJ •

    エラーハンドリングする必要がある場合は try {} catch (e) {} を使⽤する !" 最近の ECMAScript の仕様に⼊っている Top-level await に対応している環境であれば async function の中で無くても await を利⽤できます #hatenaintern)*)+
  26. async/await const getUser = async (id) => { try {

    const res = await fetch(`https://api.example.com/user/${id}`) const json = await res.json() const user = json.user console.log(`${user.name}ͷid͸${user.id}`) return user } catch (e) { console.error(e) throw new Error("error reason: *********") } } #hatenaintern)*)+
  27. async/await • async function ⾃体も Promise を返す • Promise が返ってくる関数として、then

    を呼び出すことも出来る • つまり、呼び出す側では Promise が返ってくる関数であるという ことさえ分かっていれば、await で扱うことができる getUser(1) .then(console.log) // {id: 1, name: 'hatena'} .catch(console.error) #hatenaintern)*)+
  28. class 宣⾔ • ES$%&' で class キーワードが追加された&4 • 積極的に使うことはそんなに無いかもしれないけど、読むことは多いかもしれない class

    Rect { constructor(width, height) { this.width = width this.height = height } // getter΍setterΛఆٛͰ͖Δ get area() { return this.getArea() } getArea() { return this.width * this.height } } const rect = new Rect(5, 10) console.log(rect.area) // 50 !" それ以前では function を使って、コンストラクタ関数を作って、プロトタイプ継承を利⽤してクラスを作っていた。class も単なるシンタックスシュガーなので、実体はコンストラクタ関 数です。 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritanceandtheprototypechain #hatenaintern)*)+
  29. ECMAScript Modules • プログラムをモジュールという単位に分割する機能 • 1 ファイル == 1 モジュール

    • スコープはモジュールごと • 関数や変数などをお互いに import/export できる • 歴史的経緯で CommonJS_`という形式が Node.js などを中⼼に⻑く採⽤されてきたが、 ECMAScriptxyで標準化されたモジュールの仕組みが今では Node.js、ウェブブラウザなどでも広 くサポートされているx_ !" 実際にはサポートしていないウェブブラウザやパフォーマンスのために Babel を使って変換した後に、モジュールを解釈して 1 つ〜複数のファイルをまとめて配信するために webpack な どを利⽤することが多い。詳細は付録「JavaScript をウェブアプリケーションで提供する際に使⽤するツールチェインについて」を参照してください。 !" ECMAScript は JavaScript の仕様における名称。ECMAScript 仕様については付録「JavaScript やフロントエンド周辺技術の標準(化)について」で解説しています。 !" module.exports という特殊なオブジェクトを経由してエクスポートし、require 関数を利⽤してインポートする⽅式。https://nodejs.org/docs/latest/api/modules.html #hatenaintern)*)+
  30. default export // module.js export default function doSomething() {} •

    export default はモジュールから 1 つだけエクスポートできる • export default でエクスポートすると、import 時には任意の名 前を設定できる import awesome from "./module" #hatenaintern)*)+
  31. 名前付きエクスポート(named export) • 変数や関数に export を付加することで名前付きエクスポートが出来る • default export と異なり、いくつでも

    export できる // namedModule.js export const logType = { error: 'error', log: 'log' }; export const log = (message, type) => { if (type === 'error') return console.error(message); return console.log(message); } export const hello => log('hello'); #hatenaintern)*)+
  32. インポートの⽅法について • export されている名前で取り込む import { log, hello } from

    "./namedModule" • as でリネーム出来る import { logType as LOGTYPE } from "./namedModule" • * as で export されているもの全てをオブジェクトにする;; import * as Logger from "./namedModule" Logger.hello() // 'hello' !! 必要なものだけを取り込むことで受けられる恩恵(webpack による TreeShakingなど)も多いので、基本的には * as は避ける⽅のがオススメ。 #hatenaintern)*)+
  33. Nullish coalescing operator ?? • JavaScript では短絡評価のために || を⽤いる:;が、次のようなときに意図通りに動かない const

    rect = (width: number, height?: number) => ({ width: width, height: height || 100, // ߴ͞Λলུͨ͠ͱ͖͸σϑΥϧτ஋Λ༩͑Δ }) rect(50, 0) // {width: 50, height: 100} • 0 や ''(空⽂字)は Falsy な値12なので、短絡評価されてしまう結果、意図的に height に 0 を与えることが出来ない実装になっている。 • ?? は左辺が null または undefined のときだけ、右辺を返す演算⼦になっているので、このようなときは ?? を代わりに⽤いることで問題を 回避できる。 • 基本的には ?? を使っておくのがオススメ !" !!0 や !!'' のように boolean に変換した時に false となる値。JavaScript ではこの他に false と null と undefined と NaN などがある。Falsy でない値は boolean に変換した時に true とな る。こちらは Truthy な値と呼ぶ。 https://developer.mozilla.org/en-US/docs/Glossary/Falsy !" JavaScript では || は左辺が Truthy!6なら左辺の値を、左辺が Falsy なら右辺の値を返す。 && はその逆で左辺が Falsy なら左辺を、左辺が Truthy なら右辺を返すが、短絡評価に使われる ことは少ない。 #hatenaintern)*)+
  34. Optional chaining ?. • オブジェクトが null や undefined な可能性があるとき、そのオブジェクトのプロパティを呼ぶために if

    による分岐や && によ る短絡評価HIを書く必要があった。 const val = nullOrObject && nullOrObject.method() const result = val && val.methodOfReturnValue() • ?. を⽤いると、呼び出し元が null または undefined のときはそれ以降のプロパティアクセスをしない。また、その場合は undefined として評価される。 const result = nullOrObject?.method()?.methodOfReturnValue() nullOrObject も nullOrObject.method() の返り値が共に null でも undefined でもないときに、 nullOrObject.method().methodOfReturnValue() の結果を得られる。 !" && は左辺が Falsy なときは右辺を評価しないことを利⽤して、null や undefined のときは右辺を評価しないように出来る。また、&& による短絡評価は便利なものの || と同様に Falsy に関 わる問題を時々引き起こしてしまうことに注意。この問題も Optional chaining で同時に回避できる。 #hatenaintern)*)+
  35. Spread Syntax • ... を使うと配列やオブジェクトを展開出来る const sum = (a, b,

    c, d) => a + b + c + d const nums = [1, 2] const copied = [...nums] // த਎Λෳ੡ͨ͠഑ྻΛ࡞ΕΔ const moreNums = [...copied, 5] // [1, 2, 5] sum(...nums, 10) // 18 const obj = { a: 10, b: "foo" } const obj2 = { b: "bar", c: true } // 2ͭҎ্ͷobjectΛmerge͢ΔɻΩʔ͕ॏෳ͍ͯ͠Δ৔߹͸ޙʹॻ͍ͨํͰ্ॻ͖͞ΕΔ const merged = { ...obj, ...obj2 } // {a: 10, b: 'bar', c: true} const getUserConfig = (received) => ({ force: false, allowJs: false, ...received, // σϑΥϧτ஋Λ౉͞Εͨ஋͕͋Ε͹্ॻ͖͢Δ }) #hatenaintern)*)+
  36. Rest Parameters • 関数の引数も ... で受け取ることで可変な⻑さの引数を受け取れる • Spread Syntax と違って、1

    つだけ且つ引数の末尾でしか使えな い // const sum = (num1, num2, ...nums) => num1 + num2 + nums.reduce((a, b) => a + b, 0) const numbers = [1, 2, 3] sum(3, ...numbers, 6) // 15 #hatenaintern)*)+
  37. TypeScript is 何 JavaScript に型アノテーションなどを付けられるようにしたプログラミング⾔語で、多くの場合は JavaScript に変換してから JavaScript の処理系で実⾏する。 •

    node.js や React の登場などで、サーバサイドやフロントエンドを横断して JavaScript を使⽤したそこそ こ⼤きなウェブアプリケーションの開発が盛んに⾏われるようになった • そこそこの規模の⼈数でそこそこの規模の JavaScript のコードを安全に書くために型の情報を使いたい • 組み込みの API が何を返してくるのか。ライブラリがどういう返り値を返してくるのか。 • ランタイムで実⾏した際に初めてランタイムエラーが起きるというのは防ぎたい。 • Uncaught TypeError: Cannot read property 'foo' of undefined • そのために JavaScript に静的型付けを加えた Alternative JavaScript としての TypeScript が⽀持される ようになった #hatenaintern)*)+
  38. tsc: TypeScript compiler • マイクロソフトの TypeScript チームによって開発されている TypeScript を扱うためのツール •

    https://github.com/microsoft/TypeScript • TypeScript を JavaScript に変換(コンパイル)する^_機能とコンパイル時の静的型解析機能を提供している • --noEmit オプションを付けてると、静的型解析機能だけを⾛らせることができる • TypeScript から JavaScript への変換と⾔っても、型アノテーション等の削除がほとんど • JavaScript ランタイムが実⾏するときの振る舞いには影響を与えない^îと思っておいて良い • 多くのプロジェクトでは babel を使⽤していると思うので、@babel/preset-typescript を有効にした babel で変換を⾏う⽅が設定が複雑化しなかったりしてオススメ !" enum を使っているとコードが追加されるので影響を与える。enum は後述するオブジェクトと typeof などの組み合わせで代替可能なので、基本的には利⽤しないようにしていることが多 い。 !" JavaScript ランタイムが解釈できるように型情報を落として変換(コンパイル)する ≒ トランスパイラとしての役割 #hatenaintern)*)+
  39. 変数宣⾔時の型アノテーション // JavaScriptͷ৔߹ const a = 'hello'; // TypeScriptͷ৔߹͸ม਺໊ͱ=ͷؒʹ:Λஔ͍ͯܕΞϊςʔγϣϯΛॻ͘ const

    a: ʲ͜͜ʹܕΞϊςʔγϣϯʳ = 'hello'; // ྫ͑͹ const a: string = 'hello'; これくらいだったら推論してくれるので、実際には書かないこと も多い #hatenaintern)*)+
  40. 代表的な表現 ① • プリミティブ型: string, number, boolean, null, undefined •

    'hello', 12, true, false のような特定の値(リテラル型と呼びます)も使える const a: number = 10 const b: boolean = false const c: string = 11 // Type Error const d: "hello" = "hello" const n: null = null • 配列 const arr: string[] = ["hello", "world"] const arr2: Array<number> = [1, 2, 3, 5, 8] const arr3: [string, number] = ["year", 2021] // λϓϧ(Tuple)ܕͱ΋ #hatenaintern)*)+
  41. 代表的な表現 ② • オブジェクト • JavaScript のオブジェクト同様に書いて、値を書くところに型を書く • キー名に ?

    を付けるとオプショナルなキーになって、省略可能になる。ST const person: { name: string age: number address?: string } = { name: "john", age: 21, } !" string | undefined のような記述と同じと紹介されることもある。値が⼊っているかどうかで JavaScript として実⾏した際の振る舞いが変わることがある(Object.keys() など)ので、厳密 には同じではないことに注意。特に TypeScript ^.^ で導⼊された exactOptionalPropertyTypes を有効にすると、tsc での型解析時の振る舞いも変わります。 #hatenaintern)*)+
  42. Union Type (合併型) 複数の型のいずれかを満たす type Primitive = string | number

    | boolean | undefined | null type Color = "red" | "green" | "blue" | "yellow" | "purple" const a: Primitive = "hello" const b: Primitive = 10 const c: Color = "red" const d: Color = "black" // Type Error #hatenaintern)*)+
  43. typeof 演算⼦ • 値の型(ここでの"型"は JavaScript ランタイム上での型)を 'string', 'number', 'boolean', 'object',

    'function', 'undefined' から返す console.log(typeof "hello world") // 'string' const n = 10 console.log(typeof n) // 'number' const p: Person = { name: "jonh", age: 20 } console.log(typeof p) // 'object' console.log(typeof undefined) // 'undefined' console.log(typeof null) // 'object' console.log(typeof ["a"]) // 'object' console.log(Array.isArray(["a"])) // true #hatenaintern)*)+
  44. typeof 演算⼦の利⽤例 • 特に number や string であることに制限したいときに頻出 const func

    = (val: string | number) => { // Property 'toUpperCase' does not exist on type 'string | number'. // Property 'toUpperCase' does not exist on type 'number'. val.toUpperCase() if (typeof val === "string") { // ͜͜Ͱ͸val͸stringܕ return val.toUpperCase() } // ͜͜Ͱ͸val͸numberܕ return val.toFixed(2) } #hatenaintern)*)+
  45. in 演算⼦ 値に特定のプロパティが有ることを判定する演算⼦。最近はブラウザが特定の API に対応しているかどうかを識別することにも使われる。 type Fish = { name:

    string swim: () => void } type Bird = { name: string fly: () => void } const move = (x: Fish | Bird) => { if ("swim" in x) { // swim͕༗Δͷ͸FishܕͷΈ return x.swim() } // ͳͷͰɺ͜͜ʹ౸ୡ͢Δͱ͸BirdܕͰ͋Δͱ෼͔Δ return x.fly() } #hatenaintern)*)+
  46. Tagged Union Types で型ガードする • type や kind のようなプロパティにリテラル型を設定しておいて、その中⾝を ===

    や switch を使って⽐較することで、型の絞り込みを⾏う • 個⼈的な感覚だと、in での絞り込みよりもこちらのパターンの⽅がよく利⽤する気がします • リテラル型の⽐較の⽅が書くのが簡単で、かつこちらのほうがより安全です。 type Fish = { kind: "fish" name: string swim: () => void } type Bird = { kind: "bird" name: string fly: () => void } const move = (x: Fish | Bird) => { if (x.kind === "fish") { return x.swim() } return x.fly() } #hatenaintern)*)+
  47. as を⽤いた型アサーション(Type Assertion) • TypeScript によって推論された型を上書きしたいときに使う • 型キャストではない(ランタイム上での振る舞いがなんら変わ ることはない) •

    多くの場合は害になるので、本当に必要な場合だけ利⽤する • 例えば、古い JavaScript のコードを移植するなど #hatenaintern)*)+
  48. as を⽤いた型アサーション(Type Assertion) // ܕFooΛ௥Ճ͢Δ type Foo = { bar:

    number piyo: string } const something = (x: Foo) => { // ܕͱͯ͠͸߹͍ͬͯΔ͕ɺϥϯλΠϜ্Ͱͷ࣮ߦ࣌ʹ͸piyo͕undefinedʹͳΔͷͰΤϥʔ͕ى͖Δ return x.piyo.split(",") } const foo = {} as Foo // Fooͱͯ͠ѻ͑ΔΑ͏ʹ͓ͯ͘͠ // ࣮ࡍʹ͸barͳͲ͸ແ͍͕Fooͱͯ͠Ξαʔγϣϯ͍ͯ͠ΔͷͰ౉ͤΔ something(foo) #hatenaintern)*)+
  49. const アサーション as const とすることで変数代⼊時などに変更不可能としてアサーションしてくれる。 const a = [1, 2,

    3] // aͷܕ͸number[]ͱͳΔ const b = [1, 2, 3] as const // bͷܕ͸readonly [1, 2, 3]ͱͳΔ // readonly ͳ഑ྻʹ͸ push ΍ pop ͳͲͷมߋΛՃ͑Δϝιου͕ଘࡏ͠ͳ͍ a.push(4) // OK b.push(4) // NG type Pallet = { colors: Color } const setPallet = (p: Pallet) => { /* do something */ } const pallet = { color: "red", } // ͜͜ʹ as const Λ෇͚ͳ͍ͱ{ color: string }ͱਪ࿦͞ΕͯΤϥʔʹͳΔ setPallet(pallet) #hatenaintern)*)+
  50. unknown • any 同様に何らかの値が⼊っている型。 • any と違い、unknown はメソッドにアクセスしようとすると検証時にエラーになる。 • unknown

    ≒ {} | null | undefined • 利⽤時に型ガードをして型を絞り込む const getObjectValues = (obj: {}) => Object.values(obj) // ݺͼग़͠ଆ͸unknownͳͷͰ޷͖ͳ஋Λ౉ͤΔ const stringifyForLog = (val: unknown) => { if (val === null || val === undefined) return "(anonymous)" return JSON.stringify(getObjectValues(val)) } #hatenaintern)*)+
  51. type と interface • type はエイリアス • interface はオブジェクトに型を付ける別の⽅法。 •

    既存のものに後からプロパティを追加できるのは interface • ライブラリが提供した interface を利⽤者が⾃由に拡張できる • type は別の名前を付ける必要がある • type と interface の使い分けはコード規約などで⾊々な流派や主張があ るので、郷に⼊れば郷に従ってください #hatenaintern)*)+
  52. type と interface Type • Type は同じ型定義名での拡張ができない type Blog =

    { title: string content: string } // NG: Duplicate identifier 'Blog'. type Blog = { tags: string[] } // OK type NewBlog = Blog & { tags: string[] } Interface • Interface は拡張可能 • 例えば • ES5655 では、Array に at() メソッドが追加された • TypeScript I.K での型定義を確認すると、 実際に Array 型を拡張 している // lib.es5.d.ts interface Array<T> { length: number toString(): string ... } // lib.es2022.array.d.ts interface Array<T> { at(index: number): T | undefined } #hatenaintern)*)+
  53. 関数 既にサンプルでは何度も出てきているけど、引数や返り値の型の書き⽅のパターンたち紹介しておきます。 const f = (x: string): number => {

    return x.length } // ಛʹreturnΛ͠ͳ͍৔߹͸ฦΓ஋ʹvoidΛࢦఆ͢Δ const a: () => void = () => { console.log("a") } // ΦϓγϣφϧͳҾ਺͸keyʹ?Λ෇͚Δ // ਪ࿦͞ΕΔ΋ͷͰྑ͍ͳΒฦΓ஋ͷܕ͸লུՄ const b = (n?: number) => `${n}` // Rest ParametersΛड͚औΔ৔߹͸͜͏͍͏ײ͡ const c = (...texts: string[]) => { return texts.join("|") } #hatenaintern)*)+
  54. 型引数(Generics) • 関数の返り値の型に関する制約を外から与えて、関数内部で利⽤できる。 • よくよく型定義などを⾒ると定義されていて利⽤可能だけど、気付いていないということもよくある… • TypeScript で querySelector メソッドを使うときに型引数を指定する

    - Hatena Developer Blog const getJSON = <T>(url: string): Promise<T> => { // res.json͸anyͱͳΒͣʹܕҾ਺Ͱ౉͞Εͨ΋ͷͱղऍ͞ΕΔ return fetch(url).then<T>((res) => res.json()) } // ͜͜Ͱusers͸User[]ʹͳΔ const users = await getJSON<User[]>("/api/users") // ͜͜Ͱblogs͸Blog[]ʹͳΔ const blogs = await getJSON<Blog[]>("/api/blogs") #hatenaintern)*)+
  55. never • 絶対に発⽣‧到達しないことを表す型 • 例えば無限ループがある関数は絶対に完了しない関数なので返り値は never になる • void は代⼊可能MNだが、never

    には never のみ代⼊可能(つまり変数には基 本的に代⼊出来ずにエラーになる) const infiniteLoop = (): never => { while (true) {} } !" strictNullChecking が有効になっていない場合のみ代⼊可能。⼀⽅で JavaScript のランタイム上では undefined なので、代⼊することはほとんどないでしょう。 #hatenaintern)*)+
  56. never を⽤いた網羅性チェック • never を⽤いることで型ガードなどでの網羅性をチェックできる type Color = "red" |

    "yellow" | "purple" const magical = (color: Color) => { switch (color) { case "red": return "tomato" case "yellow": return "banana" default: // શͯͷcase͕໢ཏ͞Ε͍ͯΕ͹͜͜ʹ౸ୡ͢Δ͜ͱ͸ͳ͍ // ͜ͷ৔߹͸'purple'͕୅ೖ͞ΕΔՄೳੑ͕͋ΔͷͰɺ੩తղੳ࣌ʹΤϥʔʹͳͬͯݕग़Ͱ͖Δ const val: never = color throw new Error(`"${color}" is not implemented`) } } #hatenaintern)*)+
  57. Readonly / ReturnType • Readonly はオブジェクトや配列の要素を読み込み専⽤にできる • 再帰的には適⽤されないことに注意 type PersonRo

    = Readonly<Person> • ReturnType は関数の返り値型として得る type Func = () => string type ReturnVal = ReturnType<Func> // ReturnVal͸stringͱղऍ͞ΕΔ #hatenaintern)*)+
  58. keyof 演算⼦ オブジェクトの key を列挙して union type として得られる const config

    = { checkJs: false, force: true, } // typeofͰconfigͷܕΛಘͯɺkeyofͰͦͷΦϒδΣΫτͷܕͷΩʔΛྻڍ͢Δ type ConfigKey = keyof typeof config // "checkJs" | "force" const getConfigVal = (key: ConfigKey) => config[key] getConfigVal("ignore") // Τϥʔͱͯ͠ݕग़Ͱ͖Δ #hatenaintern)*)+
  59. Indexed Access Types JavaScript で配列やオブジェクトに [] を使って値を取り出すように型でも同 じようなことが出来る。typeof や keyof

    などと組み合わせることで JavaScript で定義した配列やオブジェクトの値から列挙した型などを⽣成で きる。 type Person = { name: string age: number } type Age = Person["age"] // number const signalColors = ["red", "blue", "yellow"] as const // as const͠ͳ͍ͱstring[]ʹͳΔ // Arrayʹ[number]ͰΞΫηε͢Δͱ഑ྻͷ஋Λྻڍͨ͠ܕΛಘΒΕΔ type Signal = typeof signalColors[number] // 'red' | 'blue' | 'yellow' #hatenaintern)*)+
  60. 交差型(Intersection Types) 複数の型を組み合わせて型を⽣成する。例えばユーザーの権限に 応じてレスポンスの型を変える場合はこういう感じ。 type Blog = { title: string;

    domain: string } type Editor = { editable: true; authority: ["edit"] } type Owner = { editable: true; authority: ["edit", "publish"] } type ResponseForEditor = Blog & Editor type ResponseForOwner = Blog & Owner #hatenaintern)*)+
  61. Conditional Types JavaScript の三項演算⼦と同じことが型表現の中でも出来る。 condition ? trueExpression : falseExpression //

    JavaScriptͷࡾ߲ԋࢉࢠ // SomeType͕OtherTypeΛຬͨͤ͹TrueTypeΛಘΔ SomeType extends OtherType ? TrueType : FalseType; Overloads を使わずに関数の引数に応じて返り値の型を変える declare function getID<T extends boolean>( fancy: T ): T extends true ? string : number const stringReturnValue = getID(true) const numberReturnValue = getID(false) const stringOrNumber = getID(Math.random() < 0.5) #hatenaintern)*)+
  62. Overloads • 関数が引数の数や型などに依って振る舞いが変わることを記述する⽅法 • 引数と返り値を複数宣⾔した後に実装を書く • 最後の実装は全ての Overloads を満たす必要がある function

    request(url: string): Promise<Response> function request(url: string, onlyIsOk: true): Promise<boolean> function request(url: string, onlyIsOk = false) { const promise = fetch(url) if (onlyIsOk) return promise.then((res) => res.ok) return promise } #hatenaintern)*)+
  63. Overloads と関数の型宣⾔ • 基本は短く書くのを使っておけば良いです type f = (x: number) =>

    number • 同様のことをこのようにも書ける type f = { (x: number): number } • Overloads を型宣⾔と共に書きたい場合は後者を使う type func = { (x: number): number (x: string): string } #hatenaintern)*)+
  64.    React (宣⾔的 UI)の利点 • UI 部分を宣⾔的に記述する⽅法のこと • ウェブアプリケーションのリッチ化や スマートフォンアプリの登場で、GUI

    を動的に構築‧変更する重要度と求め られる GUI の複雑性が格段に向上した という歴史がありました #hatenaintern)*)+
  65. 魂が震える&'仮想(Virtual) DOM • React の内部では実際の DOM23ではなく、DOM と対になる単純な構造体を持っている。これが仮想 DOMと呼ば れている。 •

    React では状態の変更を検知すると、変更前後の仮想 DOM の差分を計算し、その差分がある部分だけを実際の DOM に適応する。 • ブラウザのレンダリングコストを抑えつつも、開発者は UI をそのまま記述するだけで良いという仕組みを実 現している。 • リストなどで順番が⼊れ替わる際などは key という属性を与えることでその⼦要素が同じであることを⽰すこと が出来る https://ja.reactjs.org/docs/reconciliation.html !" Document Object Model の略。HTML や XML ⽂書の構造を操作するためのプログラミングインターフェイスのこと。 !" https://zenn.dev/mizchi/books/"c66c7!"f6cc96:c!;b< #hatenaintern)*)+
  66. JSX JSX は JavaScript の拡張構⽂ <h1 className="hello">My name is Clementine!</h1>

    • React DOM は HTML の属性名ではなく、キャメルケースの命名 規則を使⽤します。(aria-* や data-* 属性は例外です。) • class も className と記述されますが、これは class が JavaScript において予約語であるためです。 #hatenaintern)*)+
  67. クラスコンポーネントと関数コンポーネント React ではコンポーネントの書き⽅が 2 種類あります。 <HelloMessage name="hatena" /> クラスコンポーネント class

    HelloMessage extends React.Component { render() { return <div>Hello {this.props.name}</div> } } 関数コンポーネント const HelloMessage = ({ name }) => { return <div>Hello {name}</div> } #hatenaintern)*)+
  68. クラスコンポーネントと関数コンポーネント 関数コンポーネントを使⽤することが推奨されている。 クラスコンポーネント React アプリでのエラーを扱うのに Error Boundary という仕組みで使うこともある。 関数コンポーネント (Function

    Component) • React Component は関数を⽤いて表現すること で関数型プログラミングの概念を利⽤できるよ うにしている • UI = f(state) • Presenter/View などの層に分けた後に、それら を組み合わせた合成関数として React Component が表現出来る • Unit Test などが簡単になった #hatenaintern)*)+
  69. Function Component と TypeScript • TypeScript で JSX を記述する場合は拡張⼦を.tsxにする •

    型推論に任せると () => JSX.Elementになる。保守性などを考 えるとReact.FC を明⽰するのが良いOP type Props = { name: string } const Welcome: React.FC<Props> = ({ name }) => { return <h1>Welcome {name}</h1> } !" React Component として不正なundefinedを返すことなどを検出してくれます #hatenaintern)*)+
  70. Props と State • React Component には値を持つ⽅法が⼤きく 2 つある •

    関数の引数として受け取るPropsと内部状態を保持するState #hatenaintern)*)+
  71. Props を渡す/受け取る • 受け取る側は関数の第 1 引数でオブジェクトとして受け取る type Props = {

    name: string } const Welcome: React.FC<Props> = ({ name }) => { return <h1>Welcome {name}</h1> } • 渡す側(親側)は JSX で属性値として渡す。オブジェクトのキーと値がそ れぞれ属性名と値に対応する <Welcome name="John" /> // <h1>Welcome John</h1> #hatenaintern)*)+
  72. Function Component と Hooks • Hooks は React -..0(2019 年)から追加された機能。

    • それまでは関数コンポーネントは、SFC ( Stateless Functional Component ) と呼ばれていた。 • 関数コンポーネントに状態を持つことができるようになった。 #hatenaintern)*)+
  73. 状態(State)を扱う • useStateという特殊な関数 を使うことで状態を持つこ とが出来る • こういったuseから始まる関 数は React でフック

    (Hooks)と呼ばれる特別な 関数 const Counter = () => { const [count, setCount] = useState(0) const increaseCount = () => setCount((prevCount) => prevCount + 1) return ( <div> Χ΢ϯτ: {count} <button onClick={increaseCount}>Χ΢ϯτ</button> </div> ) } #hatenaintern)*)+
  74. Hooks の掟 Rules of Hooks ‒ React • 名前はuseから始める •

    トップレベルで呼ぶ • ifの中などで呼ばない • early return する前に必ず呼ぶ これらはeslint-plugin-react-hooksで検出してくれるように出来る #hatenaintern)*)+
  75. useState • useState()で初期値を与えることが出来、返り値のタプルの 1 つ⽬が 現在の値で、2 つ⽬が setter になっている •

    返り値がタプル(配列)なので、好きな名前をつけるのが簡単 • setter を呼ぶと内部状態が更新されたことが Component に通知さ れて、仮想 DOM の再⽣成、⽐較、レンダリングの更新が⾏われる • 推論できないものを型指定したい場合は型引数を⽤いることが出来る const [color, setColor] = useState<Color>("red") #hatenaintern)*)+
  76. useStateの setter について • 新しい値を渡す const [color, setColor] = useState<Color>("red")

    const change2Blue = () => setColor("blue") • 直前の値を利⽤して新しい値を決定する const [count, setCount] = useState(0) const increaseCount = () => setCount((prevCount) => prevCount + 1) #hatenaintern)*)+
  77. useEffect • React Component 内外部システムと同期を⾏うためのフック • 例えば • API からデータを取得して

    Component の状態を更新する • イベントハンドラを登録する • jQuery のコードを呼び出すなど #hatenaintern)*)+
  78. setInterval でタイマーと同期する const Timer = () => { const [timer,

    setTimer] = useState(0) setInterval(() => { setTimer((prevTimer) => prevTimer + 1) }, 1000) return <div>ܦա࣌ؒ: {timer}</div> } • コンポーネントがレンダリングされるたびにsetIntervalが呼ばれてしまう #hatenaintern)*)+
  79. useEffectの中でsetIntervalを呼ぶ const Timer = () => { const [timer, setTimer]

    = useState(0) useEffect(() => { setInterval(() => { setTimer((prevTimer) => prevTimer + 1) }, 1000) }, []) return <div>ܦա࣌ؒ: {timer}</div> } • useEffect の依存配列に空配列を渡すことで、Component のマウント時にのみ実⾏される ようになる #hatenaintern)*)+
  80. useEffectと依存配列 • 依存配列で副作⽤のタイミングをコントロールできる • 第 2 引数に依存を表す配列を渡すことができる • Component のマウント時

    • useEffect(೚ҙͷॲཧؔ਺, []) • 空の配列を渡すことで、マウント時に副作⽤を実⾏できる • ある state などが更新された際に副作⽤を呼び出すには? • useEffect(೚ҙͷॲཧؔ਺, [state]) • 副作⽤に関連する state の配列を渡すことで Component の状態に応じて副作⽤を実⾏できる #hatenaintern)*)+
  81. タイマーのカウント数を変更する例 const Timer = () => { const [timer, setTimer]

    = useState(0) const [count, setCount] = useState(1) useEffect(() => { setInterval(() => { setTimer((prevTimer) => prevTimer + count) }, 1000) }, []) return ( <> <button onClick={() => setCount(c => c + 1)}> Χ΢ϯτ਺Λ૿΍͢ {count} </button> <div>ܦա࣌ؒ: {timer}</div> </> ) } count を増やしても timer の増加数は変わらない。 #hatenaintern)*)+
  82. useEffectと依存配列 • 依存配列を設定することで、それぞれの依存配列内のいずれかの値が変更されたとき>?のみ副作⽤が呼び出されるように出来る useEffect(() => { const id = setInterval(()

    => { setTimer((prevTimer) => prevTimer + count) }, 1000) return () => { clearInterval(id) } - }, []) + }, [count]) • 依存配列は中⾝が空の配列[]を指定すると、Component のマウント時@Aに限定することが出来る !" Component に対応した DOM が挿⼊(初回描画)されることをマウント(mount)、その DOM が削除されることをアンマウント(unmount)と呼びます !" 変更されたことを検知する⽐較の詳細については付録「Hooks の依存の⽐較とオブジェクトの同⼀性について」を参照 #hatenaintern)*)+
  83. useEffect とクリーンナップ • useEffect は関数を返すことでその関数が次の副作⽤が呼び出される前:;に実⾏される • これを利⽤すると副作⽤のクリーンナップが出来る 前回の setInterval の例をクリーンナップを追加すると以下のようになる

    useEffect(() => { const id = setInterval(() => { setTimer((prevTimer) => prevTimer + count) }, 1000); return () => { // clearInterval ͰλΠϚʔΛఀࢭ͢Δؔ਺ clearInterval(id) } }, [count]) !" 空配列を指定している場合などはアンマウント時にも実⾏される #hatenaintern)*)+
  84. 独⾃フック(Custom Hook) • use から名前が始まり、内部で他の Hook を呼び出せる関数のこと • 複数の Hooks

    を組み合わせたり、Component の振る舞いを共通化して、1 つの関数に切り出すときなどに利⽤する const useGetStatus = ({ userId }) => { const [status, setStatus] = useState(null) useEffect(() => { const handler = (user) => { setStatus(user.status) } Api.subscribe(userId, handler) return () => Api.unsubscribe(userId, handler) }) return status } #hatenaintern)*)+
  85. 独⾃フックについての Tips • 独⾃フックに Component の役割を共通化することで切り出した機能のみをテスト可能になる • 独⾃フックで返すオブジェクトや関数などは useState や

    useMemo、useEffect、useCallback で 同⼀性を担保しておく • 特に関数についてはうっかり素朴に関数オブジェクトを返してしまいがちなので、気にしておく と吉ij • 独⾃フックを呼び出す側が同⼀性を保持するためにメモ化する(ことを検討する)必要が出てき てしまう • 結果、そのままでは利⽤できずに useEffect の依存配列に渡すこととなり、結果的に再利⽤性 も下がってしまう !" useCallback はとにかく使え! 特にカスタムフックでは - uhyo/blog #hatenaintern)*)+
  86. お疲れさまでした • TypeScript と React についてザッと概観についてお伝えしました • これだけで皆さんが書くのは難しいかもしれませんが、 TypeScript や

    React に挑戦するきっかけになってくれればと思っ ています • JavaScript やフロントエンドの世界は更に広がっています。ウェ ブブラウザを中⼼にあらゆるところでユーザーの⽬に直接触れる ことの多いフロントエンドの世界を楽しんでください。 #hatenaintern)*)+
  87. 付録 本編に⼊り切らなかった踏み込んだ補⾜や解説について書 いています。興味があれば読んでください的なコーナーで す。 ⽬次 • Arrow Function について •

    useState の state を更新する際の注意 • Hooks の依存の⽐較とオブジェクトの同⼀性について • useMemo や useCallback を⽤いたパフォーマンス改善 • React Component 内で DOM にアクセスする • JavaScript をウェブアプリケーションで提供する際に使 ⽤するツールチェインについて • JavaScript やフロントエンド周辺の主なツールについて • JavaScript やフロントエンド周辺技術の標準(化)につい て #hatenaintern)*)+
  88. Arrow Function と this • Arrow Function と function キーワードで⽣成した関数とでは

    主に this の扱いが⼤きく異なる • このことによって単純な置き換えが不可な場合がある #hatenaintern)*)+
  89. this のスコープの違いについて • function を⽤いた関数の場合 const person = { name:

    "chris", say: function () { setTimeout(function () { console.log(`I'm ${this.name}`) }, 100) }, } person.say() // I'm • 関数の中の this は呼び出し元のオブジェクトになる。この場合は window.setTimeout(window は省略できる) からの 呼び出しなので、 window になっている • https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/ setTimeout#thethisproblem #hatenaintern)*)+
  90. this のスコープの違いについて • Arrow Function だとスコープが外と同じになる const person = {

    name: "chris", say: function () { setTimeout(() => { console.log(`I'm ${this.name}`) }, 100) }, } person.say() // I'm chris #hatenaintern)*)+
  91. useState の state を更新する際の注意 新しい値を渡すときの落とし⽳ const [count, setCount] = useState(0)

    const increaseCount = () => setCount(count + 1) としてしまうと、 const increaseDouble = () => { increment() increment() } のようなものを作ったときに、increaseDouble を呼んでも 1 しか増えないという現象に遭遇するでしょ う。 #hatenaintern)*)+
  92. 関数内の変数スコープと useState 例えば、count が 5 であるとすると const Component = ()

    => { const [count, setCount] = useState(0) // ͜͜Ͱม਺count͸είʔϓ಺Ͱݻఆ͞ΕΔ(5) const increaseCount = () => setCount(count + 1) // ↑Ͱݻఆ͞Εͨcount + 1(=6)ΛsetCountʹ౉͢ const increaseDouble = () => { increment() // ↑Ͱ࡞ͬͨؔ਺ΛݺͿͱɺ6͕setCountʹ౉͞ΕΔ increment() // ͜ͷؔ਺ΛݺͿͱ͖΋ɺ6͕setCountʹ౉͞ΕΔͷͰɺ஋͸6ͷ·· } } #hatenaintern)*)+
  93. コールバック関数を渡す⽅法で書き直す 例えば、count が 5 であるとすると const Component = () =>

    { const [count, setCount] = useState(0) // ͜͜Ͱม਺count͸είʔϓ಺Ͱݻఆ͞ΕΔ(5) const increaseCount = () => setCount((prevCount) => prevCount + 1) // prevCount͸ݺͼग़࣌͠ͷ௚લͷ஋ const increaseDouble = () => { increment() // ͜͜ͰݺͿͱɺprevCountʹ͸5͕ೖ͍ͬͯΔͷͰɺprevCount+1=6͕setCount͞ΕΔ increment() // ͜͜ͰݺͿͱɺprevCountʹ͸6͕ೖ͍ͬͯΔͷͰɺprevCount+1=7͕setCount͞ΕΔ } } 更新後の state の値が更新前の値に依存している場合は、コールバッ ク関数を渡す形式を使うようにしましょう。 #hatenaintern)*)+
  94. Hooks の依存の⽐較とオブジェクトの同⼀性 について プリミティブな値のときは === で⽐較されるが、オブジェクトや配列は Object.is で⽐較され る。つまり、中⾝が同じオブジェクトなだけでは異なる値として認識されることに注意。 const

    a = "foo" const b = "foo" console.log(a === b) // true const objA = { a: "foo", b: 10 } const objB = { a: "foo", b: 10 } console.log(objA === objB) // true console.log(Object.is(objA, objB)) // false #hatenaintern)*)+
  95. 依存配列にオブジェクトを⼊れるケースについて const config = { theme: "sports" } useEffect(() =>

    { loadConfig(config).then(() => {}) }, [config]) のような場合にはレンダリングの度に、config が再⽣成される。よって、 異なる値として認識されてしまい、毎回副作⽤が呼び出されてしまう。こ のような例の場合は依存に [config.theme] という⾵に値を書いてしまっ ても良いが、依存するオブジェクトについて知っている必要があるので難 しい。 #hatenaintern)*)+
  96. シンプルな回避策 • ⼀番簡単な回避策は Component の外で初期化してしまえば良い const config = { theme:

    "sports" } function Component() { useEffect(() => { loadConfig(config).then(() => {}) }, [config]) } • ⼀⽅で、この世には Props を利⽤してオブジェクトを⽣成するなどもよ くあるので、この⽅法が使えることは少ない #hatenaintern)*)+
  97. オブジェクトの再⽣成を回避するために useMemo を利⽤ する • useMemo は React に組み込まれている Hooks

    の 1 つで、その名の通り、値をメモ化DEすることが出来ます 英⽂章を⼦要素に持ち+,、その⽂章中の単語数を数える Component を書いてみる function WordCount({ children }) { // text͸stringͳͷͰɺͦͷ··ґଘ഑ྻʹग़དྷΔ const words = useMemo(() => children.split(" "), [text]) useEffect(() => { alert(`୯ޠ਺͸${words.length}Ͱ͢`) }, [words]) } !" Component の中の⼦要素は children という名前で受け取ることが出来る。<WordCount>Lorem ipsum dolor sit amet</WordCount> のような形で呼び出す。 !" メモ化はパフォーマンス改善のために計算結果をキャッシュしたりすること #hatenaintern)*)+
  98. useMemo や useCallback を⽤いたパフォーマ ンス改善 • useMemo や useCallback は値や関数のメモ化によって

    useEffect などの不要な呼び出しを抑制することによってパフォーマン スの改善を期待できる • 特に useMemo では依存に使わない場合でも、処理の重い計算結果をメモ化することでレンダリング時のブロックを抑えること も出来る function Component ({a, b}) => { const resultA = useMemo(() => superDuperHighCostCalculation(a), [a]); const resultB = useMemo(() => superDuperHighCostCalculation(b), [b]); return <> <span>{a}͔ΒʮΊͪΌͪ͘Όॏ͍ܭࢉʯΛͨ݁͠Ռ: {resultA}</span> <span>{b}͔ΒʮΊͪΌͪ͘Όॏ͍ܭࢉʯΛͨ݁͠Ռ: {resultB}</span> </> } #hatenaintern)*)+
  99. React Component 内で DOM にアクセスす る • React に組み込まれている、参照を保持できる ref

    オブジェクトを作成する useRef を使う • ref オブジェクトは current プロパティに現在の値を持っている const textInput = useRef(null) const submit = (e) => { e.preventDefault() if (textInput.current.value.length < 100) return alert("101จࣈҎ্͕ඞཁͰ͢") createPost({ body: textInput.current.value }) } return ( <form onSubmit={submit}> <input type="text" ref={textInput} /> </form> ) #hatenaintern)*)+
  100. DOM へのアクセスを避ける⽅が良い • DOM に直接アクセスすると、React の制御外のところで値が取得されたり変更されたりすることにな る。 • ライブラリの都合などで本当に必要なときのみにしておくと良い const

    [text, setText] = useState("") const submit = (e) => { e.preventDefault() if (text < 100) return alert("101จࣈҎ্͕ඞཁͰ͢") createPost({ body: text }) } return ( <form onSubmit={submit}> <input type="text" onChange={(e) => setText(e.target.value)} /> </form> ) #hatenaintern)*)+
  101. TypeScript のコードがユーザーに届くまでの例 • TypeScript→JavaScript • tsc でも出来るが、babel で変換することも多い • JavaScript→

    対応ブラウザへの互換性がある JavaScript • API によってはサポートしているブラウザでそのままでは動かないことがあるので、babel を⽤いて、必要な polyfill などを組み⼊れる • TS→JS の変換も babel で⾏う場合は、それぞれ必要なプラグインを⼊れて実⾏ • 個別の JavaScript ファイル → 単⼀の JavaScript ファイル • webpack などで JavaScript のファイルを統合(bundle)する。ES Modules を全ての環境でサポートしていれば不要という説もあるが、1 つ のファイルになっている⽅がパフォーマンスなどの観点で有利なこともある • JavaScript ファイル → 圧縮された JavaScript(+sourcemap) • ユーザーの⼿元に届くときには、容量を減らしたりするために minify©™されたコードにすることが多い。 • そのままでは読むのが困難なので、開発者向けやエラートラッキングのために元の TS/JS との対応を記録したものが sourcemap !" 難読化とは異なり、変数名や関数名などを 1 ⽂字に置き換えるなどして容量の削減を図る。webpack に組み込まれている Terser では、TreeShaking という不要なコードの削除や不要な分 岐の削除なども⾏われる。 #hatenaintern)*)+
  102. webpack だと‧‧‧ • TypeScript から JavaScript までの変換と互換性のための操作 は babel(babel-loader 経由で利⽤)で⾏う

    • ファイルの結合などは webpack の主たる機能 • 容量削減は Terser(webpack-terser-plugin 経由で利⽤)で⾏う #hatenaintern)*)+
  103. create-react-app について • React を使ったプロジェクトの作成、関連ツールの導⼊‧実⾏を⾃動化してくれる • 基本的にはまずはこれを使っておけば OK • webpack,

    babel, eslint など諸々の設定の⽣成も⾃動で⾏われます • npm start でローカル開発環境の⽴ち上げ、npm test でユニットテストが実⾏できる • 詳細は内部で使われているreact-scriptsに集約されている • create-react-app で利⽤するツールは定期的に⾒直されたりしていて、常に最新の React コミ ュニティの知⾒が活かされている • create-react-app をウォッチすることでそういった最新の動向というものにも触れられるの で、React の周辺について知りたい⼈はこのリポジトリを覗いてみるのがオススメ #hatenaintern)*)+
  104. JavaScript やフロントエンド周辺技術の標準 (化)について 主にブラウザ上で動く JavaScript や DOM、HTML、CSS などの API はそれぞれ

    以下のような仕様でそれぞれ標準化されている • ECMAScript • WHATWG HTML Living Standard • WHATWG DOM Living Standard • W9C CSS Specifications #hatenaintern)*)+
  105. なぜ仕様を知るのか • その API の振る舞いについて正しい振る舞いを定義しているのは仕様である。 • 世の中に公開されている情報が数多くあるが、その情報の正しさを最終的に保 証するための情報源が仕様である。 • 例えば

    JavaScript や CSS などの振る舞いがウェブブラウザ間で異なる場合、 その時に正しさを保証してくれるのは仕様である。 • 実際、フロントエンドを中⼼に⾊んなことをしていると、仕様とブラウザの 実装に差異があることに遭遇して、報告することがあります~。 !" ウェブブラウザにバグ報告をするときにやること - ぱすたけ⽇記 #hatenaintern)*)+
  106. ECMAScript について • JavaScript の標準仕様は ECMAScript という名前で、Ecma InternationalCDの中の Technical Committee

    DH(TCDH)という技術委員会が中⼼に策定されている。 • 現在の仕様はhttps://github.com/tcDH/ecmacdcで管理されていて、 常に最新版が公開され 続けている。 • こういう常に最新版が公開されている形式を Living Standard と呼びます。 • 毎年 6 ⽉頃のミーティングで ECMAScript cÑcÖ のようなタグが打たれて、バージョン番号が 付与したものも公開されますが、その時点でのスナップショットであり、そのバージョン⾃ 体にはあまり意味はないですCC !! babel や tsc の設定で利⽤することもありますが、仕様としてはそんなに気にする必要はないです。このバージョンとは独⽴して、仕様の提案が進むタイミングでそれぞれの処理系におい て実装が⾏われて、利⽤可能になるからです。また、後述しますが polyfill などによって実⾏可能になることもあります。 !" Ecma International は C#などの標準化作業も⾏っている #hatenaintern)*)+
  107. ECMAScript の Stage • ECMAScript には⾃由に提案(proposal)を出すことが出来る • Github 上で proposal

    が公開され、issue や PR や TCQR のミーティング などでの議論を経て、Stage が上がったりする • 責任者であるチャンピオンのステージを進めたいという意思も必要 • 気になるプロポーザルがなかなか取り込まれない場合は、Issue やミーテ ィングノートを⾒るとヒントがあるかも • それらは次のようなステージを経て、Stage å になった後に ECMAScript の 仕様に取り込まれる。 #hatenaintern)*)+
  108. Stage の詳細 ステージ ステージの概要 ( アイデア 状態 / 機能提案 4

    機能の仕様書ドラフトを作成した状態 A 仕様としてはほぼ完成していて、ブラウザの実装やフィードバックを求め る段階 Y 仕様策定が完了し、2 つ以上の実装YbYcが存在している状態 ECMAScript · JavaScript Primer #jsprimer !" 主に Stage+ 以前時点での実装などはその後の仕様変更などの可能性もあるので、ブラウザの開発者向けフラグを有効にしているときだけ使えたり、prefix を付けた名前で API が提供され ることもある。それらも実装として許容されるなどの緩さはある。 !" 2 つ以上の JavaScript のランタイムに実装が存在しているということが求められる。主なランタイムは Google Chrome や Node.js で使⽤されている V_ や Safari で使⽤されている JavaScriptCore、Firefox で使⽤されている SpiderMonkey、Facebook が開発している Hermes などがある。 #hatenaintern)*)+
  109. ECMAScript の仕様と実装 • Stage ( になったり、ECMAScript に取り込まれていなくても、ウェブブラウザなどに実装されて使⽤出来るようにな ることがある • 逆に実装されていないと、仕様に取り込めない

    • その状態で積極的に使⽤することでプロポーザルにフィードバックをすることができる • また、実装がない状態でもコミュニティや core-js によってできるまた polyfill 実装が提供されたり、babel による トランスパイルなどでアプリケーション内で使⽤できるようになることがある • それらを実⽤することでもプロポーザルにフィードバックができたり、有⽤性を⽰すことが出来る • ECMAScript のプロポーザルは常に全てが Open で、それに関わるディスカッションも Open なので、興味のある提案 があったら、是⾮覗いてみて、使⽤し、ポジティブな点(úや問題点があれば是⾮フィードバックをすることで、未来の JavaScript 環境をよくすることに繋がります。 !" ECMAScript のプロポーザルは多くの場合、その提案が「どのようなモチベーションがあるのか」、「どのような問題を解決するのか」、「どのようなユースケースがあるのか」などが記 されているので、その有⽤性などを⽰すことも貢献に繋がります。 #hatenaintern)*)+
  110. ウェブブラウザ上の JavaScript と ECMAScript • ウェブブラウザ上で実⾏できる JavaScript は ECMAScript の仕様に含まれているものだけではない

    • 例えば location.assign はWHATWG HTML Living Standard、document.querySelector はWHATWG DOM Living Standardで定義されている • WHATWG という組織^_によって更新‧管理されている HTML/DOM それぞれの Living Standard な仕様によ って管理されている。^k • ECMAScript の仕様ではないので、HTML や DOM を扱わない環境(Node.js など)では実⾏できない !" 以前は W(C によって HTML2、HTML2.5 などが策定されていたが、399 年 5 ⽉に WHATWG 側に歩み寄ることを表明、2021 年 1 ⽉ 29 ⽇には WHATWG が公開している Living Standard を 正式に W(C の勧告とした。/参考:[https://future-architect.github.io/articles/vwv5wxv5a/ どうして HTML2 が廃⽌されたのか | フューチャー技術ブログ] !" 2004 年に Apple‧Mozilla‧Opera の 3 社によって設⽴された。その後、WEC とのコミュニケーションを続けながらもブラウザベンダーを取り込み、外堀を埋めながら HTML Living Standard を公開‧管理し続けていた #hatenaintern)*)+
  111. ウェブブラウザベンダと WHATWG 仕様 • 主に Google Chrome 陣営は HTML や

    DOM に関わる API>?の提案を数多く⾏っている • これらは提案者である Google Chrome には取り込まれるが、Apple や Moziila の 反対に会い、仕様としては取り込まれないことも多々ある • Chrome の仕様トラッカー: Chrome Platform Status • Mozilla の⽴場を表明しているウェブサイト: Mozilla Specification Positions • W}C 内の Web Incubator Community Groupというところで様々な提案が作成‧議 論されている !" https://scrapbox.io/pastak-pub/⾯⽩ WebAPI<"" 連発 で⾊々紹介しています #hatenaintern)*)+
  112. W"C CSS Specifications • いわゆる CSS) と呼ばれる CSS の現在‧未来の仕様は WHATWG

    ではなく引き続き W)C によって更新‧管理‧策定が ⾏われている • All CSS specifications #hatenaintern)*)+