坂本研のゼミ室

イテレータとジェネレータ

ES2015から新たに導入された、イテレータ(iterator)とジェネレータ(generator)という概念についてまとめました。

イテレータ

イテレータ(反復子)は繰り返しのための機構で、以下の2つの種類のオブジェクトがあります。

この2つは相反するものでは無く、反復可能かつ反復子でもあるオブジェクトもあります。

反復可能なオブジェクト (iterable object)

  • Symbol.iteratorと呼ばれるプロパティを含んでいるオブジェクト、このプロパティの値はイテレータを返す関数

  • for...ofを使って各要素について処理できる (配列、文字列、Map、Set)

反復子オブジェクト(iterator object)

  • nextと呼ばれるメソッドを実装しているオブジェクト。nextメソッドはvaluedoneというプロパティをもつオブジェクトを返す

  • 通常はイテレータオブジェクトの意味でイテレータという

配列についてみていく

配列は反復可能なオブジェクト (iterable object)です。

  • 配列が反復可能なオブジェクトの例
const foodstuffs = [
    'egg',
    'milk',
    'sugar'
]

for (let afood of foodstuffs) {
    console.log(afood)
}

/*  実行結果を表示
egg
milk
sugar
*/

しかし、配列はイテレータ(反復子オブジェクト)ではありません。

イテレータは上記の通りnextというメソッドを呼び出す事で要素を取り出せるオブジェクトです。

先ほどの配列にnextを使うと配列はイテレータ(反復子オブジェクト)では無いのでエラーになる

foodstuffs.next()

/* 実行結果を表示
TypeError: foodstuffs.next is not a function
*/

しかし、valuesというメソッドを使う事で配列をイテレータ(反復子オブジェクト)に変換する事ができる

// itという変数にイテレータを代入している
const it = foodstuffs.values()

これによってイテレータを作成することができたのでnextメソッドを使って要素を取り出すことができる

nextを呼び出すとvaluedoneの2つのプロパティをもつオブジェクトが返される

  • value : 処理を行うために使う値

  • done : 全ての要素を返したかどうかを示す真偽値

作成したitnextを使ってみる

const foodstuffs = [
    'egg',
    'milk',
    'sugar'
]

const it = foodstuffs.values()

console.log(it.next())
console.log(it.next())
console.log(it.next())
console.log(it.next())

/* 実行結果を表示
{ value: 'egg', done: false }
{ value: 'milk', done: false }
{ value: 'sugar', done: false }
// 供給するデータがなくなるとvalueはundefinedに、doneはtrueになる 
{ value: undefined, done: true }
*/

イテレータは前の状態を記憶して、nextメソッドが呼ばれるたびに次のデータを供給している

イテレータプロトコル

イテレータ(反復子オブジェクト)であるためには、イテレータプロトコルを実装する必要があり、そのために、Symbol.iteratorを実装します。

これは、イテレータの振る舞いをするオブジェクトを戻すものです。つまり、nextメソッドを呼ぶとvaluedoneをもつオブジェクトを返すようなメソッドを実装するということです。

詳しくはこちらを参考にしてください

class Food {
    constructor() {
        this.foodstuffs = []
    }

    add(foodstuffs) {
        const now = Date.new()
        console.log(`食材追加: ${foodstuffs}(${now})`)
        this.foodstuffs.push({ foodstuffs, timestamp: now})
    }
    [Symbol.iterator]() {
        return this.foodstuffs.values()
    }
}
  • nextが呼ばれた時にvaluedoneをプロパティにもつオブジェクトを返すオブジェクト実装する方法
[Symbol.iterator]() {
    let i = 0;
    const foodstuffs = this.foodstuffs
    return (
        next: () => i => foodstuffs.length ? {value: undefined, done: true} : {value: foodstuffs[i++], done: false}
    )
}

つまり、最初の例のfoodstuffsは配列で、配列は反復可能なオブジェクト (iterable object)なので、[Symbol.iterator]と持っています。

メソッド[Symbol.iterator]は実行するとイテレータを返すので、配列は反復可能なオブジェクトをもとにイテレータを作成するのは簡単です。

配列のメソッド[Symbol.iterator]を実行して返ってくる値を使えば良いです。

  • 上の例をこのように書くこともできる
[Symbol.iterator]() {
    return this.foodstuffs[Symbol.iterator]()
}

ジェネレータ

ジェネレータは関数の一種と考えることができますが、ジェネレータを呼び出しても関数のようにすぐには実行されず、まずはイテレータが返されます。その後、イテレータのメソッドnextを呼び出すたびに実行が進んでいきます。

ジェネレータを定義する場合functionの後に*をつけます。また、呼び出し元に値を提供するためにはyieldを使います(returnも使われますが通常は値を返すためには使われません)。それ以外は普通の関数と同じ構文です。

ちなみにジェネレータはアロー関数のように記述はできないそうです。

  • ジェネレータの例
function* weeks() {
    yield '日'
    yield '月'
    yield '火'
    yield '水'
    yield '木'
    yield '金'
    yield '土'
}

上で作成したジェネレータは以下のように呼び出すことができます。

ジェネレータを呼び出すとイテレータが返ってくるので、そのイテレータnextメソッドを使って順に処理していきます。

const it = weeks()
console.log(it.next())
console.log(it.next())
console.log(it.next())
console.log(it.next())
console.log(it.next())
console.log(it.next())
console.log(it.next())
console.log(it.next())

/* 実行結果の表示
{ value: '日', done: false }
{ value: '月', done: false }
{ value: '火', done: false }
{ value: '水', done: false }
{ value: '木', done: false }
{ value: '金', done: false }
{ value: '土', done: false }
{ value: undefined, done: true }
*/

return

ジェネレータのどこかでreturnを呼び出すとdonetrueになり、valueプロパティはreturnで指定した値になります。

ジェネレータから有用な値を提供する場合yieldを使うべきです。

yield式と双方向コミュニケーション

ジェネレータを使うと呼び出し側との間で双方向の会話が可能になります。そのためにyieldを使います。

yieldは式なので、評価の結果何かしらの値を持ちます。どんな値かというとnext呼び出し時の引数の値です。

ジェネレータは遅延評価を可能にしています。

  • 双方向の会話ができるジェネレータの例
function* conversation() {
    const name = yield '名前は?'
    const food = yield '好きな食べ物は?'
    return `${name}さんの好きな食べ物は${food}だそうです!`
}

const it = conversation()
console.log(it.next())
console.log(it.next('たろう'))
console.log(it.next('オムライス'))
console.log(it.next())

/* 実行結果の表示
{ value: '名前は?', done: false }
{ value: '好きな食べ物は?', done: false }
{ value: 'たろうさんの好きな食べ物はオムライスだそうです!', done: true }
{ value: undefined, done: true }
*/

参考資料

O'Reilly Japan - 初めてのJavaScript 第3版

Symbol.iterator - JavaScript | MDN

ES2015のIteratorとGeneratorについて - mikami's blog

正規表現確認ノート (JavaScript)

  • 正規表現は下記のようにスラッシュで囲まれたパターンからなる
let pattern = /正規表現/;

match()

文字列中で一致するものを検索.検索結果の配列を返す.

match()を使った正規表現パターンの記述

let result = 文字列.match(正規表現)
let pattern = /ABC/;
let string = 'abcABC';

let result = string.match(pattern);
console.log(result);
// [ 'ABC', index: 3, input: 'abcABC', groups: undefined ]

文字列中で一致するものがあるか確認.マッチした場所の文字位置(インデックス)を返す.マッチしない場合は-1を返す

search()を使った正規表現パターンの記述

let result = 文字列.search(正規表現)
let pattern = /ABC/;
let string = 'abcABC';

let result = string.search(pattern);
console.log(result);
// 3

正規表現パターン

単純な文字列にマッチするか

/ABC/

単純にABCという文字列にマッチするか

  • マッチする
let pattern = /ABC/;
let string = 'abcABC';
  • マッチしない
let pattern = /ABC/;
let string = 'abc';

どれか1文字マッチするか

[ABC]

ABCのどれか1文字にマッチするか - マッチする

let pattern = /[ABC]/;
let string = 'aAbBcCdD';
  • マッチしない
let pattern = /[ABC]/;
let string = 'abcd';

指定文字以外のどれか1文字にマッチするか

[^ABC]

ABC以外のどれか1文字にマッチするか

  • マッチする
let pattern = /[^ABC]/;
let string = 'AaBbCc';
  • マッチしない
let pattern = /[^ABC]/;
let string = 'DdEeFf';

選択範囲のどれか1文字にマッチするか

[A-C]

AからCまでのどれか1文字にマッチするか

  • マッチする
let pattern = /[A-C]/;
let string = 'abcABC';
  • マッチしない
let pattern = /[A-C]/;
let string = 'abcefg';

エスケープ

\

\の次に書く文字列をエスケープする 例えば,下記のように/(スラッシュ)を見つけたい時などにエスケープする

//エスケープしないとこうなる
let pattern = ///;

//エスケープあり
let pattern = /\//;
let string = 'ABC/abc';

let result = string.search(pattern);
console.log(result);
// 3

文字列の先頭にマッチするか

^A

文字列(行)の先頭にマッチするか

  • マッチする
// Aが先頭にあるかどうか
let pattern = /^A/;
let string = 'ABB';

let result = string.search(pattern);
console.log(result);
// 0
  • マッチしない
// Aが先頭にあるかどうか
let pattern = /^A/;
let string = 'BAB';

let result = string.search(pattern);
console.log(result);
// -1

文字列の末尾にマッチするか

A$

文字列(行)の末尾にマッチするか

下記にマッチする時と,しない時の例を示す

  • マッチする
// Aが末尾にあるかどうか
let pattern = /A$/;
let string = 'BABA';

let result = string.search(pattern);
console.log(result);
// 3
  • マッチしない
// Aが末尾にあるかどうか
let pattern = /A$/;
let string = 'BAB';

let result = string.search(pattern);
console.log(result);
// -1

直前文字列の0回以上の繰り返しにマッチするか

hu*

直前の文字列huの0回以上の繰り返しにマッチするか

  • マッチする
// huが0回以上繰り返されているかどうか
let pattern = /hu*/;
let string = 'hhhhhuuu';

let result = string.search(pattern);
console.log(result);
// 0
// huが0回以上繰り返されているかどうか
let pattern = /hu*/;
let string = 'uuuh';

let result = string.search(pattern);
console.log(result);
// 3
  • マッチしない
// huが0回以上繰り返されているかどうか
let pattern = /hu*/;
let string = 'uuuu';

let result = string.search(pattern);
console.log(result);
// -1

直前の文字列の1回以上の繰り返しにマッチするか

a+

直前の文字列aの1回以上の繰り返しにマッチするか

  • マッチする
// aが1回以上繰り返されているかどうか
let pattern = /a+/;
let string = 'banana';

let result = string.search(pattern);
console.log(result);
// 1
  • マッチしない
// aが1回以上繰り返されているかどうか
let pattern = /a+/;
let string = 'lemon';

let result = string.search(pattern);
console.log(result);
// -1

直前の文字列の0回か1回の出現にマッチするか

a?

直前の文字列aの0回か1回の出現にマッチするか

  • マッチする
// huが0回か1回の出現しているかどうか
let pattern = /hu?/;
let string = 'huuuuu';

let result = string.search(pattern);
console.log(result);
// 0
  • マッチしない
// huが0回か1回の出現しているかどうか
let pattern = /hu?/;
let string = 'nnnnnn';

let result = string.search(pattern);
console.log(result);
// -1

任意の一文字にマッチするか

h.t

h.tの.は任意の一文字にマッチするか

  • マッチする
// h◯tに一致するかどうか
let pattern = /h.t/;
let string = 'hat';

let result = string.search(pattern);
console.log(result);
// 0
  • マッチしない
// h◯tに一致するかどうか
let pattern = /h.t/;
let string = 'heat';

let result = string.search(pattern);
console.log(result);
// -1

直前の文字をn回表示とマッチするか

{n}

直前の文字をn回表示とマッチするか

  • マッチする
// [0-9]{3}は3桁の数字
let pattern = /[0-9]{3}/;
let string = '123';
  • マッチしない
// [0-9]{3}は3桁の数字
let pattern = /[0-9]{3}/;
let string = 'abc';

直前の文字をn回以上表示とマッチするか

{n,}

直前の文字をn回以上表示とマッチするか

  • マッチする
// [0-9]{3,}は3桁以上の数字
let pattern = /[0-9]{3,}/;
let string = '1234';
  • マッチしない
// [0-9]{3,}は3桁以上の数字
let pattern = /[0-9]{3,}/;
let string = '12';

直前の文字をm~n回表示とマッチするか

{m,n}

直前の文字をm~n回表示とマッチするか

  • マッチする
// [0-9]{3~5}は3~5桁の数字
let pattern = /[0-9]{3,5}/;
let string = '1234';
  • マッチしない
// [0-9]{3~5}は3~5桁の数字
let pattern = /[0-9]{3,5}/;
let string = '12abc';

アンダースコアと英数字にマッチするか

\w

アンダースコアと英数字にマッチするか.[A-Za-z0-9_] と同じ

  • マッチする
// アンダースコアと英数字にマッチするか
let pattern = /\w/;
let string = '12a_Abc';
  • マッチしない
// アンダースコアと英数字にマッチするか
let pattern = /\w/;
let string = '$';

文字以外にマッチするか

\W

文字以外にマッチするか.[^\w]と同じ[^A-Za-z0-9_]

  • マッチする
// 文字以外にマッチするか
let pattern = /\W/;
let string = '&%#';
  • マッチしない
let pattern = /\W/;
let string = '12a_Abc';

数字にマッチするか

\d

数字にマッチするか.[0-9] と同じ

  • マッチする
// 数字にマッチするか
let pattern = /\d/;
let string = '3';
// 3桁の数字か
let pattern = /\d{3}/;
let string = '123';
  • マッチしない
// 数字にマッチするか
let pattern = /\d/;
let string = 'abc';

数字以外にマッチするか

\D

数字にマッチするか.[0-9] と同じ

  • マッチする
// 数字以外にマッチするか
let pattern = /\D/;
let string = 'abc';
  • マッチしない
// 数字以外にマッチするか
let pattern = /\D/;
let string = '3';

主なオプションフラグ

  • オプションありの正規表現は下記のようなパターンからなる
let pattern = /正規表現/オプション;

グローバルリサーチ

g

グローバルリサーチ.文字列全体に対してマッチするか (指定なしの場合は1度マッチングした時点で処理を終了).

let pattern = /[A-D]/g;
let string = 'ABCDEFGHI';

大文字と小文字を区別しない

i

大文字と小文字を区別しない (指定なしの場合は区別する).

  • マッチする
let pattern = /[a-d]/i;
let string = 'ABCDEFGHI';
  • マッチしない
let pattern = /[a-d]/;
let string = 'ABCDEFGHI';

Unicode対応

u

Unicode対応 (漢字などを認識してくれるようになる).

  • マッチする
let pattern = /[赤青]色/u;
let string = '赤色';
  • マッチしない
let pattern = /[赤青]色/u;
let string = '黄色';

参考資料

正規表現一覧

正規表現 - JavaScript | MDN

【JavaScript入門】4つのパターンで理解する正規表現の使い方まとめ! | 侍エンジニア塾ブログ(Samurai Blog) - プログラミング入門者向けサイト

JavaScript 正規表現まとめ - Qiita

【JavaScript】正規表現で文字列を検索する – 株式会社ライトコード

PHP正規表現 - 坂本研のゼミ室

正規表現サンプル集

webpackの devtool ソースマップでエラーを確認する

最近,webpackをさわっていてソースマップが便利だと思い,紹介させていただきます.

webpack

モジュール(JavaScriptなどのファイル)をバンドルする(まとめる)やつ

以下の記事に詳しく書いてありました.

qiita.com

ソースマップ

バンドル後のファイルと前のファイルを関連づける

バンドル前のコードを確認できるので,デバックがしやすいという利点がある

今回は,devtoolのプロパティにcheap-module-eval-source-mapを指定するが, 他にも種類があるので詳しくは以下を参照

webpack.js.org

環境

  • 以下の環境で動作を確認しています.
$ node -v
v11.10.1

やってみる

ファイル構成

  • ファイル構成は以下の通りです.
$ tree
│       
├── package-lock.json
├── package.json
├── public
│   ├── index.html
│   └── js
│       └── bundle.js
├── src
│   └── js
│       ├── app.js
│       └── modules
│           └── message.js
└── webpack.config.js

任意のフォルダを作成

mkdir webpack-devtool

package.jsonを作成

npm init -y

webpackをインストール

npm install --save-dev webpack webpack-cli

package.jsonを修正

  • package.jsonを以下のように修正する.
{
  "name": "webpack-devtool",
  "scripts": {
    "build": "webpack"
  },
  "devDependencies": {
    "webpack": "^4.41.6",
    "webpack-cli": "^3.3.11"
  }
}

app.js

  • message.jsを読み込んで実行するエントリーポイントのsrc / js / app.jsは以下のようする
import message from './modules/message';

message('Hello world!!');

message.js

  • console.logでログにメッセージを出力するモジュール,タイポしてエラーを出させるようにする
export default function message(msg) {
  console.log(mgs);
}

webpack.config.js

  • webpackの設定ファイル.プロパティにソースマップを指定する
const path = require('path');

module.exports = {
  mode: 'development',
  entry: './src/js/app.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'public/js')
  },
  // ソースマップの設定
  devtool: 'cheap-module-eval-source-map'
};

index.html

  • public / index.htmlはwebpackを実行して出力させるbundle.jsを読み込むファイル
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>webpack!</title>
  </head>
  <body>
    <script src="js/bundle.js"></script>
  </body>
</html>

bundle.js

  • 以下のコマンドを実行してpublic / js / の中にbundle.jsを生成する
npm run build

ブラウザで確認する

  • public / index.htmlを開いてコンソールを確認する f:id:TakaShinoda:20200229191310p:plain

f:id:TakaShinoda:20200229191727p:plain

  • message.jsでエラーが発生しているのが確認できる

f:id:TakaShinoda:20200229191800p:plain

  • message.jsの部分をクリックするとバンドル前のコードをみてエラー部分を確認できる f:id:TakaShinoda:20200229201501p:plain

  • msgをmgsとタイポしてる!

f:id:TakaShinoda:20200229201608p:plain

参考

  • 以下のサイト,文献を参考にさせていただきました

webpack 4 入門 - Qiita

Devtool | webpack

Amazon CAPTCHA

React + Firebase でGoogleアカウントでのログイン機能を実装する

はじめに

FirebaseとReactでの開発で、

今回はGoogleアカウントを使ったログイン機能の実装を行っていきます。(ログインだけです。)

Firebase Authentication

firebase.google.com

環境

以下の環境で動作を確認しています

$ node -v 
v11.10.1
"react": "^16.12.0",
"firebase": "^7.8.0",

準備

Firebaseプロジェクトの作成

Firebaseプロジェクトの作成は以下の記事にありますので、参考にしていただけますと幸いです。

takashinoda.hatenablog.com

ログイン方法を指定する

  • メニューからAuthenticationを選択

f:id:TakaShinoda:20200207224916p:plain

  • Authentication画面からログイン方法を選択

f:id:TakaShinoda:20200207225112p:plain

  • ログインプロバイダからGoogleを選択

f:id:TakaShinoda:20200207225529p:plain

  • ステータスを有効にする

f:id:TakaShinoda:20200207225622p:plain

f:id:TakaShinoda:20200207225635p:plain

実装

create-react-app

  • create-react-appでReactプロジェクトを作成
npx create-react-app auth-demo

ファイル構成

  • ファイル構成は以下の通りです
$ tree
│          
├── package-lock.json
├── package.json
├── .env
├── public
│   ├── btn_google_signin.png
│   ├── favicon.ico
│   └──  index.html
├── src
│   ├── App.css
│   ├── App.js
│   ├── App.test.js
│   ├── components
│   │   └── Signin.jsx

Firebase SDK snippet取得する

  • FirebaseプロジェクトのSettingsからapiKeyなどを取得

f:id:TakaShinoda:20200207231544p:plain

  • package.jsonと同じ階層に.envファイルを作成し、そこに取得したapiKeyなどを記述
# Firebase
REACT_APP_FIREBASE_API_KEY="×××××××××××××××××××××" 
REACT_APP_FIREBASE_AUTH_DOMAIN="×××××××.firebaseapp.com"
REACT_APP_FIREBASE_DATABASE_URL="https://×××××××.firebaseio.com"
REACT_APP_FIREBASE_PROJECT_ID="××××××××××××"
REACT_APP_FIREBASE_STORAGE_BUCKET="×××××××××××××××××"
REACT_APP_FIREBASE_MESSAGING_SENDER_ID="123456789"
REACT_APP_FIREBASE_APP_ID="××××××××××××××××××××××××××××××"
REACT_APP_FIREBASE_MEASUREMENT_ID="×××××××××××××"

App.js

  • .envファイルからapiKeyなどを読み込む

creare-react-appで作成したプロジェクトは、

process.env.REACT_APP_から始まる変数を読み込みができるようになっています

import React from 'react';
import Signin from './Signin';
import './App.css';
import firebase from 'firebase/app';
import "firebase/auth";


const firebaseConfig = {
    apiKey: process.env.REACT_APP_FIREBASE_API_KEY,
    authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN,
    databaseURL: process.env.REACT_APP_FIREBASE_DATABASE_URL,
    projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID,
    storageBucket: process.env.REACT_APP_FIREBASE_STORAGE_BUCKET,
    messagingSenderId: process.env.REACT_APP_FIREBASE_MESSAGING_SENDER_ID,
    appId: process.env.REACT_APP_FIREBASE_APP_ID,
    measurementId: process.env.REACT_APP_FIREBASE_MEASUREMENT_ID,
}
firebase.initializeApp(firebaseConfig);


const App = () => {
  return (
    <div className="App">
      <Signin />
    </div>
  );
}

export default App;

Signin.jsx

import React from 'react';
import firebase from 'firebase/app';

const Signin = () => {

    const signInWithGoogle = () => {
        // Googleプロバイダオブジェクトのインスタンスを作成
        const provider = new firebase.auth.GoogleAuthProvider()
        // ポップアップウィンドウでログインを行う場合はsignInWithPopupを呼び出す
        firebase.auth().signInWithPopup(provider)
        .then(user => {
            alert("success : " + user.user.displayName + "さんでログインしました");
          })
          .catch(error => {
              alert(error.message);
          });
    }
    
    
    return (
            <div>
                <div className="login">
                    <h1>ログイン</h1>
                </div>
                <div className="signin_button">
                    <img src="../btn_google_signin.png" onClick={()=>signInWithGoogle()} alt="google signin"/>
                </div>
            </div>
        );
}

export default Signin

実行結果

npm start

f:id:TakaShinoda:20200208005711p:plain

f:id:TakaShinoda:20200208010150p:plain

f:id:TakaShinoda:20200208010351p:plain

おわりに

今回は、React + Firebase でGoogleアカウントでのログイン機能だけを実装しました。

次回はルーティングを使って、ログインが成功したらホーム画面に遷移するような機能の実装を行っていきたいと思います。

ReactでSPA+PWAを作ってみた

はじめに

あけましておめでとうございます🎍

昨年末熊本で忘年会の幹事をやったのですが、県外から来る土地勘がない人に様々なお知らせをまとめておくシンプルなPWAを作りました。

作ったもの

f:id:TakaShinoda:20191226210111p:plain

  • お店のサイトへのリンク

  • お店へ、現在地からのルートのリンク

  • 最寄りホテルのサイトへのリンク

  • 最寄り駐車場へ、現在地からのルートのリンク

シンプルな機能ですが調べるのは面倒なことをワンタップでできるようにしました

github.com

使ったもの

  • React

  • react-router-dom

  • Material-UI

  • fortawesome

SPA

SPA(Single Page Application)とは、単一のページ内でコンテンツを切り替えるアーキテクチャの名称です。

(参考: SPA(Single Page Application)ってなに?)

PWA

PWA(Progressive Web Apps)とは、モバイル端末でウェブサイトを表示するときに、まるでネイティブアプリのような動作を可能にする仕組みの事です。

(参考:PWA(Progressive Web Apps)とは?メリットと実装事例について | デジ研)

環境構築

  • 以下のバージョンで動作確認しています
$ node -v
v11.10.1
"react": "^16.12.0"
  • Reactの環境構築
npx create-react-app myapp

実装

  • ファイル構成
(略)
├── public
│   ├── favicon.ico
│   ├── index.html
│   ├── manifest.json
│   ├── mogura.png
│   ├── robots.txt
│   └── top.png
├── src
│   ├── App.css
│   ├── App.test.js
│   ├── components
│   │   ├── About.jsx
│   │   ├── App.js
│   │   ├── Home.jsx
│   │   ├── Menu.jsx
│   │   └── page404.jsx
│   ├── index.css
│   ├── index.js
│   ├── logo.svg
│   ├── serviceWorker.js
│   └── setupTests.js
└── yarn.lock

PWA化

こちらの記事を参考にさせていただきました (Reactで触るPWA (Progressive Web App) - Qiita)

  • index.jsの修正

オフラインでもアプリを起動できるようにindex.js下記のように修正する

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './components/App';
import * as serviceWorker from './serviceWorker';

ReactDOM.render(<App />, document.getElementById('root'));
//unregister() を register()に修正する
serviceWorker.register();

アイコンを設定する

  • publicの中にアイコンにしたい画像をいれる (サイズは192×192)

画像のサイズ変更はこのサイトを使用しました => オンラインでJPGを瞬時にサイズ変更

  • publicの中のmanifest.jsonを書き換える
{
  "short_name": "hoge",
  "name": "hoge",
  "icons": [
    {
      "src": "favicon.ico",
      "sizes": "64x64 32x32 24x24 16x16",
      "type": "image/x-icon"
    },
    {
      "src": "top.png",
      "type": "image/png",
      "sizes": "192x192"
    }
  ],
  "start_url": "/",
  "display": "standalone",
  "theme_color": "#c4e4fc",
  "background_color": "#ffffff"
}

今のままだとSafariからは、アイコン画像が表示されない状態なのでpublicの中にあるindex.htmlに以下を追加することでSafariからでもアイコンが表示されるようにする (参考: 初心者がつまづいた、PWAアプリSafari版でホームボタンのアイコンが読み込まれない件 - Qiita)

<!DOCTYPE html>
<html lang="en">
  <head>
    <!--ここの一行を追加する-->
    <link rel="apple-touch-icon" href="./top.png" sizes="192x192"/>
(略)
  • できました

f:id:TakaShinoda:20191228162811p:plain

ルーティング

ルーティングに関してはこの記事を参考にさせていただきました。

react-routerに入門する - Qiita

  • react-router-domをインストールする
npm install --save react-router-dom

現在(2019/12/26)のバージョン

"react-router-dom": "^5.1.2",
  • インポートする
import {BrowserRouter as Router, Link, Route, Switch} from "react-router-dom";
  • リンクを設定する
<Router>
//リンクを<Link to='/'> </Link>で設定する
  <ul>
    <li><Link to='/'>About</Link></li>
    <li><Link to='./About'>About</Link></li>
  </ul>
</Router>
<Router>
  <ul>
    <li><Link to='/'>About</Link></li>
    <li><Link to='./About'>About</Link></li>
  </ul>
  <Switch>
  //component={ここに表示させたいコンポーネント}
    <Route path='/' exact component={Home}/>
    <Route path='/About' exact component={About}/>
    <Route exact component={page404}/>
  </Switch>
</Router>

Routeは前方一致するものがあれば全てのコンポーネントが表示されるため、一致させたくないものにはexactが必要です。

参考記事より

画像の表示

今回は存在しないパスにアクセスされた時に表示するページ(page404.jsx)にこのもぐらの画像を表示してみます。

もぐらはここから => かわいいフリー素材集 いらすとや

https://cdn-ak.f.st-hatena.com/images/fotolife/T/TakaShinoda/20191228/20191228162058.jpg

  • publicの中に表示したい画像を入れる

  • 画像の表示

//<img src="srcフォルダまでのパス" alt="" height="" width="" /> 
<img src="../mogura.png" alt="もぐらの画像" height="280px" />

現在地からのルートのリンクを作る

この機能はGoogle Maps URL を使って作ります。 こちらの記事を参考にさせていただきました => (Google Maps URL 「現在地からのルート」のリンクを作る - Qiita)

  • 座標を使う場合
<a href=https://www.google.com/maps/dir/?api=1&destination=${lat},${lng}>hoge</a>
//例えば: 宮崎駅
<a href=https://www.google.com/maps/dir/?api=1&destination=${31.915713},${131.432221}>宮崎駅</a>
  • 施設名などのキーワードを使う場合
<a href= "https://www.google.com/maps/dir/?api=1&destination=${keyword}" >hoge</a>
//例えば: 宮崎駅
<a href= "https://www.google.com/maps/dir/?api=1&destination=${宮崎駅}" >宮崎駅</a>

参考記事

GatsbyJSで自分のホームページを作る

はじめに

自分のホームページや、就職活動などでポートフォリオサイト作りたいなと思った事ありませんか? この記事では、React製の静的サイトジェネレータであるGatsbyJSを紹介させていただき、実際に自分もポートフォリオサイトを新しく作ってみました。

GatsbyJS

GatsbyJSとは、React製の静的サイトジェネレータです。モダンで高速なサイトを作ることができます。また、静的サイトのリソースのデータ取得にはGraphQLが使われています。

Vue.jsが好きな方はVue.jsの静的サイトジェネレーターGridsomeもあります。

www.gatsbyjs.org

やってみる

  • 以下の環境で動作を確認しています。
$ node -v
v11.10.1
gatsby -v
Gatsby CLI version: 2.8.19
Gatsby version: 2.18.5
  • GatsbyJSのサイトから、「Get Started」 -> 「Quick start」を選択してこのページにいきます f:id:TakaShinoda:20191219033220p:plain

  • 指示通りにGatsby CLIをインストールします。

npm install -g gatsby-cli
  • 次に、新規作業フォルダを作成するのですが、今回はGatsby Startersを使用します。

これは開発を迅速に開始することを目的に、様々なデザインのテンプレートを使用できるので好きなものを選んでください。

www.gatsbyjs.org

  • 選んだstarterをインストールする
gatsby new [SITE_DIRECTORY] [URL_OF_STARTER_GIT_REPO]

例えば、このstarterを使いたい場合は f:id:TakaShinoda:20191219035443p:plain

gatsby new gatsby-starter-resume https://github.com/anubhavsrivastava/gatsby-starter-resume

これでインストールできました。

  • サーバを起動する GatsbyJSは以下のコマンドを実行しlocaohost:8000で起動できます。
cd 作業ディレクトリ
gatsby develop
  • あとは好きなようにコードを書き換えていきます。

下記を参考にしました。

https://www.gatsbyjs.org/docs/gatsby-link/

https://www.gatsbyjs.org/packages/gatsby-image/

  • ビルドする
gatsby build

Gatsbyは、サイト用に最適化されたプロダクションビルドを実行し、静的HTMLおよびルートごとのJavaScriptコードバンドルを生成します。

公式サイトより引用(日本語訳)

ホスティング

今回ホスティングにはNetlifyを使用しました。

www.netlify.com

やり方はこちらの記事を参考にさせていただきました

ブログを Gatsby + Netlify に移行した | aobako.net

完成

できました。

f:id:TakaShinoda:20191222175648p:plain

https://taka621.netlify.com/

GitHub - TakaShinoda/portfolio_v2.0

おわりに

GatsbyJSを使ってポートフォリオサイトを作成しました。 今回はstarterを使うことで簡単にできました。 また新しく作る際は一からデザインも作っていきたいです。

Babelをざっくり理解する

この記事で扱うソースコードはこちらにもあります。 github.com

Babelとは

  • JavaScriptコンパイラーで、JavaScriptを下位互換するために使われる。
  • JSX構文やTypeScriptも変換できる。(TypeScriptは一部サポートしていないらしいです。)

babeljs.io

なぜ必要なのか

  • 使用できるJavaScriptはブラウザによって異なるから。
  • 新しいJavaScriptを各ブラウザで使用できるようにするため。

JavaScript Engine

  • JavaScriptのコードを実行するコンピュータプログラムの事。
  • 新しい機能をブラウザで使用できるかはJavaScript Engine次第。
  • JavaScript Engineはブラウザによって異なる。
ブラウザ JavaScript Engine
Google Chrome V8
Mozilla Firefox SpiderMonkey
Safari JavaScriptCore
Microsoft Edge Chakra *V8に変更予定(2020/1/15)
Internet Explorer Chakra
  • ブラウザのバージョンによって使用できる機能に差がある。

ECMAScript

  • JavaScriptの言語仕様の事。
  • TC39という専門委員会でECMAScriptを策定してる。
  • ES2015以降は仕様選定のプロセス*1が変更されて、1年に一度のペースでリリースされるようになっている。

Babelの機能

  • 構文変換
  • ターゲットブラウザにないPolyfillの提供
  • ソースコードの変換

Babelを実行するために必要なもの

@babel/cli

  • 実行コマンドを用意
  • ファイルやディレクトリの判定を行なっており、内部的には@babel/coreに依存

@babel/core

  • @babel/parser, @babel/traverse, @babel/generatorなどを使用して設定の読み込みや、コードの変換を行う

Babel Plugins

  • コードに変換を加える(pluginは最後に実際に軽くふれています)

Babel Presets

  • 独自のBabelのプラグインセット(@babel/preset-env, @babel/preset-react)

Don't want to assemble your own set of plugins? No problem! Presets can act as an array of Babel plugins or even a sharable options config.

Presets · Babelより引用

Babelの変換には3つの段階

f:id:TakaShinoda:20191215113151p:plain

Parsing

ソースコードをAST(Abstract Syntax Tree)に変換

Transformation

ASTを変換するBabelのプラグイン(@babel/plugin-transform-arrow-functions)

Code Generat

ASTをソースコードに変換

プラグイン

babelはプラグインを用いることで、JavaScriptのコードを変換できます。 今回は、プラグインを軽くさわってみました。

mkdir mydemo
cd mydemo

arrow-functions

npm install --save-dev @babel/core @babel/cli @babel/plugin-transform-arrow-functions

アロー関数を書きます。

const hoge = () => {};

babel.config.jsを作成

const plugins = ["@babel/plugin-transform-arrow-functions"];
module.exports = {plugins};

libというディレクトリの中に結果を出します。

npx babel index.js --out-dir lib

新しく作られたlibディレクトリの中のindex.jsを見てみると、おおー!アロー関数が普通の関数になりました。

const hoge = function () {};


block-scoping

npm install --save-dev @babel/plugin-transform-block-scoping

先ほどのindex.jsにブロックスコープを追加する

//arrow-functions
const hoge = () => {};

//block-scoping
{
    let a = 1;
}
let a = 1;
const plugins = [
    "@babel/plugin-transform-arrow-functions",
    "@babel/plugin-transform-block-scoping"
];

module.exports = {plugins};

先ほどと同様にコンパイルする

npx babel index.js --out-dir lib
var hoge = function () {}; 

{
  var _a = 1;
}
var a = 1;


参考資料

Plugins · Babel

現代フロントエンドに欠かせないwebpackとBabelを理解しよう! (sakito) - builderscon tokyo 2019

ECMAScript · JavaScript Primer #jsprimer

*1:詳細はここに書かれていますhttps://tc39.es/process-document/