- 投稿日:2020-10-14T23:44:59+09:00
css,js,html 備忘録
要素を上下中央に配置する方法
https://codepen.io/liang-kang/pen/GRqopmj
参考元:https://sole-color-blog.com/blog/532/
- 投稿日:2020-10-14T23:11:10+09:00
[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' ); }); });
- 投稿日:2020-10-14T22:58:58+09:00
#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
- 環境作成編
#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というソフトを開く
Git Bashを起動後に,以下のコマンドを入力して正しくインストール/更新ができているか確認.
$ git --version2. Git Hubでリポジトリの作成
ログインした後,左上のNewからリポジトリを作成.すでに作ったものはNewの下にリストとして表示されている.
Newを押すとリポジトリの作成画面が表示され,
①任意のリポジトリ名(プロジェクト名)
②リポジトリ説明文:任意
③公開するか,非公開にするか.(2019年から無料アカウントでも非公開のリポジトリを作成可能に)
④「Add a README file」はリポジトリの説明や使い方を書くREADMEファイルを事前作成するかどうか.基本的にはチェックを付けとく.⑤Git の管理に含めないファイルを指定するためのファイル(除外ファイルの指定).
⑥ライセンス=使用許諾条件,ライセンスの選択は義務ではないが,ライセンスがない場合はデフォルトの著作権法が適用され,ソースコードの複製,配布,派生物の作成は誰にも許可されない.オープンソースのプロジェクトを作成をする際にはオープンソースライセンスを設定する.
3.リポジトリとEclipseのプロジェクトを連携
でてきた画面の「HTTPS」を右側のボタンを押してコピーする.
Eclipseを開き,サーバを停止しておく.
プロジェクト・エクスプローラーからプロジェクトフォルダを右クリックし,
「インポート」→「インポートの共有」を押す.インポート画面が開くので,
「Git」→「Gitからプロジェクト」を押す.クローンを選ぶ.
①先ほどGitでリポジトリを作成した際にコピーしたHTTPSのアドレスをコピペする.
②GitHubに登録しているユーザー名
②GitHubに登録しているパスワード
をそれぞれ入力する.作成されているブランチの一覧が表示されるので(今回はmainのみ),一番最新のブランチを選択する.
どこにフォルダを作成されるか聞かれるので,自身で設定したワークスペースファルダ内にプロジェクト名のファルダを作成して設定すると編集が楽.
前回記事からの流れで作っていれば(C:\Users\ユーザ名\Documents\eclipse\プロジェクト名)となる.初めて作成する際に,秘密の質問を作成するか聞かれるので任意で作成する.一応作っていたほうがいい.
新規プロジェクト・ウィザードを使用してインポートにチェックして完了を押す.
あとは, #1 Webアプリを作る為の環境を構築しようの記事にあるように動的Webプロジェクトを新規作成していく.
次に,Eclipse上でGitHubのブランチを管理する為に不可欠なタブを,チーム→リポジトリ・ビューで表示から追加しておく.
新規で作ったプロジェクトフォルダをプロジェクト・エクスプローラから右クリックし,チーム→コミットを選択.
Gitステージングの為のタブが出るので,そこから2個の+マークを押すと編集したがコミットされていないファイルがすべて下に移動する.特定のファイルだけ更新したい際にはファイル名を右クリックすると1個づつ追加できる.
下にファイルが移動していれば大丈夫.
コミットメッセージを入力して(add:変更点など)を入力してコミットを押す.
Gitレポジトリータブを確認してmainの横にコメントついてることを確認.この時点ではオンライン上では変更されておらずGitHub上にはいないので注意.
Gitステージングタブから,HEADのプッシュを選択.
色々と確認タブが出てくるがそのままで大丈夫.
Remote Tracking 内にあるmainの横にコメントついてることを確認すれば大丈夫,GitHub上にも更新されている.
4.作業用ブランチの作成
チーム→切り替え→New Branch...を選択.
新しく作成するブランチ名を指定する.ここではnew1というブランチを作成.
Gitリポジトリを確認するとnew1が追加されているのを確認する.
ブランチ名の左上にチェックマークがついているのが編集中のものなのでnew1をクリックする.
チェックアウトするか聞かれるのでチェックアウトする.(チェックアウトって日本語だと違和感ある)
new1の左上にチェックマークがついてれば大丈夫.
「3.リポジトリとEclipseのプロジェクトを連携とコミット」でやったようにコミットをするとRemote Tracking 内にmainとは別にnew1が増えていればブランチの作成は完了.
5.共同でリポジトリを編集する(collaborator登録).
GitHubから共同で編集したいリポジトリを開き,Settingsを開く.
Stteings内のタブからManage accessを選ぶ.
Invite a collaboratorを押す.
検索窓が出てくるので,一緒に編集したい相手のユーザIDかメールアドレスを入力する.
候補がでてくるので選択.
Add ユーザ名 to this repository を押す.
Manage accessに追加されていれば問題ない.
招待された人には登録メールアドレスにメールが届くのでView invitationを押す.
リンク先で参加するかを尋ねられるのでacceptすれば参加完了.参加するとステータスがCollaboratorに変わっている.
追加されたコラボレータはレポジトリにアクセス権限が与えられ,プッシュが可能になる.これで共同編集の準備が完了.
6. 参加したレポジトリの最新版ブランチをプルする.(最新版をダウンロードしてくる)
Gitリポジトリのタブの右上にあるクローンを押す.
①コラボレータになったリポジトリのHTTPSのアドレスをコピペする.
②GitHubに登録しているユーザー名
②GitHubに登録しているパスワード
をそれぞれ入力する.プルしたいブランチのみにチェックを入れて
ディレクトリは分かりやすいところに設定して完了を押す.
レポジトリのフォルダが作成されるので,Branches→Localからクリックでそのままmainから開くか,レポジトリフォルダを右クリックしてインポートからプロジェクトを開く.(新規作成と同じ手順でやると上手く開かない時があるので注意.)
しっかりとプロジェクトが開かれているのを確認したら,あとは自分が作業するブランチを新しく作ってプログラムしていく.
7. 完成したプロジェクトをデフォルトブランチとして更新(最新の状態を共有できるように)
2つ方法があり
・Git Hubでデフォルトのブランチを変更する.
・mainに設定しているブランチにマージする.Git Hubでデフォルトのブランチを変更する方法では,
①Settingsタブを選択
②Branchesタブを選択
③デフォルトにしたいブランチを選択してUpdateを押す.mainに設定しているブランチにマージする方法では,ブランチを切り替えて最新版をプッシュすればおk.
とりあえずこれで共同開発できる準備ができました.
次回は「どんなサイトを作るか」考えていきます.
- 投稿日:2020-10-14T22:49:00+09:00
ES2015(ES6)のモジュールについてまとめ
そもそもモジュールって何?
まず前提として、アプリケーションはたくさんのプログラムから成り立っています。
この全てをひとつのファイルで管理するとファイルの肥大化を誘発させてしまいます。ファイルの肥大化による弊害は次のようなものがあります。
- コード量が多くファイルが縦長になる
- ぱっと見どこに何が書いてあるのかわからず、ものすごく見にくい。
- 先に定義した変数を上書きしてしまうことによるバグの発見が遅れる
そのため、規模の大きなアプリケーションはファイルの肥大化による弊害を防ぐためにひとつのファイルにすべてのコードを記載するのではなく、複数のファイルに分けます。この分割したファイルひとつひとつをモジュールといいます。一般的には機能ごとにコードを分けてファイル名を「機能名.js」のように命名するようです。
モジュールを利用するためには
- 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.jsfunction hello() { console.log("hello"); } hello();moduleB.jsfunction hello() { console.log("hello"); } hello();moduleC.jsfunction hello() { console.log("hello"); } hello();関数「hello」に内容を付け加えたい場合はすべてのスクリプトで追加が必要になります。
これでは非常に保守性は低いです。moduleA.jsfunction hello() { console.log("hello"); console.log('see you'); // 追加 } hello();moduleB.jsfunction hello() { console.log("hello"); console.log('see you'); // 追加 } hello();moduleC.jsfunction hello() { console.log("hello"); console.log('see you'); // 追加 } hello();ではmoduleA.jsで関数「hello」を定義し、moduleB.js、moduleC.jsからインポートしてみましょう。すっきりした書き方になりました。
moduleA.jsexport function hello() { // 関数「hello」をエクスポート console.log("hello"); console.log('see you'); } hello();moduleB.jsimport { hello } from "./moduleA.js" // 関数「hello」をインポート hello(); // 「hello」,「see you」と表示されるmoduleB.jsimport { hello } from "./moduleA.js" // 関数「hello」をインポート hello(); // 「hello」,「see you」と表示されるこの状態で関数「hello」にbyeを出力する処理を付け加えてみましょう。
moduleA.jsだけを編集すればすべての関数に反映されます。これで保守性は高くなりました。moduleA.jsexport function hello() { // 関数「hello」をエクスポート console.log("hello"); console.log('see you'); console.log('bye'); } hello();moduleB.jsimport { hello } from "./moduleA.js" // 関数「hello」をインポート hello(); // 「hello」,「see you」,「bye」と表示されるmoduleB.jsimport { 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.jsconst language = "英語"; export default language;※ひとつのモジュールにつき1回しか使えない
export.jsconst language = "英語"; function hello() { console.log("ハロー"); } // オブジェクトの宣言 const person = { name: "Kuma", age: 29, gender: "male" }; export default language; export default hello; // 2つ以上使おうとするとエラーになるデフォルトエクスポートのimportの書き方
- {}をつけずにむき出しで記載する
- ひとつしかエクスポートしないので好きな名前でインポートできる
import.jsimport 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 = "英語"; //エクスポート構文おわり
- 投稿日:2020-10-14T22:47:47+09:00
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を使ってダイレクトに調べます。
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. アプリの作成
- 投稿日:2020-10-14T21:18:02+09:00
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 続きの記事を公開予定)
- 投稿日:2020-10-14T21:08:17+09:00
【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 (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.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を使用してルーティングさせてましたので、
上記の処理を実装する流れとなりました。先輩曰く、もっと良い処理があるらしいのですがそれはまた別の機会に。
- 投稿日:2020-10-14T18:34:58+09:00
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>'); } }よりスマートな書き方があれば教えてください。
- 投稿日:2020-10-14T18:04:17+09:00
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-cliusage
$ 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が吐き出されるのでブラウザで見てみると、
画像のようにサイドバーには「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 をリニューアルした
- 投稿日:2020-10-14T17:34:40+09:00
【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
のネイティブ部分が変わった故なのかいまいち判断がつかない(そこまで知識がない)のですが、同様の事象に遭遇される方もいるかと思うので記事にしました。
- 投稿日:2020-10-14T15:37:53+09:00
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 速習実践ガイドp342windows.onload = function(){ console.log('Hello World!'); };確かに最初からタブのページを表示させた場合は、動作したけれど、別の画面からタブのページにきた時はリロードしないと表示できなかった!!この状況だったんです、先生!!
この問題は、前述したTurbolinksが提供するイベントturbolinks:loadを利用することで解決します。
引用:現場で使えるRuby on Rails 速習実践ガイドp343document.addEventListener("turbolinks:load", function() { windows.onload = function(){ console.log('Hello World!'); });上で挙げた解決法と同じことを言っていますね!!
逆に言えば、Turbolinksを利用している場合は、このようなTurbolinks専用のイベントを使わないとJavaScriptを正しく動かすことができないということです。つまり、Turbolinksに依存したJavaScriptを書くことになります。
引用:現場で使えるRuby on Rails 速習実践ガイドp343最後に結論が書いてありますね。
「Turbolinksを利用している場合は、このようなTurbolinks専用のイベントを使わないとJavaScriptを正しく動かすことができない」
解決方法にあげたコードを追加する理由はこれですね。以上です。
- 投稿日:2020-10-14T15:28:00+09:00
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 .
するようにします。参考
- 投稿日:2020-10-14T14:00:59+09:00
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であれば関数定義よりも先に関数を実行するとエラーが起きるためこの動作を防ぐことができます。ベストプラクティスは他にもあるかもしれません。こちらご教授をいただけますと幸いです!
- 投稿日:2020-10-14T11:24:06+09:00
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で書いた場合コントロールが自動生成されます。べんり。
Composition
複数のStorybookを1つにまとめる事ができます。
異なるフレームワークを使用したプロジェクト間でも、Storybookを切り替えることなく共通デザインシステムを参照したり、全体感を確認できるようになりました。
筆者はまだ試せていないので、詳細はこちらへ公式ドキュメント
v6.0に合わせて一新されました。
チュートリアルやガイドが非常に充実していて、わかりやすかったです。まとめ
v6.0からの新機能や変更点をおおまかにまとめてみました。
これ以外にも非推奨になったAPIやアドオンがあったり、変更された仕様などがあるので確認したい方は公式マイグレーションガイドを参照してください。参考
- 投稿日:2020-10-14T07:24:23+09:00
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を最初から使ってもいいかもねというお話
公式での扱いは??
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.pug
にtsx/index.tsx
をrenderする形なので、tsxディレクトリ以下を紹介していきます!index.tsx
エントリーポイントです。
各ページを呼び出し、react-router-domでルーティングを行う役割です。index.tsxconst 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.tsxpages
└── pages(各ページの呼び出し先) ├── Top │ ├── TopList.tsx │ └── Top.tsx └── Login └── Login.tsx上記の
tsx/index.tsx
に呼び出されるコンポーネントです。
各ページに1つこの要素が存在し、そのページ内で使われるコンポーネントはさらにここから呼び出して使用します。
また、そのページにしかない固有の要素は同ディレクトリ内に配置します。
上記のtreeで言うと、TopList.ts
が固有の要素にあたります。例↓
top.tsxconst 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.tsximport 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.tsxconst 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.tsxexport 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.tsindex.tsimport { configureStore } from '@reduxjs/toolkit' import loginReducer from './slices/userSlice' // それぞれのSliceを呼び出して結合する export default configureStore({ reducer: { // 識別する名前: importしてきたReducer名 login: loginReducer } })userSlice.tsimport { 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.reducerstyle
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.tsxutils
tsx内で使用する変数、定数など、全体で使用する要素を置きます。
├── utils(定数などを管理) └── constants.tsxconstants.tsxexport const apiURL = '' // など ...作ってみた感想
これを作る上でご意見、ご協力いただいた方々には最高に感謝です
まだまだ雑なところが多く、使ってみて思うことはたくさんあるのですが、
views
とstore
で分ける構成は悪くないんじゃないかな…と思ってます。
うちではこうしてる、ここはこうした方が良いなど、ご意見いただけますと幸いです
- 投稿日:2020-10-14T00:44:42+09:00
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","") ) * 1000Symbol 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