- 投稿日:2020-07-15T23:37:04+09:00
DenoでpreactのコンポーネントをSSRする
はじめに
@isihigameKoudaiさんの DenoでReact Server Side Renderingした話 の記事に触発されて、DenoでpreactコンポーネントのSSRをやってみました。
必要なもの
- Deno (v1.2.0で動作確認しています)
- htm
- preact-render-to-string
preactのコンポーネントをSSRする
まず、
mod.ts
という名前で以下のようなファイルを用意します。mod.tsimport { html } from "https://cdn.skypack.dev/htm@v3.0.4/preact/standalone.module.js"; import renderToString from "https://cdn.skypack.dev/preact-render-to-string@v5.1.10"; import { serve } from "https://deno.land/std@v0.61.0/http/server.ts"; // コンポーネント const App = () => html` <div> Hello world! </div> `; // HTTPサーバを起動 const server = serve({ port: 3000 }); for await (const req of server) { req.respond({ body: renderToString(html` <html> <head> <title>Hello world</title> <meta charset="utf-8" /> </head> <body> <${App} /> </body> </html> `) }); }次に、以下のコマンドで
mod.ts
を実行し、HTTPサーバを起動します。$ deno run --allow-net mod.tsサーバが起動したら、
curl
でリクエストを送信します。$ curl http://localhost:3000 <html><head><title>Hello world</title><meta charset="utf-8" /></head><body><div>Hello world!</div></body></html>HTMLが表示されれば成功です!
おわりに
この記事は以下のサイトを参考にしました
- 投稿日:2020-07-15T22:05:25+09:00
簡単レシート印刷 receiptline で位置揃えしてみた
日本発のオープンソース receiptline でレシート印刷に少しずつトライしています。
まだレシートプリンターがないので、前回利用した開発ツールを引き続き使います。
今回は位置揃えです。Markdown のテーブル
Markdown のテーブルを思い出してみましょう。
1 行目はヘッダー、2 行目は位置揃え、3 行目以降はデータです。Markdown|left|center|right| |:---|:---:|---:| |ABCDEFGHIJ|abcdefghij|1234567890|
left center right ABCDEFGHIJ abcdefghij 1234567890 レシートはテーブル
receiptline もテーブルなのですが、少し違いがあります。
ヘッダー行と位置揃え行がなく、どこまでもデータ行です。レシートを無限に続くテーブルと考えているのですね。
では最初のこれもテーブル?ReceiptLinehello, world!文字列の両側にテーブルの区切り記号
|
を追加してみます。
しかし、結果は変わりません。ReceiptLine|hello, world!|行の始めと終わりの
|
は省略されていたのです。
単純な文字列も立派なテーブルでした。位置揃えはスペースで
位置揃え行を使わずに、どのように設定するのでしょうか?
それはスペースです。左揃え
文字列の後にスペースを挿入すると、左揃えになります。
ReceiptLine|hello, world! |中央揃え
文字列の前にもスペースを挿入すると、今度は中央揃えに。
ReceiptLine| hello, world! |右揃え
文字列の後のスペースを削除すると、右揃えになります。
ReceiptLine| hello, world!|省略形
行の始めと終わりの
|
が省略できることを利用した位置揃えです。
|
がレシート用紙の端に見えてきませんか?ReceiptLine|左揃え |left 右揃え| right|字下げのスペースを出力する
ソースコードやポエムに欠かせない、字下げ。
しかし、スペースはすでに位置揃えのために使われています。そこで空白記号
~
の登場です。~
はスペースに変換されます。
ちなみに、チルダを出力したいときは\~
と書きます。ReceiptLine|hello, world! | |~hello, world! | |~~hello, world! | |~~~hello, world! |次回は 2 列のテーブルを試してみようと思います。
- 投稿日:2020-07-15T20:26:42+09:00
【javascript】イテラブル 文字列
書籍のアウトプットとして
[..."Isasaka"]//["I", "s", "a", "s", "a", "k", "a"]文字列を反復すると一連の文字が生成される
なぜ文字なのか
それを決めるのは文字列のイテレータ
文字の代わりにカスタムイテレータで文字列のイテレータを書き換えてみる。const myString = Object("Iterables are quite something") myString[Symbol.iterator] = function*() { for (const word of this.split(' ')) yield word } const words = [...myString]// ["Iterables", "are", "quite", "something"]ここでは文字列を作成し、文字列オブジェクトを作成するためにObject呼び出しで囲んでいる
こうしないとプロパティを更新しても維持できない。次は文字の代わりに単語を生成するカスタムイテレータを文字列の@@iteratorに割り当てる。
オブジェクトの@@iteratorを上書きするときは注意
オブジェクト自体のイテレータを使って反復処理をした場合、無限ループになる。const myArray = Object([2, 3, 5]) myArray[Symbol.iterator] = function*() { const copy = [...this] //カスタムイテレータの中で //thisを反復しようとしている。 copy.reverse(); for (const item of copy) yield item } const a = [...myArray]イテレータの中で[...this]をしていると、
値を取得するためにthisの反復処理をしようとする
→イテレータを使用する必要がある
→すでに入れテータの中にいるので再帰呼び出し
- 投稿日:2020-07-15T20:11:16+09:00
【javascript】シンボル
シンボルをオブジェクトのキーとして使用する
シンボルをプロパティ名として使えば、疑似プライベートプロパティを実現できる。
const Store = { //プロパティ名は計算されたプロパティである必要がある。 [Symbol('_internals')]: { /* */ } }ES6で追加されたgetOwnPropertySymbolメソッドではオブジェクトの直接のプロパティ農地シンボルであるものが全て含まれた配列を返す。
これによりシンボルへの参照を取得してプロパティにアクセスすることが可能。そういう意味で本当のプライベートではないが、「完全にプライベートでないがパブリックでもない」APIはメタプロパティにうってつけ。
グローバルシンボルで振る舞いへのフックを作成
グローバルシンボルはどこからでもアクセスできるシンボル
アクセスにはSymbol.forメソッドを呼ぶconst sym=Symbol.for('my symbol')グローバルシンボルは定義されたフックをオブジェクトの振る舞いに追加するのに適してる。
well-knownシンボルでオブジェクトの振る舞いを変更する
well-knownシンボルはSymbolに直接割り振られた組み込みシンボル
Symbol.toPrimitiveプロパティを利用すればオブジェクトをフックし、そのオブジェクトをプリミティブ値に型変換する方法が制御できる。const age = { number: 32, string: 'thirty-two', [Symbol.toPrimitive]: function() { return this.string } } console.log(`${age}`)//thirty-twoこれは関数なので簡略表記で整理できる。
const age = { number: 32, string: 'thirty-two', [Symbol.toPrimitive]() { return this.string } } console.log(`${age}`)この例ではオブジェクトを文字列型に型変換した。
オブジェクトが数値に型変換される時になにか別の処置を実行しても良い
- 投稿日:2020-07-15T20:05:56+09:00
Pythonでは`+++`も`+++++`も`++++++++`も許される
バズっているようなので相乗りします。
C/C++ では+++は許される
JavaScriptで+++は許されない+ ++は許されるPythonでは
++
も+++
も++++++++++
も許されます!a = 1 b = 2 print(a+b) # OK print(a++b) # OK print(a+++b) # OK # ... print(a+++++++++++++++b) # OKすなわち、
+
を繋げてどんなに長い「演算子」をつくったとしても動きます。上でつくった「演算子」は、前置演算子としても使えます。
上でつくった「演算子」は2項演算子ですが、同様に前置演算子バージョンもあります。
a = 1 print(+a) # OK print(++a) # OK print(+++a) # OK # ... print(+++++++++++++++a) # OK
-----
でもよい。
+
が嫌いな方は、-
を使って「演算子」を構成してもよいでしょう。a = 1 b = 2 print(a-b) # OK print(a--b) # OK print(a---b) # OK # ... print(a---------------b) # OKもちろん、前置演算子バージョンも使えます。
また、+
と-
を適宜まぜあわせて使ってもいいです。(+---+-+++-+
のように)用途
ソースコードの区切り
print("first") print("second") print("third") 0-+-+-+-+-+-+-0 print("100 times")JavaScriptだとダメ
Javascriptの場合、以下のように3文字以上の「演算子」はエラーとなる可能性が高いです。
let a = 1 console.log(+++a) // NG console.log(a+++) // NG console.log(++-a) // NGただし、以下はOKです。
let a = 1, b = 2 console.log(-++a) // OK console.log(+-+a) // OK console.log(a++-b) // OK console.log(a-++b) // OK console.log(a++-++b) // OKなぜダメなの?
PythonとJavaScript (or C)の違いは、インクリメント演算子
++
が定義されているかどうかです。例えば+++++
という「演算子」がある場合は演算子を最小一致で切り出していくので、++
++
+
という3つの演算子に分割されます。
++
演算子は右側(or 左側)が左辺値(変数)でなければならないため、エラーとなります。思ったこと。
インクリメンタル演算子がある言語の場合、大体どれも同じ挙動をしますね。https://t.co/7RRNSuFLBk
— yasuo_ozu (@yasuo_ozu) July 14, 2020※
++
が存在しない言語でも、Luaの場合は++
のようにつなげて書くと+
+
のように分割されず、エラーになるようです
- 投稿日:2020-07-15T16:58:31+09:00
Deno で HTML をパースし DOM を走査する
Parsing HTML in Deno
Deno は標準では DOM をサポートしていませんが、DOM を扱えるようにするライブラリが公開されています。
ところが、ドキュメントが Node.js 用のままであったり不親切だったので、初めて使うと戸惑いました… (汗) 。
1. 使うライブラリ
Deno 用:
- tbjgolden/deno-htmlparser2 - GitHub
- DenoBRComunitty/domhandler - GitHub
- DenoBRComunitty/domutils - GitHub
オリジナル:
- fb55/htmlparser2 - GitHub
- fb55/domhandler - GitHub
- fb55/domutils - GitHub
2. ソースコード
いずれのライブラリも、Deno 用はしっかりした CDN では公開されていないため、GitHub からソースコードを直接ダウンロードするか、githack.com のような非公式の WEB サービスを利用して import します。
Deno では TypeScript を利用できますが、ここでは JavaScript で記述しています。
main.jsimport { Parser } from 'https://rawcdn.githack.com/tbjgolden/deno-htmlparser2/01b2d3da3911a9d3dc134f4b68b230da5c39c5f3/htmlparser2/index.ts'; import { DomHandler } from 'https://rawcdn.githack.com/DenoBRComunitty/domhandler/5562474d5bd8d78f932fdf1df3c0ebbba50de3c0/mod.ts'; import { getText, getElementById } from 'https://rawcdn.githack.com/DenoBRComunitty/domutils/581a32557fda430c0d9ebc167f0cb4cb7414f55d/mod.ts'; // const parse = html => new Promise((resolve, reject) => { const handler = new DomHandler((error, dom) => { if ( error ) { reject(error); } else { resolve(dom); } }); const parser = new Parser(handler); parser.parseComplete(html); }); // const dom = await parse('Xyz <script id="test">const foo=\'<<bar>>\';</script><!--<!-- Waah! -- -->'); console.log(getText(getElementById('test', dom))); // 出力: const foo='<<bar>>';DOM の走査は domutils を使用します。
domutils はオブジェクトのメソッドではなく静的な関数として機能が提供されているため、メソッドチェーンでなく括弧で囲っていく形になります (少し残念) 。
domutils で利用可能な関数は以下のページで確認できます。
参考「fb55/domutils/docs/README.md - GitHub」
Web API の全ての機能が利用できるわけではありません。
- 投稿日:2020-07-15T16:32:48+09:00
フォームに特定の文字列を入力して別ページにジャンプする方法
入力フォームで特定の文字列を入力するとあるページにジャンプする秘密の合言葉的な動きをさせたい!
秘密の合言葉でページを開けたらかっこいいと思いません?
なおかついろいろ応用が利きそうだと感じたので共有させていただきます。
<form> <input type="text" id="input_message" value=""> <input type="button" onclick="func1()" value="ジャンプ"> </form> <script language="javascript" type="text/javascript"> function func1() { var input_message = document.getElementById("input_message").value; if(input_message == '自分が決めた言葉'){ window.location.href="飛んでほしいページのリンク"; } } </script>スクリプトの説明
入力、送信フォームを作るformタグの中にinputタグで入力フォーム、ボタンを作成。
その下に、scriptタグでJavascritを作成
func1という関数をつくりまず入力された文字列を取得
次に、if文で入力された文字列と自分が決めた文字列を比較して
trueならwindow.location.hrefでURLを設定
すると、自分が決めた文字列を入力しジャンプボタンをクリックすると設定したリンクへジャンプすることができるようになる。別タブで開きたい場合
設定したリンクを別のタブで開きたい場合は
window.open('URL','_brank');で別タブで開くことができる。
最後に
コードの改良点がございましたらコメントしていただけるとありがたいです。
- 投稿日:2020-07-15T16:26:58+09:00
Gatsby 2.24.2 が何故か動かない
内容まとめ
gatsby develop
で以下のエラーが出て動かないError: EBADF: bad file descriptor, uv_pipe_open at Object._forkChild (child_process.js:122:5) at setupChildProcessIpcChannel (internal/bootstrap/pre_execution.js:329:30) at prepareMainThreadExecution (internal/bootstrap/pre_execution.js:54:3) at internal/main/run_main_module.js:7:1 { errno: -4083, code: 'EBADF', syscall: 'uv_pipe_open' }環境
- win10
- node 14.4.0
- npm 6.14.5
gatsby-cliのインストール
$ npm i -g gatsby-cliバージョンの確認
$ gatsby --version Gatsby CLI version: 2.12.60新規サイトの作成
$ gatsby new gatsby-site
ローカルサーバーの立ち上げ
以下のエラーでストップする
$ cd ./gatsby-site $ gatsby develop child_process.js:122 p.open(fd); ^ Error: EBADF: bad file descriptor, uv_pipe_open at Object._forkChild (child_process.js:122:5) at setupChildProcessIpcChannel (internal/bootstrap/pre_execution.js:329:30) at prepareMainThreadExecution (internal/bootstrap/pre_execution.js:54:3) at internal/main/run_main_module.js:7:1 { errno: -4083, code: 'EBADF', syscall: 'uv_pipe_open' }BuildコマンドやServeコマンドは実行可能
$ gatsby build $ gatsby serve解決方法
- 直接の原因が分からなかった
- 同じPCで動いている別のGatsbyサイトがあった
上記から、ひとまずパッケージのバージョンを合わせることで解決
... "dependencies": { "gatsby": "^2.24.2", -> "^2.20.12" "react": "^16.13.1", -> "^16.12.0" "react-dom": "^16.13.1" -> "^16.12.0" }, "devDependencies": { "prettier": "2.0.5" -> "2.0.4" }, ...
- 投稿日:2020-07-15T15:50:34+09:00
【javascript】イテラブル
書籍のアウトプットとして
イテラブルとはなにか
ES2015で追加されたiterableプロトコルに従うオブジェクトのこと。
String,Array,Set,Maoなど
これらのオブジェクトwhすべて共通のぷとロコルに従うため同じ挙動をする。for..of文
for (var i = 0; i < myArray; i++) { car item = myArray[i]; //処理 }このコードはfor..ofで書くとこうなる
for(const item of myArray){ //処理 }for..ofによりイテラブルの値をループで処理できるようになった。
for..inとfor..ofの比較
const obj = { series: 'Get Programing', publicher: 'Manning' } const arr = ['Get Programing', 'Manning'] for (var name in obj) { console.log(name) //series,publicher } for (var name of arr) { console.log(name) //Get Programing,Manning }for..inはオブジェクトのプロパティを列挙する。
スプレッド演算子
イテラブルがスプレッド演算子が使用できる。
const num=[1,2,3,4,5,6,7,8,9] const min=Math.min.apply(null,num)これをスプレット演算子で書くとこうなる。
const num=[1,2,3,4,5,6,7,8,9] const min=Math.min.(..num)ポイント
- num配列が単一の引数としてMath.minに渡される。
- 配列のすべての値が別々の引数として渡される。
配列だけでなく文字列を含めたすべてのイテラブルでうまくいく。
スプレッド演算子がレスト演算子と同じ。
イテラブルを配列リテラルに展開
const surname = 'Isaacks' const letters = [...surname] console.log(letters)//["I", "s", "a", "a", "c", "k", "s"]スプレッドをイミュータブルなプッシュとして使用する
スプレッドを使用すれば既存の配列を新しい配列にコピーした上で新しい要素を追加できる。
function additem(item){ return [...cart,item] }pushを使うとこうなる。
function addItem(tem){ const newCart=cart.slice(0) newCart.push(item) return newCart }アローを使うともっと簡潔に
const addItem=item=>[...cart,item];配列のシャローコピー
配列で何らかの分割処理をしたいが
元の配列を変更したくない、という場合に便利function processItem(items) { copy = [...items]; //itemsを分割するのではなく、copyを分割する。 }シャローコピー
コピー元とコピー先が同じメモリ上のデータを参照している。ディープコピー
コピー元とコピー先が別々のデータを参照している。イテレータ:イテラブルの内部を調べる
@@iteratorプロパティを持ちiteratorプロトコルに従うオブジェクトはすべてイテラブル。
イテレータはnextメソッドを持つオブジェクト。
nextメソッドは以下の2つのプロパティを持つ
プロパティ 説明 done イテレータが反復処理を終えたかどうか value イテレータが生成する次の値 スプレッド演算子とfor..ofはdoneプロパティにtrueが設定され、値がもう残っていないことを示すまでイテレータでnextメソッドを繰り返し呼び出す。
@@iteratorとして使用できる関数の作成
オブジェクトをイテラブルにするには、新しいイテレータオブジェクトを返す@@iteratorメソッドを定義しなければいけない。
これから作る関数は以下のような機能を持っている。
- nextメソッドを持つ新しいオブジェクトを返す
- nextメソッドはdoneとvalueの2つのプロパティを持つ
function primesIterator() { const primes = [2, 3, 5] return { next() { const value = primes.shift(); const done = !value; return { value, done } } } }次にこの関数をイテレータとして使用するいてラブルを作成する。
const primesIterable = { }; const myPrimes = [...primesIterable]; //[2,3,5]これで作成できるが、nextメソッドとdoneプロパティを持つオブジェクトを返す関数---ジェネレータ関数を使うともっと簡潔
ジェネレータ関数はイテレータであると同時にイテラブル。
つまり、ジェネレレータ関数を直接反復処理できるし、別々のオブジェクトをイテラブルにするための@@iteratorメソッドとしても使用できる。
function* primesIterator() { yield 2; yield 3; yield 5; } const primesIterable = { }; const myPrimes = [...primesIterable]; //[2,3,5]
- 投稿日:2020-07-15T15:31:57+09:00
babel-polyfillと@babel/polyfillの違いってなんだ?備忘録
背景
Bugsnagで
オブジェクトは 'finally' プロパティまたはメソッドをサポートしていません
とエラーが吐かれることから調査をしていた。どうやらnew Promise.finally()
がIE非対応なことに起因しているようである。MDN - Promise.prototype.finally()そこで、package.jsonを確認してみると、以下のようになっていた。
- babel-polyfillは入っているじゃないか
- 最後のアップデートは3年前って化石プロジェクトか?w
普段あまり関わらないプロジェクトなので、たまに触りにくると、こういうものを見つけたりする。
さて、ここである疑問が浮かんだ。
babel-polyfill
と@babel/polyfill
の違いってなんだ?調べて見た
babel-polyfill
とググっても、結果としてヒットするのは@babel/polyfill
のほうだ。同じものなのか?と思って、
yarn upgrade babel-polyfill --latest
とやってみるも、更新されない。(*後から考えるとそりゃそうだってツッコミたくなるが、スルーしてください)Babelのバージョン一覧を確認してみる
どうやら同じである。ということは、メジャーアップデートの際に何かしら変更があったのか?
ということで、Babel 7 Releasedを記事を読んでみた。そして下の方にスクロールすると...
あった。これか
Move us to the @babel namespace by switching to using "scoped" packages (details). This helps differentiate official packages, so babel-core becomes @babel/core (and no squatting)
なるほど、バージョンが7.0.0になった時に、名前空間が変わったようだ。これが影響して、
--latest
をつけただけでは単純にパッケージが@babel/polyfill
に切り替わらなかったのである。結論
- 同じパッケージであるが、バージョン7.0.0をきっかけに名前空間が変わっている
- パッケージの更新をするには、改めて
yarn add -D @babel/polyfill
をしたのち、yarn remove babel-polyfill
となる。*ただし、Babel7.4.0から@babel/polyfillは非推奨になっているので、ご了承ください
=> Babel7.4で非推奨になったbabel/polyfillの代替手段と設定方法
- 投稿日:2020-07-15T14:36:24+09:00
JSでの非同期, Promise, async, awaitを簡単にまとめた
JavaScriptでのPromiseオブジェクトやasync, awaitを簡単にまとめてみました。
初心者なので浅いです。
まずはPromiseから
Promiseとは
非同期処理を抽象化してオブジェクトにすることで、それらを組み立てたり直列に並べたりすることを可能にする方法、またそのオブジェクトのこと
非同期処理って何だろう
ある処理を行なっている間に、他の処理を実行できる方式のこと。
重い処理などがあった場合、その処理の完了を待たずに次の処理を実行できるということです。
どうやって非同期処理を実現しているか
JavaScript(以下JS)は「シングルスレッド」であり、複数の処理を複数のスレッドをたてて実行することはしない。
つまり実行できる処理は1つだけである。
じゃあどうしていのかというと、JSはイベントループという仕組みを取っている
このイベントループは
https://coliss.com/articles/build-websites/operation/javascript/javascript-visualized-event-loop.html
このページが参考になります。ざっくり簡単にすると、
スタックという領域に関数を入れ、その関数を1行ずつ実行します。setTimeoutなどWeb APIによって提供されているメソッドを実行すると、そのコールバック関数がAPIに渡され、そこでsetTimeoutの機能であるタイマーをセットしてくれます。
スタックでタイマーをセットしている訳ではないということがわかります。
スタックには次の関数などがプッシュされ、実行されます。
ここで先ほどAPIによってセットされたタイマーが切れました。
そうすると、APIによって渡されたコールバック関数はキューと呼ばれる領域に渡されます。
イベントループという仕組みは、スタックとキューを監視しています。
スタックが空になるとキューに渡された先ほどのコールバック関数がスタックにプッシュされます。
こうして先ほどのコールバック関数が後から実行されます。
コールバック関数って何だろう
関数に渡される関数です。基本的に、ある処理が終わったら実行したい処理として渡されることが多いと思います。
Promiseオブジェクトをなぜ使うか
非同期処理ではコールバック関数を渡すことが多いと思います。コールバック関数を普通にかくと、複雑になります。
callback.jsfunction timeoutf(v) { setTimeout(() => { console.log(v + 'コールバック') setTimeout(() => { console.log(v + "2回目のコールバック") }, 500) }, 500) console.log("最初に出力される") }このコードは簡単なので、複雑ではないですが、コールバック関数が多くなるとネストが深くなりすぎたりして複雑になリす。
この複雑さを解消するため、非同期処理を同期的に直列にかけるようにするためにPromiseオブジェクトが使われます。(他にも理由はあると思います)
Promiseの実装
promiseオブジェクトをnewして生成する際に、「非同期で行いたい処理」を引数に渡します。
この渡された関数は、オブジェクト生成時にコンストラクタで実行されます。
渡された関数はsetTimeoutなどをともなってなくても「非同期」で実行されます。
関数のパラメータには、resolve, rejectを指定します。
処理のなかでresolve(), reject()を呼び出します。
resolve()は非同期処理の成功を通知するため、reject()は失敗を通知するためのものです。
resolve, rejectはPromiseから自動で渡されるのであまり意識しなくてもとりあえずは大丈夫だと思います。
promise.jsfunction returnPromsie(v) { return new Promise((resolve, reject) => { if(typeof v === 'string') { setTimeout(() => { console.log(v) resolve(v) }, 500) } else { reject("渡されたデータがstringじゃありません") } }) }thenを使いコールバック関数を呼びだす
先ほどの処理が終わってから何か関数を呼び出したい場合、thenを使います
thenは渡され元がresolve, rejectを呼び出した際に実行されるメソッドです。
このthenを使いコールバック関数を呼びだすために、resolve(), reject()を先ほど呼び出しました。
thenメソッドの第一引数にresolveが呼ばれた場合のコールバック関数、第二引数にrejectが呼び出された場合のコールバック関数を書きます。
それぞれの関数のパラメータで、resolve(値), reject(値)で呼ばれた値を取得することができます。
then.jsfunction returnPromsie(v) { return new Promise((resolve, reject) => { if(typeof v === 'string') { setTimeout(() => { console.log(v) resolve(v) }, 500) } else { reject("渡されたデータがstringじゃありません") } }) } let a = (result) => { console.log(result + 'Promsieが成功しました') } let b = (error) => { console.log(error) } function usethen(v){ returnPromsie(v).then(a, b) // コールバック関数を直列に記述している }Promise, thenを使うことで、ネストが深くなりすぎずコールバック関数を定義することができました。
async, await
async awaitって何だろう
より簡潔にコールバックを定義するためのキーワード。
async
非同期処理を定義するためのメソッド
このメソッドないで作成された関数は、必ずPromiseを返します。
awaitと組み合わせて非同期処理を実行したい場合はfunctionの前にこのasyncキーワードを必ずつけます。
async.jsasync function useasync() { return "値" // Promise.resolve("値")にラップされ、Promiseオブジェクトを返す }await
async構文の中で用いられ、右辺のPromiseオブジェクトのresolve(), reject()が呼び出されるまでそこで待機するためのキーワード。
awaitの右辺のPromiseオブジェクトがresolve()を呼び出した場合は、そのresolveの値を返します。
しかし、reject()を呼び出した場合はエラーをスローします。
なのでtry, catchを使うと、エラーをrejectの値をキャッチすることができます。
async.jsfunction returnPromise(v) { return new Promise((resolve, reject) => { if(typeof v === 'string') { setTimeout(() => { console.log(v) resolve(v) }, 500) } else { reject("渡されたデータがstringじゃありません") } }) } async function useawait(v) { try { let a = await returnPromise(v) console.log(a + "awaitで待機するので同期的な振る舞いになりPromiseの後の出力になります") } catch(error) { console.log(error) } }非同期処理とは本来完了を待たずに次のコードを実行する性質を持っていましたが、awaitで強制的に待機させることにより、同期的な振る舞いにすることができました。
- 投稿日:2020-07-15T14:18:31+09:00
Vuex の使い方(初級編)
Vuex とは
Vuex とは Vue.js アプリケーションのための 状態管理パターン + ライブラリ。
大規模なプロジェクト開発を行う際にコンポーネントごとで共通して利用するデータを効率的に
状態管理をすることを可能にするライブラリーです。初級編としてコンポーネント内で表示するところまでをこちらの記事で記載します。
ファイルディレクトリー
-- views | -- Home.vue -- store | -- index.js --main.js手順① Vuex のインストール
npm install vuex手順② main.js に記述
import Vue from 'vue' import App from './App.vue' import router from './router' import store from './store'// 追加 Vue.config.productionTip = false new Vue({ router, store, //追加 render: h => h(App) }).$mount('#app')main.jsにてstoreを使用できるように追加します。
手順③ storeを作成
今回は、storeフォルダー直下に、index.jsを作成し、その中で状態管理をしようと思います。
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state: { count: 2 //状態を指定 }, getters: { //gettersには メソッドを記述 doubleCount: state => state.count * 2, tripleCount: state => state.count * 3 }, mutations: { }, actions: { }, modules: { } })今回は初期値としてstateに数字の2。
gettersの中には、それを2倍にするメソッドと、3倍にするメソッドを記載しております。手順④ View で表示する
<template> <div class="home"> <img alt="Vue logo" src="../assets/logo.png"> <HelloWorld msg="Welcome to Your Vue.js App"/> <p>{{ count }}</p> <p>{{ doubleCount }}</p> <p>{{ tripleCount }}</p> <button @click="increment">+1</button> <button @click="decrement">-1</button> </div> </template> export default { computed: { // * store.js から 状態を呼び込み count() { return this.$store.state.count }, // * getters から関数を取得する doubleCount() { return this.$store.getters.doubleCount; }, tripleCount() { return this.$store.getters.tripleCount; } }, name: 'Home', components: { HelloWorld }, methods: { increment(){ this.$store.state.count++; }, decrement(){ this.$store.state.count--; } } }computedプロパティの中に
count() { return this.$store.state.count },記述しているこちらで、storeのstateの値を読んでいます。そしてそれをcountで呼べるようにしています。
doubleCount() { return this.$store.getters.doubleCount; }, tripleCount() { return this.$store.getters.tripleCount; }上記二つでは、gettersで作成した2倍にする処理の関数と、3倍にする関数をこちらで
読んでいます。ただ、この記述だとだいぶ冗長的ですよね。
それをmapGettersを使用し完結に書くことができます。
mapGetters
import { mapGetters } from 'vuex' exportdefault { computed: { // ? オブジェクトの中に記載する場合はスプレッド演算子を使用する ...mapGetters(["doubleCount", "tripleCount"]), },computedプロパティの中に{}(オブジェクト型)にすることで複数値を入れられるようにしています。
...mapGetters(["doubleCount", "tripleCount"])配列[]のなかに、gettersにおいた関数を複数指定するだけで、使用することができます。
- 投稿日:2020-07-15T14:18:31+09:00
Vuex の使い方 mapGetters (初級編)
Vuex とは
Vuex とは Vue.js アプリケーションのための 状態管理パターン + ライブラリ。
大規模なプロジェクト開発を行う際にコンポーネントごとで共通して利用するデータを効率的に
状態管理をすることを可能にするライブラリーです。初級編としてコンポーネント内で表示するところまでをこちらの記事で記載します。
ファイルディレクトリー
-- views | -- Home.vue -- store | -- index.js --main.js手順① Vuex のインストール
npm install vuex手順② main.js に記述
import Vue from 'vue' import App from './App.vue' import router from './router' import store from './store'// 追加 Vue.config.productionTip = false new Vue({ router, store, //追加 render: h => h(App) }).$mount('#app')main.jsにてstoreを使用できるように追加します。
手順③ storeを作成
今回は、storeフォルダー直下に、index.jsを作成し、その中で状態管理をしようと思います。
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state: { count: 2 //状態を指定 }, getters: { //gettersには メソッドを記述 doubleCount: state => state.count * 2, tripleCount: state => state.count * 3 }, mutations: { }, actions: { }, modules: { } })今回は初期値としてstateに数字の2。
gettersの中には、それを2倍にするメソッドと、3倍にするメソッドを記載しております。手順④ View で表示する
<template> <div class="home"> <img alt="Vue logo" src="../assets/logo.png"> <HelloWorld msg="Welcome to Your Vue.js App"/> <p>{{ count }}</p> <p>{{ doubleCount }}</p> <p>{{ tripleCount }}</p> <button @click="increment">+1</button> <button @click="decrement">-1</button> </div> </template> export default { computed: { // * store.js から 状態を呼び込み count() { return this.$store.state.count }, // * getters から関数を取得する doubleCount() { return this.$store.getters.doubleCount; }, tripleCount() { return this.$store.getters.tripleCount; } }, name: 'Home', components: { HelloWorld }, methods: { increment(){ this.$store.state.count++; }, decrement(){ this.$store.state.count--; } } }computedプロパティの中に
count() { return this.$store.state.count },記述しているこちらで、storeのstateの値を読んでいます。そしてそれをcountで呼べるようにしています。
doubleCount() { return this.$store.getters.doubleCount; }, tripleCount() { return this.$store.getters.tripleCount; }上記二つでは、gettersで作成した2倍にする処理の関数と、3倍にする関数をこちらで
読んでいます。ただ、この記述だとだいぶ冗長的ですよね。
それをmapGettersを使用し完結に書くことができます。
mapGetters
import { mapGetters } from 'vuex' exportdefault { computed: { // ? オブジェクトの中に記載する場合はスプレッド演算子を使用する ...mapGetters(["doubleCount", "tripleCount"]), },computedプロパティの中に{}(オブジェクト型)にすることで複数値を入れられるようにしています。
...mapGetters(["doubleCount", "tripleCount"])配列[]のなかに、gettersにおいた関数を複数指定するだけで、使用することができます。
- 投稿日:2020-07-15T14:03:04+09:00
HTMLCollectionにはforEachが無い
JavaScriptからHTML要素を扱うとき、要素の配列を受け取るとHTMLCollectionという型になることがあります。
例えばSelectのOptionなどがそれに当たります。
<select id="select-box"> <option>One</option> <option>Two</option> <option>Three</option> </select>const options = document.querySelector('#select-box').options;このようにしたときにoptionsがHTMLCollectionになりますが、これは配列ではないのでforEachは使えません。
ただし、HTMLCollectionはArrayライクなObjectではあるので、以下のような方法でforEachを使うことができます。
Array.prototype.forEach.call(options, function ..)Arrayライクなオブジェクトについてはこちら https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Indexed_collections
- 投稿日:2020-07-15T12:34:01+09:00
bootstrap画面Layout例(初心者今更聞けないいくつか)
背景
Html書く時、毎回画面LAYOUTに悩みます。はるか昔tableとgif使いましたが、CSS時代のやり方を勉強しなおし、要点を残し、将来の参考になればと思います。
要件
- こんな感じの画面を作りたい。
- 画面は左右に分け、左側は更に上下に分ける
- 画面全体はscroll発生しないよう自動調整する
- 右はjstree使い、項目が多い時画面を突き破ることなく、自分内部でscroll
利用ツール
- bootstrap :LAYOUT担当。全体の配置、大きさ、枠線、色を制御。CSSのみ実現されjavascript書く必要ないところが便利。
- jquery はjavascript内でhtmlを操作する時便利機能を提供する。今回はADDボタンとjstreeの初期化に利用。今更誰がjquery使うかと思いましたが、調べると意外に。
- jstree はデータをツリー状表示用ライブラリ。今回動的にアイテムを追加し、画面の高さの影響を確認するために利用する。
基本LAYOUT
まず全体を描く。bootscrapにLAYOUTする方法複数(grid、float、position、display、flex )あります。flexは一番柔軟で、中身に合わせて自動で改行されたり、適切に配置できるありますが、今回の要件は固定位置で表示するので、gridを選びました。
<body> <div class="container-fluid"> <div class="row"> <!-- 行 --> <div class="col-8"> <!-- 左側 --> <div class="row"> <div class="col">up</div> <!-- 上 --> </div> <div class="row"> <div class="col">down</div> <!-- 下 --> </div> </div> <div class="col-4">right</div> <!-- 右側 --> </div> </div> </body>grid layoutは
1. まずcontainerを立てる。container-fluidはcontainerと違って、画面いっぱい使えます。
1. 一つの行(row)
1. 行の中2列(col)がある
2. 全部12分割のうち、左側は8(col-8)、右側は4(col-4)で配置する
2. 更に左側の列の中に2つの行を配置し、それぞれ1つだけ列を持つ枠線追加
文字以外何もわからないので、枠線を入れる。
<body> <div class="container-fluid"> <div class="row"> <div class="col-8 border border-warning"> <!-- 枠線 --> <div class="row"> <div class="col border border-warning">up</div> </div> <div class="row"> <div class="col border border-warning">down</div> </div> </div> <div class="col-4 border border-warning">right</div> </div> </div> </body>更にpadやmarginを微調整する必要なら、bootstrapをご参考ください。
高さ固定
普通、内容に合わせて、タグの高さが変動します。高さを固定するには、
height
を明示します。bootstrapが提供するvh-100
( CSS のheight:100vh;
に相当する)を利用し、window(=viewport)の100% を使います。もう一つ、
height
をパーセントで指定する時、親タグもheight
を指定が必要です、しないタグが自動拡張され、結局画面を突き破ります。<body class="vh-100 vw-100"> <!-- 全体の高さ --> <div class="container-fluid h-100 w-100"> <!-- 各層全部高さ設定 --> <div class="row h-100"> <div class="col-8 border border-warning"> <div class="row"> <div class="col border border-warning">up</div> </div> <div class="row"> <div class="col border border-warning">down <button id="add" class="btn btn-success">add</button> </div> </div> </div> <div class="col-4 border border-warning h-100"> <!-- 各層全部高さ設定 --> <div id="jstree" class="h-100 overflow-auto"> <!-- ここの高さ指定したい --> <ul> <li id="rootnode" data-jstree='{"opened":true,"selected":true}'>Root node</li> </ul> </div> </div> </div> </div> </body>おまけ
LAYOUTではないですが、jstree使う時、落とし穴を注意ください。
- jstree追加削除など変更したい時、
"check_callback" : true,
は必須- jstreeのroot nodeに
data-jstree='{"opened":true}
入れないとツリーが開かない例全体
jsfiddle で試せます。
<!DOCTYPE html> <html> <link rel="shortcut icon" type="image/x-icon" href="https://github.com/favicon.ico"> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jstree/3.2.1/themes/default/style.min.css" /> <head> <meta charset="UTF-8"> <title>jstree bootstrap demo</title> </head> <body class="vh-100 vw-100"> <div class="container-fluid h-100 w-100"> <div class="row h-100"> <div class="col-8 border border-warning"> <div class="row"> <div class="col border border-warning">up</div> </div> <div class="row"> <div class="col border border-warning">down <button id="add" class="btn btn-success">add</button> </div> </div> </div> <div class="col-4 border border-warning h-100"> <div id="jstree" class="h-100 overflow-auto"> <ul> <!-- ↓↓↓↓ opened指定しないとツリー開けない --> <li id="rootnode" data-jstree='{"opened":true,"selected":true}'>Root node</li> </ul> </div> </div> </div> </div> </body> <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script> <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jstree/3.3.10/jstree.min.js"></script> <script> $('#jstree').jstree({ "core" : { "check_callback" : true, // これがないと修正系関数効かない }, "plugins" : [ "wholerow", ] }); $('#add').on("click", function(e, data){ $('#jstree').jstree().create_node('rootnode', "new item"); }); </script> </html>
- 投稿日:2020-07-15T11:50:12+09:00
【javascript】モジュールの使用
書籍のアウトプットとして
モジュールの使用
モジュールの場所を指定
import
基本構文import X from Yモジュールの場所とモジュールから何をインポートするかの指定は文字リテラルでする。
const myModule ='./my_format.js' import myFromatFunction from myModule//無効インポートは他のどのコードよりも実行されるため、変数はまだ定義されていないため使用できない。
ファイルの拡張子はオプション
ファイル拡張子がついていないパスはindex.jsを含んでいるディレクトリを表す事がある。
// ./src/file.js と ./src/file/index.jsの両方と一致 import myVal from './src/file' // ./src/file.jsのみと一致 import myVal from './src/file.js'モジュールから値をインポート
デフォルトインポート
export default function currency(num) { //処理 }このモジュールを
./utils/format/currency.js
に配置してインポートするとこうなるかもしれない//デフォルトエクスポートの値をインポートするとこうなる import formatCurrency from './utils/format/currency.js' function price(num) { return formatCurrency(num) }この関数名currencyをformatCurrencyでインポートしている。モジュールからデフォルトエクスポートの値のインポートには好き名前を使用できる。
名前付きインポート
function currency(num) { //処理 } function number(num) { //処理 } export { currency, number }インポート構文は似ている。
import { currency, number } from './utils/format' function details(product) { retun ` price:${currency(product.price)} ${number(product.quantityAvailable)} in stock ready to ship. `; }名前付きエクスポートをすべてインポート
1つのモジュールから名前付きエクスポートをすべてインポートしたい場合はアスタリスクを使用する
//formatという新しいイブジェクトが作成され //このモジュールの値がすべて割当られる import * as format from './utils/format' format.currency(1) format.number(3000)これにより新しいオブジェクトが作成されモジュールの名前付きエクスポートがそれぞれのプロパティとして設定される。
これは便利だが本来は使用する値だけをインポートすべきインポートされた値はどのような仕組みでバインドされるか
デフォルトインポートと名前付きインポートは読み取り専用の値。
インポートした値への再代入は不可能。import ajax from './ajax' ajax = 1 //エラー ajaxは読み取り専用ただしデフォルトインポートとは異なり、名前付きインポートはエクスポート元の変数に直接バインドされる。
つまりエクスポート元のファイルでそのヘンスが変化した場合はその変数をインポートしたファイルでも変化する。
副作用を目的としたインポート
モジュールのコードを実行したいが、モジュールの値を使用するための参照は必要ないど言う場合がある。
例としてGoogle Analyticsをセットアップするコードが含まれ他モジュール。
以下は副作用を目的としたインポートimport './google_analytics'このインポートではインポートがどこで発生するかに関わらず、インポ0ともとのファイtに含まれているすべてのコードがインポート先のファイルのコードよりも先に実行される。
setup() import '.my_script/'これはsetup()よりもimportのコード全体が先に実行される。
- 投稿日:2020-07-15T10:56:25+09:00
Vue.jsでthis.$options.methods.function()を使ってはいけない理由
- 投稿日:2020-07-15T10:53:03+09:00
【JavaScript】onclickイベントのreturn false; について
- onclickイベントに記述されているメソッドの後の「return false」役割が気になったので、それぞれの動作を確認。
「return false」が記述されている場合
<html> <body> <a href="https://www.google.com/?hl=ja" onclick="alert('Googleにページ移動しない');return false;"> リンク </a> </body> </html>
- リンク押下
- 「Googleにページ移動しない」がアラート表示される。
- アラート内のOKボタン押下してもページ移動はしない。
「return false」が記述されていない場合
<html> <body> <a href="https://www.google.com/?hl=ja" onclick="alert('Googleにページ移動');"> リンク </a> </body> </html>
- リンク押下
- 「Googleにページ移動」がアラート表示される。
- アラート内のOKボタン押下後Googleにページ移動する。
どうやら、return falseをつけることでhrefを打ち消している模様
- 投稿日:2020-07-15T08:49:49+09:00
AngularでYatzy作った。
Yatzyとは
6面ダイス版のポーカーです
詳しくはヤッツィー(wikipedia)環境
フレームワーク:Angular: 10.0.3 / Bootstrap4
エディタ:VisualStudioCode
デプロイ:FirebaseHostingDeploy
https://ngyatzy-e0cc8.firebaseapp.com/
ルール等はwikipedia参照で……
とりあえずリアルタイムでの役判定だけは作っておいたので、なんとか遊べるレベルのはず。
- 投稿日:2020-07-15T08:23:13+09:00
【Laravel】プロフィール画像アップロード
実装したいこと
- usersテーブルにプロフィール画像を追加
- デフォルト画像を用意
- プロフィール画像変更
- 変更時、選択した画像を表示
- アップロード時、画像をリサイズ
前提
- Laravel7
- マイグレーション済み
- routes/web.phpにRoute::resource('user', 'UserController');を記述済み
実装
データベースに画像のファイル名を保存しておいて、そのファイル名をもとに画像の読み出しを行う、という戦略をとることとします。
画像の保存場所はpublic/storage/profiles/とします。このディレクトリにあらかじめデフォルトの画像を配置しておいてください。profile_imageカラムの追加
マイグレーションファイルを作成します。
% php artisan make:migration add_column_to_users_table --table=usersカラムを追加する処理を書きます。
database/migrations/20XX_00_00_000000_add_column_to_users_table.phpclass AddColumnToUsersTable extends Migration { public function up() { Schema::table('users', function (Blueprint $table) { $table->string('profile_image')->default('default.png'); }); } public function down() { Schema::table('users', function (Blueprint $table) { $table->dropColumn('profile_image'); }); } }マイグレーション
% php artisan migrateUser.phpの$fillableにカラムを追加
app/User.phpprotected $fillable = [ 'name', 'email', 'password', 'profile_image' ];画像の表示
画像の表示の一例を以下に示します
コントローラー
app/Http/Controllers/ProfileController.phppublic function profile() { $user = Auth::user(); return view('profile', ['user' => $user]); }ビュー
resources/views/profile.blade.php<img src="{{ asset('storage/profiles/'.$user->profile_image) }}" alt="プロフィール画像">プロフィール画像編集(アップロード)
ここで、Intervention Imageを用います。
Intervention Imageを使用するには、以下のコマンドを実行するだけでいいです。% composer require intervention/imageIntervention Imageの使い方は以下のブログを参考にしました。
「完全網羅!Intervention Image(PHP)で画像を編集する全実例」
公式はこちら。コントローラー
Intervention Imageでの処理は、saveProfileImage()内で行っています。
バリデーションはフォームリクエストUserRequestで行います。(省略)app/Http/Controllers/UserController.phppublic function edit($id) { $user = Auth::user(); return view('user.edit', ['user' => $user]); } public function update($id, UserRequest $request) { $user = Auth::user(); $form = $request->all(); $profileImage = $request->file('profile_image'); if ($profileImage != null) { $form['profile_image'] = $this->saveProfileImage($profileImage, $id); // return file name } unset($form['_token']); unset($form['_method']); $user->fill($form)->save(); return redirect('/home'); } private function saveProfileImage($image, $id) { // get instance $img = \Image::make($image); // resize $img->fit(100, 100, function($constraint){ $constraint->upsize(); }); // save $file_name = 'profile_'.$id.'.'.$image->getClientOriginalExtension(); $save_path = 'storage/profiles/'.$file_name; $img->save($save_path); // return file name return $file_name; }ビュー
- 現在のプロフィール画像を最初に表示
- プロフィール画像をクリックしたら、画像ファイルを選択できる
- 選択した画像ファイルが表示される
以上を満たすように実装します。
「選択画像が変化したらアップロードしようとしている画像を表示する」といった処理をJavaScriptで実装しています。ファイルをアップロードするためには、enctype="multipart/form-data"をformタグに追加する必要があります。
resources/views/user/edit.blade.php<form method="post" action="{{ route('user.update', ['user' => $user->id]) }}" enctype="multipart/form-data"> @csrf @method('PATCH') <label for="profile_image">プロフィール画像</label> <label for="profile_image" class="btn"> <img src="{{ asset('storage/profiles/'.$user->profile_image) }}" id="img"> <input id="profile_image" type="file" name="profile_image" onchange="previewImage(this);"> </label> <button type="submit" class="btn btn-primary"> 変更 </button> </form> <script> function previewImage(obj) { var fileReader = new FileReader(); fileReader.onload = (function() { document.getElementById('img').src = fileReader.result; }); fileReader.readAsDataURL(obj.files[0]); } </script>SASS(CSS)
input#profile_image { display: none; }おわり
画像のリサイズやJavaScriptの部分は一例に過ぎないので、他のサイトで別の方法をとてもいいかも
Intervention Imageは便利なので上で紹介したサイトで勉強してみては?
- 投稿日:2020-07-15T05:58:40+09:00
旧石器時代のJS使いが今Kotlinでフロントエンド入門してみる
初めに
この記事はQitta夏祭りのテーマである「〇〇言語のみで△△アプリを作るなら?」に関連した内容です。
筆者は5年間ほぼバックエンド+jQueryというレガシーマンで、近代JavaScriptの環境構築の難易度に挫折したのでKotlinを使ってフロントエンドをやってみました。
同じように入門してみたい方がいらっしゃれば参考になるといいなぁと思います。
暇潰しに読んでいただければ幸いです。この記事の目次と成果物
作ったものはこんなTodoListです。
全然できてない
https://github.com/mamoru12150927/festivalFrontend
この記事は以下の順に進みます。
- Kotlinとは?
- Reactとは?
- 環境構築
- 画面を作る
- 終わりに&次回予告
全てを読むと、一応同じものが作れるはずです。
Kotlinとは?
googleとoracleのゴタゴタで一躍有名になった静的型付け・オブジェクト指向の言語です。この言語1つでスマホアプリからデスクトップアプリまで何でも作れてしまうのが大きな特徴です。言語の特性としては歴史あるJavaの思想を継承しつつうまく弱点を解消したPostJavaとも言える言語です。※JavaにはJavaの良さがあります。
今回はそんなマルチプレイヤー、Kotlinが主役です!
Reactとは?
もはや言語仕様がいい方に変わりすぎたJavaScriptのViewフレームワークで、現在のトップシェアを誇ります。
パーツ単位で画面を構成するコンポーネント指向という思想によりコードの再利用性を高く保つことができ、Reactコードの中にHTMLを書く記法のおかげでHTMLとRender()関数さえ知っていればとりあえず書き始められる、敷居の低いが使いこなすのは難しい(と感じる)優れたフレームワークです。
今回はKotlinで書いたコードをJavaScriptに変換するトランスパイルという仕組みを用いて利用します。環境構築
この見出しでインストール・導入するものは以下です。
- JDK8
- IntelliJ IDEA コミュニティ版
- Node.js
JDK8のインストール
JDK(Java Developer Kit)はJavaの開発に使うものをひとまとめにしたものでKotlinの実行環境であるJVM(Java virtual machine)もこの中に入っています。後述するIntelliJでもインストールできますが今回は普通にインストールします。
JDKには
- Oracleが提供している有償だがサポートが有るOracle JDK
- オープンソースで無償だがサポートのないOpen JDK
の2種類がありますが、今回はOpen JDKを使います。
openjdkのダウンロードサイトから,
RI Binaries (build 1.8.0_41-b04)欄にあるOSに合わせたものをダウンロードし任意のディレクトリに解凍します。(筆者はC:\Java\)にしました。解凍が完了したらコントロールパネルを開いて「システム環境変数の編集」を選択し、「システム環境変数」欄に以下の値を追加します。
変数名:JAVA_HOME、値:解凍先ディレクトリ\JDK(筆者ならc:\Java\JDK名) 変数名:Path、値:%JAVA_HOME%\binこれでJDKのインストールは完了です。お疲れさまでした。
IntelliJ IDEA コミュニティ版のインストール
IntelliJ IDEAはKotlinと同じ開発元であるJetbrainsが開発しているIDE(統合開発環境)です。JVM系言語での使用においてはかなり使いやすい上、Kotlinはほぼこれしか選択肢がないようにも思います。。。
IntelliJ IDEAのダウンロードサイトからコミュニティ版を選択しインストーラーをダウンロード、実行します。
- Create Desktop Shortcut(デスクトップにショートカットを作る)
- Update context menu(フォルダをIntelliJで開けるようにする)
にのみチェックを付けて後はNext連打でインストールを完了させましょう!
日本語化
デフォルトでは英語なので日本語化しましょう!
Pleiadesの日本語化パッチのダウンロードサイトからOSに合ったものをダウンロードし解凍します。
解凍したフォルダの中にあるsetup.exe
を実行すると以下のような画面が表示されます。
「日本語化するアプリケーション」欄にIntelliJの実行ファイル(idea or idea64.exe)を指定して「日本語化する」ボタンをクリックすると次回起動時以降日本語化されます。Node.jsのインストール
Node.jsはJavaScriptの地位をここまで押し上げた1つであり、サーバサイドのJavaScript実行を可能とします。それだけでなく色々便利な機能を追加しています。
今回はNPMというフロントエンドでよく使われるパッケージマネージャのみを利用しますが、Node.jsをインストールするのが手っ取り早い為インストールします。
Node.jsのダウンロードサイトより推奨版をダウンロードし実行して下さい。こちらはNext連打でインストール完了です!ここまでで準備はOkです!
死した私とこれから一緒にコーディングしていきましょう!画面を作る
ここまでお付き合いありがとうございます。それではコーディングしていきましょう。
まずは作業ディレクトリを一つ作成し、IntelliJ開いて下さい。
下のターミナルを開いて以下のコマンドを入力。プロジェクトの雛形を作っていきましょう!npx create-react-kotlin-app プロジェクト名 筆者の場合;npx create-react-kotlin-app frontendこれは面倒な設定を一切排除しすぐコーディングができるような雛形を作ってくれるコマンドです。
プロジェクト名のディレクトリが作成されたのでそのディレクトリに移動してからnpm startと入力し暫く待つとブラウザが開いて以下の画面が表示されます。
今後はこのプロジェクトをいじっていきましょう!
Hello world
まずはsrc/app/App.ktファイルを以下のように書き換えて下さい。
App.ktpackage app import org.w3c.dom.HTMLInputElement import react.dom.h1 class App() : RComponent<Props, State>(props) { override fun RBuilder.render() { h1 { +"Hello world" } } fun RBuilder.app() = child(App::class) {}この状態でlocalhost:3000を見てみるとHello worldと見出しで表示されているはずです。
1行ずつ見ていきましょう。App.ktclass App(props: Props) : RComponent<Props, State>(props) {React.jsの
extends Component
のKotlin版です。これによりコンポーネント(部品)として宣言します。App.ktoverride fun RBuilder.render() { h1 { +"Hello world" } }React.jsの
ReactDOM.render()
のKotlin版です。このブロック内に指定したhtmlタグが描画対象となります。
h1
はHTMLのh1タグに対応し、このコードなら大見出しでHello worldを描画します。App.ktfun RBuilder.app() = child(App::class) {}React.jsの
export default
のKotlin版です。この命令によって他クラスからApp()を扱えるようになります。実際にhtmlに描画をしているのはsrc/index/index.ktファイルとなります。
index.ktpackage index import app.* import kotlinext.js.* import react.dom.* import kotlin.browser.* fun main(args: Array<String>) { requireAll(require.context("src", true, js("/\\.css$/"))) render(document.getElementById("root")) { app() } }
requireAll(require.context("src", true, js("/\\.css$/")))
はsrc配下の全てのcssを読み込みます。
render
は、rootというIDをもつHTMLタグ内にapp()のrender()の内容を描画する処理です。基本的には
- いろんな部品を作る(Componentsを作る)
- app.tkのように1つにまとめる
- render()で指定したIDを持つhtmlに描画
という流れで動作しています。
次はJSライブラリの呼び出しをしてみましょう!JSライブラリの呼び出し
今回は画面が寂しいので、Bootstrap4とmaterial-uiを使ってみます。
まずはmaterial-uiをインストールします。npm install @material-ui/coreBootStrapの方はCDNで読み込みます。public/index.htmlにCDNの読み込みを追加します。
index.html<head> <meta charset="utf-8"> <!--追加--> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"> <!--中略--> <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script> </body> </html>kotlinでJSライブラリを使うには、ライブラリをラップする必要があります。
試しにMaterial-uiのAppbarをラップしてみます。
まずはJSのものだとわかるディレクトリをsrc配下に作成します。(筆者はJSModule/materialuiとしました)Appbar.kt@file:JsModule("@material-ui/core/AppBar") package jsmodule.materialui import react.RClass import react.RProps @JsName("default") external val AppBar: RClass<AppBarProps> external interface AppBarProps : RProps { var className:String? }1つずつ見ていきましょう
Appbar.kt@file:JsModule("@material-ui/core/AppBar")読み込む対象とするJSライブラリを指定します。当然node_modules配下に対象ライブラリがいないとエラーになります。
Appbar.ktexternal val AppBar: RClass<AppBarProps>これでReactで使用するときと同様、ReactClassとして読み込めるようになります。
Appbar.ktexternal interface AppBarProps : RProps { var className:String? }そのDOMが持つ属性をinterfaceの中で指定します。RPropsを型に指定するとReactのprops同様親に値を渡せます。
ブロック内で定義する属性は全てではなく使いたいもののみでOKです。今回はBootStrapのCSSを当てたいため、HTMLタグでのclassに相当するclassNameを指定しました。
String?
はKotlinのルールでnull許容をする変数を宣言する方法です。
nullを許容しない場合はString
となります。
動的言語たるJavaScriptの型を扱えるように全てを受け入れるdynamic型というものも存在します。ラップが成功したので読み込んでみます。
App.ktにて読み込みを行います。App.ktimport jsmodule.materialui.AppBar class App(props: Props) : RComponent<Props, State>(props) { override fun State.init(props: Props) { items = props.initialItems text = "" } override fun RBuilder.render() { AppBar { h1 { +"TodoList" } attrs.className="bg-dark text-center" }読み込みはまず対象ファイルをimportして、以下のように行います。
app.ktoverride fun RBuilder.render() { AppBar { h1 { +"TodoList" } attrs.className="bg-dark text-center" }AppBar{}の内部にh1タグを格納し、interfaceで指定したものをattrsの中で代入しています。これによりBootstrapのクラス、bg-darkとtext-centerをあてることができます。
ButtonならonClick()やdisabled等、Reactで指定できるものは基本的に問題なく設定できました。
終わりに&次回予告
ここまでお付き合いありがとうございました!
尻切れトンボ感がすごいですが、いかがでしたか?個人的にはTypeScriptに次ぐ読みやすさを誇り、日本語の情報が増えればもっと盛り上がるのかなと思いました。結構Kotlinらしく記述できますし!
KotlinはWeb Assemblyへの対応やReact/NativeならぬKotlin\Nativeといったビジネスロジックをプラットフォームを問わず共有できる仕組みがあります。次回はこのあたりを使って
- UIの改善
- バックエンド処理の追加(認証・CRUD)
- Androidアプリ化
あたりをやっていこうかと思っています!
- 投稿日:2020-07-15T05:58:38+09:00
週間ユーザーランキングに入る方法を考察するために、上位5名の記事投稿頻度を比較してみた
目的
Qiitaでフォロワー数を増やす1つの方法として、「書いている記事が目立つ!」のが大事だと思いますが、そのためには、「週間ユーザーランキングで10位以内に入る」「1日のトレンドに入る」を1つの目標とするのがいいのではないかと考えました。
では、1つの目安として、「週間ユーザーランキングで上位5名はどのくらい記事を書いているんだろう」という点に興味を持ち、分析してみました。
仮説
- 1週間に1度くらいのペースで記事を投稿している方が多いのではないか
分析方法
ユーザー毎の全ての記事の日時を取得し、記事投稿の頻度を考察しました。
const axios = require("axios"); async function main() { let response = await axios.get( "https://qiita.com/api/v2/items?per_page=100&query=user:halhal23" ); for (let i = 0; i < response.data.length; i++) { console.log(response.data[i].created_at); } } main();分析結果
2020年7月14日の週間ランキングに基づき、分析しました。
※西暦は、西暦毎の記事数
順位 ユーザー名 フォロワー数 総記事数 2020 2019 2018 1位 arowM 509 58 3 14 15 2位 okazuki 322 53 53 0 0 3位 tomo_makes 324 27 5 6 5 4位 TakahikoKawasaki 873 44 6 10 8 5位 halhal23 19 1 1 0 0
- 2位のokazukiさんのみが、1週間1本以上の記事を投稿していた(投稿開始は2020年3月)
- 3名は、1-2ヶ月に1本コンスタントに記事を投稿していた(投稿開始は2014年-2016年)
- 1名は、記事数1本のみで、5位にランクインしていた。(記事内容は、高卒フリーターからエンジニアになれた話)
考察
- フォロワー数が少なく、記事数1本でもランクインする方がいることから、記事数よりもQiitaユーザーのニーズを捉えた記事の質が重要、という当たり前の結論になった。(私はプログラミング初心者なので、記事の質は判別できないが。。)
- 5名中4名はコンスタントに記事を書いていることから、エンジニアとしての知見をたくさん持っている方々との比較になるので、なかなか難儀なことだと感じた。
- 一方で、「高卒からエンジニアになれた話」の記事のみでランクインしていることから、プロのエンジニアたちだけでなく、エンジニアになりたい層も比較的Qiitaユーザーには多いのではないか、と仮説を持った。
- つまり、私がプログラミング初心者だからこそ、初心者に寄り添える記事を書くことで、ランキングに入っていくことも可能なのではないか。
感想
はじめて記事を書きました。
現状は、非エンジニアなので、プログラミングをすること自体に全く慣れていませんが、その分、違った視点から記事が書けるのではないかと思いました。
もっと多くのビジネスパーソンが、プログラミングに慣れ親しみ、アフターコロナ、ウィズコロナ環境下で、生産性を上げていく社会にするための記事を書けるようになっていけたらと思います。
最後までお読みいただき、ありがとうございました!
- 投稿日:2020-07-15T02:38:38+09:00
three.js キーフレームアニメーション基礎
概要
three.jsには様々なアニメーションの方法がありますが、今回はキーフレームアニメーションによってアニメーションを行う方法について説明します。
デモ
今回は例として以下のデモのような回転しながら動くティーポットを作成します。
https://arihide.github.io/demos/keyframe/
ソースコードはこちら
https://github.com/Arihide/demos/tree/master/keyframeキーフレームアニメーションとは
プログラムの説明に入る前にキーフレームアニメーションについて簡単に説明します。キーフレームアニメーションとは、キーフレームと補間を用いたアニメーションのことです。ここでキーフレームとは時刻とそれに対応する値のペアのことです。数学っぽく書くと
keyframe = (t, v)という風になります。キーフレームを以下のように複数個用意します。
(t_0, v_0), (t_1, v_1), (t_2, v_2),\ldots, (t_n, v_n)座標軸上にプロットすると以下のような感じになります。(適当に値を与えています)
見てもらえばわかる通り、このままでは、キーフレームの存在する時刻上にしか値が存在せず、例えばt=4のとき値が存在せず困ってしまいます。
なので、キーフレームアニメーションに必要な補間をして任意の時刻に値が存在するようにします。このように連続な関数を作ることでアニメーションさせることができます。以下は補間の例です。
上のようなキーフレーム間を直線で補間する方法を線形補間と呼びます。
もちろん、補間の方法はこの一通りではありません。例えば、以下のようなものも考えられます。
このように、キーフレームと補間の方法を定めてアニメーションをさせる方法をキーフレームアニメーションといいます。次にthree.js上での実装方法について説明します。
three.jsでの実装
three.jsでキーフレームアニメーションをするにはKeyframeTrackオブジェクトを生成する必要があります。といっても直接KeyframeTrackクラスを呼び出す必要はなく、まず、以下のようにJSON形式で用意します。
var positionKeyframeTrackJSON = { name: ".position", type: "vector", times: [0, 1, 2], values: [0, 0, 0, 2, 1, 15, 0, 0, 0] }上からわかるように、JSONにはname, type, times, values計4つのプロパティが必要になります。
nameにはアニメーションさせたいプロパティ名を指定します。今回は位置を動かしたいので”.position”ですね。詳しくはthree.jsのドキュメントの.parseTrackNameの説明を参照してください。
typeにはプロパティの型を指定します。positionはVector3型なので、’vector’とします。このtypeには’vector’の他にも’number’,’color’,’quaternion’,’boolean’,’string’などの文字列を使用することができます。詳しくはKeyframeTrackのソースコードの_getTrackTypeForValueTypeNameメソッドを参照してください。
timesとvaluesには時刻とそれに対応する値をそれぞれ配列で渡します。例えば今回は
times: [0, 1, 2],
values: [0, 0, 0, 2, 1, 15, 0, 0, 0]
としていますが、これは0秒の時に位置が(0,0,0)、1秒の時は(2,1,15),2秒の時は(0,0,0)であることを表しています。var rotationKeyframeTrackJSON = { name: ".rotation[y]", type: "number", times: [0, 2], values: [0, 2 * Math.PI], interpolation: THREE.InterpolateSmooth }次はrotationのy軸回転に関してアニメーションさせたいのでnameは”.rotation[y]”としました。これは単なるスカラーですのでtypeは”number”とします。ここで、新しく’interpolation’というプロパティがあることに気づかれたと思います。これは先ほど説明した補間の式を表します。これは現在、’THREE.InterpolateLinear’,’THREE.InterpolateSmooth’,’THREE.InterpolateDiscrete’の三種類から選ぶことができ、何も入力しないと自動的に’THREE.InterpolateLinear’になります。
以上のようにいくつかキーフレームトラックを用意したら以下のようにまとめてパースします。
var clipJSON = { duration: 2, tracks: [ positionKeyframeTrackJSON, rotationKeyframeTrackJSON ] } var clip = THREE.AnimationClip.parse(clipJSON)これで、アニメーションクリップを作成することができました。(THREE.AnimationClip.parseメソッド内でKeyframeTrackオブジェクトが作成されています。)
あとは以下のようにAnimationMixerに登録してアニメーションループを回すことで冒頭のアニメーションを作ることができます。var mixer = new THREE.AnimationMixer(cube) var action = mixer.clipAction(clip) action.play() animate() function animate() { requestAnimationFrame(animate) mixer.update(0.01) controls.update(); renderer.render(scene, camera); }
- 投稿日:2020-07-15T02:00:00+09:00
コマンドをコピペした際に時々紛れてくる$を無視するパッケージをJavaScriptで作った
インターネットからコピーペーストをしたときに紛れてくる
$
を無視してコマンドを実行できるようにするパッケージを作りました。インストール
npm -g install dlll※注意 yarnの場合、
warning
が出ます。詳しくはこちら
https://github.com/valerybugakov/yarn/blob/84fc1b51e1d9ce424c495e225a790c2eeaca8627/src/util/normalize-manifest/util.js使用例
使用例
$ ls zsh: command not found: $npm -g install dlll $ ls README.md node_modules package.json tsconfig.json built package-lock.json src削除方法
npm -g uninstall dlll
コード
コード自体はJavaScriptで4行で書かれています。
https://github.com/yushimatenjin/dllll
JavaScript
index.ts#!/usr/bin/env node import { spawn } from "child_process"; if(!process.argv[2]) process.exit(0) spawn(process.argv[2], [...process.argv.slice(3)], { stdio: "inherit" });package.json
package.jsonの
bin
に$を指定し、パッケージとして実行できるようにします。
https://docs.npmjs.com/files/package.json#binpackage.json{ ... "bin": { "$": "./built/index.js" } ... }これで
$
が入力された際に、このプログラムが実行されるようになります。
- 投稿日:2020-07-15T02:00:00+09:00
コピペした際に時々紛れてくる$を無視するパッケージをJavaScriptで作った
インターネットからコピーペーストをしたときに紛れてくる
$
を無視してコマンドを実行できるようにするパッケージを作りました。コピーした際に$がコピーされる時とされない時がありもどかしい
インストール
npm -g install dlll※注意 yarnの場合、
warning
が出ます。詳しくはこちら
https://github.com/valerybugakov/yarn/blob/84fc1b51e1d9ce424c495e225a790c2eeaca8627/src/util/normalize-manifest/util.js使用例
使用例
$ ls zsh: command not found: $npm -g install dlll $ ls README.md node_modules package.json tsconfig.json built package-lock.json src削除方法
npm -g uninstall dlll
コード
コード自体はJavaScriptで4行で書かれています。
https://github.com/yushimatenjin/dllll
JavaScript
index.ts#!/usr/bin/env node import { spawn } from "child_process"; if(!process.argv[2]) process.exit(0) spawn(process.argv[2], [...process.argv.slice(3)], { stdio: "inherit" });package.json
package.jsonの
bin
に$を指定し、パッケージとして実行できるようにします。
https://docs.npmjs.com/files/package.json#binpackage.json{ ... "bin": { "$": "./built/index.js" } ... }これで
$
が入力された際に、このプログラムが実行されるようになります。
- 投稿日:2020-07-15T02:00:00+09:00
コピペした際に時々紛れてくる$を無視する
インターネットからコピーペーストをしたときに紛れてくる
$
を無視してコマンドを実行できるようにするパッケージを作りました。コピーした際に$がコピーされる時とされない時がありもどかしい
インストール
npm -g install dlll※注意 yarnの場合、
warning
が出ます。詳しくはこちら
https://github.com/valerybugakov/yarn/blob/84fc1b51e1d9ce424c495e225a790c2eeaca8627/src/util/normalize-manifest/util.js使用例
使用例
$ ls zsh: command not found: $npm -g install dlll $ ls README.md node_modules package.json tsconfig.json built package-lock.json src削除方法
npm -g uninstall dlll
コード
コード自体はJavaScriptで4行で書かれています。
https://github.com/yushimatenjin/dllll
JavaScript
index.ts#!/usr/bin/env node import { spawn } from "child_process"; if(!process.argv[2]) process.exit(0) spawn(process.argv[2], [...process.argv.slice(3)], { stdio: "inherit" });package.json
package.jsonの
bin
に$を指定し、パッケージとして実行できるようにします。
https://docs.npmjs.com/files/package.json#binpackage.json{ ... "bin": { "$": "./built/index.js" } ... }これで
$
が入力された際に、このプログラムが実行されるようになります。
- 投稿日:2020-07-15T01:24:10+09:00
かなりマニアックなサイトみつけたかも
ソースみたが、webpackになっててよくわからないが
コンテンツ的には、面白そうだな「わからないことがわからない」って項目、なんとなくそうゆうときあるぜ
近所にいたら、教えてもらいたいわ。
- 投稿日:2020-07-15T00:25:50+09:00
JSのincludesメソッド
特定の要素が配列に含まれているかどうかを true または false で返すメゾッド。
配列名.includes( 配列内にあるか確認したい数字や文字列 )
const array1 = [1, 2, 3]; console.log(array1.includes(2)); // => true// 第2引数以降の数字に含まれていない第1引数の配列を返す const without = (array, ...numbers) => { const withoutNumber = [] for (let i = 0; i < array.length; i++) { const filterNumber = array[i] if (!numbers.includes(filterNumber)) { withoutNumber.push(filterNumber) } } return withoutNumber } console.log(without([2, 1, 2, 3, 4], 1, 2)) // => [3, 4]