5日目: リッチフロントエンド
講義を始める前に
質問があったらその場で聞いてください
他の受講者の理解にもつながります
声を出すのが難しければ、Slack でも OK
スライドモードで画面に写しながら話しますが、手元で資料を開きつつ聞くと理解しやすいと思います
リッチなウェブページを作ってみよう
HTML
構造
CSS
見栄え
JavaScript
動き
Single-Page Application
HTML, CSS, JavaScript を駆使したリッチなウェブページ
書いたことあるよ、という方 🙋♀️
HTML
HTMLとは
セマンティクス
コンテンツカテゴリ
HTML
Webページを記述する言語
code:example.html
<!doctype html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>HTML文書</title>
</head>
<body>
<section id="section1">
<p><em>HTML</em>で文書を書いています。</p>
</section>
</body>
</html>
HyperText Markup Language の略
<tag attribute="value">content</tag>
毎日のように変更・修正が行われるので “Living”
DOM, CSS, JavaScript についての仕様も含んでいる
セマンティクス
HTML 要素の持つ「意味」
意味があるHTML要素
h1 は見出し
p は段落
em は強調
ol は順序付きリスト
etc.
意味と関係なく使える要素
div, span
意図的に意味を持たせていない
セマンティックなマークアップの利点
セマンティクスを活用してマークアップすると、コンピュータと人間双方にとって扱いやすくなる
アクセシビリティ
SEO
コンテンツカテゴリ
HTML 要素の特徴を示す分類
すべての HTML 要素は1つ以上のコンテンツカテゴリに属する
Flow content
<a>, <article>, etc.
Heading content
<h1>, <h2>, etc.
Sectioning content
<article>, <nav>, etc.
Phrasing content
<br>, <em>, etc.
etc.
例えば h1 要素は phrasing content を内包できる、などと決まっている
CSS
CSS とは
CSS の機能
AltCSS
CSSフレームワーク
CSSとは
HTML の見栄えを表現する言語
code:example.css
* {
font-family: 'sans-serif';
}
h1 {
font-size: 2rem;
}
p em {
font-weight: bold;
}
code:style-element.html
<head>
…
<style>
p { color: blue; }
</style>
…
</head>
code:style-attr.html
<p style="font-size: 20px; color: red;">20pxで赤い文字が表示されます</p>
Cascading Style Sheet
selector { property: value; }
セレクタ
スタイルを適用する要素を特定する
単純セレクタ
p, #id, .class
属性セレクタ
[attr], [attr=value], [attr~=value]
擬似クラス
:hover, :visited, :first, :nth-child(X), :not(Y)
要素の特殊な状態
擬似要素
::before, ::after, ::first-line, ::first-letter, ::selection
要素の特殊な箇所
結合子
A, B, A B, A > B, A + B, A ~ B
親子・兄弟関係
CSS の値と単位
スタイルを表現する様々な値と単位
数値
0, 8px, 8mm, 8pt, 1em, 1rem, 1vh, 1vw, 90deg, 0.3s, 50%
色
#ffffff, rgba(255, 255, 255, 1.0), hsl(0, 100%, 100%)
関数
calc(100% - 16px)
カスケード
CSS では親要素に適用されたスタイルが子・孫要素にも適用される
code:cascading.html
Body
<div class="parent">
Parent
<div class="child">
Child
</div>
</div>
code:cascading.css
* {
color: red;
}
.parent {
background-color: black;
}
.child {
font-size: 18px;
}
.child にはどんなスタイルが適用される?
詳細度
セレクタの詳細度によってスタイルの適用順序が決まる
インラインスタイル × 1000
IDセレクタ × 100
クラスセレクタ・属性セレクタ・擬似クラス × 10
要素セレクタ・擬似要素 × 1
後に書かれたものの方が優先
!important はインラインスタイルよりも詳細度が高い
ボックスモデル
HTML要素のサイズはどうやって決まるのか
https://gyazo.com/92bdbdc3917d27e9687fe7531c9249eb
width, height
幅・高さ
border
枠線
padding, margin
余白
box-sizing
width, height と padding, border の関係を調整
レイアウト
様々なレイアウト方法
回り込み
float: left;, float: right;, clear: both;
テキストを回り込ませたい
Positioned
position: relative;, position: absolute;
top: 0px;, left: 0px;
直接位置を指定したい
Flexbox
display: flex;
いい感じに一列に並べたい
Grid
display: grid;
いい感じに格子状に並べたい
AltCSS
CSSにトランスパイル(変換)できる言語
SCSS
Sass の CSS 風記法
code:example.scss
a {
color: $link-color;
&:hover {
color: darken($link-color, 25%);
}
}
Sass の元々の記法はこちらだが、SCSS 記法の方が人気
CSS フレームワーク
あらかじめ定義された UI のパターン集
レイアウト
フォームコンテンツの装飾
タブなどの標準 HTML に存在しない UI
”CSS“ Framework と呼ばれているが、オプションで JavaScript を使ってインタラクションを表現できるものもある
CSS フレームワークの例
Material Components for Web
React Component としてラップされたバージョンが公開されている
https://gyazo.com/e9cbb169e47ebb6fd6c35fb259841911
休憩タイム
ちょっと一息つきましょう
JavaScript
JavaScript とは
ES2015 (ES6)
TypeScript
モジュール
テスト
JavaScript
ブラウザ上で動作するプログラミング言語
code:hello.js
const Hello = message => {
console.log("Hello, " + message);
};
Hello("world"); // Hello, world
初めはブラウザ用として普及したが、現在は Node.js などでサーバサイドでも利用されている JavaScript の歴史
1996 - JavaScript 1.0/JScript 1.0
2011 - ECMAScript 5.1
2015 - ECMAScript 2015 (ES 6)
2018 - ECMAScript 2018
ES 2015 (ES 6)
Classes
Spread Syntax
Destructuring Assignment
Template Strings
etc.
Classes
code:class.js
class Entry {
constructor(url, title) {
this.url = url;
this.title = title;
}
}
entry.title; // はてな
Destructuring Assignment
code:destructuring.js
const a, b = arr; // a === 1, b === 2 const obj = { x: 1, y: 2, z: 3 };
const { x, y, z } = obj; // x === 1, y === 2, z === 3
Spread Syntax
code:spread-array.js
arr.push('b');
code:spread-object.js
const obj = { a: 'a' };
const obj = { a: 'a' };
const obj2 = {
...obj,
b: 'b',
};
Template Strings
code:template-strings.js
Template String Literal;
`Line 1
Line 2`;
const a = 1;
const b = 2;
Expression interpolation ${a + b};
AltJS
JavaScript にトランスパイルできる言語
JavaScript よりシンプルに書ける・読みやすい言語を使いたい
まだブラウザに実装されていない仕様を利用したい
JavaScript にない機能を使いたい
AltJS の例
TypeScript
静的型付けができる JavaScript のスーパーセット
型を活用することで大規模なアプリケーションが保守しやすくなる
JavaScriptにトランスパイルすると型情報が消えるので、実行時の型チェックはできない
サードパーティのライブラリも、型定義ファイルがあればそれを参照する
TypeScript の型宣言
code:type-declarations.ts
const message: string = "Hello, world!";
const n: number = "123"; // コンパイルエラー
const add = (a: number, b: number): number => {
return a + b;
};
add(1, "2"); // コンパイルエラー
モジュール
JavaScript のプログラムを複数のファイルに分割したり、あるファイルから別のファイルを参照する仕組み
大規模なアプリケーションを複数のファイルを組み合わせて構築したい
ファイル間の依存関係を自動で解決したい
モジュール仕様の例
ES Modules
ES Modules
ES6 仕様に含まれるモジュールシステム
code:export.ts
export function foo() { return 0; };
code:import.ts
import { foo } from "export.js";
foo(); // 0
パッケージマネージャ
ライブラリを管理するツール
ライブラリ名とバージョンを指定するだけでライブラリをダウンロードしてくれる
ライブラリ間の依存関係を解決してくれる
JavaScript のパッケージマネージャの例
Yarn
Facebook が開発した JavaScript のパッケージマネージャ
code:sh
$ yarn add react
$ yarn add --dev @types/react
NPM よりも高速
モジュールバンドラー
モジュールを使ったソースコードを単一の JavaScript ファイルにバンドルするツール
ES Modules 仕様は比較的最近のブラウザには実装されているが、古いブラウザでは動作しない
複数のモジュールシステムで記述されたソースコードの相互運用
モジュールバンドラーの例
Webpack
JavaScript 以外もバンドルできるモジュールバンドラー
code:webpack.config.js
module.exports = {
mode: "development",
entry: "./src/index.ts",
output: {
filename: "index.js"
},
resolve: {
},
module: {
rules: [
{ test: /\.ts$/, loader: "ts-loader" }
]
}
};
Webpack Loaders
JavaScript 以外のものをモジュールとして読み込む仕組み
JavaScript のテスト
テストフレームワークの例
etc.
Jest
JavaScript 用のテストフレームワーク
code:hello.test.ts
import { hello } from "./hello";
describe("hello", () => {
it("says Hello, world!", () => {
expect(hello()).toEqual("Hello, world!");
});
});
JavaScript のテストに必要な機能が一通り揃っている
テスト用の関数
カバレッジの取得
結果のリッチな表示
クラスや関数、オブジェクトなどのモック
休憩タイム
次が最後の山場です
Single-Page Application
SPAとは
DOM
Ajax
React
Single-Page Application
初回のリクエスト以降、実際のページ遷移を伴わない JavaScript アプリケーションの実装パターン
DOM 操作 と XMLHttpRequest を組み合わせて構築
通常のページ遷移より高速化しやすい
必要最小限のデータの受信で済む
ページの一部のみを書き換えることで、高速に表示を更新できる
DOM
HTML を JavaScript から操作する API
code:dom.ts
const button = document.getElementById<HTMLButtonElement>("increment-button");
const outputDiv = document.getElementById<HTMLDivElement>("output-div");
let count = 0;
button.addEventListener('click', () => {
count++;
outputDiv.textContent = count;
});
Ajax
XMLHttpRequest を用いて非同期に HTTP 通信を実行し、その内容を DOM を用いて HTML に反映する
Asynchronous Javascript + XML
Google Maps などから普及した
Progressive Web Apps
ネイティブアプリレベルの機能を持たせたWebアプリ
バックグラウンド通信
端末へのインストール
フロントエンドフレームワーク
HTML, JavaScript を用いたアプリケーションを実装しやすくするライブラリ
React
Facebook が開発した UI 構築用ライブラリ
宣言的
UI はアプリケーションの状態の写像
コンポーネント志向
状態を持った再利用可能なコンポーネントを独自に定義できる
HTML要素を新たに定義するイメージ
JSX
JavaScript の文法を拡張して、コード中に HTML のような UI 定義を宣言できるようにした
code:hello.tsx
interface HelloProps {
message: string;
}
const Hello: React.FC<HelloProps> = ({ message }) => (
<div>
Hello, {message}!
</div>
);
<Hello message="world" />
TypeScript 版は TSX と呼ぶ
Virtual DOM
実際の DOM とは別に UI の変更を検出するためのデータ構造を用意するプログラミングパターン
実際の DOM の操作は深刻なパフォーマンスの劣化を引き起こす可能性があるので、最小限にしたい
仮想 DOM と実際の DOM を比較して、必要な枝葉のみを更新する
コンポーネント
再利用可能な UI 要素
props と呼ばれる入力を受け取り、UI を出力する
props が変更されると UI が更新される
Class Based Component
Function Component
Class Based Component
JavaScript の class で定義するコンポーネント
code:class-based-component.tsx
interface CounterState {
count: number;
}
class Counter extends React.PureComponent<{}, CounterState> {
state = {
count: 0
};
onClick = () => {
this.setState(({ count }) => ({
count: count + 1
}));
};
render() {
return <button onClick={this.onClick}>count: {this.state.count}</button>;
}
}
コンストラクタの引数が props
render() で UI を返す
コンポーネント内部に状態 (state) を持てる
setState メソッドで state を更新すると再描画される
Function Component
関数で定義するコンポーネント
code:function-component.tsx
interface TimestampProps {
timestamp: Date;
}
const Timestamp: React.FC<TimestampProps> = ({ timestamp }) => (
<time dateTime={timestamp.toISOString()}>
{new Intl.DateTimeFormat().format(timestamp)}
</time>
);
コンポーネント内部に状態を持たない
引数として props を受け取り、UI の定義を返す
最適化しやすい
React 自体のパフォーマンス改善の恩恵を受けやすい
Hooks
状態をコンポーネントとは別に保持する仕組み
code:hooks.tsx
const HooksBasedCounter: React.FC = () => {
const onClick = () => {
setCount(count + 1);
};
return <button onClick={onClsick}>count: {count}</button>;
};
2019年2月にリリースされた React 16.8 で追加された
Function Component から状態を扱える
状態に関連したロジックを再利用しやすい
Class Based Component よりも複雑化しにくく、保守しやすい
Context
離れたコンポーネント間で状態を共有する仕組み
code:context.tsx
enum Theme {
LIGHT = "light",
DARK = "dark"
}
const ThemeContext = React.createContext(Theme.LIGHT);
const App: React.FC = () => (
<ThemeContext.Provider value={Theme.DARK}>
<ThemedButton>Button</ThemedButton>
</ThemeContext.Provider>
);
const ThemedButton: React.FC<
React.ButtonHTMLAttributes<HTMLButtonElement>
= props => {
const theme = React.useContext(ThemeContext);
};
React Component 間で状態をやり取りする方法は基本的には Props のみ
したがって、複数の異なるコンポーネントで同じ状態を共有したい場合、それらすべてのコンポーネントに Props を渡す必要がある
ネストが深い場合など、単純に Props を渡す方式では見通しが悪い場合、Context を利用するとスッキリしたコードを書ける
React Router
React Component で Routing を定義できるライブラリ
code:react-router.tsx
import React from "react";
import { BrowserRouter, Route, Switch } from "react-router-dom";
import { Entry } from "./Entry";
import { Index } from "./Index";
export const App: React.FC = () => (
<BrowserRouter>
<Switch>
<Route exact path="/" component={Index} />
<Route exact path="/:id" component={Entry} />
</Switch>
</BrowserRouter>
);
ブラウザのアドレスバーの書き換え
ブラウザの履歴へのページの追加
パスに応じたコンポーネントの切り替え
React Apollo
Apollo という GraphQL API のクライアントライブラリを React と統合したライブラリ
code:react-apollo-app.tsx
import React from "react";
import { ApolloProvider } from "react-apollo";
import { ApolloClient } from "apollo-client";
import { InMemoryCache } from "apollo-cache-inmemory";
import { HttpLink } from "apollo-link-http";
const client = new ApolloClient({
cache: new InMemoryCache(),
link: new HttpLink({
credentials: 'same-origin',
headers: {
"X-Requested-With": "apollo"
}
})
});
export const App: React.FC = () => (
<ApolloProvider client={client}>
...
</ApolloProvider>
);
code:react-apollo-index.tsx
import React from "react";
import gql from "graphql-tag";
import { useQuery } from "react-apollo";
import { GetEntries } from "../graphqlTypes";
import { EntryList } from "./EntryList";
const query = gql`
query GetEntries {
listEntries {
id
title
url
}
}
`;
export const Index: React.FC = () => {
const { loading, error, data } = useQuery<GetEntries>(query);
return (
<>
<h2>Entries</h2>
{loading && <p>Loading…</p>}
{error && <p>{error.message}</p>}
{!loading && !error && data && <EntryList entries={data.listEntries} />}
</>
);
};
GraphQL API を使うための Provider コンポーネントや Hooks があらかじめ用意されている
読み込み中やエラー時の表示制御が比較的簡単にできる
課題
インターンブログを Single-Page Application にしよう
JS / CSS の配信
React Router でページ遷移
4日目に作った GraphQL API を Apollo を使って呼び出す
機能
記事一覧
記事の投稿
STEP 1(必須)
ビルドされた JS / CSS を配信する
ヒント: go-Intern-Bookmark
React コンポーネントを表示する
React Router でページを増やす
ヒント: <BrowserRouter>, <Switch>, <Route>, <Link>
STEP 2(必須)
Apollo で GraphQL API を呼び出す
記事一覧 (Query)
ヒント: useQuery
記事の投稿 (Mutation)
ヒント: useMutation
STEP 3(任意)
好きな機能を追加してみる
記事詳細
記事削除
コメント表示
etc.
Material Components for React を使って UI を組み立てる
複雑なロジックにテストを追加する
npm から React コンポーネントを探してきて組み込む