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">
<h1><a href="https://example.com/">HTML文書</a></h1>
<p><em>HTML</em>で文書を書いています。</p>
</section>
</body>
</html>
HyperText Markup Language の略
<tag attribute="value">content</tag>
WHATWG という組織が HTML Living Standard と呼ばれる標準仕様を策定している
毎日のように変更・修正が行われるので “Living”
DOM, CSS, JavaScript についての仕様も含んでいる
W3C という組織も仕様を策定していた
HTML 4.01, HTML 5.2 などは W3C が発行した仕様
2019年5月に WHATWG と W3C の HTML が統合される ことが発表された
セマンティクス
HTML 要素の持つ「意味」
意味があるHTML要素
h1 は見出し
p は段落
em は強調
ol は順序付きリスト
etc.
意味と関係なく使える要素
div, span
意図的に意味を持たせていない
参考:
セマンティックなマークアップの利点
セマンティクスを活用してマークアップすると、コンピュータと人間双方にとって扱いやすくなる
アクセシビリティ
SEO
参考: https://developer.mozilla.org/en-US/docs/Glossary/Semantics#Semantics_in_HTML
コンテンツカテゴリ
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 を内包できる、などと決まっている
参考: https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Content_categories
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
親子・兄弟関係
参考: https://developer.mozilla.org/en-US/docs/Learn/CSS/Introduction_to_CSS/Selectors
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)
参考: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Values_and_Units
カスケード
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 の関係を調整
参考: https://developer.mozilla.org/en-US/docs/Web/CSS/box-sizing
レイアウト
様々なレイアウト方法
回り込み
float: left;, float: right;, clear: both;
テキストを回り込ませたい
例: https://hatenacorp.jp/information/message
Positioned
position: relative;, position: absolute;
top: 0px;, left: 0px;
直接位置を指定したい
Flexbox
display: flex;
いい感じに一列に並べたい
Grid
display: grid;
いい感じに格子状に並べたい
参考: https://developer.mozilla.org/en-US/docs/Web/CSS/Layout_mode
AltCSS
CSSにトランスパイル(変換)できる言語
LESS
Sass
PostCSS
SCSS
Sass の CSS 風記法
code:example.scss
$link-color: #0088ff;
a {
color: $link-color;
&:hover {
color: darken($link-color, 25%);
}
}
Indented Syntax という記法も存在する
Sass の元々の記法はこちらだが、SCSS 記法の方が人気
CSS フレームワーク
あらかじめ定義された UI のパターン集
レイアウト
フォームコンテンツの装飾
タブなどの標準 HTML に存在しない UI
”CSS“ Framework と呼ばれているが、オプションで JavaScript を使ってインタラクションを表現できるものもある
CSS フレームワークの例
Bootstrap
Semantic UI
Material Components for Web
Material Components for Web
Google が開発した Material Design という UI デザインガイドラインに沿ったフレームワーク
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;
}
}
const entry = new Entry("http://www.hatena.ne.jp", "はてな");
entry.title; // はてな
参考: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes
Destructuring Assignment
code:destructuring.js
const arr = 1, 2;
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
参考: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
Spread Syntax
code:spread-array.js
const arr = 'a' ;
arr.push('b');
const arr = 'a' ;
const arr2 = ...arr, 'b'; // 'a', 'b'
code:spread-object.js
const obj = { a: 'a' };
obj'b' = 'b';
const obj = { a: 'a' };
const obj2 = {
...obj,
b: 'b',
};
参考: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
Template Strings
code:template-strings.js
Template String Literal;
`Line 1
Line 2`;
const a = 1;
const b = 2;
Expression interpolation ${a + b};
参考: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
AltJS
JavaScript にトランスパイルできる言語
JavaScript よりシンプルに書ける・読みやすい言語を使いたい
まだブラウザに実装されていない仕様を利用したい
JavaScript にない機能を使いたい
AltJS の例
CoffeeScript
Babel
TypeScript
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 のプログラムを複数のファイルに分割したり、あるファイルから別のファイルを参照する仕組み
大規模なアプリケーションを複数のファイルを組み合わせて構築したい
ファイル間の依存関係を自動で解決したい
モジュール仕様の例
AMD
CommonJS
ES Modules
ES Modules
ES6 仕様に含まれるモジュールシステム
code:export.ts
export function foo() { return 0; };
code:import.ts
import { foo } from "export.js";
foo(); // 0
参考1: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export
参考2: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
パッケージマネージャ
ライブラリを管理するツール
ライブラリ名とバージョンを指定するだけでライブラリをダウンロードしてくれる
ライブラリ間の依存関係を解決してくれる
JavaScript のパッケージマネージャの例
NPM
Yarn
Yarn
Facebook が開発した JavaScript のパッケージマネージャ
code:sh
$ yarn add react
$ yarn add --dev @types/react
NPM よりも高速
モジュールバンドラー
モジュールを使ったソースコードを単一の JavaScript ファイルにバンドルするツール
ES Modules 仕様は比較的最近のブラウザには実装されているが、古いブラウザでは動作しない
複数のモジュールシステムで記述されたソースコードの相互運用
モジュールバンドラーの例
Browserify
Webpack
rollup.js
Webpack
JavaScript 以外もバンドルできるモジュールバンドラー
code:webpack.config.js
module.exports = {
mode: "development",
entry: "./src/index.ts",
output: {
filename: "index.js"
},
resolve: {
extensions: ".ts", ".js"
},
module: {
rules: [
{ test: /\.ts$/, loader: "ts-loader" }
]
}
};
Webpack Loaders
JavaScript 以外のものをモジュールとして読み込む仕組み
ts-loader: TypeScript
css-loader: CSS
sass-loader: Sass
file-loader: ファイル一般
JavaScript のテスト
テストフレームワークの例
Jest
Jasmine
etc.
Jest
JavaScript 用のテストフレームワーク
code:hello.test.ts
import { hello } from "./hello";
describe("hello", () => {
it("says Hello, world!", () => {
expect(hello()).toEqual("Hello, world!");
});
});
JavaScript のテストに必要な機能が一通り揃っている
テスト用の関数
カバレッジの取得
結果のリッチな表示
クラスや関数、オブジェクトなどのモック
参考1: https://jestjs.io/docs/en/api
参考2: https://jestjs.io/docs/en/expect
参考3: https://jestjs.io/docs/en/mock-function-api
休憩タイム
次が最後の山場です
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アプリ
バックグラウンド通信
端末へのインストール
参考: https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Introduction
フロントエンドフレームワーク
HTML, JavaScript を用いたアプリケーションを実装しやすくするライブラリ
React
Vue.js
Angular
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 を比較して、必要な枝葉のみを更新する
この処理を reconciliation と呼ぶ
コンポーネント
再利用可能な UI 要素
props と呼ばれる入力を受け取り、UI を出力する
props が変更されると UI が更新される
Class Based Component
Function Component
参考: https://reactjs.org/docs/components-and-props.html
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 を更新すると再描画される
参考: https://reactjs.org/docs/state-and-lifecycle.html
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 count, setCount = React.useState(0);
const onClick = () => {
setCount(count + 1);
};
return <button onClick={onClsick}>count: {count}</button>;
};
2019年2月にリリースされた React 16.8 で追加された
Function Component から状態を扱える
状態に関連したロジックを再利用しやすい
Class Based Component よりも複雑化しにくく、保守しやすい
参考: https://reactjs.org/docs/hooks-intro.html
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);
return <button {...props} className={theme, props.className.join(" ")} />;
};
React Component 間で状態をやり取りする方法は基本的には Props のみ
したがって、複数の異なるコンポーネントで同じ状態を共有したい場合、それらすべてのコンポーネントに Props を渡す必要がある
ネストが深い場合など、単純に Props を渡す方式では見通しが悪い場合、Context を利用するとスッキリしたコードを書ける
参考: https://reactjs.org/docs/context.html
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>
);
ブラウザのアドレスバーの書き換え
ブラウザの履歴へのページの追加
パスに応じたコンポーネントの切り替え
参考: https://reacttraining.com/react-router/web/guides/quick-start
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({
uri: 'http://localhost:8000/graphql',
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 があらかじめ用意されている
読み込み中やエラー時の表示制御が比較的簡単にできる
参考: https://www.apollographql.com/docs/react/
課題
インターンブログを Single-Page Application にしよう
JS / CSS の配信
React Router でページ遷移
4日目に作った GraphQL API を Apollo を使って呼び出す
機能
記事一覧
記事の投稿
STEP 1(必須)
ビルドされた JS / CSS を配信する
Echo の Static Middleware を使って JS / CSS をブラウザに送れるようにしよう
ヒント: go-Intern-Bookmark
React コンポーネントを表示する
http://localhost:8000/spa にアクセスしたときに、自分で App.tsx に書いた JSX が表示されるようにしよう
React Router でページを増やす
3日目: RDBMS・MySQL で作ったエンドポイントをSPAで実装する
ヒント: <BrowserRouter>, <Switch>, <Route>, <Link>
参考: https://reacttraining.com/react-router/web/guides/quick-start
STEP 2(必須)
Apollo で GraphQL API を呼び出す
記事一覧 (Query)
ヒント: useQuery
参考: https://www.apollographql.com/docs/react/essentials/queries/
記事の投稿 (Mutation)
ヒント: useMutation
参考: https://www.apollographql.com/docs/react/essentials/mutations/
STEP 3(任意)
好きな機能を追加してみる
記事詳細
記事削除
コメント表示
etc.
Material Components for React を使って UI を組み立てる
複雑なロジックにテストを追加する
npm から React コンポーネントを探してきて組み込む