20220322のJavaScriptに関する記事は18件です。

(簡易メモ) JavaScript+FastAPI で FormData 送信時にハマったこと

例えば、FastAPI に、 # import 等は省略 @app.post("/post") def route_post(key1: str = Form(...), key2: str = Form(...)): print(key1) print(key2) return といったルートがあり、そこに JavaScript で以下のようにフォームデータを送信するとき、 var form = new FormData(); form.append("key1", "any_value1"); form.append("key2", "any_value2"); var xhr = new XMLHttpRequest(); xhr.open("POST", "/post"); //xhr.setRequestHeader("Content-Type", "multipart/form-data"); xhr.send(form); といった感じに書くと思います。(簡単のため、ここではappendを使う方法で。) ここで、setRequestHeaderを入れてしまうと、400エラーが返されてしまいます。 INFO: 127.0.0.1:46198 - "POST /post HTTP/1.1" 400 Bad Request 自力でリクエストヘッダを設定せず、自動にさせると正常に動きます。 [2022/3/23 追記] 編集リクエスト頂き、ありがとうございました。 「メモ タグを追加してみました by @Tetsu_Oikawa」
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

# JavaScriptで「世界時計」Webアプリケーションを作ってみた

私がプログラミングの勉強を始めて2年くらい経ちましたが、昔初めて作ったWebアプリケーションのファイルが残っていたので、これについて記事にしていこうと思います。 「World Clock Maker」 今回紹介するものは、任意の国のアナログ時計とデジタル時計を一括表示するwebアプリケーションです。イメージは以下のような感じです。 Demoページは以下のリンクからどうぞ! 「World Clock Maker」- Display World Date & Time 仕様 使い方はDemoページに記されていますが、ここにも簡単に記しておきます。 Addボタンを押してタイムゾーンを選択する。 選択した後Addボタンをクリックすることで、その地域の日付、時刻を表示される。 削除する場合はDelete Allボタンをクリックする。 これだけなので、シンプルな仕組みだと思います。 作ったきっかけ きっかけはiPhoneにある「世界時計」をデジタルとアナログ両方の形式で表されるものを作ろうと思ったことです。これのおかげで、JavaScriptのいい勉強になったのではないかと思います。 作った時計の問題点 現状一つの問題があって、それは表示時刻が少しずつ遅延してしまうことです。 表示時刻の更新は1秒ごとにやっていますが、その際処理に若干時間がかかってしまいます。そのため、以下の図のようにわずかな遅延が積み重なってしまい正しい時間表示ができなくなっております。現在その解決策は検討できておりません。 コード 特にアップロードしませんが、ブラウザの「開発ツール」を開けば全て見れます。 JavaScript,HTML,CSSの3つを使って作っているためです。 興味のある方はそちらをどうぞ。 ※初心者が作成したものなので、コードの汚さはご了承いただければと思います(笑) 注意事項 Demoページで表示可能な時計の個数は無制限にしておりますが、あまりにも多く表示させるとPCのパフォーマンスに影響が出る場合がありますので、ご注意ください。 参考 アナログ時計の作り方は、YouTubeの"Online Tutorials"さんのこちらの動画を参考にさせてもらいました。 以上となります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

バンドル・コンパイルについて

はじめに これまで、なんとなくにしてきたバンドル、コンパイルについて理解したいと思い記事を書きます。 モジュール化 バンドルについて理解する前に、モジュールについて知る必要があります。 モジュールとは機能ごとに分割されたファイルのこと。 変数や関数が書かれたプログラムのことで、基本的には1ファイルのことをモジュールと呼びます。 元々JavaScriptはブラウザ上で動かすために生まれた言語のため、モジュール定義や読み込みがありませんでした。 モジュール化して開発を効率的にしたいよねーということで、誕生したのがCommonJSです。 CommonJS (require構文) hogehoge.js // エクスポート module.exports = { hoge } // インポート const hoge = require('hoge') JavaScriptの仕様を定めるプロジェクトであるCommonJSでサポートされている構文。 このCommonJSは、あくまでサーバーサイド上でのJavaScript仕様なので、ブラウザ上で動かすには、後述するwebpackなどで依存関係を解決する必要があります。 ESmodules (import/export構文) hogehoge.js // エクスポート export const hoge = hogehoge // インポート import { hoge } from '.インポート先のパス' ES2015(ES6)から言語仕様としてモジュールの仕組みが導入されました。 ただES2015に対応していないブラウザ(IEなど)で動かすには、 後述するBabelというコンパイルツールを使ってrequire構文に変換した後、webpackなどで依存関係を解決してあげる必要があります。 バンドル 上記のようなモジュール化したファイルの依存関係を解決して1ファイルに変換することです。 このバンドルするためのツールで広く使われているのがwebpackです。 webpack webpackとはモジュールバンドラーツールです。 webpackを使うわなければ以下のように、記述が多くなりパフォーマンスの低下につながり、読み込む順番も気をつける必要があります。 index.html //バンドラーを使わない場合 <script src="a.js"></script> <script src="b.js"></script> <script src="c.js"></script> ・  ・  ・ //バンドラーを使う場合 <script src="bundle.js"></script> webpack公式より参照 このように依存関係のあるjsファイル、cssファイル、アセットの依存関係を解消し1つのファイルにまとめます。 Code Splitting webpackにはCode Splittingという機能があります。 バンドルによって1つにまとめられたファイルはサイズが大きくなることがあり、ローディングに時間がかかってしまいます。 これを解消するのがCode Splittingです コードをchunkという単位で複数に分割して、非同期的にローディングするため最初のロード時間を短くできます。 コンパイル ES2015(ES6)ではモジュールの仕組みの導入以外にも let,const,Promise,アロー関数,スプレッド構文など様々な仕様が追加されました。 しかしこれらの仕様に対応していないブラウザはまだまだあります。 そのため、ES2015を従来の環境でも動く古いJavaScriptに変換する必要があります。 この変換をコンパイルと言います。 Babel BabelはES2015で書かれたJavaScriptをES2015以前のJavaScriptに変換するコンパイラーです。 最後に 簡単にではありますが、バンドル・コンパイルについてまとめてみました。 まだまだ、勉強中なので間違っていることや補足があればコメントください!!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

requestAnimationFrameのループはPromiseに変換すると簡潔に書ける(かも)

requestAnimationFrame()は、フレームレートを考慮してコールバック関数を実行する関数です。主にcanvasなどのアニメーションで使います。 このrequestAnimationFrameですが、コールバックに渡した関数は1度だけ実行されます。 globalThis.requestAnimationFrame(()=>{ // この部分は1度だけ実行される console.log("raf!"); }); これをループさせて、複数回実行させるためには、例えばこのサイトでは以下のような方法が紹介されています。 const startTime = Date.now(); //描画開始時刻を取得 (function loop(){ globalThis.requestAnimationFrame(loop); console.log(startTime - Date.now()); //経過時刻を取得 })(); 名前付きの即時関数を使って実装されているのですが、ループしていることが一目では分かりづらくなっています。 ループを抜けるための条件分岐が足されると、更に読みづらくなってしまいそうです。 ループを簡潔に書くためにはどうしたらよいでしょうか。 requestAnimationFrameをPromiseに変換する requestAnimationFrameをPromiseに変換することで、ループが書きやすくなります。 requestAnimationFrameをPromiseに変換 function animationFramePromise() { return new Promise<number>((resolve) => { globalThis.requestAnimationFrame(resolve); }); } Promiseに変換した後は、ループを以下のように書くことができます。 const startTime = Date.now(); //描画開始時刻を取得 while (true) { await animationFramePromise(); console.log(startTime - Date.now()); //経過時刻を取得 } while文を使うことで、ループであることが一目で分かるようになりました。 ループを抜ける条件を書く時は、while文のbreak条件の部分に指定するか、if文でbreakするだけです。 const startTime = Date.now(); while (startTime - Date.now() < 10000) { // 終了条件 await animationFramePromise(); console.log(startTime - Date.now()); // 終了条件はこう書くこともできる // if (startTime - Date.now() > 10000) { // break; // } } setTimeoutにも応用できる ちなみにこの方法、setTimeoutにも応用できます。 function delay(ms) { return new Promise<number>((resolve) => { globalThis.setTimeout(resolve, ms); }); } const startTime = Date.now(); //描画開始時刻を取得 while (true) { await delay(1000); console.log(startTime - Date.now()); //経過時刻を取得 } setTimeoutをPromise化する際の注意点として、時間計測のスタート地点が「前の処理が終了した時刻」になります。そのため、例えば1000ミリ秒を指定してもぴったり1000ミリ秒間隔になるわけではありません。 GUIの操作など、「ぴったりn秒」が要求されない箇所では、setintervalよりこちらのほうが書きやすい事もあるかもしれません。 まとめ requestAnimationFrameを使ったループ処理はPromiseに変換すると簡潔に書ける setTimeoutも同様にPromiseに変換すると簡潔に書ける
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue 状態管理変数を実装してみた

実現したかったこと aタグなどの要素としては同じだが遷移先が異なるものについてcomponent化 生成した数に応じて、表示を右寄せ・左寄せとする 実装 index.html <!-- // ... --> <body> <!-- // ... --> <vue_header></vue_header> <!-- // ... --> </body> <script type="text/javascript"> Vue.component('vue_a', { props: [ 'list', 'position_class', ], template: ` <a v-bind:class="position_class" v-bind:href="list.href" > {{ list.name }} </a> `, }); Vue.component('vue_header', { beforeCreate: function () { this.count = 0; }, data: function () { return { lists: [ { href: "/href1", name: "リンク1", }, { href: "/href2", name: "リンク2", }, { href: "/href3", name: "リンク3", }, ], }; }, methods: { getPositionClass: function () { if (this.count < 2) { this.count++; return "left"; } else { this.count++; return "right"; } } }, template: ` <header> <div> <vue_a v-for="(list, index) in this.lists" v-bind:key=index v-bind:list=list v-bind:position_class="getPositionClass()" > </vue_a> </div> </header> `, }); <!-- // ... --> </script> <!-- // ... --> つまずいたところ vue_headerのdata内にcountを保持していたが、 countの更新と共にtemplateが再描画されてしまう beforeCreate内で変数初期化することで、 処理内で更新してもtemplateは更新されない さいごに 他にいい対応方法・至らぬ点ありましたら、コメントで指摘して頂けるとありがたいです...
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.js 状態管理用の変数を実装してみた

実現したかったこと aタグなどの要素としては同じだが遷移先が異なるものについてcomponent化 生成した数に応じて、表示位置を制御するクラスを適用する 実装 index.html <!-- // ... --> <body> <!-- // ... --> <vue_header></vue_header> <!-- // ... --> </body> <script type="text/javascript"> Vue.component('vue_a', { props: [ 'list', 'position_class', ], template: ` <a v-bind:class="position_class" v-bind:href="list.href" > {{ list.name }} </a> `, }); Vue.component('vue_header', { beforeCreate: function () { this.count = 0; }, data: function () { return { lists: [ { href: "/href1", name: "リンク1", }, { href: "/href2", name: "リンク2", }, { href: "/href3", name: "リンク3", }, ], }; }, methods: { getPositionClass: function () { if (this.count < 2) { this.count++; return "left"; } else { this.count++; return "right"; } } }, template: ` <header> <div> <vue_a v-for="(list, index) in this.lists" v-bind:key=index v-bind:list=list v-bind:position_class="getPositionClass()" > </vue_a> </div> </header> `, }); <!-- // ... --> </script> <!-- // ... --> つまずいたところ vue_headerのdata内にcountを保持していたが、 countの更新と共にtemplateが再描画されてしまう beforeCreate内で変数初期化することで、 処理内で更新してもtemplateは更新されない さいごに 他にいい対応方法・至らぬ点ありましたら、コメントで指摘して頂けるとありがたいです...
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaScript】非同期のtry-catchをいい感じに書く

はじめに エラーハンドリングって大変ですよね。 非同期がはいってくるともっと大変になります。 さいきんそう思うことがあって調べていたら、いい感じの書き方を見つけました。 ここでは try-catch ではなくて await-catch を使って非同期のエラーハンドリングを書くことで、すっきりした書き方になることを目指します。 TL;DR これが try { const response = await callApi() apiSuccess = true console.log('res is', response) } catch(e) { if (e instanceof TypeError) { console.log('handle type') } else if (e instanceof SyntaxError) { console.log('handle syntax') } else { throw e } } finally { console.log('success?', apiSuccess) } こうなります const response = await callApi() .then(() => { apiSuccess = true }) .catch(handleType) .catch(handleSyntax) .catch(Promise.reject) .finally(() => { console.log('success?', apiSuccess) }) console.log('res is', response) まずは同期処理から 通常のエラーハンドリングはこんな感じになるかと思います。 まずは同期処理ですが、以降の例でこれを変えていきます。 function callApi() { throw new SyntaxError('a') } let apiSuccess = false try { const response = callApi() apiSuccess = true console.log('res is', response) } catch(e) { if (e instanceof TypeError) { console.log('handle type') } else if (e instanceof SyntaxError) { console.log('handle syntax') } else { throw e } } finally { console.log('success?', apiSuccess) } 結果はこうなります。 API を呼ぶときは非同期にしたい API は非同期で呼びたいですね。 なので非同期にします。 async function callApi() { // async にする throw new SyntaxError('a') } let apiSuccess = false try { const response = await callApi() // await にする apiSuccess = true console.log('res is', response) } catch(e) { if (e instanceof TypeError) { console.log('handle type') } else if (e instanceof SyntaxError) { console.log('handle syntax') } else { throw e } } finally { console.log('success?', apiSuccess) } 結果はこうなります。 await がないと catch されずにエラーが突き抜けるので注意です。 await の try-catch といえば(本題) さて、 javascript の await はメソッドチェーンでエラーハンドルができます。 するとどうなるか。コードがすっきりして嬉しいです。 というわけで、同期処理の try-catch を非同期処理の書き方に変換します。 throw についてはこのままでも動きますが、await にあわせて Promise.reject を使います。 throw を Promise.reject にする async function callApi() { throw new SyntaxError('a') } // throw を Promise.reject にする async function callApi() { return Promise.reject(new SyntaxError('a')) } try を変換する try の処理を、[ throw するもの] と [throw しないもの] と [戻り値を扱うもの] に分けます。 throw をするものはそのまま書いて、throw をしないものは .then() に書きます。 もし処理の中で throw されると .then() は無視して、直近の .catch() にはいります。 また、戻り値を扱う処理はメソッドチェーンのあとに書きます。 try { const response = await callApi() apiSuccess = true console.log('res is', response) } // ↓ try を消す ↓ const response = await callApi() // throw するもの .then(() => { apiSuccess = true }) // throw しないもの // 戻り値を扱うもの console.log('res is', response) catch を変換する catch の中の処理を書き換えます。 メソッドチェーンにしたいので、エラーを引数にしたメソッドに切り出します。 今回の例は処理が複数あるので、メソッドを複数個つくっています。 catch(e) { if (e instanceof TypeError) { console.log('handle type') } else if (e instanceof SyntaxError) { console.log('handle syntax') } else { throw e } } // ↓ if (...) { ... } を非同期関数にする ↓ async function handleType(e) { if (e instanceof TypeError) { console.log('handle type') } else { return Promise.reject(e) } } async function handleSyntax(e) { if (e instanceof SyntaxError) { console.log('handle syntax') } else { return Promise.reject(e) } } // ↓ catch を消す ↓ .catch(handleType) .catch(handleSyntax) .catch(Promise.reject) finally を変換する finally { console.log('success?', apiSuccess) } // ↓ finally を変換する ↓ .finally(() => { console.log('success?', apiSuccess) }) 変換した結果 というわけで、まとめると以下のようになります。 変換前 async function callApi() { throw new SyntaxError('a') } let apiSuccess = false try { const response = await callApi() apiSuccess = true console.log('res is', response) } catch(e) { if (e instanceof TypeError) { console.log('handle type') } else if (e instanceof SyntaxError) { console.log('handle syntax') } else { throw e } } finally { console.log('success?', apiSuccess) } 変換後 async function handleType(e) { if (e instanceof TypeError) { console.log('handle type') } else { return Promise.reject(e) } } async function handleSyntax(e) { if (e instanceof SyntaxError) { console.log('handle syntax') } else { return Promise.reject(e) } } async function callApi() { return Promise.reject(new SyntaxError('a')) } let apiSuccess = false const response = await callApi() .then(() => { apiSuccess = true }) .catch(handleType) .catch(handleSyntax) .catch(Promise.reject) .finally(() => { console.log('success?', apiSuccess) }) console.log('res is', response) 記述量が増えた?その通りです。 ですが、見やすくなるので私はこちらの方が好きです。こっちのほうがスマートじゃない? あと handleType や handleSyntax は他のエラーハンドリングでも使いまわせるので、総合的には記述量が減ることを見込めます。 締め こちらを参考に書きました もっといい方法がありましたら教えてください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

useQuery で取得した変数を 別の useQuery の variables に代入する方法

備忘録。 下のコードのようなことをしようとしても、怒られたり、動かなかったりする。 const {data: data1, loading: loading1, error: error1} = useQuery(SOME_QUERY1) const {data: data2, loading: loading2, error: error2} = useQuery(SOME_QUERY2, { variables: {hoge: data1.someQuery1.hoge} }) そのような場合は、useLazyQuery と useEffect を使う。 const {data: data1, loading: loading1, error: error1} = useQuery(SOME_QUERY1) const [fetchSecondData , {data: data2, loading: loading2, error: error2}] = useLazyQuery(SOME_QUERY2) useEffect(() => { if (data1.someQuery1.hoge) { fetchSecondData({variables: {hoge: data1.someQuery1.hoge}}) } }, [data1]) if (loading1 || loading2) return <div>loading</div> if (error1 || error2) return <div>error</div> return( <>data2.someQuery2.hoge</> )
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UseQuery を複数使う場合

備忘録。 Apollo の useQuery を使う時、複数の data で名前がかぶってしまう問題があります。 const {data, loading, error} = useQuery(SOME_QUERY1) const {data, loading, error} = useQuery(SOME_QUERY2) このような場合は、次のようにコードを書けば、複数の data があっても名前がかぶることはありません。 const {data: data1, loading: loading1, error: error1} = useQuery(SOME_QUERY1) const {data: data2, loading: loading2, error: error2} = useQuery(SOME_QUERY2) // 呼び出しは、data1, loading1, error1 などでする
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.js 入門3 if文

v-ifのサンプルになります。 ボタンをクリックしたら文字列が消えます。 index.html <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>Vue.jsのif文</title> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="app"> <span v-if="seen">あいうえお</span> <button v-on:click="off">表示のOFF</button> </div> <script> const App = { data() { return { //最初は表示 seen: true } }, methods: { off: function() { //クリックでfalseにして非表示 this.seen = false } } } app = Vue.createApp(App) app.mount('#app') </script> </body> </html> 下記動画を参考にしています。 https://www.youtube.com/watch?v=wlCwyouRbtE
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Phaser.jsでCPU対戦ポーカーを作りました

この3連休(2022年3月19日~21日)でPhaser.jsを使用して CPU対戦ポーカーを作りました。 Phaser.jsはすごく使いやすくて、便利でした。 トランプ系のゲームは、ルールを知っている人が多いので 練習に作るには良いのではないかと思いました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.js 入門2 v-on:click

v-on:clickの使い方のサンプルになります。 ボタンをクリックするとカウントが1ずつ上がります。 index.html <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>カウントアップ</title> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="app1"> <p> {{ count }}回クリックしました。</p> <button v-on:click="increment">カウントを増やす</button> </div> <script> const App1 = { data() { return { //初期値を定義 count: 0 } }, methods: { increment: function(){ //count変数に+1する this.count += 1; } } } app1 = Vue.createApp(App1) app1.mount('#app1') </script> </body> </html>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Deno】パスやURLの操作方法まとめ

Denoでパスの操作をする方法について解説します。 基本は URL API を使う パス文字列を手動で操作する前に、まずはURL APIの使用を検討しましょう。 const pathToFoo = new URL("./foo.txt", import.meta.url); URL APIはWeb APIの一つです。 URL APIを使うと Windowsにおけるドライブ文字の処理 Windows/linuxにおけるパス区切り文字の差異の吸収(スラッシュとバックスラッシュ) パス同士の結合 を全てやってくれます。 特にドライブ文字の処理は手で書いてデバッグするの大変ですから、積極的にURL APIを使いましょう。 パスの結合 パスの結合にはURLコンストラクタを使用します。 const pathA = "./foo.txt"; const pathB = "/path/to/dir/"; const path = new URL(pathA, pathB); // pathBにpathAを結合したもの URLコンストラクタの左と右に文字列を渡すと両者が結合されたURLが返ります。 自ファイル基準の相対パス import.meta.urlを使うと、自分のファイルを基準にした相対パスが取れます。 // 自分のファイルが`file:///a/b/c.ts`のとき、`file:///a/b/foo.txt` // 自分のファイルが`https://example.com/a/b/c.ts`のとき、`https://example.com/a/b/foo.txt` const relativePath = new URL("./foo.txt", import.meta.url); ライブラリ等を https://deno.land/x に公開する場合、import.meta.urlとfetchを使うと、ライブラリで読み込まれた時とローカルで実行した時の両方で動作するコードになります。 // このファイルをローカルで実行した場合、`file:///...` // このファイルが https://deno.land/x からimportされた場合、`https://deno.land/x/...` const url = new URL("./foo.wasm", import.meta.main); // fetchは`https://...`と`file:///...`の両方のURLを読むことができる。 // ローカルで実行した場合はローカルファイルが、 // deno.land/x からimportされた場合は deno.land/x からファイルが読み込まれる。 const res = await fetch(url); console.log(await res.text()); ここで例えばimport.meta.urlの代わりにカレントディレクトリを使ったり、fetchの代わりにDeno.readTextFileを使ったりしてしまうと動きません。 他のAPIと組み合わせて使う Denoのファイル操作系APIや、fetchなどのWeb APIは、URLオブジェクトを引数に取ることができます。 Deno.readTextFile(new URL(...)); fetch(new URL(...)); 標準ライブラリのpathモジュール URL APIではできない特殊な操作については、標準ライブラリのpathモジュールを使います。 例えばDeno.cwd()で得られるカレントディレクトリはパス文字列ですから、URLとして扱うことはできません。そのため、下のtoFileUrlなどを使ってURLに変換することになります。 fileURLとパス文字列の相互変換 まずはパス文字列とfileURLの変換方法です。標準ライブラリのfromFileUrl/toFileUrlを使います。 import { fromFileUrl, toFileUrl } from "https://deno.land/std@0.130.0/path/mod.ts"; // パス文字列→fileURLの変換 console.log(toFileUrl("/foo/bar.txt")); // => URL { href: "file:///foo/bar.txt" } console.log(toFileUrl(`${Deno.cwd()}/`)) // => URL { href: "file:///C:/Users/path/to/cwd/"} // fileURL→パス文字列の変換 console.log(fromFileUrl("file:///foo/bar.txt")); // => "\foo\bar.txt" console.log(fromFileUrl(new URL("file:///foo/bar.txt"))); // => "\foo\bar.txt" パス文字列からファイル名・ディレクトリ・拡張子の取得 それぞれbasename/dirname/extnameを使います。 import { basename, dirname, extname } from "https://deno.land/std@0.130.0/path/mod.ts"; console.log(basename("/foo/bar.txt")); // => "bar.txt" console.log(dirname("/foo/bar.txt")); // => "/foo" console.log(extname("/foo/bar.txt")); // => ".txt" パスの結合 パス文字列の結合にはjoinかresolveを使います。 import { join, resolve } from "https://deno.land/std@0.130.0/path/mod.ts"; console.log(join("./foo/", "./bar/", "../baz.txt")); // => "foo\baz.txt" console.log(resolve("./foo/", "./bar/", "../baz.txt")); // => "C:\Users\azusa\work\deno\eki\foo\baz.txt" joinが単純に複数のパス文字列を結合するのに対し、resolveはカレントディレクトリを考慮した上でフルパスを計算してくれます。 おそらく普通はjoinを使えばいいと思います。 2つのパスの比較 パスAからパスBへの相対パスを計算するrelativeと、パス同士の共通部分を計算するcommonがあります。 import { common, relative } from "https://deno.land/std@0.130.0/path/mod.ts"; // "./foo/"から"./bar/"への相対パスを計算する console.log(relative("./foo/", "./bar/")); // => "..\\bar" // ".\\foo\\bar\\a.txt"と".\\foo\\bar\\b.txt"の共通部分を計算する console.log(common([".\\foo\\bar\\a.txt", ".\\foo\\bar\\b.txt"])); // => ".\\foo\\bar\\" パスの正規化 normalizeを使います。 import { normalize } from "https://deno.land/std@0.130.0/path/mod.ts"; console.log(normalize("./foo/bar/baz/../../")); // => "foo\\" globを正規表現に変換 globToRegExpを使います。 詳しい仕様は公式ドキュメントへ import { globToRegExp } from "https://deno.land/std@0.130.0/path/mod.ts"; const re = globToRegExp("/foo/*.txt"); console.log(re); // => /^(?:\\|\/)+foo(?:\\|\/)+[^\\/]*\.txt(?:\\|\/)*$/ console.log(re.test("/foo/bar.txt")); // => true console.log(re.test("/bar/")); // => false パス文字列のパース parse関数は、パス文字列をオブジェクトへ変換します。 format関数は、オブジェクトをパス文字列に変換します。 import { format, parse } from "https://deno.land/std@0.130.0/path/mod.ts"; // パス文字列→オブジェクト const parsed = parse("\\foo\\bar\\baz.txt"); console.log(parsed); // => { root: "\\", dir: "\\foo\\bar", base: "baz.txt", ext: ".txt", name: "baz" } // オブジェクト→パス文字列 console.log(format(parsed)); // => "\\foo\\bar\\baz.txt" parse関数の返り値は以下のようなinterfaceを持つオブジェクトです。 { /** パスのルート('/' 又は 'c:\') */ root: string; /** ディレクトリのフルパス */ dir: string; /** ファイル名(拡張子付き) */ base: string; /** 拡張子 */ ext: string; /** ファイル名(拡張子なし) */ name: string; } 絶対パスかどうかの判定 isAbsoluteを使うと絶対パスかどうか判定できます。 import { isAbsolute } from "https://deno.land/std@0.130.0/path/mod.ts"; console.log(isAbsolute("/foo")); // => true console.log(isAbsolute("c:/foo")); // => true console.log(isAbsolute("cat:/foo")); // => false console.log(isAbsolute("./foo")); // => false パス区切り文字 sepとSEPはパスの区切り文字です。 SEP_PATTERNはパスの区切り文字にマッチする正規表現です。 delimiterは環境変数PATHの区切り文字と思われます。 import { SEP, sep, SEP_PATTERN, delimiter } from "https://deno.land/std@0.130.0/path/mod.ts"; console.log(sep); // => "\\" (windowsとposixで異なる) console.log(SEP); // => "\\" (windowsとposixで異なる) console.log(SEP_PATTERN); // => /[\\/]+/ console.log(delimiter) // => ";" (windowsとposixで異なる) URLのパターンマッチ URLのパターンマッチにはURLPattern APIを使うことができます。 const pattern = new URLPattern({ pathname: "/foo/:id.txt" }); console.log(pattern.test({ pathname: "/foo/bar.txt" })); // => true console.log(pattern.exec({ pathname: "/foo/bar.txt" })); // => { pathname: { input: "/foo/bar.txt", groups: { id: "bar" } }, ...} pattern.test()でマッチしたかどうか(trueかfalse)、pattern.exec()でマッチ結果を得ることができます。詳しい使用方はMDNを見てください。 まとめ パス文字列を直接操作することは避け、極力URL APIを使うのがオススメ 特殊なパス文字列の操作には標準ライブラリのpathモジュールを使う URLへのパターンマッチにはURLPattern APIを使う
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Deno】パスの操作方法まとめ

Denoでパスの操作をする方法について解説します。 基本は URL API を使う パス文字列を手動で文字列操作する前に、まずはURL APIの使用を検討しましょう。 const pathToFoo = new URL("./foo.txt", import.meta.url); URL APIはWeb APIの一つです。 URL APIを使うと Windowsにおけるドライブ文字の処理 Windows/linuxにおけるパス区切り文字の差異の吸収(スラッシュとバックスラッシュ) パス同士の結合 を全てやってくれます。 特にドライブ文字の処理は手で書いてデバッグするの大変ですから、積極的にURL APIを使いましょう。 パスの結合 パスの結合にはURLコンストラクタを使用します。 const pathA = "./foo.txt"; const pathB = "/path/to/dir/"; const path = new URL(pathA, pathB); // pathBにpathAを結合したもの URLコンストラクタの左と右に文字列を渡すと両者が結合されたURLが返ります。 自ファイル基準の相対パス import.meta.urlを使うと、自分のファイルを基準にした相対パスが取れます。 // 自分のファイルが`file:///a/b/c.ts`のとき、`file:///a/b/foo.txt` // 自分のファイルが`https://example.com/a/b/c.ts`のとき、`https://example.com/a/b/foo.txt` const relativePath = new URL("./foo.txt", import.meta.url); 他のAPIと組み合わせて使う Denoのファイル操作系APIや、fetchなどのWeb APIは、URLオブジェクトを引数に取ることができます。 Deno.readTextFile(new URL(...)); fetch(new URL(...)); 標準ライブラリのpathモジュール URL APIではできない特殊な操作については、標準ライブラリのpathモジュールを使います。 例えばDeno.cwd()で得られるカレントディレクトリはパス文字列ですから、URLとして扱うことはできません。そのため、下のtoFileUrlなどを使ってURLに変換することになります。 fileURLとパス文字列の相互変換 まずはパス文字列とfileURLの変換方法です。標準ライブラリのfromFileUrl/toFileUrlを使います。 import { fromFileUrl, toFileUrl } from "https://deno.land/std@0.130.0/path/mod.ts"; // パス文字列→fileURLの変換 console.log(toFileUrl("/foo/bar.txt")); // => URL { href: "file:///foo/bar.txt" } console.log(toFileUrl(`${Deno.cwd()}/`)) // => URL { href: "file:///C:/Users/path/to/cwd/"} // fileURL→パス文字列の変換 console.log(fromFileUrl("file:///foo/bar.txt")); // => "\foo\bar.txt" console.log(fromFileUrl(new URL("file:///foo/bar.txt"))); // => "\foo\bar.txt" パス文字列からファイル名・ディレクトリ・拡張子の取得 それぞれbasename/dirname/extnameを使います。 import { basename, dirname, extname } from "https://deno.land/std@0.130.0/path/mod.ts"; console.log(basename("/foo/bar.txt")); // => "bar.txt" console.log(dirname("/foo/bar.txt")); // => "/foo" console.log(extname("/foo/bar.txt")); // => ".txt" パスの結合 パス文字列の結合にはjoinかresolveを使います。 import { join, resolve } from "https://deno.land/std@0.130.0/path/mod.ts"; console.log(join("./foo/", "./bar/", "../baz.txt")); // => "foo\baz.txt" console.log(resolve("./foo/", "./bar/", "../baz.txt")); // => "C:\Users\azusa\work\deno\eki\foo\baz.txt" joinが単純に複数のパス文字列を結合するのに対し、resolveはカレントディレクトリを考慮した上でフルパスを計算してくれます。 おそらく普通はjoinを使えばいいと思います。 2つのパスの比較 パスAからパスBへの相対パスを計算するrelativeと、パス同士の共通部分を計算するcommonがあります。 import { common, relative } from "https://deno.land/std@0.130.0/path/mod.ts"; // "./foo/"から"./bar/"への相対パスを計算する console.log(relative("./foo/", "./bar/")); // => "..\\bar" // ".\\foo\\bar\\a.txt"と".\\foo\\bar\\b.txt"の共通部分を計算する console.log(common([".\\foo\\bar\\a.txt", ".\\foo\\bar\\b.txt"])); // => ".\\foo\\bar\\" パスの正規化 normalizeを使います。 import { normalize } from "https://deno.land/std@0.130.0/path/mod.ts"; console.log(normalize("./foo/bar/baz/../../")); // => "foo\\" globを正規表現に変換 globToRegExpを使います。 詳しい仕様は公式ドキュメントへ import { globToRegExp } from "https://deno.land/std@0.130.0/path/mod.ts"; const re = globToRegExp("/foo/*.txt"); console.log(re); // => /^(?:\\|\/)+foo(?:\\|\/)+[^\\/]*\.txt(?:\\|\/)*$/ console.log(re.test("/foo/bar.txt")); // => true console.log(re.test("/bar/")); // => false パス文字列のパース parse関数は、パス文字列をオブジェクトへ変換します。 format関数は、オブジェクトをパス文字列に変換します。 import { format, parse } from "https://deno.land/std@0.130.0/path/mod.ts"; // パス文字列→オブジェクト const parsed = parse("\\foo\\bar\\baz.txt"); console.log(parsed); // => { root: "\\", dir: "\\foo\\bar", base: "baz.txt", ext: ".txt", name: "baz" } // オブジェクト→パス文字列 console.log(format(parsed)); // => "\\foo\\bar\\baz.txt" parse関数の返り値は以下のようなinterfaceを持つオブジェクトです。 { /** パスのルート('/' 又は 'c:\') */ root: string; /** ディレクトリのフルパス */ dir: string; /** ファイル名(拡張子付き) */ base: string; /** 拡張子 */ ext: string; /** ファイル名(拡張子なし) */ name: string; } 絶対パスかどうかの判定 isAbsoluteを使うと絶対パスかどうか判定できます。 import { isAbsolute } from "https://deno.land/std@0.130.0/path/mod.ts"; console.log(isAbsolute("/foo")); // => true console.log(isAbsolute("c:/foo")); // => true console.log(isAbsolute("cat:/foo")); // => false console.log(isAbsolute("./foo")); // => false パス区切り文字 sepとSEPはパスの区切り文字です。 SEP_PATTERNはパスの区切り文字にマッチする正規表現です。 delimiterは環境変数PATHの区切り文字と思われます。 import { SEP, sep, SEP_PATTERN, delimiter } from "https://deno.land/std@0.130.0/path/mod.ts"; console.log(sep); // => "\\" (windowsとposixで異なる) console.log(SEP); // => "\\" (windowsとposixで異なる) console.log(SEP_PATTERN); // => /[\\/]+/ console.log(delimiter) // => ";" (windowsとposixで異なる) URLのパターンマッチ URLのパターンマッチにはURLPattern APIを使うことができます。 const pattern = new URLPattern({ pathname: "/foo/:id.txt" }); console.log(pattern.test({ pathname: "/foo/bar.txt" })); // => true console.log(pattern.exec({ pathname: "/foo/bar.txt" })); // => { pathname: { input: "/foo/bar.txt", groups: { id: "bar" } }, ...} pattern.test()でマッチしたかどうか(trueかfalse)、pattern.exec()でマッチ結果を得ることができます。詳しい使用方はMDNを見てください。 まとめ パス文字列を直接操作することは避け、極力URL APIを使うのがオススメ 特殊なパス文字列の操作には標準ライブラリのpathモジュールを使う URLへのパターンマッチにはURLPattern APIを使う
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

React Conf 2021 全体ざっくりまとめ

いよいよ React 18 のリリースが近づいてきています。 いい機会なので今更ながら React Conf 2021 の内容をざっくりとまとめて紹介しようと思います。 この記事では、具体的な内容は書かないので詳細は動画を御覧ください。 早速、各セッションのまとめをざっくり記載していこうと思います。 セッション React 18 Keynote React がどのように活用されているかと React Conf のセッションの紹介。Suspense や新しい API での取り組みについての説明もされています。Suspense など React 18 の機能は、Native アプリ開発 のインスピレーションを得たことについても説明されています。 React 18 for app developers React 18 の新機能の説明・アップグレードについての説明です。新機能では、自動バッチ処理・Suspense・その他新 API の仕組みについて話しています。最後にはアップデートの仕方と実際にデモを行って紹介をしています。 Streaming Server Rendering with Suspense SSR をするときに、Suspense を使うことでレンダリングが最適化される話。一般的にハイドレートが完了しないとユーザーの操作を受け付けないが、Suspense を使うことで作業分割や非同期表示を行うことができる。ユーザー操作によるハイドレートの順序の最適化についても話をしている。 The first React Working Group ワーキンググループがどんな動きをしているかを API の名前の変更の経緯などと合わせて紹介しています。併せて、useSyncExternalStore, useId, useInsertionEffect に関しての変更経緯の話もしています。 React Developer Tooling React の Developer Tools の最新更新情報とロードマップの話。hooks 部分のソースマップから変数名を解析して表示させたり、コンポーネントのプロファイルやタイムラインの解析を紹介しています。 将来的にプロファイルの改善、React Native のデバッグ改善、SSR のデバッグなどを考えていることを話していました。 React without memo 自動メモ化コンパイラの製品化を目指している話。現状は可読性とパフォーマンスがトレードオフになっています。しかし、それらをいいとこ取りする「React Forget」というプロジェクトがあります。まだ複雑なケースには対応できていないようです。 React Docs Keynote React のドキュメントの beta 版の紹介。 ( https://beta.reactjs.org/ ) Hooks のカリキュラムを求める声が多くあったことから行ったアプローチを解説しています。 Things I learnt from the new React docs 新しい React のドキュメントのわかりやすさを紹介。新しいドキュメントを表示しながら、children、 context、 hooks などがどのような挙動なのかわかりやすく解説されていることを説明しています。 Learning in the Browser ブラウザだけで React を学んだ経験の話。scrimba を利用してターミナルもシェルもコンソールもない状態ですぐに React アプリを書いた体験を話していました。また、最終的に Astro という静的サイトジェネレーターでブログを作って OSS への貢献をした話をしていました。 The ROI of Designing with React デザイナーが React を学ぶことによる ROI の話。figma や framer などで props などの概念を理解することでエンジニアとデザイナーの対話がかんたんになる。また、"ノーコード(アートボード上の component や variants)"、"デザインからコードを活用"、"React内でプロトタイプを構築"のいずれであってもエンジニアとのシームレスな連携などの利益を得ることができるという話。 Interactive Playgrounds with React 静的ページにユーザーのインタラクティブが必要な要素を追加するときは mdx がおすすめという話。React Docs beta でも利用されています。Docusaurus、 Nextra、 next/mdx、 next-mdx-remote、 mdx-bundler などを利用して使うことができる。 Re-introducing Relay Relay の紹介。GraphQL のデータを利用するアプリを構築するためのフレームワーク。コンパイルを挟むことによってクエリを最適化した状態にする。動画後半ではSuspenseとの連携などの説明を行っています。 React Native Desktop Messenger Desktop React Native Migration Electron で Messenger Desktop に拡張現実の機能を追加した際に React Native を採用した話。 WebRTC の制限に遭遇したときに "Electronのフォーク"、"WebGL"、"Qt"、"React Native" を検討したが、"労力"、"パフォーマンス"、"スキルセットのアンマッチ" から React Native を採用した。結果としてパフォーマンスと信頼性が大幅に向上した。 React Native at Microsoft Microsoft が React Native への投資を行っている理由の話。クロスプラットフォームのため、 Windows や MacOS で自然に利用することができる。React のコミュニティの力を活かしてプラットフォーム構築を考えている。 On-device Machine Learning for React Native React Native で機械学習できるフレームワーク PyTorch Live を紹介する話。カメラで撮ったキャラクターを文字で表示するデモを披露しています。 React 18 for External Store Libraries useSyncExternalStore についての話。並列描画を行う際に、外部 store の更新が描画中に行われて、整合性が取れなくなる場合があります。React 17 から 18 へ移行する前に、 use-sync-external-store というパッケージを利用して不整合が起きないようにしておきましょうという話でした。 Building accessible components with React 18 ariakit を利用してかんたんに accesible なものを作ることができる話。Suspense でローディング表示を行ったり、 useDeferredValue で古い状態を維持するデモで紹介しています。 Accessible Japanese Form Components with React 行政手続きの煩雑さと多様性を解決するための UI Kit である "SmartHR UI" の紹介。和暦があることによる手間や、エラーの表示の順序によるアクセシビリティの低下などを例に紹介されています。 UI Tools for artists Netflix Studio で利用している React ベースの Hawkins というデザインシステムの話。アーティストの制作時間を最大化するために他の作業を最小化する必要があるため、画像アップロードをコピー&ペーストでかんたんにアップロードすることができるようになったようです。 Hydrogen + React 18 Hydrogen という、 React 18 ベースで shopify のカスタムストアを構築するための framework の話。一般的な SPA と比べて初期ロードされる bundle のサイズが小さいため、高速な FCP (First Contentful Paint) を実現しているようです。まだデベロッパープレビュー版ですが、オープンソース化しているので気軽なコミュニティへの参加を募っています。 さいごに 拙いまとめで解釈が違ったりするかもしれませんが、少しでも動画を見る判断材料になればと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PropTypes.array の役割

配列を受け付ける PropTypes.array // 受け付けるもの [33, 'こんにちわ', true] [{ name: '山田'}, { fruit: 'りんご' }] 配列であれば中身は気にしない。 そのため非推奨となっている。 配列の値の型もすべてチェックするなら、 arrayOfを使用する必要がある。 PropTypes.arrayOf(PropTypes.string) // 受け付けるもの ['1','あいうえお']
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【React hooks】 useReducer って何?

はじめに フックAPIリファレンスでuseReducerの存在は知っていましたが、実際に使ったことがなかったので、使い所を調べて使ってみた話をします。 useReducerとは useStateの代用品 役割は、useStateと大体おんなじ感じってことですかね。 現在の state を dispatch メソッドとペアにして返します(もし Redux に馴染みがあれば、これがどう動作するのかはご存じでしょう)。 useReducer が useState より好ましいのは、複数の値にまたがる複雑な state ロジックがある場合 複数階層にまたがって更新を発生させるようなコンポーネントではパフォーマンスの最適化にもなります。 useStateまみれになったりするの嫌だなぁと思ったことあります。 それを1つのオブジェクトというかReduxのStoreのように、管理できるような感じってことですかね。 Todoアプリのサンプルを作成 Todoアプリのサンプルを作成しました。 ファイル構成はこんな感じです。 src/components/pages/Todos/index.tsx Todoの追加フォーム、リストデータの表示 src/stores/TodosStore/index.ts TodosコンポーネントからuseStoreを呼び出している。 useStateを使って、loadingとtodosを管理 Todoの追加用と削除用のメソッドを提供 画面の動きはこんな感じです。 バックエンドは用意していないので、それっぽく見せるために、Todoの追加時、削除時に、ダミーのfetchを使って、3秒間、ロードが発生するようにしています。 Todo追加時の状態変更の流れは以下の3ステップです。 loadingをtrueに更新 todosに1件追加更新 loadingをfalseに更新 頭の中のイメージ的には、ステップが1つ少ないです。 loadingをtrueに更新 todosに1件追加とloadingをfalseに更新 今回は、stateが2つなので、それほど複雑ではありませんが、useReducerを使って、ReduxのStoreのように1つのオブジェクトのように管理してみます。 useReducerでリファクタリング useStateを使っているところを、useReducerに変更 src/stores/TodosStore/index.ts -const [loading, setLoading] = useState(initialState.loading); -const [todos, setTodos] = useState(initialState.todos); +const [state, dispatch] = useReducer(reducer, initialState); reducerはこんな感じで定義しておきます。 reducer const reducer = (state: TodosStore.State, action: TodosStore.Action) => { switch (action.type) { case "fetch": return fetchAction(state); case "add": return addAction(state, action); case "del": return delAction(state, action); default: return { ...initialState }; } }; useReducerで受けったdispatchは、dispatch({ type: "fetch" });という感じで呼び出すと、reducer関数の"fetch"のcaseが実行されるイメージです。 reducerは、元のstateを各処理によって、変更を加えた新しいstateを返すように記述します。 各処理はこんな感じで定義しておきます。 fetchAction const fetchAction = (state: TodosStore.State): TodosStore.State => { return { ...state, loading: true }; }; addAction const addAction = ( state: TodosStore.State, { data: { name } }: TodosStore.AddAction ): TodosStore.State => { return { ...state, loading: false, todos: [ ...state.todos, { id: new Date().getTime(), name, }, ], }; }; delAction const delAction = ( state: TodosStore.State, { data: { id } }: TodosStore.DelAction ): TodosStore.State => { const todos = state.todos.filter((todo) => todo.id !== id); return { ...state, loading: false, todos, }; }; Todosコンポーネント側から呼び出している、Todo追加と削除の関数内で、dispatchを使ったstateの更新方法に修正します。 src/stores/TodosStore/index.ts + const dispatchAddTodo = (name: string) => { + dispatch({ type: "fetch" }); + + return dummyFetch().then(() => dispatch({ type: "add", data: { name } })); + }; + + const dispatchDelTodo = (id: number) => { + dispatch({ type: "fetch" }); + + return dummyFetch().then(() => dispatch({ type: "del", data: { id } })); + }; const store: TodosStore.Store = { state, dispatchAddTodo, dispatchDelTodo, }; return store; } 動きに変化はありませんが、頭の中のイメージ通りのステップ数になったので、レンダリング回数が減りました。 というわけで、リファクタリング完了! おわりに ベストプラクティスは分かっていませんが、自分なりにReduxを意識して、Store、Reducer、Action用にファイルを分けて、簡潔に実装できたのではないかと思います、 今回は、1ファイルに全ての機能を実装していますが、実際は、ファイルが分かれたりして、でも同じstateを参照した状態でアクションを実行したいことがあると思います。 その時は、useContextを使えば、各ファイルから同じstateを参照できるような仕組みにもできると思います。(propsで頑張って渡す方法でも...) stateに直接変更を加えるわけではなく、Store側でアクションを定義して、そちら側でstateを変更する仕組みの方が、安全性があると思いますし、何より、たくさん管理したい状態がある程、1箇所にまとまっていて、そこにアクションを追加していく方が、分かりやすいのかなと思いました。 参考文献 React hooksを基礎から理解する (useReducer編) - Qiita
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript環境構築まわりの用語

Node.js Node.jsは、JavaScriptの実行環境のこと。 Node.jsはサーバーサイドのプログラミング言語だと誤解されることがあるが、 プログラミング言語であるJavaScriptを、サーバーサイドでも実行できるようにするのがNode.js。 また、現在ではフロントエンドの開発環境としても広く使われている。 詳しくは↓の記事様がわかりやすい。   npm npmは、Node.jsのパッケージ管理ツールのこと。 phpで言うところのcomposer、Pythonで言うところのpip。 npmにおけるパッケージとは、JavaScript のライブラリやフレームワークのこと。 ReactやVue.jsもnpmを利用してインストールできる。 詳しくは↓の記事がわかりやすい   nvm、nodebrew、nodist... nvm、nodebrew、nodistは、いずれもNode.jsのバージョン管理ツールのこと。 バージョン管理ツールを使用することで、Node.jsのバージョンを新しくしたり、過去のバージョンに戻したり、プロジェクトごとにバージョンを切り替えたり、といったことが可能になる。 nodebrewはMac用。nodistはwindows用。nvmはMac用とWindows用(nvm for windows)。 上記3つの他にも沢山ある。 ※なお、nodistは更新が止まっており不具合が出やすいらしいので、他を使用したほうがよさそう。 そのあたりも含めて詳しくは↓の記事様がわかりやすい。   webpack webpackは、モジュールをまとめるためのツールのこと。 ここで言うモジュールとは、jsファイルやhtmlファイル、sass(css)ファイル、画像などのこと。 webpackを使うことで複雑なプロジェクトの管理・保守・分担などがしやすくなり効率が上がる。 なぜwebpackを使うのかの具体的な話は↓の記事様がわかりやすい。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む