20210724のJavaScriptに関する記事は14件です。

ラムダ式プログラミング一時間体験講座(Python/Ruby/JavaScript同時並行版)

筆者『一時間体験講座』第4弾(1,2,3).内容的にはこちらの方が近いかもしれませんが,より実践的な内容となっています.複数言語対応の体験ということもあって,用語や仕組みの正確なところの解説はほとんどありません1. 前書き この記事は,ともすれば難読化扱いされかねないラムダ式を用いたプログラミングの普及の一助になればという筆者の被害妄想想いから生まれました.3言語同時並行で進めますが,いずれの言語も事前知識はあまり必要ありません.講座の目標として,次の記述が何を行うものかわかるようになればいいなあという感じです. Python $ python >>> (lambda u: u(u))(lambda u: (lambda n,a,b: a if n == 0 else u(u)(n - 1, b, a + b)))(40,0,1) 102334155 >>> exit() $ Ruby $ irb --simple-prompt >> ->u{u[u]}[->u{->n,a,b{n == 0 ? a : u[u][n-1,b,a+b]}}][40,0,1] => 102334155 >> exit $ JavaScript $ node > (u=>u(u))(u=>(n,a,b)=> n == 0 ? a : u(u)(n-1,b,a+b))(40,0,1) 102334155 > (Ctrlキーを押しながらD) $ 実行例はいずれも,UNIXシェルからの対話モード(REPL)で記載しています.それぞれの環境で適宜読み替えてもらえればと思います. ※JavaScriptについては,上記Node.jsの他,Chrome/EdgeでCtrl+Shift+Jで呼び出されるconsoleでも実行可能です. ラムダ式の基本 ラムダ式は無名関数とも呼ばれ,名前のない関数です.関数そのものは数学のそれと基本的には同じで,ある変数に依存して決まる値あるいはその対応を表す式によって構成されています.プログラミングでは,変数やその変数の値を引数(ひきすう)と呼んでいます.次の図は,関数処理の大まかな流れです. 引数として変数$x$,$y$をとってその変数の値を足した結果を返すラムダ式は,次のように記述できます. Python >>> lambda x, y: x + y Ruby >> ->x,y{x+y} JavaScript > (x,y)=>x+y 上記を実行2しただけでは『関数だよ』という結果表示しかありません.ですが,ラムダ式はこの実行時に,変数と値の対応を保持するための記憶領域を内部に作ります3. 変数に値を対応させて処理を行わせる4には,実際の値を引数として指定します. Python >>> (lambda x, y: x + y)(10, 20) 30 Ruby >> ->x,y{x+y}[10,20] => 30 JavaScript > ((x,y)=>x+y)(10,20) 30 実際の値については,ラムダ式を指定5することもできます.次は,3つの引数$f$,$x$,$y$をとり,$f(x,y)$,すなわち,関数$f$に$x$,$y$の値を渡して計算させるラムダ式です. Python >>> lambda f, x, y: f(x, y) Ruby >> ->f,x,y{f[x,y]} JavaScript > (f,x,y)=>f(x,y) $f$に上記の足し算を行うラムダ式を,$x$,$y$にそれぞれ10,20を引数として指定して実行すると,足し算のラムダ式の時と同じ結果が得られます6. Python >>> (lambda f, x, y: f(x, y))(lambda x, y: x + y, 10, 20) 30 Ruby >> ->f,x,y{f[x,y]}[->(x,y){x+y},10,20] => 30 JavaScript > ((f,x,y)=>f(x,y))((x,y)=>x+y,10,20) 30 注意してほしいのは,$f$,$x$,$y$のラムダ式の$x$,$y$と,足し算を行うラムダ式の$x$,$y$は全くの別物であるということです7.先の記憶領域の仕組みにより,$x$,$y$はそれぞれのラムダ式で独自に値の対応付けの管理が行われます. もし,あるラムダ式が別のラムダ式の変数を参照したい場合は,ラムダ式を返すラムダ式を定義して親子関係とし8,子が親の変数を参照可能とします.次は,引数$x$をとって『引数$y$をとって$x+y$を計算するラムダ式』を返すラムダ式の例です. Python >>> lambda x: lambda y: x + y Ruby >> ->x{->y{x+y}} JavaScript > x=>y=>x+y この式に値10を指定すると,引数xが10に対応付けられ,その対応付けを引き継いだ『引数$y$をとって$x+y$を計算するラムダ式』が返ります.最初のラムダ式の例と同じく,ラムダ式だけが返っても『関数だよ』という表示しか行われません. Python >>> (lambda x: lambda y: x + y)(10) Ruby >> ->x{->y{x+y}}[10] JavaScript > (x=>y=>x+y)(10) このラムダ式に値20を指定すると,引数$y$に20が対応付けられ,なおかつ,$x+y$が計算されますから,晴れて$10+20⇒30$が表示されます. Python >>> (lambda x: lambda y: x + y)(10)(20) 30 Ruby >> ->x{->y{x+y}}[10][20] => 30 JavaScript > (x=>y=>x+y)(10)(20) 30 ループ式と条件分岐 次は,ループを発生させるラムダ式です. Python >>> (lambda u: u(u))(lambda u: u(u)) Ruby >> ->u{u[u]}[->u{u[u]}] JavaScript > (u=>u(u))(u=>u(u)) 今回取り上げているプログラミング言語の処理系は,一定回数以上のループ(正確には,再帰)が発生するとエラーとなり停止します. Python >>> (lambda u: u(u))(lambda u: u(u)) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 1, in <lambda> File "<stdin>", line 1, in <lambda> File "<stdin>", line 1, in <lambda> [Previous line repeated 996 more times] RecursionError: maximum recursion depth exceeded Ruby >> ->u{u[u]}[->u{u[u]}] Traceback (most recent call last): 16: from (irb):1:in `block in irb_binding' 15: from (irb):1:in `block in irb_binding' 14: from (irb):1:in `block in irb_binding' 13: from (irb):1:in `block in irb_binding' 12: from (irb):1:in `block in irb_binding' 11: from (irb):1:in `block in irb_binding' 10: from (irb):1:in `block in irb_binding' 9: from (irb):1:in `block in irb_binding' 8: from (irb):1:in `block in irb_binding' 7: from (irb):1:in `block in irb_binding' 6: from (irb):1:in `block in irb_binding' 5: from (irb):1:in `block in irb_binding' 4: from (irb):1:in `block in irb_binding' 3: from (irb):1:in `block in irb_binding' 2: from (irb):1:in `block in irb_binding' 1: from (irb):1:in `block in irb_binding' SystemStackError (stack level too deep) JavaScript > (u=>u(u))(u=>u(u)) Thrown: RangeError: Maximum call stack size exceeded at u (repl:1:11) at u (repl:1:14) at u (repl:1:14) at u (repl:1:14) at u (repl:1:14) at u (repl:1:14) at u (repl:1:14) at u (repl:1:14) at u (repl:1:14) at u (repl:1:14) なぜ,ループが発生するのでしょうか?それは,上記の式が,引数に値として同じラムダ式を指定することで,全く同じ式が構成されるからです.構成された式は再び引数に同じラムダ式を指定していますから,引数への値の指定が次々と行われます. Python (lambda u: u(u))(lambda u: u(u)) ➡ u(u) ※ラムダ式の記憶領域に『u = lambda u: u(u)』を保持 ➡ (lambda u: u(u))(lambda u: u(u)) ➡ u(u) ※ラムダ式の記憶領域に『u = lambda u: u(u)』を保持 ➡ (lambda u: u(u))(lambda u: u(u)) ➡ … Ruby ->u{u[u]}[->u{u[u]}] ➡ u[u] ※ラムダ式の記憶領域に『u = ->u{u[u]}』を保持 ➡ ->u{u[u]}[->u{u[u]}] ➡ u[u] ※ラムダ式の記憶領域に『u = ->u{u[u]}』を保持 ➡ ->u{u[u]}[->u{u[u]}] ➡ … JavaScript (u=>u(u))(u=>u(u)) ➡ u(u) ※ラムダ式の記憶領域に『u = u=>u(u)』を保持 ➡ (u=>u(u))(u=>u(u)) ➡ u(u) ※ラムダ式の記憶領域に『u = u=>u(u)』を保持 ➡ (u=>u(u))(u=>u(u)) ➡ … このループ式の意味するところは,適切な脱出方法があれば,繰り返し相当の処理をラムダ式のみで記述できることです.ここでは,値として指定している(右側の)ラムダ式の中に,『繰り返すたびに値が変化する変数』と『その値によって条件分岐する仕組み』を組み込むことを考えます. まず,値として指定しているラムダ式について,同じラムダ式を指定している箇所の代わりに,単に『引数の値を1減らして返す』だけのラムダ式としてみます. Python (lambda u: u(u))(lambda u: u(u)) ⬇ (lambda u: u(u))(lambda u: (lambda n: n - 1)) Ruby ->u{u[u]}[->u{u[u]}] ⬇ ->u{u[u]}[->u{->n{n-1}}] JavaScript (u=>u(u))(u=>u(u)) ⬇ (u=>u(u))(u=>n=>n-1) この場合,ラムダ式の実行は2巡だけであり,ループで用いていた変数がなくなるため,『引数の値を1減らして返す』ラムダ式がそのまま戻ります.したがって,実際の値として数値を与えると,1減った値が結果として返ります. Python >>> (lambda u: u(u))(lambda u: (lambda n: n - 1))(3) # ➡ (lambda u: (lambda n: n - 1))(lambda u: (lambda n: n - 1))(3) # 左のラムダ式は,uがどのような値であっても(lambda n: n - 1)を返す # ➡ (lambda n: n - 1)(3) 2 Ruby >> ->u{u[u]}[->u{->n{n-1}}][3] # ➡ ->u{->n{n-1}}[->u{->n{n-1}}][3] # 左のラムダ式は,uがどのような値であっても->n{n-1}を返す # ➡ ->n{n-1}[3] => 2 JavaScript > (u=>u(u))(u=>n=>n-1)(3) // ➡ (u=>n=>n-1)(u=>n=>n-1)(3) // 左のラムダ式は,uがどのような値であってもn=>n-1を返す // ➡ (n=>n-1)[3] 2 そして,この『引数の値を1減らして返す』ラムダ式を更に,『引数の値が0ならば1を返し,そうでなければ,引数の値を1減らした値を返す』ラムダ式に修正します. Python (lambda u: u(u))(lambda u: (lambda n: n - 1)) ⬇ (lambda u: u(u))(lambda u: (lambda n: 1 if n == 0 else n - 1)) Ruby ->u{u[u]}[->u{->n{n-1}}] ⬇ ->u{u[u]}[->u{->n{n == 0 ? 1 : n - 1}}] JavaScript (u=>u(u))(u=>n=>n-1) ⬇ (u=>u(u))(u=>n=>n == 0 ? 1 : n - 1) 上記の条件分岐には,各言語の三項演算子を用いています.三項演算子は,条件式,条件式が真の時の式,条件式が偽の時の式から構成されています(PythonとRuby/JavaScriptで条件式の順番が異なることに注意して下さい). Python >>> a = -100 >>> a * -1 if a < 0 else a * 1 100 >>> a * -1 if a > 0 else a * 1 -100 Ruby >> a = -100 >> a < 0 ? a * -1 : a * 1 => 100 >> a > 0 ? a * -1 : a * 1 => -100 JavaScript > a = 100 100 > a < 0 ? a * -1 : a * 1 100 > a > 0 ? a * -1 : a * 1 -100 話を戻し,条件分岐を組み込んだラムダ式を用いて,引数が0の場合とそれ以外の場合で実行してみます. Python >>> (lambda u: u(u))(lambda u: (lambda n: 1 if n == 0 else n - 1))(0) # ➡ (lambda u: (lambda n: 1 if n == 0 else n - 1))(lambda u: (lambda n: 1 if n == 0 else n - 1))(0) # ➡ (lambda n: 1 if n == 0 else n - 1)(0) 1 >>> (lambda u: u(u))(lambda u: (lambda n: 1 if n == 0 else n - 1))(3) # ➡ (lambda u: (lambda n: 1 if n == 0 else n - 1))(lambda u: (lambda n: 1 if n == 0 else n - 1))(3) # ➡ (lambda n: 1 if n == 0 else n - 1)(3) 2 Ruby >> ->u{u[u]}[->u{->n{n == 0 ? 1 : n - 1}}][0] # ➡ ->u{->n{n == 0 ? 1 : n - 1}}[->u{->n{n == 0 ? 1 : n - 1}}][0] # ➡ ->n{n == 0 ? 1 : n - 1}[0] => 1 >> ->u{u[u]}[->u{->n{n == 0 ? 1 : n - 1}}][3] # ➡ ->u{->n{n == 0 ? 1 : n - 1}}[->u{->n{n == 0 ? 1 : n - 1}}][3] # ➡ ->n{n == 0 ? 1 : n - 1}[3] => 2 JavaScript > (u=>u(u))(u=>n=>n == 0 ? 1 : n - 1)(0) // ➡ (u=>n=>n == 0 ? 1 : n - 1)(u=>n=>n == 0 ? 1 : n - 1)(0) // ➡ (n=>n == 0 ? 1 : n - 1)(0) 1 > (u=>u(u))(u=>n=>n == 0 ? 1 : n - 1)(3) // ➡ (u=>n=>n == 0 ? 1 : n - 1)(u=>n=>n == 0 ? 1 : n - 1)(3) // ➡ (n=>n == 0 ? 1 : n - 1)(3) 2 引数が0の時は1を返し,それ以外では1を減らした値を返すようになりました.ただしこれは先と同じく,ループで用いていた変数がなくなるためであり,条件分岐を組み込んだラムダ式のみを用いた場合と同じです.異なるのは,この条件分岐を組み込んだラムダ式が2巡してから同じラムダ式として戻ってくる(そして,引数が0の時は1を返し,それ以外では1を減らした値を返す),ということです. ここで,条件分岐を組み込んだラムダ式について,次のように,『引数の値を1減らした値を返す』箇所に,一度なくした『同じラムダ式を指定する』記述を組み込み,そして,引数に0以外の値を渡してみます. Python >>> (lambda u: u(u))(lambda u: (lambda n: 1 if n == 0 else u(u)(n - 1)))(3) # ➡ (lambda u: (lambda n: 1 if n == 0 else u(u)(n - 1)))(lambda u: (lambda n: 1 if n == 0 else u(u)(n - 1)))(3) # ➡ (lambda n: 1 if n == 0 else u(u)(n - 1))(3) ➡ u(u)(2) # ➡ (lambda u: (lambda n: 1 if n == 0 else u(u)(n - 1)))(lambda u: (lambda n: 1 if n == 0 else u(u)(n - 1)))(2) # ➡ (lambda n: 1 if n == 0 else u(u)(n - 1))(2) ➡ u(u)(1) # ➡ (lambda u: (lambda n: 1 if n == 0 else u(u)(n - 1)))(lambda u: (lambda n: 1 if n == 0 else u(u)(n - 1)))(1) # ➡ (lambda n: 1 if n == 0 else u(u)(n - 1))(1) ➡ u(u)(0) # ➡ (lambda u: (lambda n: 1 if n == 0 else u(u)(n - 1)))(lambda u: (lambda n: 1 if n == 0 else u(u)(n - 1)))(0) # ➡ (lambda n: 1 if n == 0 else u(u)(n - 1))(0) 1 Ruby >> ->u{u[u]}[->u{->n{n == 0 ? 1 : u[u][n - 1]}}][3] # ➡ ->u{->n{n == 0 ? 1 : u[u][n - 1]}}[->u{->n{n == 0 ? 1 : u[u][n - 1]}}][3] # ➡ ->n{n == 0 ? 1 : u[u][n - 1]}[3] ➡ u[u][2] # ➡ ->u{->n{n == 0 ? 1 : u[u][n - 1]}}[->u{->n{n == 0 ? 1 : u[u][n - 1]}}][2] # ➡ ->n{n == 0 ? 1 : u[u][n - 1]}[2] ➡ u[u][1] # ➡ ->u{->n{n == 0 ? 1 : u[u][n - 1]}}[->u{->n{n == 0 ? 1 : u[u][n - 1]}}][1] # ➡ ->n{n == 0 ? 1 : u[u][n - 1]}[1] ➡ u[u][0] # ➡ ->u{->n{n == 0 ? 1 : u[u][n - 1]}}[->u{->n{n == 0 ? 1 : u[u][n - 1]}}][0] # ➡ ->n{n == 0 ? 1 : u[u][n - 1]}[0] => 1 JavaScript > (u=>u(u))(u=>n=>n == 0 ? 1 : u(u)(n - 1))(3) // ➡ (u=>n=>n == 0 ? 1 : u(u)(n - 1))(u=>n=>n == 0 ? 1 : u(u)(n - 1))(3) // ➡ (n=>n == 0 ? 1 : u(u)(n - 1))(3) ➡ u(u)(2) // ➡ (u=>n=>n == 0 ? 1 : u(u)(n - 1))(u=>n=>n == 0 ? 1 : u(u)(n - 1))(2) // ➡ (n=>n == 0 ? 1 : u(u)(n - 1))(2) ➡ u(u)(1) // ➡ (u=>n=>n == 0 ? 1 : u(u)(n - 1))(u=>n=>n == 0 ? 1 : u(u)(n - 1))(1) // ➡ (n=>n == 0 ? 1 : u(u)(n - 1))(1) ➡ u(u)(0) // ➡ (u=>n=>n == 0 ? 1 : u(u)(n - 1))(u=>n=>n == 0 ? 1 : u(u)(n - 1))(0) // ➡ (n=>n == 0 ? 1 : u(u)(n - 1))(0) 1 ループが復活しましたが,『繰り返すたびに値が変化する変数』と『その値によって条件分岐する仕組み』が揃いましたので,停止する繰り返し処理となりました.次は,この仕組みを応用した,5の階乗を求めるラムダ式です. Python >>> (lambda u: u(u))(lambda u: (lambda n,r: r if n < 1 else u(u)(n - 1, n * r)))(5, 1) 120 Ruby >> ->u{u[u]}[->u{->n,r{n < 1 ? r : u[u][n-1,n*r]}}][5,1] => 120 JavaScript > (u=>u(u))(u=>(n,r)=>n < 1 ? r : u(u)(n-1,n*r))(5,1) 120 今回のラムダ式による繰り返しパターン9を各言語でまとめると次のようになります. Python (lambda u: u(u))(lambda u: (lambda 引数変数: 停止時に返す式 if 停止の条件式 else u(u)と引数の変数を含む繰り返し計算))(引数値) Ruby ->u{u[u]}[->u{->引数変数{停止の条件式 ? 停止時に返す式 : u[u]と引数変数を含む繰り返し計算}}][引数値] JavaScript (u=>u(u))(u=>(引数変数)=>停止の条件式 ? 停止時に返す式 : u(u)と引数の変数を含む繰り返し計算)(引数値) 備考 記事に関する補足 説明はするけど対応する用語は出さないという….あとで用語一覧を追加するかも.→脚注にしてみました. 更新履歴 2021-07-25:用語の類を脚注に記載 2021-07-24:初版公開 用語の類は脚注にしました. ↩ 評価(eval) ↩ クロージャ ↩ 関数適用(apply) ↩ 高階関数(引数が関数) ↩ ラムダ計算のβ簡約相当 ↩ レキシカルスコープ ↩ 高階関数(戻り値が関数) ↩ Uコンビネータ ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript の Promise 試してみた

promise Promiseとは: Promise オブジェクトは非同期処理の最終的な完了処理 (もしくは失敗) およびその結果の値を表現します。 Promiseを返す関数を作成 function resolveAfter2Seconds() { console.log("starting slow promise") return new Promise(resolve => { console.log('promise') }) } resolveAfter2Seconds() console.log('==end==') 結果 # node async.js starting slow promise promise ==end== 2秒後に実行される関数をPromiseでラップする function resolveAfter2Seconds() { console.log("starting promise") return new Promise(resolve => { setTimeout(function() { resolve("slow") console.log("2秒後に実行されます") }, 2000) }) } resolveAfter2Seconds() console.log('==end==') 結果. 実行結果が変わった。Promiseによって非同期に実行されている. # node async.js starting promise ==end== 2秒後に実行されます # thenメソッドを使用してresolveを受け取る function resolveAfter2Seconds() { console.log("starting slow promise") return new Promise(resolve => { setTimeout(function() { resolve("slow") console.log("2秒後に実行されます") }, 2000) }) } resolveAfter2Seconds() .then(resolve => console.log(resolve)) console.log('==end==') 結果 # node async.js starting promise ==end== 2秒後に実行されます slow # 1秒後に実行される関数をラップしたPromiseを追加する function resolveAfter2Seconds() { console.log("starting slow promise") return new Promise(resolve => { setTimeout(function() { resolve("slow") console.log("2秒後に実行されます") }, 2000) }) } function resolveAfter1Second() { console.log("starting fast promise") return new Promise(resolve => { setTimeout(function() { resolve("fast") console.log("1秒後に実行されます") }, 1000) }) } resolveAfter2Seconds() resolveAfter1Second() console.log('==end==') 結果. 処理の実行順序にかかわらず非同期で実行されている. # node async.js starting slow promise starting fast promise ==end== 1秒後に実行されます 2秒後に実行されます Promise.all()を使用すると非同期処理を平行で処理し、すべての処理が終了するのを待ってから結果を実行できる function resolveAfter2Seconds() { console.log("starting slow promise") return new Promise(resolve => { setTimeout(function() { resolve("slow") console.log("2秒後に実行されます") }, 2000) }) } function resolveAfter1Second() { console.log("starting fast promise") return new Promise(resolve => { setTimeout(function() { resolve("fast") console.log("1秒後に実行されます") }, 1000) }) } Promise.all([resolveAfter2Seconds(), resolveAfter1Second()]) .then(([result1, result2]) => { console.log(result1) console.log(result2) }) console.log('==end==') 結果 # node async.js starting slow promise starting fast promise ==end== 1秒後に実行されます 2秒後に実行されます slow fast 直列実行, 逐次実行 reduceを使うことで直列的に実行できる function resolveAfter2Seconds() { console.log("starting slow promise") return new Promise(resolve => { setTimeout(function() { resolve("slow") console.log("2秒後に実行されます") }, 2000) }) } function resolveAfter1Second() { console.log("starting fast promise") return new Promise(resolve => { setTimeout(function() { resolve("fast") console.log("1秒後に実行されます") }, 1000) }) } [resolveAfter2Seconds, resolveAfter1Second] .reduce((p, f) => p.then(f), Promise.resolve()) .then(result3 => console.log(result3)) console.log('==end==') 結果 # node async.js ==end== starting slow promise 2秒後に実行されます starting fast promise 1秒後に実行されます fast async/awaitを使うと簡潔に書ける function resolveAfter2Seconds() { console.log("starting slow promise") return new Promise(resolve => { setTimeout(function() { resolve("slow") console.log("2秒後に実行されます") }, 2000) }) } function resolveAfter1Second() { console.log("starting fast promise") return new Promise(resolve => { setTimeout(function() { resolve("fast") console.log("1秒後に実行されます") }, 1000) }) } async function sequentialStart() { const slow = await resolveAfter2Seconds() console.log(slow) const fast = await resolveAfter1Second() console.log(fast) } sequentialStart() console.log('==end==') 結果 # node async.js starting slow promise ==end== 2秒後に実行されます slow starting fast promise 1秒後に実行されます fast async と await asyncはPromiseを返す(返値は暗黙的にPromise.resolveでラップされている) awaitは非同期関数の実行を一時停止してPromiseの解決を待つ. 非同期関数が一時停止している間も, 呼び出し側の関数は実行が続く. function resolveAfter2Seconds() { console.log("starting slow promise") return new Promise(resolve => { setTimeout(function() { resolve("slow") console.log("2秒後に実行されます") }, 2000) }) } async function asyncCall() { console.log('calling') const result = await resolveAfter2Seconds() console.log(result) } asyncCall() console.log('==end==') 結果 # node async.js calling starting slow promise ==end== 2秒後に実行されます slow awaitを使用しないとasync関数内の実行は続いく function resolveAfter2Seconds() { console.log("starting slow promise") return new Promise(resolve => { setTimeout(function() { resolve("slow") console.log("2秒後に実行されます") }, 2000) }) } async function asyncCall() { console.log('calling') const result = resolveAfter2Seconds() console.log(result) } asyncCall() console.log('==end==') 結果 # node async.js calling starting slow promise Promise { <pending> } ==end== 2秒後に実行されます async関数の戻り値を確認 async function asyncCall() { console.log('calling') return 'success' } console.log(asyncCall()) console.log('==end==') 結果 # node async.js calling Promise { 'success' } ==end== Promiseで返ってくる。Promise.resolve()でラップされているのでthenメソッドで繋げると async function asyncCall() { console.log('calling') return 'success' } asyncCall() .then(result => console.log(result)) console.log('==end==') 結果. async関数の戻り値を実行できる. # node async.js calling ==end== success 並列で実行させる Promise.allを使用して並列で実行させる function resolveAfter2Seconds() { console.log("starting slow promise") return new Promise(resolve => { setTimeout(function() { resolve("slow") console.log("2秒後に実行されます") }, 2000) }) } function resolveAfter1Second() { console.log("starting fast promise") return new Promise(resolve => { setTimeout(function() { resolve("fast") console.log("1秒後に実行されます") }, 1000) }) } async function parallel() { await Promise.all([ (async() => console.log(await resolveAfter2Seconds()))(), (async() => console.log(await resolveAfter1Second()))() ]) console.log('parallel end') } 結果 Promise.allを使用しているので, すべてのプロミスが解決されるまで次の処理が実行されていない. # node async.js starting slow promise starting fast promise ==end== 1秒後に実行されます fast 2秒後に実行されます slow parallel end 非同期関数を並列で実行させて返り値を受け取る function resolveAfter2Seconds() { console.log("starting slow promise") return new Promise(resolve => { setTimeout(function() { resolve("slow") console.log("2秒後に実行されます") }, 2000) }) } function resolveAfter1Second() { console.log("starting fast promise") return new Promise(resolve => { setTimeout(function() { resolve("fast") console.log("1秒後に実行されます") }, 1000) }) } async function parallel() { const result = await Promise.all([ resolveAfter2Seconds(), resolveAfter1Second() ]) .then(([result1, result2]) => { return ('resolve : ' + result1 + result2) // return Promise.resolve('resolve : ' + result1 + result2) ← これでも可 // return new Promise(resolve => { // resolve('resolve : ' + result1 + result2) ← これでも可 // }) }) console.log('parallel end') return result } parallel() .then(result => console.log(result)) console.log('==end==') 結果 # node async.js starting slow promise starting fast promise ==end== 1秒後に実行されます 2秒後に実行されます parallel end resolve : slowfast 参考 https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/async_function
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

axiosをクラス化してメソッドを再利用しやすくする

概要 現在udemyでReact.jsとNode.jsでECサイトを作成する講座を学んでいるのですが、その際axiosで使うメソッドを再利用するための方法が少し面倒で、こっちの方が良いんじゃないかと思ったのでここにメモを残しておきます。 udemy講座のやり方 // src/fucntions/auth.js import axios from "axios"; export const createOrUpdateUser = async (authtoken) => { return await axios.post( `${process.env.REACT_APP_API}/create-or-update-user`, {}, { headers: { authtoken, }, } ); }; export const currentAdmin = async (authtoken) => { return await axios.post( `${process.env.REACT_APP_API}/current-admin`, {}, { headers: { authtoken, }, } ); }; // src/fucntions/category.js export const updateCategory = async (slug, category, authtoken) => await axios.put(`${process.env.REACT_APP_API}/category/${slug}`, category, { headers: { authtoken, }, }); なんかごちゃごちゃして見づらいです。 メソッドを取り出して使う時もいちいちimportする際にメソッドを指定する必要があります。 import { createorUpdateUser, currentAdmin } from '../../functions/auth'; import { updateCategory } from '../../functions/auth'; const handleSubmit = (e) => { e.preventDefault(); createCategory({ name }, user.token) .then((res) => { ... } クラス化 再利用しやすくするためaxiosのメソッドをクラスに入れます。 // src/utils/API.js import axios from 'axios'; // 共通処理 const instance = axios.create({ baseURL: process.env.REACT_APP_API_URL, timeout: 30000, cors: true, }) // クラス化 class API { createOrUpdateUser(authToken) { return instance.request({ method: 'POST', headers: { authToken, }, url: '/create-or-update-user' }); } currentUser(authToken) { return instance.request({ method: 'POST', headers: { authToken, }, url: '/current-user' }); } updateCategory (slug, category, authToken) { return instance.request({ method: 'PUT', headers: { authToken }, url: `/category/${slug}`, data: { name: category, } }) } } export default new API(); これで大分見やすくなりました。こちらの方が何のためのメソッドかがぱっと見でわかるので良い気がします。 使う時はAPI.メソッド名とするだけで呼び出すことができます。 axiosは非同期処理ですので呼び出す時はasync/awaitを追加するのを忘れないでください。 import API from '../../utils/API'; const handleSubmit = async (e) => { e.preventDefault(); await API.createCategory(name, user.token) .then((res) => { ... } まとめ クラス化した方がコードがスッキリして再利用しすくなりました。 話が変わりますが.envファイルに入れた環境変数が呼ばれないのなんでだろうと思ってたのですがcreate-react-app を使うと REACT_APP_ を環境変数の前に入れないといけないみたいです。 参照
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[React] styled-component を利用する際の最初に知っておきたいことまとめ

はじめに これまでVuejsで開発をしてきたのですが、新しいプロジェクトでReactを利用することになりました。 キャッチアップのために、CSS in JS の代表的なライブラリの1つであるstyled-componentsを利用して簡単なWebサイトを作成しました。 実装を進める中で自分が調査して、なるほどと思ったポイントをまとめました。 [作成したサイト] [Github] CSS in JS について Reactのスタイリングの方法について CSS in JS について説明する前にまず基本として、Reactでは要素をどのようにスタイリングするかについて記載します。 Reactでは、要素のスタイリングに下記の2つの方法を用意しています。 通常のHTMLでスタイリングをするようにcssファイルを別に指定して、classNameプロパティに指定する Reactエレメントのstyleプロパティにcssに解釈できるオブジェクトを渡して、スタイリングを行う React公式では、パフォーマンスの観点から1の方法を推奨しています。 この公式の見解をそのまま当てはめるとcssファイルでスタイリングは定義したほうがいいとなります。 ただしCSSはグローバルスコープなので、比較的大きめのアプリケーションを実装する場合には名前の衝突やスタイルの影響範囲などをどのように担保するかの解決方法を検討する必要があります。 CSS in JS をなぜ利用するか CSS in JS は、cssの定義をjsファイルに記載して、上記の2のようにインラインスタイルで定義することでcssのスコープをそのコンポーネントに閉じようというコンセプトでスタイリングを行う手法です。 Facebookの@Vjeuxさんの「React: CSS in JS」 でこの手法が有名になったようで現在では様々なライブラリが実装されています。 CSS in JS を採用する理由は、主に下記です。 cssのスコープを対象のコンポーネントに閉じることでスタイル崩れを心配することなく開発したい JSXで記載したHTMLの構造とcssのスタイルを一緒に管理できる JSの既存のツールを利用して、静的解析やコードの入力補完がしやすい [CSS in JS の各ライブラリ] 2021年 CSS in JS のライブラリはかなり色々と出ています。 Emotion styled-components JSS glamorous Linaria CSS Modules などがざっと調べただけでも出てきます。 npm trend を確認すると、emotion が一番人気ですが、jss / styled-components もかなり近いレベルで採用されているようです。 最初はemotionを利用してみようと思ったのですが、css記法を利用する際には JSX Pragmaの対応が必要とのことでまずはReactに慣れることを優先して簡単に導入できるstyled-componentsを採用しました。 (v4以降ではstyled-componentsでもcss propに対応したため、css propを利用したいという場合でもstyled-componentsで対応できるようになりました。) styled-componentsでできること styled-componentsはReactコンポーネントをCSSでスタイルすることが主な目的なのですが、下記のように様々なスタイリングのパターンに対応しています。 コンポーネントに共通したテーマの設定(ThemeProviderを利用) 既存コンポーネントへのスタイルの適用(参考:Styling any component ) 擬似クラス / 擬似要素の適用(参考:Pseudoelements, pseudoselectors, and nesting ) React Nativeでの利用(参考: https://styled-components.com/docs/basics#react-native) styled-componentsの使い方をざっくり知りたいという入門者向けの情報 まずは、ざっくりstyled-componentsの使い方が知りたいという方は下記のリンクが参考になります。 公式サイト: https://styled-components.com/docs/basics#motivation styled-componentsの使い方の初歩を丁寧に解説してくれている https://www.webprofessional.jp/style-react-components-styled-components/ styled-componentsの基本的な使い方、Theme, Global https://akabeko.me/blog/2020/11/styled-components/ 比較的新しめのstyled-componentsの機能の紹介 個人的にこんなことまでできるんだと驚いた最後に比較的新しめの機能を3つ紹介させていただきます。 ポリモーフィズムによるHTMLタグの動的な切り替え css prop への対応 transient props 1. ポリモーフィズムによるHTMLタグの動的な切り替え これまではstyledの適用をする際にラップするHTML要素を決めなれけばならず、cssは同じだけどタグだけ変更したいとかpropsによって動的にHTMLの属性を変更したいというケースでは対応が難しい状態でした。 しかし、この機能を利用すれば、HTMLの属性をas propsによってrenderする際に決めることができます。 (乱用すると定義した場所でのタグの意味がなくなるので、注意は必要です。) データによって表示を切り替える必要のある複雑なリストやグリッドを実装するときには重宝すると思います。 // as propsを利用した例: import styled from "styled-components"; const Component = styled.div` color: red; `; render( <Component as="button" > Hello World! </Component> ) 上記のJSXは下記のようにbuttonタグに解釈されます。 // render されたHTML <button class="sc-bqyKva ehfErK">Hello World!</button> 2. css prop への対応 以前はstyled-composentはcss propに対応できておらず、必ずスタイリング対象のHTMLタグが必要となっていたため無駄なDOMが構築されてしまうケースがありました。 CSS in JS系の有名なライブリとしてはemotionはcss propに対応できていたため、css prop が利用したい場合はstyled-composentを採用せず、emotionを利用することが多かったようです。 css propに対応できたことで今後はstyled-composentが採用されることも増えるかと思います。 補足ですが、Typescriptで利用する場合には、下記の記載を追加しないと型エラーが出るようなのでご注意ください。 import {} from 'styled-components/cssprop' 3. transient props styled-composentで既存コンポーネントのスタイリングをpropsを利用して行う場合に、propsが意図せず伝播してしまうことがありました。 少しわかりにくいので、例で説明します。 下記のように普通にスタイリングを行うと、Containerコンポーネントにのみprops.colorを適用させたい場合でもTextにまでprops.colorが伝播されてしまいます。 // 普通にスタイリングした場合 import styled from "styled-components"; const Text = props => <p {...props} />; const Container = styled(Text)` color: ${p => p.color}; `; // colorがTextにまで伝播してしまう <Container color="blue">Hello world!</Container> この例のように自作コンポーネントの場合は、注意して実装すれば問題ないのですが、ライブラリなどで実装されたコンポーネント(例えば react-routerのなど) は自分でpropsを設定できないので伝播させたくない場合に困ってしまいます。 このような場合には、対象のpropsに$を利用することでpropsを伝播させなくするのがtransient propsの機能です。 // 普通にスタイリングした場合 import styled from "styled-components"; const Text = props => <p {...props} />; const Container = styled(Text)` color: ${p => p.$color}; `; // colorはContainer <Container $color="blue">Hello world!</Container> transient props の詳細な仕様については下記の記事が詳しいので、気になる方はご参照ください。 参考リンク React公式: CSS とスタイルの使用 CSS Modules 所感 食べログ フロントエンドエンジニアブログ:SassからCSS Modules、そしてstyled-componentsに乗り換えた話 クックパッド開発者ブログ:レシピサービスのフロントエンドに CSS in JS を採用した話 Emotion > css props > JSX Pragma styled-component > API Reference > "as" polymorphic prop styled-component > API Reference > Transient props Evan Jacobs: Introducing “transient” props Github:[styled-component] Support css prop
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

plunkerでvue その53

概要 plunkerでvueやってみた。 phinaやってみた。 サンプルコード new Vue({ el: '#app',  data: { //phina: phina, //scene:, }, beforeMount: function() { //mounted() { this.init() }, methods: { init() { phina.globalize(); phina.main(function() { var app = GameApp({ startLabel: 'splash', scenes: [{ className: 'SplashScene', label: 'splash', nextLabel: 'title', }, { className: 'TitleScene', label: 'title', nextLabel: 'count', }, { className: 'CountScene', label: 'count', nextLabel: 'pause', }, { className: 'PauseScene', label: 'pause', nextLabel: 'result', }, { className: 'ResultScene', label: 'result', nextLabel: 'title', }, ], }); app.run(); }); }, } }); 成果物 以上。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

HTML + CSS + JavaScript + React でカウンター機能を実装する

はじめに この記事ではReactのHooksである「useState」を使用して、カウンターアプリを作成する方法を解説します。必要な部分のみ参考にしていただけたらと思います。 ソースコード 「App.jsx」「index.js」「index.html」「styles.css」のコードはこちらになります。 App.jsx import { useState } from "react"; import "./styles.css"; export default function App() { // カウント保持(初期値:0) const [count, setCount] = useState(0); // カウントアップ処理 const countUp = () => { setCount(count + 1); }; return ( <div className="App"> <h1>カウンタ</h1> <p> {count} </p> <button onClick = {countUp}> + </button> </div> ); } index.js import { StrictMode } from "react"; import ReactDOM from "react-dom"; import App from "./App"; const rootElement = document.getElementById("root"); ReactDOM.render( <StrictMode> <App /> </StrictMode>, rootElement ); index.html <!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /> <title>React App</title> </head> <body> <noscript> You need to enable JavaScript to run this app. </noscript> <div id="root"></div> </body> </html> styles.css .App { text-align: center; } 実行結果 実行結果はこのようになります。 プラスボタンをクリックすると、下記画像のように数字がカウントアップされます。 以上になります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptを使った面白い作品

はじめに javascriptを初めて触って学習していた時に、codepenで見つけた面白い作品を紹介していきます。 学習のモチベーションやインスピレーションになれば幸いです。 時計をモチーフにした作品 1 Color Clock See the Pen Color Clock by Ellgine (@guardian) on CodePen. 2 Hex Clock See the Pen Hex Clock by Kyle Shanks (@mavrK) on CodePen. 3 Binary Clock 4 3D Clock See the Pen Splitting: 3D Clock by Shaw (@shshaw) on CodePen. 5 Neu Times! 6 Slide Clock 7 Column clock See the Pen Column clock by Nelson Rodrigues (@nelsonr) on CodePen. 8 Proportionate Clock See the Pen Proportionate Clock by Jamie Brook (@sketchbrook) on CodePen. 円運動をモチーフにした作品 1 Cursor with progress indicator See the Pen Cursor with progress indicator by Ivan Grozdic (@ig_design) on CodePen. 2 Back to top with progress indicator See the Pen Back to top with progress indicator by Ivan Grozdic (@ig_design) on CodePen. 雨をモチーフにした作品 1 Animated Weather Cards See the Pen Animated Weather Cards by Steve Gardner (@ste-vg) on CodePen. 2 rain See the Pen rain by Rich East (@REast) on CodePen. 3 Pure CSS random rain w/ SVG + CSS variables ☔️? See the Pen Pure CSS random rain w/ SVG + CSS variables ☔️? by Jhey (@jh3y) on CodePen. 4 rainy season See the Pen rainy season by K-T (@K-T) on CodePen. 5 Rain Cloud 6 Rain See the Pen Rain by minten (@minten) on CodePen. 色相環をモチーフにした作品 1 wheel color picker [vue.js] See the Pen wheel color picker [vue.js] by Kuba (@jakub_antolak) on CodePen. 2 Vue Color Picker Wheel basic demo See the Pen Vue Color Picker Wheel basic demo by Nino Vrijman (@nino-vrijman) on CodePen. 3 Radial Color Picker - Vue See the Pen Radial Color Picker - Vue by Rosen (@rkunev) on CodePen.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

slickを使用したスライド自動化、その他カスタム

説明 本記事はslickを使用した自動スライドを導入に関する記事です。 前提条件 javascriptを導入できている。スライド化するビューが用意されている。 完成コード application.html.erb <link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/npm/slick-carousel@1.8.1/slick/slick.css"/> <link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/npm/slick-carousel@1.8.1/slick/slick-theme.css"/> <script type="text/javascript" src="//cdn.jsdelivr.net/npm/slick-carousel@1.8.1/slick/slick.min.js"></script> index.html.erb <% if @recommend_tweets != [] && user_signed_in? %> <div class="recommend"> <ul class="recoTweet-lists slider"> <% @recommend_tweets.each do |tweet| %> <li class='recoTweet-list'> <%= link_to tweet_path(tweet.id), remote: true, class: "show-link" do %> <% if tweet.image.attached? %> <%= image_tag tweet.image, class: "recoTweet-img" %> <% else %> <div class="recoTweet-img"></div> <% end %> <div class="recoTweet-ttl"><%= tweet.title %></div> <div class="recoTweet-message"><%= tweet.message %></div> <div class="recoTweet-txt"><%= current_user.name %>さんへオススメの投稿</div> <% end %> </li> <% end %> </ul> </div> <% end %> tweet.scss .slider{ margin: 50px auto; width: 100%; @media (max-width: 1024px) { width: 90%; } @media (max-width: 767px) { width: 100%; } } .slick-next { right: 10px !important; z-index: 100; } .slick-prev { left: 0 !important; z-index: 100; } .slick-prev:before, .slick-next:before { font-size: 30px !important; color: #000 !important; } script.js //slide $('.slider').slick({ dots: true, autoplay: true, autoplaySpeed: 3000, slidesToScroll:1, slidesToShow: 3, pauseOnHover: false, arrows: true, responsive: [ { breakpoint: 1025, settings: { slidesToShow: 1, arrows: false, } }, ] }); slick導入 application.html.erb <link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/npm/slick-carousel@1.8.1/slick/slick.css"/> <link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/npm/slick-carousel@1.8.1/slick/slick-theme.css"/> <script type="text/javascript" src="//cdn.jsdelivr.net/npm/slick-carousel@1.8.1/slick/slick.min.js"></script> htmlのhead部分に上記の記載をすることでslickを使えるようにします。 ビューの修正 index.html.erb <ul class="recoTweet-lists slider"> </ul> 自身のスライドにしたい複数の要素を囲んでいる一つ上の要素にsliderクラスを追加してあげます slick設定 script.js //slide $('.slider').slick({ dots: true, autoplay: true, autoplaySpeed: 3000, slidesToScroll:1, slidesToShow: 3, pauseOnHover: false, arrows: true, responsive: [ { breakpoint: 1025, settings: { slidesToShow: 1, arrows: false, } }, ] }); ビューの修正で追加した、sliderクラスに対して設定します。上から順に解説します。 dots: true | ページ送りを確認できるドットを表示する autoplay: true | 自動でスライドするようにする autoplaySpeed: 3000 | スライドする感覚を3秒にする slidesToScroll:1 | スライドの際何枚分スライドするかの設定 slidesToShow: 3 | スライドを何枚表示するか pauseOnHover: false | ホバーした際にスライドが止まらない設定 arrows: true | スライドを送るアローボタンを表示する設定 responsive部分はbreakpointで指定したい幅の時に設定が変わるようにするレスポンシブ用の記述です。 スライドの見た目の詳細設定 tweet.scss .slider{ margin: 50px auto; width: 100%; @media (max-width: 1024px) { width: 90%; } @media (max-width: 767px) { width: 100%; } } .slick-next { right: 10px !important; z-index: 100; } .slick-prev { left: 0 !important; z-index: 100; } .slick-prev:before, .slick-next:before { font-size: 30px !important; color: #000 !important; } スライドのアローボタンには右のボタンにslick-nextクラス、左にはslick-prevクラスが設定されているのでアローの見た目はそのクラスに当てることで設定できます。!importantはcssが反映されない時にこのcssを優先させるという記述なのでcssが効かない時だけ記述してください。 まとめ 今回はslickを用いた自動スライドとその見た目の書き方について記載しました。閲覧ありがとうございました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Reactとaxiosで.envを用いて環境変数を利用する方法

ReactでAPI通信を行う際に、エンドポイントやAPIキーを環境変数に格納する必要がありますが、その方法で30分くらいググったのでメモ。。。 初心者の方や、しばらく触ってなくて忘れてしまった方の参考になれば幸いです。 ちなみにReactの環境構築はcreate-react-app、API通信はaxiosで行う想定としています。 ぱっと試す環境を作りたい方は以下コピペでどうぞ。 npx create-react-app sample-app cd sample-app yarn add axios yarn start Reactで環境変数を使用する際の重要ポイント Reactで環境変数を使用する際は、以下の3つのポイントに注意する必要があります。 envファイルはルート直下に配置する 環境変数を使用する際は「process.env.REACT_APP_」を接頭辞に使う 変更を反映する際はサーバーを再起動 envファイルはルート直下に配置する envファイルは、プロジェクトのルート直下に配置する必要があります。 create-react-appでReactアプリを作成すると、初期状態でルート直下に以下のようなディレクトリ、ファイルが作成されているはずです。 public/ ←この下にindex.htmlなど src/ ←この下にindex.jsなど .gitignore README.md package.json yarn.lock ※すみません、tree出力するのめんどくさかったのでこのような表記になりました... 重要な事は、 'envファイルは.gitignoreやREADMEなどと同じレベルの階層に設置する必要がある' という事です。 src/の下などに配置しないようにしましょう。 環境変数を使用する際は「process.env.REACT_APP_」を接頭辞に使う 環境変数を実際に呼び出す際は、環境変数の頭に「process.env.REACT_APP_」をつける必要があります。 以下のような感じです。 // これは.envファイル REACT_APP_API_URL=https://hogehogehogehoge REACT_APP_API_KEY=******-********-******** hoge.js // これは環境変数を用いたいコンポーネントのjsファイル import axios from "axios"; const options = { method: 'GET', url: process.env.REACT_APP_API_URL, headers: { 'api-key': process.env.REACT_APP_API_KEY, 'api-host': process.env.REACT_APP_API_HOST } }; 環境変数は '' や "" で囲む必要はないです。 おしまい おそらくこれでいけるんじゃないかと。 気になったところとかあればコメントいただけると幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Reactで.envを用いて環境変数を利用する方法

Reactを使用してAPI通信を行う際に、エンドポイントやAPIキーを環境変数に格納する必要がありますが、その方法で30分くらいググる必要があったのでメモ。。。 初心者の方や、しばらく触ってなくて忘れてしまった方の参考になれば幸いです。 ちなみにReactの環境構築はcreate-react-app、API通信はaxiosで行う想定としています。 ぱっと試す環境を作りたい方は以下コピペでどうぞ。 npx create-react-app sample-app yarn add axios yarn start Reactで環境変数を使用する際の重要ポイント Reactで環境変数を使用する際は、以下の3つのポイントに注意する必要があります。 envファイルはルート直下に配置する 環境変数を使用する際は「process.env.REACT_APP_」を接頭辞に使う 変更を反映する際はサーバーを再起動 envファイルはルート直下に配置する envファイルは、プロジェクトのルート直下に配置する必要があります。 create-react-appでReactアプリを作成すると、初期状態でルート直下に以下のようなディレクトリ、ファイルが作成されているはずです。 public/ ←この下にindex.htmlなど src/ ←この下にindex.jsなど .gitignore README.md package.json yarn.lock ※すみません、tree出力するのめんどくさかったのでこのような表記になりました... 重要な事は、envファイルは.gitignoreやREADMEなどと同じレベルの階層に設置する必要があるという事です。 src/の下などに配置しないようにしましょう。 環境変数を使用する際は「process.env.REACT_APP_」を接頭辞に使う 環境変数を実際に呼び出す際は、環境変数の頭に「process.env.REACT_APP_」をつける必要があります。 以下のような感じです。 // これは.envファイル REACT_APP_API_URL=https://hogehogehogehoge REACT_APP_API_KEY=******-********-******** hoge.js // これは環境変数を用いたいコンポーネントのjsファイル import axios from "axios"; const options = { method: 'GET', url: process.env.REACT_APP_API_URL, headers: { 'api-key': process.env.REACT_APP_API_KEY, 'api-host': process.env.REACT_APP_API_HOST } }; 環境変数は '' や "" で囲む必要はないです。 おしまい おそらくこれでいけるんじゃないかと。 気になったところとかあればコメントいただけると幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

node.js+Express+pugでDBDのマップカウンターを作る

はじめに DBDという対人ゲームで、あるキャラクターを使うと苦手なマップばかり当たるので、猜疑心からマップカウンターを作りました。(なお結果としては私が苦手なマップがDBDに多いだけでした!) 目次 node.js+Express+pugインストール DBDのマップをスクリピング JSONの読み書き 動的リンクを扱う .pugへ変数を渡す .pugでhtmlを生成 cssの記述 1.node.js+Express+pugインストール ここを見ながらインストール 2.DBDのマップをスクレイピング 稚拙なソースですが動きます! dbdmap.py from time import sleep import json def main(): import requests from bs4 import BeautifulSoup FullURL = "https://deadbydaylight.fandom.com" URL = "https://deadbydaylight.fandom.com/wiki/Dead_by_Daylight_Wiki" page = requests.get(URL) soup = BeautifulSoup(page.content, "html.parser") results = soup.find(id="fpRealms") results = results.find("div", {"class": "fplinks"}) results = results.find_all("a") maplst = [] mapdict = {} for result in results: dbdmap = result["href"] if not dbdmap in maplst: maplst.append(dbdmap) for mapdbd in maplst: mapurl = FullURL + mapdbd mappage = requests.get(mapurl) mapsoup = BeautifulSoup(mappage.content, "html.parser") mapresults = mapsoup.find("table", {"class": "wikitable"}) mapresults = mapresults.find("tbody") mapresults = mapresults.find("tr") mapresults = mapresults.find_all("td") for mapresult in mapresults: mapresult = mapresult.find("center") mapresult = mapresult.find("a") mapdict[mapresult.text.strip()] = 0 sleep(3) with open('<保存したい場所>.json', 'w') as outfile: json.dump(mapdict, outfile) if __name__ == "__main__": main() これにより以下の様なJSONを得られる。 {"Coal Tower": 0, "Groaning Storehouse": 0, "Ironworks of Misery": 0, ... 3.JSONの読み書き ここで詰まったのでいくつか注意点を書いておきます jsonはクライアント側からは読み書きできません。 つまり生成されたウェブページに組み込まれたスクリプトからは操作できないです。そのためサーバー側で操作する必要があります。 また、サーバー側での操作はroutes/index.jsonで行います。(リクエストされたページに対応したファイル) const fs = require('fs'); const filePath = "./routes/dbdmap.json" //jsonを読み取る const dbdmaplst = require("./dbdmap.json"); //jsonに書き込む fs.writeFile(filePath, JSON.stringify(dbdmaplst), (err) => { if (err) console.log('Error writing file:', err) }) 4.動的リンクを扱う カウンターなので、index/"map名"/"up/down"のような動的リンクを扱えるようにします。 複数個設定できます。 router.get('/', function(req, res, next) { router.get('/:map/:vote', function(req, res, next) { 受け取ったリンクのキーワードを引数のように扱えます router.get('/:map/:vote', function(req, res, next) { const dbdmaplst = require("./dbdmap.json"); const vote = req.params.vote; if (vote == "up") { dbdmaplst[req.params.map] += 1; } else if (vote == "down") { if (dbdmaplst[req.params.map] > 0) { dbdmaplst[req.params.map] -= 1; } } 5.pugへ変数を渡す json形式で変数を渡します。 res.render('index', { title: 'Epress', dbdmap: dbdmaplst}); 以上index.jsの完全なソースはこちら index.js var express = require('express'); var router = express.Router(); //read json const fs = require('fs'); const filePath = "./routes/dbdmap.json" /* GET home page. */ router.get('/', function(req, res, next) { const dbdmaplst = require("./dbdmap.json"); res.render('index', { title: 'Epress', dbdmap: dbdmaplst}); }); router.get('/:map/:vote', function(req, res, next) { const dbdmaplst = require("./dbdmap.json"); const vote = req.params.vote; if (vote == "up") { dbdmaplst[req.params.map] += 1; } else if (vote == "down") { if (dbdmaplst[req.params.map] > 0) { dbdmaplst[req.params.map] -= 1; } } fs.writeFile(filePath, JSON.stringify(dbdmaplst), (err) => { if (err) console.log('Error writing file:', err) }) res.render('index', { title: 'DBD MAP LOG', dbdmap: dbdmaplst}); }); module.exports = router; 6 .pugでhtmlを生成 pugはpythonのようにインデントで管理されているようです。 以下ソース内に注意点を記載 index.pug //layout.pugをテンプレートに使う extends layout //テンプレート内のblock contentに置き換えられる block content //'-'を先頭に記載することでjavascriptを組み込むことができる。 -var count = 0; div(class="tbl") table(cellpadding=10) //pug 独自のfor/while/if-else文を扱える //ここの'dbdmap'はindex.jsから受け取ったもの each val, key in dbdmap -count += val; tr //'#{}'で変数を扱うことができる。 td #{val} td #{key} td a(href= "/" + key + "/up") up td a(href= "/" + key + "/down") down h2 Match count: #{count} pugの記述方法はこちら 7.cssの記述 cssよく分からないのですが、こんな感じにしたらいい感じになりました。。。 style.css body { padding: 50px; font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; } a { color: #00B7FF; } table, tr, td { border: 1px solid black; padding: 10px; } table { display: inline-block; float: left; } cssの読み込み方。テンプレートのlayout.pugに記載する。 layout.pug doctype html html head title= title body block content //javascript script(src='/javascripts/todo.js') //css link(rel='stylesheet', href='/stylesheets/style.css') 終わりに 以上すべてのソースコードを記載したわけではありませんが、詰まる恐れがある点について解説できました。 もし質問があればどうぞコメントにどうぞ。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

正規表現

はじめに 正規表現とは「いくつかの文字列を一つの形式で表現するための表現方法」 難しい... この正規表現を使いこなすことができればたくさんの文章から特定の文字列を検索することが容易となるため習得したい。 正規表現とは 例えば電話番号 0120-828-828(やずや)を口頭で表現するには 「はじめに数字が4桁あって、ハイフンがあって、数字が3桁あって、またハイフンがあって、数字が3桁ある感じ」 となる これを1つの形式で表現すると '/\d{4}-\d{3}-\d{3}/' となる。 短くなり便利そうだが、なんせ分かり辛い メタ文字 正規表現で使用する文字の中で特に注意すべきものが「メタ文字」である index.html . ^ $ [ ] * + ? | ( ) メタ文字は aやb、1や2といった一般的な文字と違い、文字以上に特別な意味を含んだ文字である メタ文字の前にバックスラッシュ()を入れることによって一般的な文字列として表現できる index.html \. \^ \$ \[ \] \* \+ \? \| \( \) 電話番号の例 数字4桁を \d{4} と表現したが、これはメタ文字の一種であるエスケープシーケンスである 詳しくは メタ文字(特殊文字)の一覧 ...前方一致や繰り返し等、その他正規表現も追記予定
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

node.jsを使ってxmlをjsonにParseしたい

node.jsを使ってxmlをjsonにParseしたい 使ったライブラリ xml2js いくつかあるライブラリの中で最も人気が高かった。xmlに準拠していて、jsonをxmlに変換することも可能。 fs 今回はxmlファイルを読み込むのでこのファイルを読んだり書いたりするnodeの標準モジュールを使用した。 const fs = require("fs");; const xml2js = require("xml2js"); var sampleXml = './exmample/test.xml' //parseしたいxmlファイルのあるパスを書きます。 まずは、xmlのファイルを読み込んでいきます。 var xmlData = fs.readFileSync(sampleXml, "utf-8"); //fsのreadFilesSyncメソッドを使います //このメソッドは、第一引数に読み込みたいもの。第二引数にそのものの文字コードを入れます。 次に読み込んだファイルをjson形式にparseしませう。 xml2js.parseString(xmlData, function (err, result) { if (err) { console.log(err.message) } else { console.log(result) } } これで、parseされたjsonがコンソールに表示されます。 最近はjson形式でのデータのやり取りが多いですが、古めかしい大きなシステムではいまだにxml を使っていたりします。 データの加工はjsonの方がやりやすいと思うので、ひとつくらいpaese方法を知っておくと良きでしょう。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SvelteでModalを作ってみた

SvelteでWebサイトを作っているときに、Modalを表示したいなと思ったので調べてみた。 イメージとしては、ReactのcreatePortalのような感じ。 modal.svelteを、メインのApp.svelteとは、別のHTMLElementで作成して、表示したい。 使ったもの ● svelte-portal 上記のComponentを自身のプロジェクトにインストールして使用した。 作成したコード modal.svelte <script> import Portal from 'svelte-portal'; </script> <Portal target="#rootModal"> <div class='Modal'> <slot></slot> </div> </Portal> <style> .Modal{ position:absolute; top:0; left:0; display:flex; justify-content:center; align-items: center; height:100%; width:100%; background-color:rgba(0,0,0,.5); } </style> インストールしたsvelte-portalから、Portalコンポーネントを使用します。target="指定したいHTMLElement"になるので、htmlファイルにあるmodal用のidを指定します。 作成したModal 感想 Reactをかじり、Vueをかじった私からしても、svelteはとても使いやすいと実感している。でも、日本語のドキュメントが少ないので、調べたものを地道にまとめていきたいと思う。がんばるぞ。。。 参考 svelte練習用のWebサイトとして、やっすんさんのReact入門を参考にsvelteで再構築しています。  React入門 未経験から1週間でReactをマスターする #01. プロジェクトの作成と立ち上げ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む