【19日目】フロントエンド/JavaScriptを学ぶ 【作曲の補助ツールを作るまでの日記】

プログラミング

2023年9月30日~2023年10月12日

やっていこう

今回やりたいことは見た目の部分。

  1. マイページにもう少し”特別感”を
    1. プロフィール設定のレイアウトを弄る
  2. JavaScriptを学ぶ
    1. JavaScriptの文法を学ぶ
    2. JavaScriptってどう使うの?
    3. ReactとVue.jsとは
      1. そもそも
      2. モダンとは
      3. ReactとVue.jsのどちらを使うのか
      4. Vue.jsを使う
      5. 足りないJavaScript知識を学ぶ
        1. はじめての「`」(バッククォート)
        2. var,let,const
        3. 変数のスコープ
        4. 変数の巻き上げ
        5. breakやcontinueのラベル
        6. 2通りの関数の宣言方法
        7. プロシージャとクロージャ
        8. プロパティとオブジェクト
          1. 何故混乱するか
        9. 改めて、プロパティとオブジェクトとは
        10. 他言語のプロパティ・オブジェクトとどう違うか
        11. プロトタイプベースとクラスベース
        12. クラスベース
        13. プロトタイプベース
        14. JavaScriptにクラスは無いのか?
        15. DOM(Document Object Model)をザックリ理解する
          1. なぜJavaScriptはDOMの操作が必要なのか?
          2. DOMをより正確に理解する
        16. アロー関数
        17. Windowオブジェクト
        18. 癖強の「this」を大雑把に理解する
          1. thisを大雑把に理解する為の知識①「スコープはネストしている」
          2. thisを大雑把に理解する為の知識②「スコープの種類」
        19. Re.アロー関数
        20. n進数
        21. 非同期処理とPromise
          1. そもそもJavaScriptはシングルスレッドであるが、並行処理と非同期処理に対応している
          2. 非同期処理を順番に処理したい
          3. コールバック関数とは何か
        22. Promiseとは
          1. Promiseを軽く理解する
          2. Promiseを重く理解する
          3. Promiseの状態について
          4. Promiseの状態が変化する条件
          5. Promiseの連鎖[.thenと.catch]
          6. .then()のreject処理
          7. .catch()
          8. .catch()と.then()の第二引数の使い分け
          9. .thenと.catch()が処理を走らせる条件
          10. .thenと.catch()のPromiseの状態変化について
          11. とりあえず
  3. Nuxt.jsを使いたい
    1. Nuxt.jsとは
    2. 何故Nuxt.jsを使いたいのか
    3. レンダリング方法[SPA,MPA,CSR,SSR,SSG]
      1. そもそもレンダリングってなに
      2. MPAの対であるSPA
        1. SPAでURLが変わるのは何故?
      3. SSRとは何か
  4. 本当にNuxt.jsやVue.jsは必要か?
    1. コスパを考える・技術は手段であるということ
    2. Laravelは半SSRとも言える
  5. つまりフロントエンドはどうするか
  6. 改めて、Alpine.jsでSoundCloudっぽいモーダル画面を作る
    1. Alpine.jsとは
    2. ChatGPTにモーダル画面の骨組みを作ってもらう
    3. Alpine.jsでモーダルの処理を書く

マイページにもう少し”特別感”を

表現が難しいんだけど、マイページというからには”特別感”≒”楽しさ”が欲しい。

最初は自分の作った音楽・好きな音楽1曲を貼って置けるといいかな、と思ったんだけど、アップロード型だと著作権的に良くないっていうのと、音楽を1曲も完成出来ていないDTMerも結構いると思ったので今のところ却下。

これはマジなんだけど、音楽知識0からの初めてのオリジナル曲は本当に難しい。少なくとも私は初めてのオリ曲が出来るまで、DAWを買ってから2年くらいかかったんじゃないか?

逆に、そういう機能があると「早く作らないと」という気持ちが沸くという効果はありそう。

因みに今のマイページは少しCSSを弄ってこんな感じ。

別に悪くはないんだけど、なんかパッとしないというか、面白くないよね。

これが例えばQiitaだったら

Zennだったら

YouTubeだったら

GitHubだったら

SoundCloudだったら

Spotifyだったら

YouTube Musicだったら

X/Twitterなら

それぞれ、サイトの役割が違うので個性はあるけど、どれもマイページを見ていて面白い。

これらを参考にした上で欲しいマイページやそれに付随する機能

  • 任意サイトへのリンク
  • フォロー機能とその一覧表示
  • ブロック機能・ミュート機能・通報機能
  • 本人認証機能(運営負担が大きいので要検討)
  • Qiitaのような記事のストック・ブックマーク機能

うーん。コスパが良さそうなのはフォロー機能/ブロック系の機能/任意サイトへのリンクとかかなぁ。

フォロー機能はコストえぐそうだけど、パフォーマンスがデカい。
今の時代ブロック系の機能は必須と言ってもいいと思うんだよね。
任意サイトへのリンクはコスト低そうなのでやってみよう。

プロフィール設定のレイアウトを弄る

もう、ここら辺はSoundCloud様を参考にさせてもらう。
いや~、本当個人的な好みなんだけど、SoundCloudのデザインが好きなのよね。

SoundCloudは右下にeditボタンがあって、そこを押すと

こんな感じでモーダル画面? に遷移して色々弄れるようになる。

このモーダル画面っぽいのはJavaScriptだよね。


どうせWebアプリ作るならJavaScriptなしはきついだろうし、もう、ここら辺もいっそ学ぼう。

モーダル画面に欲しい機能は

  • アイコンの変更
  • ニックネームの変更
  • URLリンクの追加
  • 説明欄(自己紹介欄)の追加

とかかなぁ。

とりあえず、モーダル画面っぽいのを出す方法・JavaScriptを学ぶ。

JavaScriptを学ぶ

JavaScriptの文法を学ぶ

とうとうJavaScirptを学ぶ時が来た。
いきなりモーダル画面の実装とかで検索かけてもいいんだけど、基礎文法・ルールは押さえておきたい。

こちらもLaravelと同じように最初はPaizaで学ばせていただく。

……といってもPaizaのJavaScript講座はあんまり充実してないっぽい?
とりあえず学んでみる。

JavaScript入門編講座メモ

  • JavaScriptは基本ブラウザ側の処理だが、Node.jsを使えばサーバー処理もできる。
    これ、わざわざJavaScriptをサーバ―側で実行する意味は何だろう?
    ちょっと調べて見た感じ、言語の一貫性がやっぱり大きいのかな。バグも減るし、人材もわざわざPHP使える人を揃える必要が無いし。
    他に非同期処理・シングルスレッド処理というメリットもあったけど、説明できるほどの理解度でない。
  • console.log()で標準出力できるのは、分かりやすくてとても良い。
    Pythonから入った勢だと、SwiftとかPHPとかJavaの標準出力が難しいからね。
  • “西暦” + 2000 + 5 + “年”の結果
    この結果は”西暦20005年”という文字列になる。
    これは、左から計算が始まり、最初に”西暦”+2000=”西暦2000”という文字列が出来るから。
    エラーにならないんだね。
  • これ、入門編すぎて要らないかもしれない
    文字の型・if・for・while・配列とかの初歩的なところしかやんないのねこれ。
    流石に使い方は分かるから、演習問題を少しやるだけいいかな。
  • for文はPHPっぽくて、配列はPythonっぽい

とりあえず、一通り見終わった。
うーん。JavaScriptの真髄は見れなかったけど、基礎的なところは理解したかなぁ。

JavaScriptってどう使うの?

とりあえず基礎文法は分かったけど、これ、どう使うの?

よく聞くReactとかVue.jsとかそういうフレームワークを使うのか?

というか、そもそもReactとかVue.jsってなんなん?

マジでフロントエンドは分からない事だらけ(バックエンドもわからんが)なので、とりあえず有名どころなReactとVue.jsについて理解しよう。

ReactとVue.jsとは

正直なことを言っていい?
なんか、ReactとかVue.jsがどうたらって言ってる人はキラキラ系、スタバとかでMac開いてそう(ド偏見)

そんな偏見も含めて理解したい。

React公式ページ
Vue.js公式ページ

そもそも

ReactとVue.jsはどちらもJavaScriptで使える便利な拡張機能みたいなもの。
厳密に言えば、ReactはライブラリでVue.jsはフレームワークらしいんだけど、そんなに気にする所ではないと思う。

つまり、既に作られた関数群を簡単に使えて、その関数でいろんな実装が出来るよというもの。

どちらも、”モダンな技術”と言われたりする。

モダンとは

恐らく、私がReactやVue.jsをキラキラに見る理由に”モダン”がある。
つまり、新しいということ。

この場合、ReactやVue.jsの何が新しいのだろうか。

こちらの記事によると、モダンWebアプリの定義を「ページの全体読み込みをせずに、ユーザーのアクションによって部分的に変化するか」的な事を言っている(だいぶ意訳)。

例えば私のWebサイトの場合、「下書きを保存」ボタンを押すと「下書きを保存するURLに遷移→下書きを保存する→元のページにリダイレクトする」という処理をしている。

その為、「下書きを保存」ボタンを押すたびに画面全体の再読み込みが入って来る。
これは、非モダンってことだよね?

逆に言えば、この「下書きを保存」ボタンを押しても画面全体が再読み込みされず、JavaScriptでデータの保存処理を書いてあげればそれはモダンってことになる?

ReactとVue.jsのどちらを使うのか

とりあえず有名どころから触っていっとけば良い感があるので、どちらかの技術を利用してJavaScriptを使いたいんだけど、どっちがいいんだろうか。

色々調べて、それぞれの特徴をメモしてみる

React

  • Reactの方が何かと機能追加等のリリースが早い(Reactが新機能出して、それにVue.jsが追従する形が多い)
  • 世界的なユーザー数はトップシェア
  • 日本シェア的にはVue.jsと拮抗?
  • 学習コストがVue.jsより高い
  • TypeScriptとの相性がいい
  • コードがシンプル
  • しかし、学習コストは高い
  • 大規模向け

Vue.js

  • 日本シェアが一定数ある
  • 学習コストがReactより低い
  • 小・中規模向け

うーん。マジで迷うな。

ChatGPTのアシストを受けるとすれば、データ数が多くてリリースが早いReactがいいのかなぁ。
しかし、学習コストが高いのはちょっと怯んじゃうのよね。

Vue.jsを使う

マジで2,3時間くらい悩んで出た結論がVue.js。
Vue.jsを使う理由は、日本のシェアが高かったり、学習コストが低いというのもあるんだけど……この2つの画像を見てほしい。

左がReact、右がVue.jsなんだけど、Vue.jsの緑色……初音ミク色……だよね(ちょっと濃いけど)。
なんならネギに見えるまである。

決定。

足りないJavaScript知識を学ぶ

色々ReactとVue.jsの違いを学ぶ過程で、JavaScriptをもうちょっと学べるなと思ったので、Mdn Web DocというサイトのJavaScript中級編まで学ぶ。

はじめての「`」(バッククォート)

xを変数とすると「console.log(`Hello${x}`); 」のようにバッククォートで囲むことで変数を使ったり、関数を入れたり、改行が入ったテキストをそのまま使ったりできるみたい。
これ、最初シングルクォートかと思って引っかかった。

var,let,const

Pythonから入った者として、型とかそういうの苦手なのよね。
JavaScriptの場合、以下のような感じ

  • varは普通の変数
  • letは再宣言できないけど、再代入可能
  • constは再宣言も再代入もできない。
変数のスコープ

ここらへんも、競プロから入るとマジで意識できない。
JavaScriptには以下の4つのスコープがある

  • グローバルスコープ
    他の言語と同じように、同じスクリプトならどこからでも参照できる変数
  • モジュールスコープ
    モジュールがまだよくわからないけど、同じモジュールからのみ参照できる変数
  • 関数スコープ
    同じ関数からのみ参照できる変数
  • ブロックスコープ
    こいつが少し癖あり。
    if文やfor文の{}←この括弧の中で宣言した変数は、その中でしか参照できない。というものなんだけど、letやconstの場合のみ適応されて、varは適応されない。
変数の巻き上げ

混乱ポイント

console.log(x === undefined); // true
var x = 3;

(function () {
  console.log(x); // undefined
  var x = "local value";
})();

理解できなくはないんだけど、説明が難しい。
簡潔に言えば「var変数の宣言をすると、そのスクリプト内関数の最上部に変数の宣言だけが巻き上げられる」、つまり、var x = 3;のvar xだけが巻き上げられて、x = 3は無視される。
だから、変数の代入がされる前に宣言だけされちゃってundefinedになっているということ。

breakやcontinueのラベル

「break ラベル;」のようにすることで、ネストループしている時とかに指定したループ文をbreak・continueできる。
どうってことないんだけど、Pythonにもこれ欲しい。

2通りの関数の宣言方法

関数自体はめっちゃ普通。でも、宣言方法が2つあったので整理。

  • 関数宣言
    恐らくオーソドックスな方。
    function square(number) {
    return number * number;
    }
    みたいな感じで、関数を宣言する。
    この場合、宣言は巻き上げられる(スクリプト内や関数等の最上部で宣言したのと同じことになる)。
  • 関数式
    とりあえず形から。
    const square = function (number) {
    return number * number;
    };
    console.log(square(3)); // 9
    こんな感じで式にできる。
    これは巻き上げられない。
プロシージャとクロージャ

まあまあ聞くけど、何かあんまりよくわかんない単語たち。

プロシージャ

プロシージャは複数の処理を一つにまとめて、外部から呼び出せるようにしたものらしい。
関数とほぼ同じものと思ってもらっていいんだけど、VBAとかSQLとかの文脈だと「戻り値を持つ」のが関数で、「戻り値を持たない」のがプロシージャらしい。

クロージャ

一番わかりやすかった記事はこちら

クロージャとは

前提知識として、JavaScriptは関数の入れ子が出来る。
そして、関数の入れ子をしたとき、外側の関数から内側の関数のスコープにアクセスは出来ないが、内側から外側のスコープにアクセスはできる。
スコープが分からない場合は、スコープを変数という単語に置き換えてもう一回上の文章を読むと分かるかも。
コードで例えると、以下は[ReferenceError: y is not defined]エラーになる。

// 外側関数の宣言
function outside() {
    // 外側関数で変数xの宣言
    var x = 1
    // 関数内で内側関数の宣言(関数の入れ子)
    function inside(){
        // 内側関数で変数yの宣言
        var y = 2
        return y;
    }
    // 外側関数から内側関数の変数は取得できないのでエラー
    return x + y;
    }
    console.log(outside());

しかし、以下はエラーを吐かずに3を返す

function outside() {
    // 外側関数で変数xの宣言
    var x = 1
    // 関数内で関数を宣言(関数の入れ子)
    function inside(){
        // 内側関数で変数yの宣言
        var y = 2
        // xを参照できる
        return x + y;
    }
    // 戻り値を内部関数にする
    return inside;
}
// 戻り値が内部関数なので、func1は内部関数の定義を持っている。
func1 = outside();
// 内部関数(inside)の実行
console.log(func1());

これは、内側の関数は外側の関数のスコープにアクセスできるから。

これが、クロージャ。
カプセル化ともいえる?

クロージャのメリットとは

クロージャのメリットは変数の保護と可読性の向上。

内側の関数で宣言された変数は外側から参照したり変更したりできないので、変数の保護に使える。
さらに、関数の外で先にlet x = 0みたいな感じで変数を宣言しなくていいので、可読性も上がる。

以下はメリットのコード例

function counter(){
    // 関数内に変数を宣言できる
    var count = 0;
    function addOne(){
        count += 1;
        return count;
    }
    return addOne;
}
func1 = counter();
console.log(func1()); // 出力:1
console.log(func1()); // 出力:2
// count = 0をしても影響しない。
var count = 0;
console.log(func1()); // 出力:3

ちょっとややこしかったけど、理解はできた。
後は使えるかというところ。

プロパティとオブジェクト

本当に恥ずかしいんだけど、プロパティとオブジェクトについて説明できない=理解できてない。

わからんもんはわからんので理解する。
というか、何か調べれば調べるほど混乱してくる。

何故混乱するか

混乱の原因は、「オブジェクト」という単語がJavaScriptにおいて二重の意味で利用されているからだと思った。

ここら辺に関しては、「なぜJavaScriptにおいて『オブジェクト』という言葉は二重に使われているのか」を参考に。

「オブジェクト」が指す2つの意味は以下の通り。

1つ目は、オブジェクト指向プログラミングにおける文脈のオブジェクト。つまり、属性や関数などの手続きを一つにまとめたものを指す。
2つ目は、連想配列としてのオブジェクト。つまり、PythonやSwiftでいう辞書、PHPでいう連想配列のこと。

なぜ、どちらの意味でも「オブジェクト」が使われるかというと、実際にオブジェクト=連想配列だから。
厳密に言うと、値に関数とかも入る連想配列。だから、それは実質オブジェクトとしても扱える。
どっちなんだろ、連想配列が実際はオブジェクトなのか、オブジェクトが実際は連想配列なのか。

改めて、プロパティとオブジェクトとは

プロパティとは簡単に言うと、「値と名前が対になったもの」

オブジェクトとは簡単に言うと、「プロパティを集めたもの」


私が理解しやすいのはPython・Swiftの辞書型。PHPで言う連想配列。

JavaScriptでは以下のように使える

let user = {
    name:'初音ミク',
    age:'16',
  }
  console.log(user.name);   // 出力:初音ミク

つまり、name:’初音ミク’というこの関係がプロパティで、userがオブジェクトと言える。

他言語のプロパティ・オブジェクトとどう違うか

更に、なんで混乱したんだろうと考えたんだけど、私が今まで学んで来た言語でここら辺を曖昧に理解してたからだと思う。

おさらい……というかしっかり学んだ覚えは無いんだけど、頭の中を整理する為にPython,Swift,PHPのオブジェクトとプロパティを理解する。

プロトタイプベースとクラスベース

Python,Swift,PHP,JavaScriptはどれもオブジェクト指向プログラムなんだけど、JavaScriptはプロトタイプベース、他はクラスベースと言われるらしい。

ここら辺は「Javascriptでオブジェクト指向するときに覚えておくべきこと」や「JavaScript オブジェクトの基本」等参照。

ちなみに、超簡潔に言うとJavaScriptなどのプロトタイプベース言語は、クラスがなくて全てインスタンス。インスタンスを参照してインスタンスを作成したりする。

クラスベース

クラスベースの場合、「インスタンス≒実際に使えるオブジェクト」を生成するにはクラスという設計図を作り、そこからインスタンスを作成する手段を取る。

具体的に、PHPの場合新しいインスタンスを作ろうとすると以下のようなコードになる。

$hoge = new NewClass();

これは、クラスの初期化を行い変数hogeにNewClassのインスタンスを入れているという感じ。

クラスベースのメリットでもありデメリットでもあるところは、クラスを宣言すると後から(ランタイム中に)変更ができないというところ。厳密に言えば、継承をすれば後から定義を実質的に変えることはできるし、Pythonとかはクラスの内容を途中で変えられるみたい。

プロトタイプベース

プロトタイプベースの場合、”クラス”という概念が存在せず全てインスタンスとしてオブジェクトが存在する。
こちらも同じようにJavaScriptの場合

const hoge = new NewClass();

で新しいインスタンス(オブジェクト)を作成している。

これは、クラスと同じようにオブジェクト内にコンストラクタ(初期化する手続き)を書いておいて、newで宣言したときそれを実行し初期化している。

プロトタイプベースのメリットでありデメリットでもあるところは、簡易性と柔軟性。

他言語で”Class”と宣言してくれると、「これを元にインスタンスを作ってるんだなぁ~」ってわかるけど、プロトタイプベースの場合はそれが分かり難い。

また、オブジェクト宣言して後から(ランタイム中に)中身を変更できるから、それに伴って親オブジェクトが変更されると子オブジェクトがバグる可能性がある。

JavaScriptにクラスは無いのか?

答えは、ある。

昔はオブジェクトのみだったんだけど、要望が多くてECMAScript 2015でClassが追加されている

使い分けは上で挙げたメリット・デメリットから考えてもいいし、Classに慣れてるからとかでもいいかも。

DOM(Document Object Model)をザックリ理解する

DOMって聞きはするんだけど、あんま分かんないな。

正確ではないけど、大体合ってるぐらいで説明する。

DOMは、HTMLのタグ同士の関係を階層構造にしたもの

図で見るとこんな感じ。

// DOM前
<html>
  <head>
    <title> My Web Page </title>
  </head>
  <body>
    <h1> Welcome to my website! </h1>
    <p> This is a paragraph. </p>
  </body>


// DOM化
Document
 ├── <html>
 │   ├── <head>
 │   │   └── <title> My Web Page </title>
 │   └── <body>
 │       ├── <h1> Welcome to my website! </h1>
 │       └── <p> This is a paragraph. </p>

こうすると、どのタグがどの子を持っていて、どれが親なのかっていうのが一目で分かるし、プログラムで扱いやすそうだよね。

例えば、JavaScriptの
const paragraphs = document.querySelectorAll(“p”);
というコードはquerySelectorAllメソッドはDOM内の<p>要素を全て取得しろという事になる。

なぜJavaScriptはDOMの操作が必要なのか?

それは、ブラウザはDOMを元にリアルタイムでレンダリング、WEBページの表示をするから。

だから、JavaScriptでDOMを操作してしまえばページの再読み込み無しでWebページの再レンダリングが出来るという訳。

つまり、前述したモダン化になる。

DOMをより正確に理解する

さっきの私の説明には嘘がまぁまぁある。
例えば、
「DOMはHTMLのタグ同士の関係を階層構造にしたもの」
とあるが、正確にはHTMLだけじゃなくXMLにも使われるし、タグだけじゃなくテキスト、属性、空白なども階層構造にする。

それに
「ブラウザはDOMを元にリアルタイムでレンダリング、WEBページの表示をする」
ともあるが、正確には「ブラウザはDOMの変更を検出すると再レンダリングする」と言える。

他にも色々あるので、詳しく知りたければWHATWGの公式ドキュメントを。

アロー関数

はい、何となくで理解してる系第2号のアロー関数。

Laravelでよく使ってたんだけど、正直、こんがらがってよくわかってない。

理解して説明してみる。

恐らく、このアロー関数を理解するには「Windowオブジェクト,this」を理解しないといけない。だから、まずここから理解する。

Windowオブジェクト

mdnのドキュメントはこちら

Windowはちょっと誤解も入れて説明すると、「今開いているページの情報が沢山入ったオブジェクト」になる。

正確に言うと、タブごとにwindowオブジェクトを持っているんだけど、コードでは特に指定しない限りコードを実行するタブのことを指すので、今開いているページとも言えるのかな〜。

Windowオブジェクトでは本当に色んな情報を取得できて、さっき説明したDOMやウィンドウの解像度、現在タブを開いているか閉じているかなど色々な情報が取得できる。

他にも、命令とかもできて、Window.close()でウィンドウを閉じたり、window.alert()でアラートを出せたりする。

癖強の「this」を大雑把に理解する

thisってPHPの場合は現在のインスタンスを意味するよね。
Python、Swiftだとthisは無くて、selfになる。意味は同じ。

これと同じであれば結構単純なんだけど、JavaScriptのthisはちょっと癖強。

mdnドキュメントはこちら

というか、マジで色んな記事とかめっちゃ調べてようやく何となく理解できたくらいになった。

簡潔に言ってしまえば、thisはコードの文脈によって指すものが変化する
つまり、thisを理解するにはある程度の場合分けが必要になる。


じゃあ、どんくらい場合分けするの?
A.抽象的に分けると3種類に、具体的に分けると157種類に分けられる。
ソースはこちら「JavaScriptのthisは結局何種類あるのか」。

そんで、じゃあ全部理解するのかと言ったら、答えはノーで、めっちゃ大雑把に理解する。

thisを大雑把に理解する為の知識①「スコープはネストしている」

プログラミングをしてると、絶対に聞いたことある単語、スコープ。

何となくわかるけど、スコープ≒変数や関数が使える影響範囲くらいでしか理解してなかったので、改めて。

このスコープ、実はネストしてる……いや、当たり前か。
当たり前なんだけど、意識したことなかった。

ネストしてるとどうなるかってところなんだけど、変数を参照しようとしたとき、もし現在位置のスコープに参照しようとした変数が無かったら、一個外側のスコープを探す動作をする。

それを繰り返して最終的に一番外側のスコープ(グローバルスコープ)にも変数が無かったらエラーを出すということ。

そして、スコープには最初からthisを持つものと持たないものがある
だから、thisを持たないスコープでthisを参照すると、その外側のスコープのthisを参照することになる

この挙動は結構大事なのかなと思った。

ここだけ見るとthisは変数なのか? と思うんだけど、厳密には変数ではなく、キーワードっぽい。ソースはこちら
キーワードっていうのはifとかtrueとかそういうの。

でも、殆どの解説ではthisを変数って説明してるから、別に変数だと理解してても問題ないんだろうなぁと思う。

thisを大雑把に理解する為の知識②「スコープの種類」

スコープによって最初からthisを持つか持たないかがある→
どのスコープが最初からthisを持つか理解すれば、thisを理解できるのでは? 
って思うので、重要そうなスコープの種類をみてみる。

グローバルスコープ

いわゆる一番外側のスコープ。

グローバルスコープで宣言した変数は、上で説明したwindowオブジェクトのプロパティとして操作できる。

var hoge = 39;
console.log(hoge);  		// 出力:39
console.log(window.hoge);   	// 出力:39

windowオブジェクトで変数を操作できることからイメージもしやすいのかなと思うんだけど、グローバルスコープでのthisはwindowオブジェクトを指す

console.log(this); // Window

つまり、thisを持っているということだし、もし内側のスコープからthisを探していって最終的にグローバルスコープに行き着いたら、thisはwindowオブジェクトになるということでもある。

関数スコープ

ここら辺のソースも「JavaScriptのthisは結局何種類あるのか」より。

ハイー、一番ややこしくしてる原因、関数スコープ。

スコープによって最初からthisを持つか否かがあると言ったけど、関数の場合、関数の呼び出し方によってthisをどうするかが分かれている。

というか、157種類のthisの内155種類のthisがこの関数スコープ由来
後の2種類はさっきのグローバルスコープとモジュールスコープというもの。うーんこの。
因みにモジュールスコープもthis=window持ち。

このthisの場合分け155種類はある程度抽象化ができて、頑張って9種類にまとめられる。

恐らく仕方がない場合分けだと思うんだけど、それでも多いなぁ。

155種類のthisをどう理解するか?

私の結論は、理解しない

あと数時間くらいかければ155種類とは言わなくても、9種類のthisを理解するくらいはいけそう。でも、流石にちょっとこれは習うより慣れろ系だなぁと思ったので、慣れながら習うことにする。

逃げともいう。

Re.アロー関数

あ、そういえばこれ、アロー関数を理解するためにやってんだった。忘れてた。

アロー関数はその名の通り関数である。
mdnから構文一覧をそのまま引用すると

() => 式


引数 => 式


(引数) => 式


(引数1, 引数N) => 式


() => {
  文
}


引数 => {
  文
}


(引数1, 引数N) => {
  文
}

なんか結構自由。

引数a,bを足して戻り値にする関数を作ってみると

var plus = (a,b) => a+b;
plus(1,2); // 出力:3

thisの理解と比べて簡単でいいねぇ。

そして、このアロー関数はthisを最初から持たない。
だから、thisを使うと以下のようになる。

var a = () => this;

console.log(a()); // Window

分かりやすくていいね。

因みに、マジでこの2行を理解するためにwindowと特にthisを1日以上かけて調べた。

プログラミングも音楽も積み重ねなんやなって。

n進数

mdnのドキュメントを見ていて、気を付けたいなぁと思ったのがn進数の処理。

以下みたいな感じで、0先頭かつ8,9が含まれないと8進数と認識されるっぽい。
var bit8 = 0777;
console.log(bit8); // 出力:511

ちょっと困るので気を付ける。

非同期処理とPromise

前、oEmbedのAPI処理で使ったやつだ。

あの時はやんわり理解だったので、しっかり理解したいところ。

そもそもJavaScriptはシングルスレッドであるが、並行処理と非同期処理に対応している

シングルスレッドってのは、処理する腕が一本しかないみたいなこと。

だから、右手のフライパンで野菜を炒めながら左手でスマホ弄るみたいなことができない。

逆にマルチスレッドだと同時にスレッド数だけ処理できる。並列処理ってやつ。
手が2本あれば右手で野菜炒めながら左手でスマホ弄れるよね。

厳密にJavaScriptを説明すると、JavaScriptは逐次処理≒同期処理だけど並行処理非同期処理に対応している。ただし、並列処理はできない。

逐次処理≒同期処理というのは、処理は同時に1個までしかやらないし、今やってるタスクの処理が終わるまで他のことは何もしない。だから、野菜を炒めるというタスクが終わるまで他のことは何もできない、ということ。

並行処理≒非同期処理というのは、処理は同時に1個までしかできないけど、手が空いてれば他のこともやるよということ。だから、野菜を炒めててちょっと手が空いたら他の事もやったりできる。気が利くね。

並行処理と逐次処理の具体例は以下みたいな感じ。

var cnt = 0;
// 10万回cntにiを足した後、2とcntの値を出力する関数
function hoge() {
  for (var i = 0; i < 100000; i++){
    cnt += i;
  }
  console.log("2:"+cnt)
}


// 上から順に処理されている
console.log("1");
hoge();
console.log("3")


// 出力:1
// 出力:2:4999950000
// 出力:3

プログラミングをやったことあれば、当たり前っちゃ当たり前だ。

次は”setTimeout()”という、指定した時間経った後に処理を非同期で行う関数をつかってみる。

だから、これは非同期処理・並行処理の具体例

// 1秒の遅延処理をした後に2を出力する非同期の関数
function hoge() {
  setTimeout(() => console.log("2:1秒の遅延"),1000);
}
console.log("1");
hoge();
console.log("3")

// 出力:1
// 出力:3
// 2:1秒の遅延

出力の順番が逐次でなくなっているのがわかる。
これは、1秒待ってる間他の処理が止まらないように、さっき言った並行処理をsetTimeout()がやってくれているから。

仕組みは「JavaScript 非同期処理 仕組み」とかで調べて欲しいんだけど、良い感じにしてくれてるらしい。

非同期処理を順番に処理したい

この非同期処理はとても便利なんだけど、非同期処理をした後に何か処理をしたいという時はどうするのだろうか。

これには

  1. コールバック関数
  2. Promise
  3. async&await

の3つの方法がある。

数字が若い順に古い方法で、async&awaitの方法が一番新しい方法。

コールバック関数とは何か

「非同期処理だけど、必ず非同期処理の後に処理をしたい」という時、コールバック関数というテクニックを使う。
非同期処理関数Aの後に関数Bを実行したい! → 関数B=コールバック関数。

このコールバック関数は、JavaScriptで定義されている関数とかではなく、テクニックとか概念とかそういう感じ。
けど、一応mdnのドキュメントはある。

どんなテクニックなのかは具体例を見てもらうのが早いと思う。

具体的にどんな感じか見てみる。

今から書く具体例のコードでやりたいことは

  1. 外部APIから文字列”hello”を取得(1秒かかる仮定)
  2. その文字列を外部APIに渡し、大文字にしてもらう(0.5秒かかる仮定)

勿論仮の話だけど、これを非同期でやりたいと仮定する。

// 1. 文字列を受け取る関数
function getString(callback) {
    setTimeout(function() {
        text = 'hello';
        console.log(text+"を取得しました")
        callback(text);  // 1秒後に文字列textコールバック関数に入れて実行
    }, 1000);
}

// 2. 受け取った文字列を大文字にする関数
function toUpperCase(input, callback) {
    setTimeout(function() {
        const result = input.toUpperCase();
        callback(result);  // 0.5秒後に大文字にした文字列をコールバック関数に入れて実行
    }, 500);
}

// これらの関数を使用して非同期処理を行う
getString(function(data) {
    toUpperCase(data, function(uppercasedData) {
        console.log(uppercasedData);  // "HELLO"
    });
});

/*出力結果
helloを取得しました
HELLO
*/

これは、関数の引数に関数を入れることで”後で実行する”コールバック関数を実現している。

つまり、このコードは以下のような処理で実行されている。

  1. getString関数とtoUpperCase関数を定義
  2. getString関数を実行
  3. callback(text)でgetString関数の引数にある「引数data,実行がtoUpperCaseのfunction」を実行
  4. そのfunctionでtoUpperCaseを実行
    callback(result)で「引数がdata,実行がconsole.logの関数を実行」

うん。ちょっと説明下手なんだけど、以下のような1行コードに整形した方が分かりやすいかなと個人的には思う。

// これらの関数を使用して非同期処理を行う
getString(function(data) {toUpperCase(data, function(uppercasedData) {console.log(uppercasedData);});});


とにかく、こんな感じでコールバック関数を使うことで非同期処理ながらも順序立てて実行が可能になった。

Promiseとは

さっきのコールバック関数には欠点があって、非同期処理が連なれば連なるほどネストが深くなって行ってしまう。要は読みにくくなる。

そこで、Promiseという方法を使ってみよう。

ちょっと話は変わるんだけど、このPromise、私はとても苦手。
理解してしまえばかなり単純な感じはするんだけど、mdnの解説とqiitaの解説で言ってることが違ったりと、かなり混乱する。
結論から言えば、mdnの解説はかなり正確で、Qiitaの方は簡単に説明するために少し嘘をついてしまっている感じ。
かといっていきなりmdnを見ても私はよく理解できなかったし、難しいね。

Promiseを軽く理解する

Promiseはさっきのコールバック関数をネストせずに書けるようにするオブジェクト。

mdnドキュメントはこちら

最初にPromiseのインスタンスとその処理内容を作り、それに続くように.then(コールバック関数)を書けばとりあえず使える。

具体例見てみよう

const promise = new Promise((resolve) => {
    setTimeout(() => {
        var text = "初音ミク";
        resolve(text); // 渡したい変数を入れる
    }, 1000);
}).then((text) => {
    text = text + "と重音テト";
    return text
}).then((text) => {
    console.log(text); // 出力:初音ミクと重音テト
});

最初のpromise処理は1秒の遅延があるのに、しっかり”と重音テト”という文章がtextに追加されて出力されてる。

なんなら、渡したい引数をreturnすることで次のthen処理に引数を渡せてる。

とりあえず、こんな感じに浅いネストで非同期処理の逐次処理を書ける。

Promiseを重く理解する

Promiseの理解で大事なのは以下の4つだと私は思う。

  • Promiseの状態について
  • Promiseの連鎖[.thenと.catch]
  • .thenと.catch()が処理を走らせる条件
  • .thenと.catch()のPromiseの状態について
Promiseの状態について

mdnからそのまま引用してしまうとPromiseには以下の状態がある

  • 待機 (pending): 初期状態。成功も失敗もしていません。
  • 履行 (fulfilled): 処理が成功して完了したことを意味します。
  • 拒否 (rejected): 処理が失敗したことを意味します。

更に言えば、Promiseは一旦fulfilledかrejectedになると、その値を一生変えない
これ、大事だから覚えてて欲しい。もう一回言うと、
Promiseは一旦fulfilledかrejectedになると、その値を一生変えない

Promiseの状態が変化する条件

Promiseは最初pendingという状態を持っている。
状態が変化する条件は、’resolve’または’reject’という関数を実行した時。

実際にPromiseを使う例を見ていきたい。

const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        // 1秒後にresolve()を呼び出す
        resolve();
    }, 1000);
});

これは、よくあるPromiseを使う例。

大前提として、Promiseは引数として関数を1個受け取る。今回の場合はアロー関数の

(resolve, reject) => {

    setTimeout(() => {

        // 1秒後にresolve()を呼び出す

        resolve();

    }, 1000);

}

を受け取っている。

そして、Promiseの引数になる関数の引数はresolveとrejectという関数になる
これは、自動的に渡されている。

そして、関数内でresolve()を呼び出すとそのPromiseはfulfilledに、関数内でreject()を呼び出せばrejectedになる

Promiseの連鎖[.thenと.catch]

今までの説明だけではPromiseの意味が無い。
やりたいことは非同期処理の逐次処理を簡単に実装すること。

.then()

Promiseでは、.then()でコールバック関数を簡単に続けることができる

以下は具体例

const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        // 1秒後にresolve()を呼び出す
        console.log("1個目の処理")
        resolve();
    }, 1000);
}).then(() => {
    // 上の処理が終わってから呼び出される
    console.log("2個目の処理")
});
/*出力
1個目の処理
2個目の処理
*/

この.then()の引数に関数(今回の場合はアロー関数)を入れることで、実質的にコールバック関数として使える。

他にも、前の関数の戻り値を次のコールバック関数に渡したり、何個も.then()をつなげたりできる。

const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        var cnt = 1;
        console.log(cnt + "回目の処理");
        resolve(cnt);
    }, 1000);
}).then((cnt) => {
    // 上の処理が終わってから呼び出される
    cnt += 1;
    console.log(cnt + "回目の処理");
    return cnt;
}).then((cnt) => {
    // 上の処理が終わってから呼び出される
    cnt += 1;
    console.log(cnt + "回目の処理");
});
/*出力
1回目の処理
2回目の処理
3回目の処理
*/

最初のPromise処理から次の.then()に引数を渡すには、resolve()かreject()関数の引数に渡したい値を入れればおk。
.then()から次のコールバック関数に引数を渡すにはreturnで返せばおk。

こうすればネストが浅くなってさっきのコールバック地獄から抜け出せるよね。

.then()のreject処理

実は.then()は引数を2個まで受け取れる。さっきまでの例は1個だった。

具体例を見てもらうのが早いと思う↓

function resolveFunc() {
    console.log("これが作動したらresolve");
}
function rejectFunc() {
    console.log("これが作動したらreject");
}




const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject();  // rejectに変えた
    }, 1000);
}).then(resolveFunc,rejectFunc);
/*出力
これが作動したらreject
*/

今回、Promiseはreject()関数でreject状態にしている。

この具体例から言えるのは、
Promiseがrejectの場合.then()の第二引数のコールバック関数を実行するようになる

だから、今回は「これが作動したらreject」というログが出てる。

※もし、第二引数が設定されていない場合は.then()の処理はスルーされる。

つまり、第一引数のコールバック関数は成功時(fulfilled)・第二引数のコールバック関数は失敗時(rejected)の時に実行されるということになる。

.catch()

.catch()は、Promiseがrejected状態の時に動く処理。

さっきのコードに合わせると、こんな感じ。

const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        var cnt = 1;
        console.log(cnt + "回目の処理");
        reject(cnt);  // Promiseの状態がreject
    }, 1000);
// 第二引数を指定していないのでthenはスルー
}).then((cnt) => {
    cnt += 1;
    console.log(cnt + "回目の処理");
    return cnt;
// 第二引数を指定していないのでthenはスルー
}).then((cnt) => {
    cnt += 1;
    console.log(cnt + "回目の処理");
// promiseがrejectなので処理が実行
}).catch((cnt) => {
    console.log("catch処理:cnt = " + cnt + "です");
});
/*出力
1回目の処理
catch処理:cnt = 1です
*/

最初のPromiseの処理がreject(cnt);だから、Promiseの状態がrejectedになって.then()の処理がスルーされている。

catch()はPromiseの状態がRejectの時に処理される処理なので、

console.log(“エラーが発生しました:” + cnt + “回目の処理”)

の処理が走るという感じ。

.catch()と.then()の第二引数の使い分け

これ、.then()の第二引数と.catch()ってどう違うの? って思うよね。

一応、エラー処理とかは分かりやすいように.catch()に書くのが普通っぽい。

他にも使い分けはあるのかもしれないんだけど、とりあえずこれしかわからんかった。

.thenと.catch()が処理を走らせる条件

ちょっと、以下のコードとその処理を見てほしい。

const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        var cnt = 1;
        console.log(cnt + "最初の処理");
        reject(cnt);  // promiseがreject状態
    }, 1000);
// promiseがrejectなのでcatch()処理が実行される
}).catch((cnt) => {
    console.log("catch処理:cnt = " + cnt + "です");
    return cnt;
// promiseがrejectなのに.then()の第一引数の処理が実行している???
}).then((cnt) => {
    cnt += 1;
    console.log(cnt + "回目の処理");
});
/*出力
最初の処理
catch処理:cnt = 1です
2回目の処理
*/

ん? どういうことだろう?

Promiseがreject状態のはずなのに.catch()の後にある.then()の処理が走ってる?

最初に言ってた通り「Promiseは一旦fulfilledかrejectedになると、その値を一生変えない」はずだから、.then()の第一引数のコールバック関数が実行されるのはおかしいよね?

ここが混乱ポイント。

実は、.then()や.catch()は新しく生成されたPromiseオブジェクトを返している

もう一回言っとく。
.then()や.catch()は新しく生成されたPromiseオブジェクトを返している

だから、.catch()では新しくfulfilled状態のpromiseを次の.then()に返していて、promiseがfulfilledだから、第一引数のコールバック関数が実行されているということ。

.thenと.catch()のPromiseの状態変化について

さっき、Promiseの説明で「関数内でresolve()を使えばそのPromiseはfulfilledに、関数内でreject()を使えばrejectedになる」と言ったけど、.then()や.catch()で新しく生成されたPromiseはどのように状態が変わるのだろうか?

結論から言えば、.then()または.catch()のコールバック関数でエラーが起きなければfulfilledに、エラーが発生すればrejectedになる

なんか、めっちゃ分かりやすくていいじゃん。

とりあえず

一応まだ「型付き配列,イテレータ,プロキシ」とかあるんだけど、サラッと見た感じ恐らくここら辺は使わなさそうだと思ったので、一回実践に移ってみる。

使いそうになったら学ぼう。

Nuxt.jsを使いたい

Nuxt.jsとは

いろいろフロントエンド関連を調べるとNext.jsやら、Node.jsやら、Nuxt.jsやらの単語が出てくる訳だけど、その中でも私はNuxt.jsというのに興味を持っている。

この、Nuxt.js(ナクストジェーエス)はVue.jsをベースとしたフレームワークだそうだ。

ちなみに、Node.jsはサーバー側でJavaScriptを動かすための技術で、Next.jsはReactをベースにしたフレームワーク。

何故Nuxt.jsを使いたいのか

なぜわざわざNuxt.jsを使いたいのかってところなんだけど、使いたい理由はSSR(Server Side Rendering)というレンダリング方法を採用したいから

SSRを使うとSEOに強いし、表示速度も早くなるみたい。
それならついでに使いたいよね、という感じ。

レンダリング方法[SPA,MPA,CSR,SSR,SSG]

なんか、ここらへんも結構ややこしいというか、しっかり理解しようとするとちょっと沼る。

ここら辺の参考はこちら

  1. Webブラウザのレンダリングの仕組みを理解する
  2. SPA(Single Page Application)と利用されるレンダリング技術まとめ
  3. SPA/MPAとCSR/SSR/SSGの分類

そもそもレンダリングってなに

参考元ではレンダリングを「指定したリソースをブラウザに表示すること」と定義している。わかりやすいね。

そのレンダリングなんだけど、私達が関われるのは「どういう順序でブラウザにHTTPで情報を送れるか」というところになる。
だから、ブラウザのHTML・CSS・JavaScriptを読み込んで処理をするという根本的なところには関わらないし、関われない。

例えば、静的で非モダンなサイト(Larabelなど)ではMPA(Multi Page Application)という方式でレンダリングが行われる。
これは一番直感的な方法で、画面遷移毎に新しいHTMLを再取得する方法。

MPAで問題なくないか? とも思うんだけど、なんか他にもいろいろあるんだよね。

MPAの対であるSPA

静的な非モダンなWebアプリではMPAが採用されている。
これは画面遷移、つまりURLで他のページに移動するときに新しくHTMLを再取得してるというもの。

それに対してSPA(Single Page Application)というレンダリング方法がある。
MPAがMulti Page Applicationだから対になるというのもわかりやすい。

このSPAは「最初のアクセスでHTMLを取得する」というところまでは一緒なんだけど、画面更新をするときに「更新が必要な箇所だけJavaScriptで処理をする」というもの。

SPAで代表的なのはGoogleマップ

ページの再読み込みは一切されないけど、ページが動的に変わっていくよね。

SPAでURLが変わるのは何故?

Googleマップを触ると、確かにページの再読み込みはされないけどURLが変わっていくのが分かると思う。

これ、まじでどういうこと??? って最初はなったんだけど、これはhistoryAPIという技術を使ってURLを動的にJavaScriptで生成してるみたい。そもそもJavaScriptでURLを弄れるのを知らなかった。

だから、MPAのような戻る/進む処理やURLに直接アクセスなどができる。

SSRとは何か

SSRはServer Side Renderingの略で、サーバー側でHTMLのレンダリング処理をしてしまうこと

ちょっとここら辺が結構曖昧というか、そもそも抽象的な意味を無理やり具体的にしてる感がある。

さっきのSPAは「更新が必要な箇所だけJavaScriptで処理する」というものだった。これは、新たなHTMLを生成するときもそうで、HTMLを生成するJavaScriptでHTMLを生成するということをやっている。

それに対してSSRはJavaScriptもサーバー側で処理しちゃって、結果のHTMLをレスポンスするという感じ。

これの何がいいか、つまりメリットは何かっていうとSEOに強い

クライアント側で処理が必要なSPAはHTMLが最初から完成された状態で存在しないので、クローラがちゃんと認識してくれないというデメリットがある。

それに対して、SSRは既に完成したHTMLを取得するので、クローラがしっかり認識してくれて、万々歳という感じ?

これ、結構今は何とも言えなくて、「クローラがJavaScriptを処理してある程度認識してくれるようになったので、SPAでも問題ない」と言われてたりする。

本当にNuxt.jsやVue.jsは必要か?

これ、色々調べてて思ったんだけど、これ今の私に本当にJavaScriptのフレームワークっているのか?

コスパを考える・技術は手段であるということ

既にLaravelである程度形が出来ているところにNuxt.jsを採用して、そこで更にSSR処理をできるようにしようとするとルーティングとか色々面倒そうなんだよね。

しかも、今のところNuxt.jsを取り入れて享受できるメリットが「SSRを実装できる」「フロントエンドの実装がある程度楽になる」「最新の技術に触れれる」程度なんだよね。

SSRの実装さえ諦めてしまえば、残りの2つのメリットって今の私には結構薄い。
「フロントエンドの実装がある程度楽になる」は別にNuxt.jsじゃなくてもいいし、「最新の技術に触れる」は技術が目的になってしまう

技術はあくまでも手段であって、私の目標は「誰でも自分が感動する音楽を自分で作れるようになること」なのでNuxt.jsの採用はスルーでいいかもしれない。

Laravelは半SSRとも言える

結構ややこしいんだけど、LaravelはMPAでありながらSSRであるとも言える。

何故なら、SSRの条件はSSRという単語だけ拾えば「サーバー側でHTMLのレンダリングをしている」ということになるから。

Laravelはアクセスしたユーザーによって動的にHTMLを生成して処理できるよね、例えば「自分の記事一覧を見るページ」とかは自分のユーザー情報を元に動的にサーバー側でHTMLのレンダリングをしていることになる。

かといってLaravelはJavaScriptの処理までは行わない。JavaScriptはそのままHTMLと一緒に送ってクライアント側で処理をしている。だから、世間一般で言われるSSRにはならないのかなぁ、と思った。

つまりフロントエンドはどうするか

最初はVue.jsを使いたいと言ったり、その次はNuxt.jsを使いたいと言ったり色々右往左往したんだけど、色々学んだ結果「私はとりあえず素のJavaScriptか、JQueryなどの軽いプラグインでいい」と思った。

JQueryって正直「もう古い技術」とか「JQueryは終わった」とかよく聞くから、何も考えずに避けてたんだよね。
でも、現時点だと2023/08/28にv.3.7.1がリリースされてたりと、別に更新が終わった訳ではないので問題はないかなぁと思う。

また、「jQueryは終わったのか論争を、終わらせにきました。」という記事ではかなり分かりやすく、JQueryの現状を説明してくれている。

この記事の方いわく、「Web開発を中心にやりたいなら終わってる」しかし、「Web制作を中心にやりたいなら終わってない」らしい。

就活とか考えるならJQueryとかじゃなくReact・Vue.jsとかの方がいいかもね。
でも、私は言った通りこのサイトを運営するのが今の小目標なので、JQuery、なんなら素のJavaScriptでいいかなぁという感じ。

改めて、Alpine.jsでSoundCloudっぽいモーダル画面を作る

Alpine.jsという、Vue.jsをもっと簡単にしたようなJavaScriptのフレームワークがあるので、それを使ってモーダル画面を実装する。

といっても、以前にも何かで使ったことはあるけどJavaScriptがよくわかってなかったので、よくわからないまま終わった気がする、

Alpine.jsとは

Alpine.jsは結構最近に出たフレームワーク。
GitHubのリポジトリをみると、v.0.10が2019年にリリースされていて、今までで2万3千スターを得ている。ある程度期待は得られているのがわかる。

公式サイトに「Your new, lightweight, JavaScript framework.」

とある通り、動作も学習コストも軽いらしく、気軽に使えるのが特徴。
今の私にピッタリだね。

Alpine.jsのドキュメントはまだ日本語に対応していないみたいだけど、最近の和訳はすごいので、たぶんいける。

ChatGPTにモーダル画面の骨組みを作ってもらう

最近ChatGPTに画像認識機能が追加されたので、それを使ってSoundCloudのモーダル画面を真似してもらおう。

以下のような感じでお願いしてみる。

そんで、出力されたコードをほぼそのまま貼り付けた画面が以下の通り。

うん。いいんじゃないか?

勿論いらない欄はあるけど。

Alpine.jsでモーダルの処理を書く

一応、2年前のQiita記事によると、Alpine.jsで覚えることは22個らしい。

実際に少し触ってみるとモーダルに必要なのは

  1. 変数を保持するx-data
  2. DOM要素の表示・非表示を操れるx-show
  3. クリックを検知するc-on:click

の3つだけで良さそう。

やることは

  1. x-dataでトグル(True/False)変数を保持
  2. x-on:clickでクリックを検知
  3. 「x-show=トグル変数」で開閉

簡単だなぁ。

実際にできたコードが以下みたいな感じ。

<div x-data="{ isOpen: false }">
    <!-- ボタン: モーダルを開く -->
    <button @click="isOpen = true">プロフィールを編集</button>
    <!-- モーダルの内容 -->
    <div class="fixed z-10 inset-0 overflow-y-auto" x-show="isOpen">
        <div class="flex items-center justify-center min-h-screen">
            <!-- 背景 -->
            <div class="fixed inset-0 transform transition-all"
                x-show="isOpen"
                @click="isOpen = false">
                <div class="absolute inset-0 bg-gray-500 opacity-75"></div>
            </div>
            <!-- ここにモーダル中身 -->


        </div>
    </div>
</div>

ちょっと中途半端なんだけど、一回ここで区切る。

おやすみなさい。