20201014のJavaScriptに関する記事は16件です。

css,js,html 備忘録

要素を上下中央に配置する方法
https://codepen.io/liang-kang/pen/GRqopmj
参考元:https://sole-color-blog.com/blog/532/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[JavaScript] thisを使用する理由

概要

フロントエンド初心者による学習忘備録。
本投稿ではJavaScript(jQuery)で使用するthisについてまとめる。

環境

  • jquery-3.5.1.min.js
<script src="./js/vendor/jquery-3.5.1.min.js"></script>

thisを使用するメリット

メリットとして以下3点が主に挙げられる。

  • 処理のパフォーマンス向上
  • コードの流用
  • 複数のセレクタを指定した場合の処理の切り分け

パフォーマンス向上

jQueryの処理はメソッドチェーンとして順に処理される仕組みであり、ブラウザは指定されたセレクタを手がかりにHTMLから要素を集めるが、thisを使用しないとメソッドが実行されるたび毎回集めることになりCPUに負荷が掛かってしまう。

$(function () {
  $('.test').on('click', function () {
    $('.test').css('color', '#ebc000');
  });
});

以下のように変更する。

$(function () {
  $('.test').on('click', function () {
    // thisは上の状態を保持している
    $(this).css('color', '#ebc000');
  });
});

コードの流用

以下はそれぞれのクラスに対してcssのプロパティを変更する処理だが、内側の処理に重複が見受けられる。

$(function () {
  $('.test').on('mouseover', function () {
    $('.test').css('color', '#ebc000')
  });
});

$(function () {
  $('.test-class').on('mouseover', function () {
    $('.test-class').css('color', '#ebc000')
  });
});

内側のcssを変更する処理は同じのため、thisを使用し以下のように処理を簡潔にすることがベスト。

$(function () {
  function colorChange() {
    $(this).css('color', '#ebc000')
  };

  $('.test').on('mouseover', colorChange);
  $('.test-class').on('mouseover', colorChange);
});

処理の切り分け

以下処理では複数のセレクタを指定しcssメソッドを実行しているが、いずれかの要素にイベントが発生した際指定されている全ての要素にメソッドが反映されてしまう。

$(function () {
  $('header, .test-class, .test').on('mouseover', function () {
    $('header, .test-class, .test').css(
      'background-color', '#ae5e9b'
    );
  });
});

thisを使用し以下のように変更することで、各セレクタに対しそれぞれメソッドが反映されるようになる。

$(function () {
  $('header, .test-class, .test').on('mouseover', function () {
    $(this).css(
      'background-color', '#ae5e9b'
    );
  });
});

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

#2【初心者】知識0からeclipseでWebアプリ(Webサイト)を作る.「 GitHubとEclipseを繋げて動的Webアプリを共同開発する」

はじめに

前回の#1【初心者】知識0からEclipseでWebアプリ(Webサイト)を作る.「 Webアプリを作る為の環境を構築しよう」では,EclipseをPCにインストールしてプロジェクトを作り,サーバーを立てて動かしてみた.
次に誰かと開発したり,自分の変更をオンライン上で管理する為にEclipseでGitHubを使用するための設定と,アップロード方法について書いていく.
使用するプロジェクトは前回作ったものをそのまま使う.

環境一覧
プログラミングソフト:Eclipse 2020
JAVA SE 14
Tomcat 8.5.88
Git for windows 2020 10月時点最新版 :version 2.28.0
Git Hub

  • 環境作成編

#1 Webアプリを作る為の環境を構築しよう

#2 GitHubとEclipseを繋げて動的Webアプリを共同開発する(この記事)2020 10月更新

  • デザインを考えよう編

  • フロントエンド作成編

  • バックエンド作成編

  • 実際にサービスを動かしてみよう編

今回の記事では,

1.Git Hubのインストール(更新)
2.Git Hubでリポジトリの作成
3.リポジトリとEclipseのプロジェクトを連携とコミット
4.作業用ブランチの作成
5.共同でリポジトリを編集する(collaborator登録).
6. 参加したレポジトリの最新版ブランチをプルする.(最新版をダウンロードしてくる)
7.完成したプロジェクトをデフォルトブランチとして更新(最新の状態を共有できるように)

という流れ.

単語メモ
リポジトリ:バージョン管理によって管理されるファイルと履歴情報を保管する領域
コミット :ローカルレポジトリのに自分の変更を上書きする(リビジョンとも呼ばれる).
プッシュ :リモートリポジトリに自分の変更履歴をアップロードする.
プル   :リモートレポジトリの内容をダウンロードしてくる.
マージ  :リポジトリの内容をコピーすること.
ソリューション:プロジェクトのこと.

1. Git Hubのインストール(更新)

Git for windowsにアクセスしてDownloadを押すだけ.
特に設定は変えなくても大丈夫なのでデフォルト状態でインストール.元々過去のバージョンがインストールされていても勝手にアップデートされる.
インストールされたらGit Bashというソフトを開く
01-1.jpg

Git Bashを起動後に,以下のコマンドを入力して正しくインストール/更新ができているか確認.

$ git --version

2. Git Hubでリポジトリの作成

ログインした後,左上のNewからリポジトリを作成.すでに作ったものはNewの下にリストとして表示されている.
02-1.jpg

Newを押すとリポジトリの作成画面が表示され,
①任意のリポジトリ名(プロジェクト名)
②リポジトリ説明文:任意
③公開するか,非公開にするか.(2019年から無料アカウントでも非公開のリポジトリを作成可能に)
④「Add a README file」はリポジトリの説明や使い方を書くREADMEファイルを事前作成するかどうか.基本的にはチェックを付けとく.

⑤Git の管理に含めないファイルを指定するためのファイル(除外ファイルの指定).

⑥ライセンス=使用許諾条件,ライセンスの選択は義務ではないが,ライセンスがない場合はデフォルトの著作権法が適用され,ソースコードの複製,配布,派生物の作成は誰にも許可されない.オープンソースのプロジェクトを作成をする際にはオープンソースライセンスを設定する.

02-2.jpg

3.リポジトリとEclipseのプロジェクトを連携

リポジトリのトップ画面からCodeを押す.
04-1.jpg

でてきた画面の「HTTPS」を右側のボタンを押してコピーする.

04-2.jpg

Eclipseを開き,サーバを停止しておく.

スライド1.JPG

プロジェクト・エクスプローラーからプロジェクトフォルダを右クリックし,
「インポート」→「インポートの共有」を押す.

スライド3.JPG

インポート画面が開くので,
「Git」→「Gitからプロジェクト」を押す.

スライド4.JPG

クローンを選ぶ.

スライド5.JPG

①先ほどGitでリポジトリを作成した際にコピーしたHTTPSのアドレスをコピペする.
②GitHubに登録しているユーザー名
②GitHubに登録しているパスワード
をそれぞれ入力する.

スライド6.JPG

作成されているブランチの一覧が表示されるので(今回はmainのみ),一番最新のブランチを選択する.

スライド7.JPG

どこにフォルダを作成されるか聞かれるので,自身で設定したワークスペースファルダ内にプロジェクト名のファルダを作成して設定すると編集が楽.
前回記事からの流れで作っていれば(C:\Users\ユーザ名\Documents\eclipse\プロジェクト名)となる.

スライド8.JPG

初めて作成する際に,秘密の質問を作成するか聞かれるので任意で作成する.一応作っていたほうがいい.

スライド9.JPG

新規プロジェクト・ウィザードを使用してインポートにチェックして完了を押す.

スライド10.JPG

あとは, #1 Webアプリを作る為の環境を構築しようの記事にあるように動的Webプロジェクトを新規作成していく.

次に,Eclipse上でGitHubのブランチを管理する為に不可欠なタブを,チーム→リポジトリ・ビューで表示から追加しておく.

スライド12.JPG

新規で作ったプロジェクトフォルダをプロジェクト・エクスプローラから右クリックし,チーム→コミットを選択.

スライド13.JPG

Gitステージングの為のタブが出るので,そこから2個の+マークを押すと編集したがコミットされていないファイルがすべて下に移動する.特定のファイルだけ更新したい際にはファイル名を右クリックすると1個づつ追加できる.

スライド14.JPG

下にファイルが移動していれば大丈夫.

スライド15.JPG

コミットメッセージを入力して(add:変更点など)を入力してコミットを押す.

スライド16.JPG

Gitレポジトリータブを確認してmainの横にコメントついてることを確認.この時点ではオンライン上では変更されておらずGitHub上にはいないので注意.

スライド17.JPG

Gitステージングタブから,HEADのプッシュを選択.

スライド18.JPG

色々と確認タブが出てくるがそのままで大丈夫.

スライド19.JPG

スライド20.JPG

スライド21.JPG

Remote Tracking 内にあるmainの横にコメントついてることを確認すれば大丈夫,GitHub上にも更新されている.

スライド22.JPG

4.作業用ブランチの作成

チーム→切り替え→New Branch...を選択.

スライド23.JPG

新しく作成するブランチ名を指定する.ここではnew1というブランチを作成.

スライド24.JPG

Gitリポジトリを確認するとnew1が追加されているのを確認する.

スライド26.JPG

ブランチ名の左上にチェックマークがついているのが編集中のものなのでnew1をクリックする.

スライド26.JPG

チェックアウトするか聞かれるのでチェックアウトする.(チェックアウトって日本語だと違和感ある)

スライド27.JPG

new1の左上にチェックマークがついてれば大丈夫.

スライド28.JPG

「3.リポジトリとEclipseのプロジェクトを連携とコミット」でやったようにコミットをするとRemote Tracking 内にmainとは別にnew1が増えていればブランチの作成は完了.

スライド29.JPG

5.共同でリポジトリを編集する(collaborator登録).

GitHubから共同で編集したいリポジトリを開き,Settingsを開く.

スライド1.JPG

Stteings内のタブからManage accessを選ぶ.

スライド2.JPG

Invite a collaboratorを押す.

スライド3.JPG

検索窓が出てくるので,一緒に編集したい相手のユーザIDかメールアドレスを入力する.

スライド4.JPG

候補がでてくるので選択.

スライド6.JPG

Add ユーザ名 to this repository を押す.

スライド8.JPG

Manage accessに追加されていれば問題ない.

スライド9.JPG

招待された人には登録メールアドレスにメールが届くのでView invitationを押す.
リンク先で参加するかを尋ねられるのでacceptすれば参加完了.

スライド11.JPG

参加するとステータスがCollaboratorに変わっている.

スライド13.JPG

追加されたコラボレータはレポジトリにアクセス権限が与えられ,プッシュが可能になる.これで共同編集の準備が完了.

6. 参加したレポジトリの最新版ブランチをプルする.(最新版をダウンロードしてくる)

Gitリポジトリのタブの右上にあるクローンを押す.

104.jpg

①コラボレータになったリポジトリのHTTPSのアドレスをコピペする.
②GitHubに登録しているユーザー名
②GitHubに登録しているパスワード
をそれぞれ入力する.

スライド6.JPG

プルしたいブランチのみにチェックを入れて

106.jpg

ディレクトリは分かりやすいところに設定して完了を押す.

107.jpg

レポジトリのフォルダが作成されるので,Branches→Localからクリックでそのままmainから開くか,レポジトリフォルダを右クリックしてインポートからプロジェクトを開く.(新規作成と同じ手順でやると上手く開かない時があるので注意.)

108.jpg

しっかりとプロジェクトが開かれているのを確認したら,あとは自分が作業するブランチを新しく作ってプログラムしていく.

110.jpg

7. 完成したプロジェクトをデフォルトブランチとして更新(最新の状態を共有できるように)

2つ方法があり
・Git Hubでデフォルトのブランチを変更する.
・mainに設定しているブランチにマージする.

Git Hubでデフォルトのブランチを変更する方法では,
①Settingsタブを選択
②Branchesタブを選択
③デフォルトにしたいブランチを選択してUpdateを押す.

09-3.jpg

mainに設定しているブランチにマージする方法では,ブランチを切り替えて最新版をプッシュすればおk.

とりあえずこれで共同開発できる準備ができました.
次回は「どんなサイトを作るか」考えていきます.

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ES2015(ES6)のモジュールについてまとめ

そもそもモジュールって何?

まず前提として、アプリケーションはたくさんのプログラムから成り立っています。
この全てをひとつのファイルで管理するとファイルの肥大化を誘発させてしまいます。

ファイルの肥大化による弊害は次のようなものがあります。

  • コード量が多くファイルが縦長になる
  • ぱっと見どこに何が書いてあるのかわからず、ものすごく見にくい。
  • 先に定義した変数を上書きしてしまうことによるバグの発見が遅れる

そのため、規模の大きなアプリケーションはファイルの肥大化による弊害を防ぐためにひとつのファイルにすべてのコードを記載するのではなく、複数のファイルに分けます。この分割したファイルひとつひとつをモジュールといいます。一般的には機能ごとにコードを分けてファイル名を「機能名.js」のように命名するようです。

image.png

モジュールを利用するためには

  • scriptタグに「type="module"」を付与することでモジュール化
  • 使う側のファイルは"モジュールを使いますよ~"という宣言(import)
  • 使われる側のモジュール"僕のコード使っていいですよ~"という宣言(export)

という設定が必要になります。

下記ファイル構成を用いて解説します。

  • HTMLファイル:「index.html」
  • モジュールを使う側のファイル:「import.js」
  • 使われる側のモジュールのファイル:「export.js」

書き方は以下のとおりです。

index.html
<script type="module" src="import.js"></script>
export.js
// 変数のエクスポート
export const language = "英語";
// 関数のエクスポート
export function hello () {
  console.log('ハロー')
}
// オブジェクトのエクスポート
export const person = {
  name: 'Kuma',
  age: 29,
  gender: 'male'
}
import.js
// 変数のインポート
import { language } from "./export.js";
// 関数のインポート
import { hello } from "./export.js";
// オブジェクトのインポート
import { person } from "./export.js";

モジュールを使うメリット

  • 保守性を高められる
  • 名前空間を分けることができる

保守性を高められる

保守性を高めるとは、メンテナンスをしやすい状態にする事です。

極端な例ですが、たとえば同じ関数のコードを100箇所に記載しているとします。その関数の内容に変更があった場合、100箇所以上書き換えなければなりません。この関数をモジュールで作成していれば、変更するのはモジュールで定義しているコードだけで済みます。

またコードを分けることでファイルは縦長になりすぎず、コードの見通しも良くなりどこに何を書いてあるのか分かりやすくなります。
このようにモジュールは仕様変更に強い特徴を持ちます。

その効果をコードでみてみましょう。以下のように各jsファイルで関数「hello」をコピペして使っていたとします。

moduleA.js
function hello() {
  console.log("hello");
}
hello();
moduleB.js
function hello() {
  console.log("hello");
}
hello();
moduleC.js
function hello() {
  console.log("hello");
}
hello();

関数「hello」に内容を付け加えたい場合はすべてのスクリプトで追加が必要になります。
これでは非常に保守性は低いです。

moduleA.js
function hello() {
  console.log("hello");
  console.log('see you'); // 追加
}
hello();
moduleB.js
function hello() {
  console.log("hello");
  console.log('see you'); // 追加
}
hello();
moduleC.js
function hello() {
  console.log("hello");
  console.log('see you'); // 追加
}
hello();

ではmoduleA.jsで関数「hello」を定義し、moduleB.js、moduleC.jsからインポートしてみましょう。すっきりした書き方になりました。

moduleA.js
export function hello() { // 関数「hello」をエクスポート
  console.log("hello");
  console.log('see you');
}
hello();
moduleB.js
import { hello } from "./moduleA.js" // 関数「hello」をインポート
hello(); // 「hello」,「see you」と表示される
moduleB.js
import { hello } from "./moduleA.js" // 関数「hello」をインポート
hello(); // 「hello」,「see you」と表示される

この状態で関数「hello」にbyeを出力する処理を付け加えてみましょう。
moduleA.jsだけを編集すればすべての関数に反映されます。これで保守性は高くなりました。

moduleA.js
export function hello() { // 関数「hello」をエクスポート
  console.log("hello");
  console.log('see you');
 console.log('bye');
}
hello();
moduleB.js
import { hello } from "./moduleA.js" // 関数「hello」をインポート
hello(); // 「hello」,「see you」,「bye」と表示される
moduleB.js
import { hello } from "./moduleA.js" // 関数「hello」をインポート
hello(); // 「hello」,「see you」,「bye」と表示される

名前空間を分けることができる

名前空間とは定義した変数や定数、関数の影響を及ぼす範囲の事をいいます。
モジュールはこの範囲をモジュール内だけに閉じ込め、意図しない変数の上書きなどによるバグを予防することができます。

たとえば以下では定数はコンソール上で「英語」と表示されます。

index.html
<script>
    const langage = "英語";
    console.log(langage); // 「英語」と表示される
</script>

以下では「Identifier 'langage' has already been declared」というエラーになります。
理由はconstの再定義はできないからです。スクリプト内の変数の名前空間が、他のスクリプト内に影響を及ぼしている状態です。

index.html
<script>
    const langage = "英語";
</script>
<script>
    const langage = "日本語"; //「Identifier 'langage' has already been declared」というエラー
    console.log(langage);
</script>

ではこうするとどうでしょう。「日本語」と表示されます。
「type="module"」を付与してモジュール化することにより、「langage = "英語"」の及ぼす範囲をスクリプト内に閉じ込めました。
そのためもう一つのスクリプトではconstの宣言をできています。

index.html
<script type="module"> // 「type="module"」を付与
    const langage = "英語";
</script>
<script>
    const langage = "日本語";
    console.log(langage); // 「日本語」と表示される
</script>

規模の大きなアプリケーションを作ったり、大人数で共同開発するような場合は、名前が被ってしまうことはおおいに考えられるのでモジュールを使って名前空間を分けることはとても有用になります。

名前付きエクスポート(named export)とimport

名前付きエクスポートのexport

  • 変数、定数、関数、オブジェクトの"命名した名前"でエクスポートする
  • 一つずつでも複数まとめるのどちらでもエクスポートできる
export.js
// 一つずつエクスポートする場合の書き方
export const language = "英語";
export function hello() {
  console.log("ハロー");
}
export const person = {
  name: "Kuma",
  age: 29,
  gender: "male"
};

// 複数まとめてエクスポートする場合の書き方
const language = "英語";
function hello() {
  console.log("ハロー");
}
const person = {
  name: "Kuma",
  age: 29,
  gender: "male"
};
export { language, hello, person };

名前付きエクスポートのimport

  • エクスポートした名前を{}(かぎかっこ)で囲いインポートする
  • 一つずつでも複数まとめるのどちらでもインポートできる
import.js
// 一つずつインストール
import { language } from "./export.js";
import { hello } from "./export.js";
import { person } from "./export.js";

// 複数まとめてインポート
import { language, hello, person } from "./export";

別名(エイリアス)でエクスポート時/インポート時することもできる

  • 使いどころとしては同じ名前でエクスポートされた識別子を区別する時に使う
  • 実際の現場ではインポート側でエイリアス名を付与して読み込むことが多い
export.js
// languageを「lang」、helloを「hel」、personを「per」としてエクスポート
export { language as lang, hello as hel, person as per };
import.js
// langを「lan」、helを「he」、perを「pe」としてインポート
import { lang as lan, hel as he, per as pe } from "./export";

デフォルトエクスポート(default export)とimport

デフォルトエクスポートのexportの書き方

export.js
const language = "英語";
export default language;

※ひとつのモジュールにつき1回しか使えない

export.js
const language = "英語";
function hello() {
  console.log("ハロー");
}
// オブジェクトの宣言
const person = {
  name: "Kuma",
  age: 29,
  gender: "male"
};

export default language;
export default hello; // 2つ以上使おうとするとエラーになる

デフォルトエクスポートのimportの書き方

  • {}をつけずにむき出しで記載する
  • ひとつしかエクスポートしないので好きな名前でインポートできる
import.js
import languageeeee from "./export.js";

名前付きエクスポートとデフォルトエクスポート、どちらを使うべきか

デフォルトエクスポートを使ったほうがよいみたいです。
下記記事が参考になります。
https://engineering.linecorp.com/ja/blog/you-dont-need-default-export/

余談(JavaScriptで利用するモジュールの種類)

JavaScriptにはモジュールの種類は2つあります(厳密には他にもありますが、大きく分けてという意味で)
知っておけば混乱しません。

ESModule形式

・JavaScriptの公式の言語仕様(これをECMAScriptという)として正式に策定されたモジュール
・"JavaScriptの公式の言語仕様"なのでサーバーサイド(node.js)でもブラウザ側のどちらでも使える
・ES2015(ES6)の構文
・ブラウザ(IE)やそのバージョンによっては対応していない場合もある
・開発時(コードを書くとき)はES6で、対応していないブラウザにも読み込めるようにwebpackを利用して変換するやり方が一般的
importでデータの読み込み、exportでモジュールから外部にデータを出力する

// ESModule形式
import { language } from './moduleA.mjs'; //インポート構文
export const language = "英語"; //エクスポート構文

CommonJS形式

・サーバーサイド側(node.js)のJavaScriptの言語仕様であるCommonJSのモジュール
requireでデータの読み込み、exportsでモジュールから外部にデータを出力する

// CommonJS形式
import { language } from './moduleA.mjs'; //インポート構文
export const language = "英語"; //エクスポート構文

おわり

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JXA + Spotify + Macでワンクリックでアプリの起動+好きな曲を再生する

JXAとは

これです。
知らないうちにMacがシステム標準でJavaScriptで操作できるようになってた (JXA)

練習がてらSpotifyを起動して好きな曲を再生するアプリを作ってみます。

STEP1. スクリプトエディタを起動、新規書類をjavascriptモードで開く

STEP2. コードを書く

追記:はじめにシャッフルを有効にして、通知センターを使って曲名も表示することにしてみました。
私はURIに My Library > お気に入りの曲 を指定しています。

参考:

var spotify = Application("Spotify")
var target = "流したい曲/アルバム/アーティスト/プレイリストのURI"

// シャッフルを有効にする
if(spotify.shuffling() == false){
    const shuffleMenu = Application("System Events").applicationProcesses.byName("Spotify").menuBars.at(0).menuBarItems.byName("曲の再生").menus.byName("曲の再生").menuItems.byName("シャッフル")
    shuffleMenu.click()
}

spotify.playTrack(target)

// 現在の曲のtrackを取得
var current = spotify.currentTrack
delay(0.1)

//通知を表示
var app = Application.currentApplication()
app.includeStandardAdditions = true
app.displayNotification(current.artist() , {
    withTitle: current.name(),
    subtitle: current.album(),
    soundName: 'Pop'
})

STEP3. 流したい曲/アルバム/アーティスト/プレイリストのURIを調べる

URIとはSpotifyの中での住所的なやつです。
調べる方法は次の2つ。

1. ブラウザ版SpotifyのURLを見る

アルバムやプレイリストを開くと
https://open.spotify.com/album/4RlqowFUNcZR7UTUinXZlT
(アルバム「かつて天才だった俺たちへ」 by Creepy Nuts)
のように https://open.spotify.com/ + 〇〇 という形式になっていると思います。
この〇〇はURIに対応しているので

var target = "spotify:album:4RlqowFUNcZR7UTUinXZlT"

のように書けばOKです。

2. Spotify APIを使って調べる

1の方法だとアルバム/アーティスト/プレイリストのURIは分かるのですが個別の曲のURIの調べ方がいまいち分かりませんでした。(追記:「曲のリンクをコピー」という機能から普通に調べられました。。)そこでAPIを使ってダイレクトに調べます。

参考: Spotify APIで遊んでみる

1の方法でアルバムのURIが既に分かっている時は、簡単に曲のURIを検索できます。

import pprint

import spotipy
from spotipy.oauth2 import SpotifyClientCredentials

client_id = 'xxxxxxxxxxxxxx'
client_secret = 'xxxxxxxxxxxxxx'
client_credentials_manager = spotipy.oauth2.SpotifyClientCredentials(
    client_id, client_secret)
spotify = spotipy.Spotify(
    client_credentials_manager=client_credentials_manager)

results = spotify.album('アルバムのURI(英数字部分)')
# 例えば、results = spotify.album('4RlqowFUNcZR7UTUinXZlT')
results = results['tracks']['items']

pprint.pprint(results)

これで、下のように曲ごとの詳細情報が一覧表示されるので、その中のuriの項目を使えばOKです。

~~省略 ~~~
'name': 'かつて天才だった俺たちへ',
'preview_url': 'https://p.scdn.co/mp3-preview/037b28ab2dbdc68efb5b6a85398645a16a010f60?cid=67c1745df8724b52918df69aab8ff0bd',
'track_number': 7,
'type': 'track',
'uri': 'spotify:track:4qjpey2aljl28C40NmTcgQ'
~~省略 ~~~

STEP4. アプリの作成

スクリプトエディタに戻って、ファイル > 書き出す より、フォーマットでアプリケーションを選んで保存します。

アイコンを変えてDockに追加すれば、ワンクリックで曲の再生までやってくれるアプリができました。
image.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

javascriptのLocalStorage を使ってお気に入り機能を自作してみた ①準備編

どうも7noteです。プラグインより多機能なお気に入り機能を作成。まずは準備編。

wordpressの投稿にお気に入り機能を実装するため、プラグインの導入を検討したのですが欲しい機能がついているプラグインがなく、

「プラグインがないならもう自作で実装してしまえ!」

と思って、javascript開発経験が浅い頃に3日かけて作成したお気に入り機能です。
書き方など至らぬ所多々あるかもしれませんが、優しく見守ってください。

この記事を読む前に

できるだけド素人でもわかりやすく説明していく予定です。
ですが、本当に1から始めると日が暮れてしまうので、最低限WEBに関する事をある程度理解をされている以下のような方向けの記事になっています。

・HTML・CSS・javascriptの初歩的な書き方を知っている人
・WordPressがなんとなくどういうものか知っている人(wordpressに応用しなければ別にOK)
・ゴリゴリのプロじゃない人

よく出てくる用語集

単語・用語 説明
Wordpress 代表的なCMSの1つ。サイトのページ管理や記事の投稿などができる。データベースの知識がない人でも簡単に使うことができる。
LocalStorage 今回の主役。webブラウザ(ローカル環境)にデータを保管できる仕組み。ここにお気に入りしたデータなどを保存する。
JSON JavaScript Object Notationの略で、データ形式の種類の事。文字列しか扱えない場合に仕様される事が多い(と思う。)人が見ても分かりやすいフォーマットのため、様々なシーンでよく使われる。

LocalStorageとは

LocalStorage

別称でwebstoroageなんて呼ばれ方もします。
ホームぺージを表示するには、ホームぺージのデータが格納されているサーバーと、それを表示するためのブラウザが必要になります。主にホームぺージに関わるデータのほとんどははこの2箇所で扱われます。その中で、ブラウザの中にデータを保管しておくことができる仕組みのことをLocalStorageと言います。

似たようなもの

LocalStorageと似ているものでクッキーセッションと呼ばれるものがあります。
詳しい違いは検索していただければ多くの情報が見つかるので詳しくは説明しませんが、Local Storageは消さない限り半永久的にデータが保存されるということと、個人情報は取扱わない事だけ覚えておきましょう。

※詳しく知りたい方は違いを書かれた記事がありましたので、こちらをどうぞ。
https://qiita.com/pipiox/items/95554673ba3b078ac112

特徴

  • データはブラウザに保管される。
  • 消さない限り半永久的にデータが保存される。
  • 永久保存されるので、個人情報に関するようなデータは取扱わない方が良い。
  • 形式は文字列のみ。
  • {キー:値}の形で保存できる。
  • 複数の値を保持するには、JSON形式に変換する必要がある。

開発に必要なもの

特に特別な何かをインストールしたりせずに扱うことができます。
コマンドプロンプトを立ち上げてなんやかんやをインストールするなどの手順は必要ありません!
ただ、javascriptの記述にjQueryを使用していますので、ご注意ください。

jQueryって何?という方はこちら

「Wordpressで使いたいよ!」という方は、自由にカスタマイズできるWordpressの開発環境を整えてください。
Wordpressを使用する場合少しだけphpの記述も必要になるので、基礎的なphpの書き方を理解しておくことをオススメします。


これで準備は整いました。次からは実際にソースを書いていきます。
(明日10/15 続きの記事を公開予定)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【React+TypeScript】GETエラーを感知して、404ページに遷移させる

めちゃめちゃ久しぶりの投稿です。
先日までコミットしていたプロジェクトの中で自分のリサーチとスキルではどうにもできなかった404ページへのリダイレクトの処理について。
先輩に教えていただいたので備忘録として書いておきます。

今回実現させたかったこと

今回実現させたかったのは、大きく分けて2つです。

  • /hogehoge/tokenのようなURLが叩かれたときにtokenが正規のものか判断する
  • 正規のものである場合、tokenが合致するデータをデータベースからGETする
  • 正規のものでない場合、404ページへリダイレクトさせる

これらをReact+TypeScriptのプロジェクト内で実現させるというタスクでした。

行き詰まった点

「tokenが正しいかどうかとか、どうやって判断したらいいん。。。」
大きくいってこの点でした。
今までHTMLとCSSでやってきたので、もう何を使えばいいんかもわからずどう調べればいいのかも分かりませんでした。

あとは今までリダイレクトさせる処理は.htaccessで記載してたので、それ以外でどうやった手法があるのかもわからず行き詰りました。

エラーを検知するtry...catch文

tokenの正誤判別にはtry...catch文を使いました。
MDNのドキュメントのリンクを貼っておきます。

try...catch文

で、サンプルコードです。

try {
  // テストする文
}
catch (e) {
  // エラー時に走る処理
}

このような感じにtry...catch文は使われます。
なので、今回私が実現したかったことを実現しようとすると

try {
  const { data: hogehogeList } = await request.get({
    uri: '/hoge',
    params: { token },
  });
}
catch (e) {
  // リダイレクト処理
}

みたいな書き方になります。
こうすることで、tokenが正規のものである場合はデータベースから該当するリストをGETしてきて、
もしtokenが正規のものでない場合は、errorとなりリダイレクトの処理が走ります。

ちなみにuriはAPIが置いてある場所で、paramsはデータをGETする際に必要なパラメータです。

リダイレクト処理をさせるモジュールhistory

リダイレクトさせるにあたって、今回はhistoryというモジュールを使用しました。

history

これを使用してリダイレクトの処理を実装しました。
参考にした記事は以下の記事になります。

history.push();でアクションを起こした時にページ遷移をする

実際に書いたコードは以下になります。

try {
  const { data: hogehogeList } = await request.get({
    uri: '/hoge',
    params: { token },
  });
}
catch (e) {
  history.push('/not-found');
  window.location.reload();
}

このようにすることで、先ほど書いたGETが失敗に終わった際には
404ページに遷移させることができました。

まとめ

今回の総括ですが、

  • try...catch文を使うことで、ある処理が失敗したときには別処理を走らせることができる
  • historyを使うと、遷移させたいページに遷移させる処理を行うことができる

なお、このプロジェクトではreact-router-domを使用してルーティングさせてましたので、
上記の処理を実装する流れとなりました。

先輩曰く、もっと良い処理があるらしいのですがそれはまた別の機会に。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

javascriptでitemの件数を指定し、シャッフルする関数

忘備録です。
既に設置してあるliが多い時、無理やり件数を絞ってシャッフル表示がしたい時用です。

var shuffleItem = function (max) {
   var randomContent = [];
    i = 0;
    $('.itemList .item').each(function() {
        randomContent.push($(this).html());
    });

    randomContent.sort(function() {
        return Math.random() - Math.random();
    });

    $('.itemList .item').remove();

    for (i = 0; i < max; i++) {
        $('ul.itemList').append('<li class="item">' + randomContent[i] + '</li>');
    }
}

よりスマートな書き方があれば教えてください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

E2Eテストの始め方 番外編 - reg-cliで差分比較 -

今回は、TestCafeで撮影したスクリーンショットの差分比較をreg-cliを使って実装していきます。

スクリーンショットについては↓を参考にしてください。
E2Eテストの始め方 TestCafe③ -スクリーンショット編-

reg-cli(reg-suit)とは

Visual Regression Testingのためのコマンドラインインターフェイスです。
画像は別途用意する必要がありますが、アサートする画像を指定するだけで現在の画像と以前の画像を比較し、差分のHTMLを作成してくれます。

2つの違いとして、
reg-cli:2つの画像(ディレクトリ)を指定し実行すると分かりやすい差分HTMLレポートを生成するコマンドライン。
reg-suit:reg-cliの進化版みたいなものでreg-suitの内部でreg-cliが使われています。正解データや差分レポートを自動的にクラウドストレージ(AWS S3, Google Cloud Storageなど)に保存できたり、GitHubと連携してPull Requestに対する差分レポートを表示してくれます。

reg-suitの方が多機能ですが、今はクラウドストレージに保存したりする必要がなかったのでシンプルなreg-cliを導入しました。将来的にはGitHub連携もできたらいいなと思っているのでその時にreg-suitに切り替えようと思います。

では、さっそくreg-cliを使ってみましょう!

install

npmもしくはyarnでインストール。Node.jsはver.8以上をサポートしています。

$ npm i -D reg-cli

usage

$ reg-cli ./path/to/actual-dir ./path/to/expected-dir ./path/to/diff-dir -R ./report.html

./path/to/actual-dirに比較元のディレクトリパス、./path/to/expected-dirに比較対象のディレクトリパス、./path/to/diff-dirに差分画像保存ディレクトリパス、./report.htmlにレポートHTMLの吐き出し先を指定します。

上のコマンドを実行すればreport.htmlが吐き出されるのでブラウザで見てみると、
スクリーンショット 2020-10-14 16.26.12.png
画像のようにサイドバーには「CHANGED:差分があるもの」「NEW:新規追加されたもの」「PASSED:差分のないもの」に分けられています。

差分があった箇所は赤く表示されており、確認したい画像を選択すると
Diff:生成した差分のOverview
Slide:BeforeとAfterのハンドルをスライドして比較
2up:BeforeとAfter2つを並列に並べて比較
Blend:BeforeとAfterを透過させながら滑らかに比較
Toggle:BeforeとAfterを切り替えながら比較
の5つの比較方法で確認することができます。
リニューアル前のUIですがサンプルが用意されているので下記URLから体験してみてください!
https://reg-viz.github.io/reg-cli/

reg-cli導入の基本は以上となります。インストールしてコマンド実行すればよいだけなのでとても簡単に導入することができます!

実行環境をpackage.jsonにまとめる

reg-cliに関してはターミナルで上記コマンドを実行すれば良いですが、私の場合TestCafeでスクリーンショットを撮り、reg-cliで差分比較、sharpでスクリーンショット連結(今後記事を書く予定!)などの処理をしているのでpackage.jsonに諸々書いてそれらを実行しています。

pacakge.json
"scripts": {
 "vrt": "yarn run vrt:screenshot && node e2e/convert.js && yarn run vrt:reg ",
 "vrt:screenshot": "testcafe e2e/screenshot.js",
 "vrt:reg": "reg-cli ./screenshots/20201009/**/corporate ./screenshots/20201010/**/sample ./screenshots/diff/sample -R ./screenshots/diff/sample/report.html -U"
}

$ npm run vrtとコマンド打てばスクリーンショット→画像連結系の処理→reg-cliで差分比較が順番に実行されるようにしています。(&一つで並列処理、&&で直列処理)

GitHub

reg-cli:https://github.com/reg-viz/reg-cli
reg-suit:https://github.com/reg-viz/reg-suit

参考

コンポーネント/単体テスト単位でのvisual regressionテストを行うためのツールを作った話し
reg-cli の Report UI をリニューアルした

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【ReactNative】タイムゾーン無し文字列をDate()に渡した時の挙動がiOS14から異なる件

概要

iOS14の公開に伴いreact-nativeで開発したアプリの挙動を確認していたら、何やら日時がずれているのを発見しました。
細かく処理を追跡していったところ、どうやらDate(string)の挙動が若干変わったようです。

※個人ブログで同様の記事を書いています。今後追記があればそちらを更新していきます。

環境

  • react-native@0.63.1
  • iOS14
  • 2020/10/14時点の情報です(今後のアップデートで修正される可能性あり)

コンストラクで文字列を渡している場合は注意

例えば以下のように文字列からDate型のコンストラクタを利用した場合です。
サンプル①はISO-8601の拡張形式のタイムゾーンを指定していない形式です(この時点でオラオラ仕様なのも問題なのですが・・・)。

// サンプル①
const sample1 = '2020-10-10T00:00:00';
// サンプル②
const sample2 = '2020/10/10 00:00:00';

// それぞれDate型にキャストする
const result1 = new Date(text1);
const result2 = new Date(text2);

// 結果を表示
console.log('result1 --> ', result1.toString());
console.log('result2 --> ', result2.toString());

この実行結果がiOS14前後で異なります。
それぞれ以下の通りです。

出力結果(iOS14未満)

result1 -->  Wed Oct 14 2020 09:00:00 GMT+0900 (JST)
result2 -->  Wed Oct 14 2020 00:00:00 GMT+0900 (JST)

出力結果(iOS14)

result1 -->  Wed Oct 14 2020 00:00:00 GMT+0900 (JST)
result2 -->  Wed Oct 14 2020 00:00:00 GMT+0900 (JST)

サンプル①の場合の結果が変わっているのが分かります。

まとめ

そもそも渡している文字列が正しいフォーマットから少し外れたものであるのも悪いですが、iOS14前後でタイムゾーン無しの文字列を渡した際の解析処理が変わったのだと思います。
react-native固有の事象なのか、iOSのネイティブ部分が変わった故なのかいまいち判断がつかない(そこまで知識がない)のですが、同様の事象に遭遇される方もいるかと思うので記事にしました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript リロードしないと動作しない問題

記事の対象者

Railsアプリにおいて、JQueryを使用して投稿を分けて表示させるタブを実装。
実装したはいいが、リロードしないと正常に動作しない問題が発生、、。
Turbolinksはそのままに、解決する方法とその理由を示したいと思います。

他の記事で解決策はあったものの、「なぜ?」が解消されず附に落ちなかった為、
Rails定番の参考書「現場Rails」の引用と独り言を添えて、初学者向けの記事にしてみました。

初学者の為、解釈が間違っていれば、ご指摘頂けると幸いです。

リロードしないと正常に動作しないタブ部分のjsファイルのコード

tab.js
$(function() {
  let tabs = $(".menu-tab");
  $(".menu-tab").on("click", function() {

.
(省略)
.

})

コードは間違ってないはずだが、なぜかタブの切り替えができない。
他の記事で以下の解決策が示されていました。

解決方法

以下のように、document.addEventListener("turbolinks:load", function() {})を加える。

document.addEventListener("turbolinks:load", function() {
$(function() {
  let tabs = $(".menu-tab");
  $(".menu-tab").on("click", function() {
.
(省略)
.
})

リロードせずともタブの切り替えができ、タブごとの投稿を表示させることができた。解決。
原因は、turbolinksによりイベントが実行されないらしい。。ん??
初学者でない方は、おそらくこれで説明されて理解できるが、初学者の私はあまり附に落ちない。(turbolinksを理解していれば附に落ちますが、私は理解しておりませんでした。)

以下、現場RailsのTurbolinks部分の説明を引用しながら、上の「turbolinksによりイベントが実行されない」原因を理解します。

「turbolinksによりイベントが実行されない」理由

まず、turbolinksとは?

Railsが提供するJavaScriptの中には、ページ遷移を高速化する仕組みもあります。それがTurbolinksです。Turbolinksはすべてのリンククリックに対するページ遷移を自動的にAjaxすることで高速化を図ります。
仕組みを簡単に説明すると、Turbolinksはa要素のクリックイベントをフックして、遷移先のページをAjaxで取得します。そして、取得したページが要求するJavaScriptやCSSが現在のものと同一であれば現在のものをそのまま利用し、title要素やbody要素のみを置き換えます。リクエストごとにJavaScriptやCSSをブラウザが評価しなくなるため、パフォーマンスを向上させることができるのです。title要素やbody要素のみを置き換えるだけなので、ページ遷移は発生しませんが、...
引用:現場で使えるRuby on Rails 速習実践ガイドp340

高速化を図る為の仕組みで、ページ遷移を発生させず置き換えて表示させる仕組みということか。ふーん。

turbolinks:loadとは?

Turbolinksは、ページ遷移や戻る、進むといったブラウザの動作をフックして動作する形になるため、ブラウザが用意しているイベントだけでは、意図したとおりの処理を実現できないことがあります。
そのため、 Turbolinksは自身の処理状態に応じてイベントを発行するようになっています。例えば、「turbolinks:load」イベントは初回のページ表示時やTurbolinksによりページの状態が遷移した際に発火するイベントです。
引用:現場で使えるRuby on Rails 速習実践ガイドp342

ブラウザが用意しているイベントでは実現できない場合は、自身の処理状態に応じてイベントを発行させる記述が必要ということか。「turbolinks:load」は、その例でこれを記述することによって、ページ表示・遷移時(=処理状態)にイベントが発火できるということか?
今回の場合は、ブラウザが用意しているイベントでは実現できないのだな。

8-3 2-1 ブラウザのページ遷移が発生しないことが多くなる
「ページが表示される際、DOMの用意ができたタイミングで何らかの処理を実行したい」といったことはよくあります。(省略)...jQueryの「$(document).ready()」がありますが、Tubolinksによるページ遷移の場合はうまく機能しません。なぜなら実際にはブラウザのページ遷移が発生しておらず、イベントが発火しない為です。
引用:現場で使えるRuby on Rails 速習実践ガイドp342

上でいう、何らかの処理(jQueryの「$(document).ready())は今回のケースに合致するな。。
お!ここに「実際にはブラウザのページ遷移が発生しておらずイベントが発火しない」と書かれていますね!!
今回の場合も、ページ遷移が発生しない結果イベントが発火しないから、表示できなかったわけですね。

たとえば、Turbolinks環境下において、次のJavascriptコードが読み込まれる画面があるとしましょう。この画面を、ブラウザ(タブ)で、このアプリケーション内で最初に開くページとしてアクセスするならば、この処理は実行され、「Hello,World」と表示されます。しかし、アプリケーションの別の画面のリンクを辿って到達した画面にこのスクリプトがあっても実行されないのです。
引用:現場で使えるRuby on Rails 速習実践ガイドp342

windows.onload = function(){
 console.log('Hello World!');
};

確かに最初からタブのページを表示させた場合は、動作したけれど、別の画面からタブのページにきた時はリロードしないと表示できなかった!!この状況だったんです、先生!!

この問題は、前述したTurbolinksが提供するイベントturbolinks:loadを利用することで解決します。
引用:現場で使えるRuby on Rails 速習実践ガイドp343

document.addEventListener("turbolinks:load", function() {
windows.onload = function(){
 console.log('Hello World!');
});

上で挙げた解決法と同じことを言っていますね!!

逆に言えば、Turbolinksを利用している場合は、このようなTurbolinks専用のイベントを使わないとJavaScriptを正しく動かすことができないということです。つまり、Turbolinksに依存したJavaScriptを書くことになります。
引用:現場で使えるRuby on Rails 速習実践ガイドp343

最後に結論が書いてありますね。

「Turbolinksを利用している場合は、このようなTurbolinks専用のイベントを使わないとJavaScriptを正しく動かすことができない」

解決方法にあげたコードを追加する理由はこれですね。以上です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

eslint-plugin-prettier って generally not recommended だったのか

背景

手元のアプリで npm audit したら万単位で脆弱性がレポートされてきたので、そろそろ package-lock.json を再作成しようか…とやってみたところ、 prettier のルール違反が消えなくなってしまった。正確には、 prettier の警告にしたがって修正したら eslint(prettier/prettier) のエラーが出て、そちらを直したら元の prettier エラーになってしまうという conflict 状態。なんでかいな、とググったら…

「eslint で prettier 実行」はお勧めではない(generally not recommended)

見つけたのが この記事。 Prettier 公式が

  • prettier と eslint を組み合わせて使うなら、 prettier はコード・フォーマッターに、 eslint は静的解析に各々専念させたほうがよい
  • eslint-plugin-prettier などのような「eslint から prettier を実行するような連携方法」はお勧めではない

という趣旨のことを明言しちゃってるんですよね。 eslint-plugin-prettier を使うやり方しか知らなかったのでびっくりしました。

実践

  • eslint-plugin-prettier 使うのやめます。 prettier-eslint-cli もやめます。
  • prettier そのものは VSCode のプラグインとして常に裏で動くようにしておきます(いちいち意識しなくても済むように)。
  • lint-staged で実行されるコマンドに prettier --write --ignore-unknown を加えます。
  • .prettierignore ファイルを作ります。中身は .gitignore と同じでよいです。
  • CI 環境があるときは、 npm test の前に prettier --check . するようにします。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javascript 関数宣言と関数式について

◎はじめに

Javascriptを扱う上で関数宣言と関数式のどちらを使用するべきか気になったのでまとめてみました。

◎関数の役割

Javascriptの関数は第一級オブジェクトと言われており、変数への代入や配列、オブジェクト、関数に関数を持たせることができる非常に柔軟なオブジェクトです。

頻発して使用する一定の処理を予め定義して、それを何度も使用することが関数の役割です。

◎関数宣言とは

function fn(){
  //処理
}
//このように関数を定義すること。

◎関数式(関数リテラル)とは

const fn = function(){
  //処理
}

const fn = () => {
  //処理# 
}
//このように関数を式として変数に代入することです。

※基本的には、無名関数として使用します。

関数宣言と関数式の違いについて

①関数宣言では巻き上げ(ホイスティング)が発生します。
具体的には下記内容です。

◎関数宣言の場合

fn();
function fn() {
  console.log("関数宣言")
};
// 関数宣言(実行される)

◎関数式の場合

//関数式
fn();
const fn = () => {
  console.log("関数式");
}

// Uncaught ReferenceError: Cannot access 'fn' before initialization

②関数宣言は上書き(再代入)が可能で、もし関数が上書きされると途中で実行処理が変わるため、意図しない挙動をすることが考えられます。

◎関数宣言の場合

function fn() {
  console.log("関数宣言")
};

fn = function (){
  console.log("上書きされました。")
}
fn();
// 上書きされました。

上記を確認するとfn関数が別の処理の関数に上書きされたことがわかるかと思います。文字列や数値を代入することもできてしいますのでバグの温床になってしまう可能性があります。

◎関数式の場合(変数宣言はconst)

const fn = () => {
  console.log("関数式");
}

fn = () => {
  console("関数式再代入")
}
fn();
// Uncaught TypeError: Assignment to constant variable.

関数式の場合、再代入するとエラーが発生します。そのため、意図しない再代入を予防することができます。
変数宣言がlet/varですと再代入が可能なため、この事象を防ぐことはできません。そのため、安全なコードを書くためには変数宣言にconstを使用しましょう。
※変数定義について

const fn = () => {
  //処理
}

◎ホイスティングとは

変数や関数の定義をコード実行前にメモリー領域に配置することです。

fn();

function fn() {
  console.log("fnが実行されました。");
};

このfunction fn()がコード実行前にメモリー領域に保持されるため、コード実行中にはすでにfn関数を実行することができる。
この巻き上げはどの変数定義でも起きてしまう現状ですが、let/constであれば関数定義よりも先に関数を実行するとエラーが起きるためこの動作を防ぐことができます。

ベストプラクティスは他にもあるかもしれません。こちらご教授をいただけますと幸いです!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Storybook v6.0新機能まとめ

はじめに

https://storybook.js.org/releases/6.0
2020年8月10日にStorybook6.0がリリースされました。
5.3からアップグレードを行う際に調べた新機能や変更点などを自分なりにおおまかにまとめてみました。
基本的にはドキュメントや公式の記事に書いてある内容になるので詳しく知りたい方はこちら

※間違いなどがありましたらご指摘お願いします。

Storybook 5.3からの主な変更点

ざっくり

  • セットアップが爆速でできるようになった
  • ストーリーの再利用性があがった
  • 複数のStorybookを一つにまとめられるようになった
  • ドキュメントが充実

Zero-config

公式がconfig書かなくていいよ!と言っている通り、セットアップが非常に簡単になりました。
具体的には以下になります。

  • Typescriptのサポート
  • Storybook Essentialsの登場

Typescriptのサポート

Typescriptサポートが組み込まれました。これにより、プロジェクト内のTS固有のwebpackやbabel構成を削除できるようになりました。指定がない限りは各フレームワークの基本構成を使用するそうです。わかりやすい。

Storybook Essentials

開発チームが厳選した、ほぼ必須アドオン達のよくばりパックです。もちろん面倒な設定を書く必要はありません。
Storybookには公式アドオンの他に多くのサードパーティアドオンがあり、カスタマイズ性がありましたが、ありすぎるが故にセットアップが困難になる可能性がありました。
このEssentialsはinitをしただけで勝手に追加され、よしなに動いてくれます。どれだけわかりやすいんだ。

Storybook Args

ArgsはStorybookの記法であるComponent StoryFormat(CSF)の新しい概念です。
コンポーネントのpropsの初期値の宣言や再利用ができるようになります。

こうかいていたのが

export const Default = () => (
  <Button label="hello" backgroundColor="#222222" />
);

export const Disabled = () => (
  <Button label="hello" backgroundColor="#222222" disabled={true} />
);

export const Large = () => (
  <Button label="hello" backgroundColor="#222222" size="large" />
)

Storybook Argsの場合こうやってかけます

const Template = (args) => <Button {...args} />;

export const Default = Template.bind({});
Default.args = { label: "hello", backgroundColor: "#222222" };

export const Disabled = Template.bind({});
Disabled.args = { ...Default.args, disabled:true };

export const Large = Template.bind({});
Loading.args = { ...Default.args, size: "large" };

propsの数が多くなればなるほど、恩恵を受けることができます。
また、この書き方はもはやStorybookに依存していないためJestやその他ツールなどでも利用できます。
便利です。

Controls

Storybook上からコンポーネントを動的に編集することができる新しいアドオンです。Essentialsに含まれており、特別なセットアップは必要ありません。
Storybook Argsで書いた場合コントロールが自動生成されます。べんり。
ezgif.com-video-to-gif.gif

Composition

複数のStorybookを1つにまとめる事ができます。
異なるフレームワークを使用したプロジェクト間でも、Storybookを切り替えることなく共通デザインシステムを参照したり、全体感を確認できるようになりました。
筆者はまだ試せていないので、詳細はこちら

公式ドキュメント

v6.0に合わせて一新されました。
チュートリアルやガイドが非常に充実していて、わかりやすかったです。

まとめ

v6.0からの新機能や変更点をおおまかにまとめてみました。
これ以外にも非推奨になったAPIやアドオンがあったり、変更された仕様などがあるので確認したい方は公式マイグレーションガイドを参照してください。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Reactのディレクトリ構成どうしてる?

前置き

Reactのスターターキットを作ってみて、ディレクトリ構成について迷いながらも作ってみたので、その構成について紹介したいと思います。

構成について言及している記事はまだ少なく、ベスト構成とというものは存在しないと思います。
今回紹介する構成もまだまだ改良していかなければいけないと思いますが、構成について迷っている方に少しでも役立てればと思います。

開発環境について

node v12.13.1
npm v6.13.4

使用している技術について

  • React (reactHooks)
  • Redux (Redux-toolkit)
  • TypeScript

今回作成したスターターは、redux-toolkitを使っています。
小さいプロジェクトにはredux不要では??という考えもあると思います。(自分もちょっとそう思います)
ですが、redux-toolkitの登場によってreduxを簡単に使えるようになったこと、
またreduxを追加実装するコストを考えると最初から入れてしまった方がいいのでは、という考えのもと、初期から含めることにしました!
↓この記事にものすごく感銘を受けました!
TypeScriptでReactをやるときは、小さいアプリでもReduxを最初から使ってもいいかもねというお話

公式での扱いは:eyes:??

Reactは推奨されるディレクトリ構成はありません。
公式でも考えすぎるべきではないと書かれています。

React はファイルをどのようにフォルダ分けするかについての意見を持っていません。

まだプロジェクトを始めたばかりなら、ファイル構成を決めるのに 5 分以上かけないようにしましょう。上述の方法の 1 つを選ぶか、自分自身の方法を考えて、コードを書き始めましょう! おそらく実際のコードをいくらか書けば、なんにせよ考え直したくなる可能性が高いでしょう。

規模によって適切なディレクトリ構成が変わり、一つの答えはありません。
また、React周りは新しいツールがどんどん出てきて、何を使うか、どのように使用するかは目まぐるしく変わっているように感じます。
それも相まってディレクトリ構成を決めることが難しくなってるのだと思います。

構成について

さて本題です!
早速ですが実際作成したコードはこちら↓
https://github.com/Sotq17/rtk_starter

簡単にどのディレクトリでどういったファイルを扱うかを説明したいと思います。
(実際のコードを見てもらえると早いかもしれません…)
構成は以下の通りになっています。

─── src
    ├── img (画像置き場)
    │   └── common
    │       └── favicon.png
    ├── pug (render先を指定)
    │   └── index.pug
    ├── ts (react外でjsを使うときに使用)
    │   └── app.ts
    ├── tsconfig.json
    └── tsx
        ├── index.tsx (エントリーポイント)
        ├── stores
        │   ├── index.ts (slicesディレクトリで作られたSliceを結合する)
        │   └── slices (このディレクトリ下で各sliceファイルを扱う)
        │       └── userSlice.ts
        ├── style(各page,componentのcssを切り分けたい時に使用)
        │   ├── GlobalStyle.tsx
        │   ├── components
        │   │   ├── atoms
        │   │   │   └── Button.tsx
        │   │   └── block
        │   │       ├── Footer.tsx
        │   │       └── Header.tsx
        │   ├── pages
        │   │   ├── Login.tsx
        │   │   └── Top.tsx
        │   ├── resetStyle.tsx
        │   └── variables.tsx
        ├── utils(定数などを管理)
        │   └── constants.tsx
        └── views
            ├── components(使い回しのできる要素)
            │   ├── atoms(最小単位のcomponent)
            │   │   └── Button.tsx
            │   ├── block(atomsを組み合わせたり、atomsでは管理しきれないcomponent)
            │   │   ├── Footer.tsx
            │   │   └── Header.tsx
            │   └── modules(機能を持ったcomponent)
            │       └── ScrollTop.tsx
            └── pages(各ページの呼び出し先)
                ├── login
                │   └── Login.tsx
                └── top
                    ├── Top.tsx
                    └── TopList.tsx(そのページでしか使われないreact-componentは同階層に設置)

index.pugtsx/index.tsxをrenderする形なので、tsxディレクトリ以下を紹介していきます!

index.tsx

エントリーポイントです。
各ページを呼び出し、react-router-domでルーティングを行う役割です。

index.tsx
const app = document.getElementById('app')
// page
import Top from './views/pages/top/Top'
import Login from './views/pages/login/Login'

// react-router-domでページ遷移
ReactDOM.render(
  <Provider store={store}>
    <GlobalStyle />
    <Router>
      <Switch>
        <Route path="/top" component={Top} />
        <Route path="/" component={Login} />
      </Switch>
    </Router>
  </Provider>,
  app
)

views

└── views
    ├── components(使い回しのできる要素)
    │   ├── atoms(最小単位のcomponent)
    │   │   └── Button.tsx
    │   ├── block(atomsを組み合わせたり、atomsでは管理しきれないcomponent)
    │   │   └── Header.tsx
    │   └── modules(機能を持ったcomponent)
    │       └── ScrollTop.tsx
    └── pages(各ページの呼び出し先)
        └── 各ページのディレクトリ
            └── Login.tsx

pages

└── pages(各ページの呼び出し先)
       ├── Top
       │    ├── TopList.tsx
       │    └── Top.tsx
       └── Login
            └── Login.tsx

上記の tsx/index.tsxに呼び出されるコンポーネントです。
各ページに1つこの要素が存在し、そのページ内で使われるコンポーネントはさらにここから呼び出して使用します。
また、そのページにしかない固有の要素は同ディレクトリ内に配置します。
上記のtreeで言うと、TopList.tsが固有の要素にあたります。

例↓

top.tsx
const Top = () => {

  return (
    <div>
      <Header />
      <h1 css={TopTitle}>TOP</h1>
      <TopList />
      <Footer />
    </div>
  )
}

components

pageとは異なる、使い回すことのできる要素をこちらに置きます。
その中にも意味合いが異なる要素が多いため、さらに下記のディレクトリに分けていきます。
atomicデザインでできれば良かったのですが、知見がないためとりあえず下記のようにざっくり作ってます)

├── components(使い回しのできる要素)
       ├── atoms
       │  
       ├── block
       │   
       └── modules
  • atoms
  • block
  • module
atoms

ボタンなどの最小の要素です。変更出来るよう、styleや機能は流し込めるように作成します。

Button.tsx
import React from 'react'
import { css } from '@emotion/core'

export const Button = props => {
  const button = css({
    backgroundColor: `${props.bgColor}`,
    color: `${props.color}`,
    border: `1px solid ${props.bgColor}`,
    padding: '8px 16px',
    borderRadius: '4px',
    cursor: 'pointer'
  })

  return (
    <button css={button} onClick={props.onClick}>
      {props.name}
    </button>
  )
}

ちなみに呼び出すのはこんな感じです↓

Sample.tsx
const Sample = () => {
  const handleClick = () => {
   console.log("click!")
  }

  return (
    <div>
      <Button
        onClick={handleClick}
        name="Click!"
        color="#ffffff"
        bgColor="#33333"
      />
    </div>
  )
}
block

ページに1個しかないHeaderやFooter、また使い回すけどatomsを組み合わせて使う要素を置きます。

modules

atomsへの機能の流し込みが難しかったり、そのコンポーネント固有の機能があるものをここに置きます。
例えば、パンくずリストなどが該当します。

パンくずではないですが、サンプルです↓(この程度ならatomsでいいですけど…)

ScrollTopBtn.tsx
export const ScrollTop = () => {

  const scrollToTop = () => {
    scroll.scrollToTop()
  }

  return (
    <div css={ButtonContainer} onClick={scrollToTop}>
      <p css={ButtonContent}></p>
    </div>
  )
}

store

reduxで使用する要素は全てこちらに配置します。
redux-toolkitを使用しておりますが、使い方に関しては今回割愛します。

slicesディレクトリ以下で Reducer / Actionを定義し(createSlice)、
store/index.tsxでslicesディレクトリ以下で作られたファイルを結合するといった流れです。

├── stores
     ├── index.ts 
     └── slices
         └── userSlice.ts
index.ts
import { configureStore } from '@reduxjs/toolkit'
import loginReducer from './slices/userSlice'

// それぞれのSliceを呼び出して結合する
export default configureStore({
  reducer: {
    // 識別する名前: importしてきたReducer名
    login: loginReducer
  }
})
userSlice.ts
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
import axios from 'axios'
import {apiURL} from "../../utils/constants"


// 非同期はSliceの外に出してcreateAsyncThunkを使用する
export const fetchAsyncLogin = createAsyncThunk('login/get', async () => {
  //  ログインAPIを叩く想定
  await axios.get(apiURL)
  //   console.log(res.data)
})

const loginSlice = createSlice({
  //   slice名
  name: 'login',
  //   初期値
  initialState: {
    auth: {
      username: '',
      password: ''
    },
    isLogin: false
  },
  //各reducer 第一引数でstate情報を受け取り、第二引数でユーザーが操作した情報を受け取る
  reducers: {
    editUsername: (state, action) => {
      state.auth.username = action.payload

    },
    editPassword: (state, action) => {
      state.auth.password = action.payload
    },
    logout: (state, action) => {
      state.isLogin = false
    }
  },
  //   非同期の結果を受け取る
  extraReducers: builder => {
    builder.addCase(fetchAsyncLogin.fulfilled, (state, action) => {
      state.isLogin = true
    })
  }
})

// actionをexport
export const { editUsername, editPassword, logout } = loginSlice.actions
// state情報をexport
export const selectUser = (state: any) => state.login
// reducerをexport → storeへ
export default loginSlice.reducer

style

resetCSSや、CSS変数などを定義するために使います。

また、emotionを使っているのでtsxファイル内に書くことができますが、あえて分けたいと言う場合に使用します。
例えば、tsxファイル内が肥大化してしまう場合や、同じcssを使い回す場合などです(コンポーネント切り分けるのが難しいこともあると思うので…)。

ディレクトリの分け方はとりあえずviewsディレクトリと同じ構成にしております。

├── style(各page,componentのcssを切り分けたい時に使用)
       ├── GlobalStyle.tsx
       ├── components
       │   ├── atoms
       │   │   └── Button.tsx
       │   └── block
       │       ├── Footer.tsx
       │       └── Header.tsx
       ├── pages
       │   ├── Login.tsx
       │   └── Top.tsx
       ├── resetStyle.tsx
       └── variables.tsx

utils

tsx内で使用する変数、定数など、全体で使用する要素を置きます。

├── utils(定数などを管理)
     └── constants.tsx
constants.tsx
export const apiURL = ''
// など ...

作ってみた感想

これを作る上でご意見、ご協力いただいた方々には最高に感謝です:raised_hands:

まだまだ雑なところが多く、使ってみて思うことはたくさんあるのですが、viewsstoreで分ける構成は悪くないんじゃないかな…と思ってます。
うちではこうしてる、ここはこうした方が良いなど、ご意見いただけますと幸いです:hugging:

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

NTPに頼らず、Symbol from NEMブロックチェーンで同期された現在時刻を取得する

今回はブロックチェーンとは少し離れた内容です。
Symbol from NEM では内部で独立したネットワーク時刻を保有しています。
これを外部から利用するための方法です。NTPサービスなどにアクセスする必要はありません。

事前準備

NODE = 'https://sym-test.opening-line.jp:3001';

(script = document.createElement('script')).src = 'https://xembook.github.io/nem2-browserify/symbol-sdk-pack-0.21.0.js';
document.getElementsByTagName('head')[0].appendChild(script);

ライブラリインポート

nem = require("/node_modules/symbol-sdk");
nodeHttp = new nem.NodeHttp(NODE);
networkHttp = new nem.NetworkHttp(NODE);

Symbol のエポックタイム取得

props = await networkHttp.getNetworkProperties().toPromise()
epoch = nem.UInt64.fromNumericString(
  props.network.epochAdjustment.replace("s","")
) * 1000

Symbol from NEMでは2019 11/11 0時(GMT)がエポックタイムとして指定されているため、その秒情報をネットワークより取得しておきます。

現在時刻取得

nodeTime = await nodeHttp.getNodeTime().toPromise()
console.log(new Date(epoch + nodeTime.receiveTimeStamp.compact()));

ノードより現在時刻を取得してエポックタイムと足し合わせてJavaScriptのDate関数より現在時刻を出力します。

Wed Oct 14 2020 00:10:51 GMT+0900 (日本標準時)

無事出力されました。

ちなみにライブラリを使用したくない場合は以下のエンドポイントより情報を取得できます。

network. epochAdjustment

http://api-01.ap-northeast-1.0.10.0.x.symboldev.network:3000/network/properties

communicationTimestamps.receiveTimestamp

http://api-01.ap-northeast-1.0.10.0.x.symboldev.network:3000/node/time

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む