- 投稿日:2022-01-19T23:45:58+09:00
【小ネタ】政令指定都市で市と区と分けたいときの書き方
Introduction 住所のデータを引っ張ってきた時に都道府県、市区町村、町字ってわけるのが多いと思うのですが 政令指定都市だと市区町村のところが、〇〇市〇〇区になっていたりする。 ※町字に入ることもあるけど。。。 地味にここを分けて表示したいというニーズが意外となかったので、備忘録!! Code const cityName = "川崎市中原区" const cityNameString = cityName.replace(/^(.{1,4}市)(.+)/, '$1 $2').split(' '); // 四日市など”市”の前に”市”を使う地名があるためここで整形 const cityNameArray = cityNameString.match(/^(四日市|廿日市|野々市)/) !== null ? cityNameString.replace(/\s+/g, "").split('') : cityNameString.split(' ') // ['川崎市', '中原区'] // 政令指定都市の中で最長は'名古屋'の3文字、最短が'堺'の1文字のため、1〜3文字で判定するようにしてます Conclusion 町字で区とそれ以下を分けるときも同じことできそうw 追記・編集 2022年1月21日 @jnchito さんからのアドバイスにより、文字列作成の箇所を更新いたしました!! ご助言ありがとうございます!! https://qiita.com/ChaaaBooo/items/781fa952b58198eb2a0b#comment-2c831ede953799252614 References 都道府県と市町村区を分割し、配列に格納する。 https://qiita.com/kajitack/items/d457fb4a811ddc53952a
- 投稿日:2022-01-19T23:22:32+09:00
React 基礎知識から Props, useState, useEffect について
学習の経緯 一番の理由はフロント側に興味津々で早く触りたいと思ってたためです。 また、周りで触っているエンジニアさんも多かったこともあり、Reactを習得することを決意。 前提 CodeSandboxを使用 JSX記法のルール JSXとは JavaScript を拡張し、UI 要素を記述するのに HTML のようなタグ構文が使えるようにしたJavascriptの拡張構文のこと コード説明(下記参照) import React from "react"; React でコードを書くためのオマジナイ import ReactDom from "react-dom"; HTML にコンポーネントを反映するための記述 const App Hello! と Tom が出力される関数の定義 複数要素の場合は、returnを()で囲う必要がある 且つ、 return する HTML の内容は1つのタグ(<> </>)で囲う必要がある ReactDom.render(<App />, document.getElementById("root")); 画面に反映させるための記述 構文 : ReactDom.render(関数等(element), 反映させる場所(container)); index.js import React from "react"; import ReactDom from "react-dom"; const App = () => { return ( <> <h1>Hello!</h1> <p>Tom</p> </> ); }; ReactDom.render(<App />, document.getElementById("root")); コンポーネント UIの一部分となるビュー(View)を切り出したもの。 また、単にUIを切り出すだけでなく、新たにコードを含むことも可能で、プログラムとして再利用できる。 import React from "react"; Reactでコードを書くためのオマジナイ export default 記述することで他のファイルで使用することができる xxx.jsx React のコンポーネント専用に用意されている拡張子 xxx.jsでもエラーは起きないが、明示的に示すことができる 【コンポーネントの作成その①】 src/App.jsx import React from "react"; const App = () => { return <h1>Hello!</h1> }; export default App; App.jsxコンポーネントを使用するための記述 import は相対パスで記述する("./App") src/index.js import React from "react"; import ReactDom from "react-dom"; import App from "./App"; // 追加 ReactDom.render(<App />, document.getElementById("root")); 【コンポーネントの作成その②】 src/App.jsx import React from "react"; export const App = () => { return <h1>Hello!</h1> }; export const App = () => { 関数コンポーネント前にexportを記述する src/index.js import React from "react"; import ReactDom from "react-dom"; import { App } from "./App"; ReactDom.render(<App />, document.getElementById("root")); import { App } from "./App"; インポートは、分割代入をして記述する 命名規則 ファイル名・・・パスカルケースで記述する パスカルケース・・・先頭が大文字で始まり、単語の区切りを大文字とする記法のこと ○ App.jsx ✕ app.jsx ○ SomeComponents.jsx ✕ someComponets.jsx HTMLのタグ内で扱うイベント名・・・キャメルケースで記述する キャメルケース・・・先頭が小文字で始まり、単語の区切りを大文字とする記法のこと ○ onClickButton ✕ OnClickButton ✕ on-click-button CSS(スタイル)の当て方 HTMLの要素にstyles.cssを適用 jsxファイルの場合、classではなく、classNameとする <div className="クラス名"> HTMLの要素内に記述 値を記述する際の1つ目の{ }は、JavaScriptを記述するためのもので、2つ目の{ }は、オブジェクトを記述するためのもの プロパティーの値は必ずダブルコーテーション""もしくは、シングルコーテーション''で囲うこと <h1 style={{ color: "red" }}>Hello!</h1> 変数に定義する スタイル名はキャメルケースで記述する ○ fontSize ✕ font-size const App = () => { const contentStyle = { color: "blue", fontSize: "18px" }; return <h1 style={contentStyle}>Hello!</h1> Props コンポーネントに渡す引数のようなもの componentsディレクトリ直下に、コンポーネントファイルを作成 ■ 文字色とメッセージを渡すコンポーネントファイルの場合 ・ 画面にHello!(青色)とTom(ピンク色)を表示 src/components/ColorfulMessage.jsx import React from "react"; const ColorfulMessage = (props) => { console.log(props); // {color: "blue", message: "Hello!"}, // {color: "pink", message: "Tom"} const { color, message } = props; const contentStyle = { color, message }; return <h1 style={contentStyle}>{message}</h1>; }; export default ColorfulMessage; const ColorfulMessage = (props) => { 関数の引数(props)は、任意の文字でも問題ない(明示的にするため、propsとしている) const { color, message } = props; 記述量を減らすため、分割代入している しない場合、<h1>タグ内の{message}を{props.message}と記述する必要がある src/components/App.jsx import React from "react"; import ColorfulMessage from "./components/ColorfulMessage"; const App = () => { return ( <> <ColorfulMessage color="blue" message="Hello!" /> <ColorfulMessage color="pink" message="Tom" /> </> ); }; export default App; <ColorfulMessage color="blue" message="Hello!" />; コンポーネント名のタグを作成することでコンポーネントを使用することができる color="blue" message="Hello!" 動的に文字色とメッセージを変更するため、プロパティー値("blue"と"Hello!")をColorfulMessageコンポーネントに渡している porps名(color, message)は任意の文字を付けることができる ■ 親コンポーネントのタグにメーセージを記述する場合 ・ 画面にHello!(青色)とTom(ピンク色)を表示 src/components/ColorfulMessage.jsx import React from "react"; const ColorfulMessage = (props) => { console.log(props); // {color: "blue", children: "Hello!"} // {color: "pink", children: "Tom"} const { color, children } = props; const contentStyle = { color }; return <h1 style={contentStyle}>{children}</h1>; }; export default ColorfulMessage; {children} 親コンポーネントのタグで囲んでいる要素を表示することができる <ColorfulMessage color="blue">Hello!←ココ</ColorfulMessage> src/components/App.jsx import React from "react"; import ColorfulMessage from "./components/ColorfulMessage"; const App = () => { return ( <> <ColorfulMessage color="blue">Hello!</ColorfulMessage> <ColorfulMessage color="pink">Tom</ColorfulMessage> </> ); }; export default App; useState 各コンポーネントが持つ状態のことで、画面上に表示されるデータ等を保持している状態を指す。 Stateが変更されると再レンダリングされる 条件によって動的に変わる部分を定義することで、様々な状態の画面を表示することができる 親コンポーネントが再レンダリングされた場合、子コンポーネントも再レンダリングされる ■ 値を動的に更新するクリックイベントの場合 ・ ボタンをクリックする毎にnumの値が +1 され、更新後の値が画面に表示される src/components/App.jsx import React, { useState } from "react"; const App = () => { const onClickCountUp = () => { setNum(num + 1); }; const [num, setNum] = useState(0); return ( <> <button onClick={onClickCountUp}>カウントアップボタン</button> <p>{num}</p> </> ); }; export default App; import React, { useState } from "react"; useState を使用するため、分割代入で取り出している const [num, setNum] = useState(0); 構文 : const [<stateとして使用する変数名>, <値を更新するための関数名>] = useState(<初期値>); setNum(num + 1); 設定したい値を記述する ■ 要素を表示/非表示させるクリックイベントの場合 ・ ボタンをクリックすると、要素の値がfalseになることで非表示になり、再度クリックすると、要素の値がtrueになり Hello! が表示される(表示/非表示の繰り返し) import React, { useState } from "react"; const App = () => { const [showFlag, setShowFlag] = useState(true); const onClickGreeting = () => { setShowFlag(!showFlag); } return ( <> <button onClick={onClickGreeting}>on/off</button> <p>{showFlag && "Hello!"}</p> </> ); }; export default App; setShowFlag(!showFlag); 変数の先頭に!を付けることで、要素の値を切り替えている <p>{showFlag && "Hello!"}</p> showFlag が true の場合、 Hello! が表示されるように論理演算子を使用している useEffect レンダリングと副作用を切り離すことができる。 そのため、関数の実行タイミングをレンダリングされるまで遅らせることができる。 ■ カウントアップボタンをクリックし、3 の倍数時のみ、 Hello! を表示させ、且つ、on/offボタンのクリックイベントも反映させている場合 import React, { useEffect, useState } from "react"; const App = () => { const [num, setNum] = useState(0); const [showFlag, setShowFlag] = useState(false); const onClickCountUp = () => { setNum(num + 1); }; const onClickGreeting = () => { setShowFlag(!showFlag); }; useEffect(() => { if (num > 0) { if (num % 3 === 0) { showFlag || setShowFlag(true); } else { showFlag && setShowFlag(false); } } }, [num]); return ( <> <button onClick={onClickCountUp}>カウントアップボタン</button> <p>{num}</p> <button onClick={onClickGreeting}>on/off</button> <p>{showFlag && "Hello!"}</p> </> ); }; export default App; import React, { useEffect, useState } from "react"; useEffect を使用するため、分割代入で取り出している if (num > 0) { num の値が 0 以上の場合に表示させるための条件分岐 showFlag || setShowFlag(true); showFlag が false の場合のみ、setShowFlag(true) を実行 setShowFlag(true) のみの記述の場合、state が必ず呼び出されることで再レンダリングされてしまい、「無限ループが起きてしまうから気をつけて」というエラーが表示される showFlag && setShowFlag(false); showFlag が true の場合、setShowFlag(false) を実行 }, [num]); useEffect の第引数の配列に num を指定することで num の値が更新された場合のみ、実行させるように制御することができる 終わりに 触ってみた感想として、とても魅力的な言語だと感じました(語彙力とは) これからも継続的に学習していきます。 参考教材
- 投稿日:2022-01-19T23:14:24+09:00
Unity WebGLでおえかきした画像の保存
はじめに おえかきアプリを作っていて保存機能を実装することになったが、WebGLなのでFile.WriteAllBytes()もApplication.persistentDataPath使えない…となったので、javascriptプラグインを作ったお話。 ※おえかきアプリの実装はメインではないので割愛します。 開発環境 Unity 2020.3.25f1 Visual Studio Code プラグインの作成(.jslib) Unityには、ブラウザのスクリプトと通信を行う方法が大きく2つあります。 1. Application.ExternalCall() か SendMessage()を使う。 2. プラグイン(.jslib)を作り、コードを記述。 今回は後者の方法で実装を進めます。 plugin.jslib mergeInto(LibraryManager.library, { ExportTextureJS:function(base64){ // 手順1 const image = Pointer_stringify(base64); // 手順2 var bin = atob(image.replace(/^.*,/, '')); var buffer = new Uint8Array(bin.length); for (var i = 0; i < bin.length; i++) { buffer[i] = bin.charCodeAt(i); } try{ var blob = new Blob([buffer.buffer], { type: 'image/png' }); }catch (e){ return; } // 手順3 var url = (window.URL || window.webkitURL); var dataUrl = url.createObjectURL(blob); var a = document.createElement('a'); a.download = "picture.png"; a.href = dataUrl; a.click(); }, }); 手順1 javascript文字列への変換 const image = Pointer_stringify(base64); まず引数から入ってきたbase64文字列を、ヘルパー関数Pointer_stringify()を使用してJavaScript文字列に変換します。 これは文字列がemscripten ヒープ内のポインターとして渡されるためです。 手順2 Blob形式への変換 var bin = atob(image.replace(/^.*,/, '')); var buffer = new Uint8Array(bin.length); for (var i = 0; i < bin.length; i++) { buffer[i] = bin.charCodeAt(i); } try{ var blob = new Blob([buffer.buffer], { type: 'image/png' }); }catch (e){ return; } 変換した文字列をBlob形式のFileに変換します。 手順3 アンカー要素を使用して保存 var url = (window.URL || window.webkitURL); var dataUrl = url.createObjectURL(blob); var a = document.createElement('a'); a.download = "picture.png"; a.href = dataUrl; a.click(); HTMLのアンカー要素<a>を作ってdataURLを渡し、クリックイベントを強制的に呼び出します。 プラグイン呼び出し側の実装(C#) using System; using UnityEngine; using UnityEngine.UI; using System.Runtime.InteropServices; public class ExportTexture : MonoBehaviour { #if UNITY_WEBGL && !UNITY_EDITOR [DllImport("__Internal")] private static extern void ExportTextureJS(string base64); #endif [SerializeField] private RawImage _image; public void OnPressExport() { #if UNITY_WEBGL && !UNITY_EDITOR var texture = (Texture2D)_image.texture; var png = texture.EncodeToPNG(); var base64 = Convert.ToBase64String(png); ExportTextureJS("data:image/png;base64," + base64); #endif } } ブラウザでのみ動くため#if UNITY_WEBGL && !UNITY_EDITOR #endifで括っています。 プラグインの関数のインポート [DllImport("__Internal")] private static extern void ExportTextureJS(string base64); jslibに記述した関数を呼び出せるようにしています。 この時、関数名と引数を一致させる必要があります。 テクスチャのbase64文字列への変換、関数の呼び出し var texture = (Texture2D)_image.texture; var png = texture.EncodeToPNG(); var base64 = Convert.ToBase64String(png); ExportTextureJS("data:image/png;base64," + base64); 今回はRawImageを使用しているので、RawImageのtextureをTexture2Dにキャストして、EncodeToPNG()を使用してbyte[]に変換。さらにそれをSystem.Convert.ToBase64String()に渡してbase64形式に変換しています。 ExportTextureJS()に渡す際に"data:image/png;base64,"と連結しているのは、今回HTMLのアンカー要素に埋め込む手順を踏んでいるためです。(ただここでやる理由はない) 動作物 おわりに 物凄く雑に紹介しましたが、面倒くさいというのが伝わったなら幸いです。 追伸:PlayCanvasならこんなに面倒くさくない リンク・参考記事など GitLab Unity(WebGL)のC#とブラウザのJavaScriptの連携 JS: base64文字列をBlob形式のFileに変換する
- 投稿日:2022-01-19T23:13:31+09:00
Vue3対応! Nuxt3のドキュメントを適当に読んで基本的な使い方を学ぶ
はじめに Nuxt3に関する日本語の情報が少ないため、公式ドキュメントを読みつつ基本的な使い方を書いていく。 良かったらLGTM押してください! 目次 Getting started ルーティングとか importとか その他新機能 感想 参考文献 Getting started まずNode.js,VSCodeを入れて、VSCode拡張のVolar(Veturは不要)を入れる。 次に以下のコマンドでプロジェクト構築から開発サーバー起動まで npx nuxi init nuxt3-app code nuxt3-app yarn yarn dev -o http://localhost:3000 にアクセスできるか確認 Nuxt2と違ってディレクトリも生成されないので環境構築はちょっとめんどくさいかも。 この辺は方針が変わったのか今後実装されるのかは不明。 開発サーバーが立ち上がったところで公式のGetting Startedが終わってしまったけど、ここからどうすればいいのかな? とりあえずDocsを見てみると左側にディレクトリ構成が書かれている。 ここにそれぞれのディレクトリの役割が書いてあるようなので読んでいく。 ルーティングとか app.vue,layouts,pagesの項に説明がある。この辺りはNuxt2とあまり変わらないかな。 app.vueを以下のように変えればpagesディレクトリ以下のコンポーネントが表示されるらしい。 Nuxt2と同様にディレクトリ構造がそのままルーティングになる。 app.vue <template> <div> <NuxtPage/> </div> </template> http://localhost:3000/ にアクセスすると以下のコンポーネントが表示される。 pages/index.vue <template> <div>Hoge</div> </template> http://localhost:3000/hoge/huga にアクセスすると以下のコンポーネントが表示される。 pages/hoge/huga/index.vue <template> <div>Hoge Huga</div> </template> 動的ルーティングも可能だがここでは割愛。 共通のレイアウトはlayouts/default.vueを作成し以下のようにする。 layouts/default.vue <template> <div> // ヘッダーとか <slot /> // フッターとか </div> </template> slotに各ページのルートコンポーネントが挿入されるようになっている。シンプル。 もちろんdefault以外のカスタムレイアウトの設定も可能だがここでは割愛。 importとか Nuxt3の新機能でコンポーネントおよびvueでよく使う関数等が暗黙にインポートされる機能がある。 例えば、componentsディレクトリ以下のコンポーネントがimport不要で使える。 やり方は簡単で、例えばcomponents/hoge.vueがあるとすると <template> ... <Hoge /> ... </template> だけでOK。インポートは要らない。 components/huga/hoge/Example.vueのようにディレクトリがネストされている場合は <template> ... <HugaHogeExample /> ... </template> と書くと読み込める。 これってコーディングルールで制限しないとクソ長い名前になり得るので、正直要らんのではないかと思っちゃう。 まあそういうオプションもあるよってだけ。 あと、Nuxt2にはなかった機能としてcomposablesがある 簡単に言うとグローバルに参照できる状態を定義するものだ。VuexとかPiniaの簡易的なやつ PiniaというのはVue3時代の状態管理のスタンダードになりそうなやつ。 僕の書いたPiniaの記事も良かったらぜひ読んでください。 composablesディレクトリ以下に以下のように定義する。 composables/useHoge.ts import { useState } from '#app' export const useHoge = () => { return useState('hoge', () => 'hogehoge') } ここでuseStateが出てきたが、これはReactのアレではなく、SSRと相性の良いrefの代替らしい。 SSRには詳しくないためよく分からないが、Nuxtのドキュメントには以下のように書いてある 「<script setup>またはsetup()の外ではref()を使うなよ。メモリリークするぜ。」 これを読む限りコンポーネント内のローカル状態に関しては普通にrefを使えばいいということかな。 話を戻すが、composables以下で宣言したuseHogeは例によってインポート無しで使える <script setup lang="ts"> const hoge = useHoge() </script> <template> <div> {{ hoge }} </div> </template> これはぶっちゃけPiniaでいいかな... SSRでメリットあるとか?この辺はよくわからない ちなみにPiniaのNuxt3での設定方法はplugins/ディレクトリにこれを置くだけ plugins/pinia.ts import { defineNuxtPlugin } from '#app' import { createPinia } from 'pinia' export default defineNuxtPlugin((nuxtApp) => { const pinia = createPinia() nuxtApp.vueApp.use(pinia) }) その他新機能 データフェッチング useFetchだけ押さえとけばOKかな? API叩く時に使うやつで、そのままデータがリアクティブになる。 <script setup> const { data } = await useFetch('/api/hoge') </script> <template> <span> {{ data.hoge }} </span> </template> これは便利そう メタタグ title, base, script, style, meta, linkのようなタグはコンポーネントとしてtemplate内に書ける 以下は公式から引用 <template> <div> <Html :lang="dynamic > 50 ? 'en-GB' : 'en-US'"> <Head> <Title>{{ dynamic }} title</Title> <Meta name="description" :content="`My page's ${dynamic} description`" /> <Link rel="preload" href="/test.txt" as="script" /> </Head> </Html> ... </div> </template> 直感的でいいね 感想 一通り読んだけどよく使いそうなのはこのぐらいかな? 他の記事だとかなりベタ褒めされていたのだが、個人的にはNuxt2と比較してすごく便利になった感じはしないかも。 まあそもそもNuxt自体とても便利だし、得に難しいこともないので使い得な感じはする。 正式リリースが楽しみ 参考文献 公式
- 投稿日:2022-01-19T17:44:24+09:00
意外とハマってしまった比較演算子のタイポ=>|デバッグの記録
単純なタイポですがjavascriptで遭遇し恥ずかしながら少しハマったので 改めて原因や対策などをまとめてみます。 >=が正解 aはb以上はa >= b。 aはb以下はa <= b。 大なりイコールと読み方で覚えるのや アロー関数と逆という覚え方も良いかも。 これでも実際コード見ている時は映像として馴染んでしまってわかりづらいので やっぱり後述するIDEのLintをしっかり設定するのが最優先だと思います。 特にPHPのアロー演算子=>やjavascriptのアロー関数=>をいじってたりすると 目がなれてしまって見逃しがちなんですよね。。。と言い訳してみる。 状況 window.innerWidthで判定している部分でそれをresizeイベントで呼び出していました。 changeWidth.js const changeWidth = () => { const nowWidth = window.innerWidth; if (nowViewPortWidth => 768) { alert("判定true") } } window.addEventListener('resize', changeWidth); これでどのような動きになったかというと ぬるぬる動きながらランダムにバグ的な動きが発動するという感じでした。 具体的な動きを書くと、changeWidthはresizeイベントで設定している関数ですが スクロールしたときに発動し「判定true」が表示され、そのタイミングがよくわからない (早くスクロールすると発動したり、トップに移動したときに発動したり、はたまたしなかったり) という状況でした。 resizeはiosでスクロールでも発動する こちらの独特な挙動もあり、原因を特定するのに手惑いました。 iOS Safariでスクロールしただけでリサイズイベントが発生する原因と対処法 IDEにESLintが設定されていなかった。 ESLintが設定されていないと警告も出ないんですね。 またデベロッパーツールでも特に警告もエラーも出ていませんでした。 個人的にはこの辺りがハマりポイントだな〜という感じがしています。 PhpStorm WebStorm 上記のページを参考にESLintを設定したら無事警告を出してくれるようになりました。 所感|自戒 IDEの設定は継続的に見直した方が良さそう。 複数のバグが重なると原因がよくわからなくなる傾向があるので問題の切り分けを細かくしたほうがよい。 デベロッパーツールでも捕捉されず動くバグがあるということを忘れない。
- 投稿日:2022-01-19T16:50:46+09:00
Webの勉強はじめてみた その22 〜Slack上で動くbot作成2〜
N予備校「プログラミング入門Webアプリ」を受講しています。 今回は第三章10,11節です。 同期・非同期処理 Node.jsは非同期で処理が実行される。 for (let count = 0; count < 500; count++) { fs.appendFile(fileName, 'あ', 'utf8', () => { fs.appendFile(fileName, 'い', 'utf8', () => { fs.appendFile(fileName, 'う', 'utf8', () => { fs.appendFile(fileName, 'え', 'utf8', () => { fs.appendFile(fileName, 'お', 'utf8', () => { fs.appendFile(fileName, '\n', 'utf8', () => {}); }); }); }); }); }); } 「あいうえお」を順番に出力しようとするとネストが深くなる。 一見うまくいきそうだけれど、1回目のループで行われるfs.appendFile(fileName, 'あ', 'utf8', () => {} と2回目のループで行われるappendFileは非同期なので、うまくいかない。 これは解説を聞いて、なるほどと思った。 Promise 非同期に実行される未来の結果を表すオブジェクト const waitPromise = new Promise((resolve, reject) => { setTimeout(() => resolve(), 1000); //1秒まってresolveを実行 }); waitPromise.then(() => console.log('hoge')); console.log('fuga'); Promiseはあくまでオブジェクトで、thenのあとに実行したい処理を書く。そして、console.log('hoge')が引数のresolveに入る。 Promiseチェーン new Promise((resolve) => { const nowDate = new Date(); resolve(nowDate); }).then((v1) => { //v1は現在時刻 new Promise((resolve) => { const monthAndDate = { month: v1.getMonth(), date: v1.getDate() } resolve(monthAndDate); }).then((v2) => { //v2 は 日付の情報 new Promise((resolve) => { const text = `今日は${v2.month+1}月${v2.date}日です。`; resolve(text); }).then((v3) => { // v3 は日付を示す文章 console.log(v3); // 今日の日付に関する文章が出力される }); }); }); 正直わかりにくすぎて混乱します。 resolve(nowDate)の内容がthen以下になると。 最終的にconsole.log(v3)がresolve(nowDate)の処理になる。 async/await 非同期処理を同期的に動かせる。 async function内でしか awaitは使えない。 function appendFilePromise(fileName, str){ return new Promise((resolve) =>{ fs.appendFile(fileName, str, 'utf8', ()=> resolve()); }); } async function main(){ for(let count = 0; count < 500; count++){ await appendFilePromise(fileName, 'あ'); await appendFilePromise(fileName, 'い'); await appendFilePromise(fileName, 'う'); await appendFilePromise(fileName, 'え'); await appendFilePromise(fileName, 'お'); await appendFilePromise(fileName, '\n'); } } main(); わかりやすい。 botに登録したデータの読み書き 今回はJSONファイルに出力 JSON : JavaScriptで扱うオブジェクトや配列の書き方の形式 /** * 登録された言葉を保存する */ function saveWords(){ fs.writeFileSync(fileName, JSON.stringify(praiseWords), 'utf8'); } JSON.stringifyで配列をJSON形式に変換 try{ const data = fs.readFileSync(fileName, 'utf8'); praiseWords = JSON.parse(data); }catch(ignore){ console.log(fileName + 'から復元できませんでした'); } JSON.parseでJSONから配列に変換。 fs.unlink(fileName, err =>{ //削除した後の処理 }) 自分が作ったbotでは使わなかったけど削除処理。 まとめ Promiseチェーンの理解はちょっとあやふや。 async/awaitが使えるならそっちをできるだけ使いたい。 さて、今回でbot作成は終了。 次からhttpサーバーの節に入ります。
- 投稿日:2022-01-19T13:21:48+09:00
Vite×Vue3×Typescriptで最新の環境構築する
Vite(ヴィート)とは 従来のビルドツールであるVue CLI等に比べて高速で動作するビルドツールのこと。 フランス語で速くという意味らしい。 公式サイト↓ 環境構築 Vite×Vue3×Typescript 1.npmのVersionを確認する(6系とそれ以降でその後のコマンドが変わる) $ npm -v 8.12 2.プロジェクトの作成 # npm 6.x $ npm init vite@latest <ここにプロジェクト名> --template vue-ts # npm 7+ は追加で 2 つのダッシュが必要: npm init vite@latest <ここにプロジェクト名> -- --template vue-ts 上記は公式サイトに記述されていた vueの後に-tsを付けることでTypescriptを導入することができる。 コマンド入力後の結果は以下の通り Scaffolding project in (中略) Done. Now run: cd <自分が指定したプロジェクト名> npm install npm run dev 3.開発に必要なライブラリのインストールをする # 生成された Vue プロジェクトフォルダに移動する $ cd <自分が指定したプロジェクト> # 開発に必要なライブラリ群をインストールする # ライブラリ群の情報は package.json に記載あり $ npm install めちゃくちゃ一瞬でインストールされた!!!Vue-cliを使用した場合はかなり時間がかかった印象がある。あちらはESLintやPrettierやVue Router Vue storeがオプションで使用するのを選べるメリットはあるが.... 3.サーバーを立ち上げる $ npm run dev vite v2.5.8 dev server running at: > Local: http://localhost:3000/ > Network: use `--host` to expose ready in 419ms. 推奨IDEを揃える 以前まではvscodeの拡張機能としてVeturを使っていたが、Volarが推奨のようなので、Vscodeに行き拡張機能をインストールした。それに伴って使っていたVeturは無効にした. 4. 生成されたCodeを確認する <script setup lang="ts"> // This starter template is using Vue 3 <script setup> SFCs // Check out https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup import HelloWorld from "./components/HelloWorld.vue"; </script> <template> <img alt="Vue logo" src="./assets/logo.png" /> <HelloWorld msg="Hello Vue 3 + TypeScript + Vite" /> </template> <style> #app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; margin-top: 60px; } </style> <script setup lang="ts"> import { ref } from 'vue' defineProps<{ msg: string }>() const count = ref(0) </script> <template> <h1>{{ msg }}</h1> <p> Recommended IDE setup: <a href="https://code.visualstudio.com/" target="_blank">VSCode</a> + <a href="https://github.com/johnsoncodehk/volar" target="_blank">Volar</a> </p> <p>See <code>README.md</code> for more information.</p> <p> <a href="https://vitejs.dev/guide/features.html" target="_blank"> Vite Docs </a> | <a href="https://v3.vuejs.org/" target="_blank">Vue 3 Docs</a> </p> <button type="button" @click="count++">count is: {{ count }}</button> <p> Edit <code>components/HelloWorld.vue</code> to test hot module replacement. </p> </template> <style scoped> a { color: #42b983; } label { margin: 0 0.5em; font-weight: bold; } code { background-color: #eee; padding: 2px 4px; border-radius: 4px; color: #304455; } </style> デフォルトで気になっていたscript setupの記法で書かれていた!!
- 投稿日:2022-01-19T12:21:09+09:00
GeoJSON(ライン)からsource, targetをペアにした配列を作る
ラインデータを地図ライブラリにデータを渡す際にsource, targetをペアにしたデータに変換して渡したいときがあるので、その変換方法のメモ。 もととなるGeoJSON(ライン) const geojson = { "type": "FeatureCollection", "features": [ { "type": "Feature", "properties": {}, "geometry": { "type": "LineString", "coordinates": [ [ -1.0272216796875, 47.97889140226657 ], [ -1.9720458984374998, 47.743017409121826 ], [ -2.537841796875, 48.05605376398125 ], [ -3.1256103515625, 48.03034580796616 ], [ -4.04296875, 48.25759852914997 ], [ -4.290161132812499, 48.36354888898689 ] ] } } ] } 変換処理 const result = geojson.features .map(d => d.geometry.coordinates) .flat() .reduce((a,c, i, array)=>{ if(!array[i-1]) return a; a.push({ source: array[i - 1], target:c}) return a; },[]) console.log(result) > [ { "source": [ -1.0272216796875, 47.97889140226657 ], "target": [ -1.9720458984374998, 47.743017409121826 ] }, { "source": [ -1.9720458984374998, 47.743017409121826 ], "target": [ -2.537841796875, 48.05605376398125 ] }, { "source": [ -2.537841796875, 48.05605376398125 ], "target": [ -3.1256103515625, 48.03034580796616 ] }, { "source": [ -3.1256103515625, 48.03034580796616 ], "target": [ -4.04296875, 48.25759852914997 ] }, { "source": [ -4.04296875, 48.25759852914997 ], "target": [ -4.290161132812499, 48.36354888898689 ] } ]
- 投稿日:2022-01-19T10:11:48+09:00
検索キー(Lookup Key)を利用して、Stripeに登録した最新の価格情報を検索・取得しやすくする
商品一覧やサービスのプラン表など、Stripeに登録した価格データを一覧表示させたいケースは少なくありません。 そしてStripeでは、表示させたい価格データを絞り込み・検索する方法がいくつか用意されています。 今回はその方法のうち、「検索キー(Lookup Key)」を利用する方法について紹介します。 検索キーとは? 検索キーは、StripeのPrice APIの機能の1つです。 Stripeに登録した価格データをより検索しやすく、また価格の変更を反映しやすくするために作られました。 以下のコードサンプルのように、Price APIのListで特定の検索キーを持つ価格データだけを取得することができます。 const result = await stripe.prices.list({ lookup_keys: ['membership_free', 'membership_bronze', 'membership_silver'] }) 検索キーを価格に登録する 検索キーは、APIから登録することができます。 登録済みの価格に設定する場合は、update APIを利用しましょう。 await stripe.prices.update('price_xxxx', { lookup_key: 'monthly_membership_bronze', }) 新しく登録する価格に設定する場合は、create APIを利用します。 await stripe.prices.create({ product: 'prod_xxxx', nickname: 'Monthly Membership (Silver)', currency: 'jpy', unit_amount: 2000, lookup_key: 'monthly_membership_silver', recurring: { interval: 'month' } }) 一度登録した検索キーは、Stripe Dashboardの価格詳細ページからも確認できます。 「検索キー」はアカウント全体でユニーク 同じ「検索キー」を異なる価格に設定することはできません。 設定しようとすると、以下のようなエラーが発生しますのでご注意ください。 A price (`price_xxxx`) already uses that lookup key. 検索キーを使って検索する 検索キーの設定が終われば、あとはList APIで検索するだけです。 const result = await stripe.prices.list({ lookup_keys: ['membership_free', 'membership_bronze', 'membership_silver'] }) 配列で設定した検索キーと一致する価格データを、一覧で取得することができます。 また、以下のようにほかのList API引数と併用することも可能です。 const result = await stripe.prices.list({ recurring: { interval: 'year' }, active: true, lookup_keys: ['membership_free', 'membership_bronze', 'membership_silver'] }) Product IDでの検索との違い 価格データの絞り込み取得には、商品(Product)のIDを利用することもできます。 こちらの場合は、指定した商品に関連付けられている価格だけ取得できます。 const result = await stripe.prices.list({ product: 'prod_xxx' }) 「商品に関連する価格データを一覧取得したい場合」は商品IDで検索することをお勧めします。 一方で、「異なる商品に紐づいている価格データを、個別に指定して一覧取得したい場合」は検索キーを利用しましょう。 対応するデータがない場合の振る舞い APIで指定した検索キーを持つ価格が存在しない場合、APIは空の配列を返してきます。 { "object": "list", "data": [], "has_more": false, "url": "/v1/prices" } transfer_lookup_keyを利用して、価格改訂に対応する 検索キーを利用するメリットはもう1つあります。それは「価格改訂時、価格データ取得側の実装を変える必要がなくなること」です。 検索キーを利用した検索の場合、設定した検索キーを別の価格に割り当て直すことができます。 そのために利用する引数が、transfer_lookup_keyです。 通常、すでに利用している検索キーをほかの価格に設定しようとすると、エラーが発生します。 しかしtransfer_lookup_keyを併用することで、検索キーを別の価格に移動させることができます。 /** * 新しい価格を作成し、検索キーの割り当て先も変更する **/ await stripe.prices.create({ product: 'prod_xxxx', nickname: 'Monthly Membership (Silver)', currency: 'jpy', unit_amount: 3000, + transfer_lookup_key: true, lookup_key: 'monthly_membership_silver', recurring: { interval: 'month' } }) /** * 作成済みの価格に、検索キーの割り当て先を変更する **/ await stripe.prices.update('price_xxxx', { + transfer_lookup_key: true, lookup_key: 'monthly_membership_bronze', }) この操作を行うことで、検索キーを利用して価格データを取得している処理側のコードを変更することなく、新しい価格データをAPIやフロントエンドに表示させることができます。 まとめ 検索キーを利用することで、取得したい任意の価格データをまとめて取得できるようになります。 また、検索キーを割り当てる価格データを変更することで、新しい価格体系に変更する際に実装を変更する箇所を減らすこともできます。 REST APIだけでなく、Next.jsやGatsbyなどの静的サイトジェネレーターでも利用できるかと思いますので、ぜひお試しください。 参考記事 [PR] Stripe開発者向け情報をQiitaにて配信中! 2021年12月よりQiitaにて、Stripe開発者のためのブログ記事更新を開始しました。 [Stripe Updates]:開発者向けStripeアップデート紹介・解説 ユースケース別のStripe製品や実装サンプルの紹介 Stripeと外部サービス・OSSとの連携方法やTipsの紹介 初心者向けのチュートリアル(予定) など、Stripeを利用してオンラインビジネスを始める方法について随時更新してまいります。 -> Stripe Organizationsをフォローして最新情報をQiitaで受け取る
- 投稿日:2022-01-19T07:04:44+09:00
【JS】undefinedについてまとめみた!
undefinedとは undefiendは、「未定義」である事を示す値です。 undefinedになるケース 以下がundefinedになるケースです。 - 初期化されていない変数の値 - オブジェクトに指定されていないキーの呼び出し - 関数のreturnを書かなかった場合の戻り値 - 引数のある関数を呼び出すときに引数を与えなかった場合の値 以下がコードになります。 Javascriptを実行出来る環境で試してみてください!! // 初期化されていない変数の値 let testUndefined; console.log(testUndefined); // undefined // オブジェクトに指定されていないキーの呼び出し const test = {}; console.log(test.name); // undefined // 関数の`return`を書かなかった場合の戻り値 function testFunc() {} console.log(testFunc()); // undefined // 引数のある関数を呼び出すときに引数を与えなかった場合の値 function emptyFunc(param) { console.log(param); // undefined } // 引数になにも与えない emptyFunc(); undefinedとnullは似ているが同じではない 同じであるケース 真偽値で判断するときは、両方falseである プロパティを呼び出そうとするとエラーになる 同じでないケース 厳密等価演算子で比較するとfalseになる // falseになる console.log(undefined === null); // false // 厳密でない場合は、trueになる console.log(undefined == null); // true 最後に プログラムを書く際は、undefinedやnullはバグの原因になるので、無闇に増やさないようにしましょう!! 先日購入した以下の本を参考にさせて戴きました! ステップアップ javascript
- 投稿日:2022-01-19T01:40:51+09:00
画像ギャラリーに使う画像を拡大させる方法
lightboxを使うと簡単にポップアップ表示できます。 まず、lightboxのサイトでファイルをダウンロードします。 参考にした記事 ファイルを解凍してファイルのdistファイル内のcssとjsの中身を index.html用のcssファイルとjsファイルに移します。 index.html <head>内に読み込む <link rel="stylesheet" href="css/lightbox.min.css"> index.html </body>前に読み込む <script src="js/lightbox.min.js"></script> 最後にポップアップ画像のコードを書きます。 index.html <a href="https://haniwaman.com/cms/wp-content/uploads/2018/02/cat.jpg" data-lightbox="demo">画像文字を書き込む</a> 参考にした記事
