- 投稿日:2020-09-26T23:11:29+09:00
Socket.IO を触ってみた
Socket.IO とは
WebSocket 通信を実装するための JavaScript ライブラリです
WebSocket とはなんぞや、という記事は こちら公式のチュートリアル に沿って「チャットアプリ」を作っていきます
細かいことはチュートリアルを見ればわかるので、ポイントだけコネクションの確立
サーバ側
index.jsvar app = require('express')(); var http = require('http').createServer(app); // httpサーバーオブジェクトを渡して Socket.IO のインスタンスを初期化する var io = require('socket.io')(http); // 中略 // コネクションが確立すると呼ばれる io.on('connection', (socket) => { console.log('a user connected'); });クライアント側
jQuery はサンプルコードで使用するので読み込んでます
index.html<!-- いろいろ省略 --> <html> <body> <!-- クライアント用の js を読み込む(これはサーバからGETしてるだけ) --> <script src="/socket.io/socket.io.js"></script> <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> // インスタンスを初期化、これで接続される var socket = io(); </script> </body> </html>クライアント => サーバ
クライアント側
emit('イベント名', {データ})
でサーバにイベントを通知しますindex.html<html> <body> <script src="/socket.io/socket.io.js"></script> <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> <script> var socket = io(); // チャットメッセージの送信 $('form').submit(function(e) { e.preventDefault(); var msg = $('#m').val() socket.emit('chat message', msg); // サーバに伝達 $('#m').val(''); return false; }); </script> </body> </html>サーバ側
socket.on('イベント名', {コールバック関数})
でイベントを受け取りますindex.js// 中略 io.on('connection', (socket) => { console.log('a user connected'); socket.on('chat message', (msg) => { // なんらかの処理 }); });サーバ => クライアント
サーバ側
さきほどのクライアント側と同じく
emit('イベント名', {データ})
で送りますindex.js// 中略 io.on('connection', (socket) => { console.log('a user connected'); socket.on('chat message', (msg) => { // io.emit => 送信者含む全員に送る場合 io.emit('chat message', msg); // socket.broadcast.emit => 送信者を除く全員に送る場合 socket.broadcast.emit('chat message', msg); }); });クライアント側
さきほどサーバ側と同じく
socket.on('イベント名', {コールバック関数})
でイベントを受け取りますindex.html<html> <body> <script src="/socket.io/socket.io.js"></script> <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> <script> var socket = io(); // 中略 socket.on('chat message', function(msg){ // なんらかの処理 }); </script> </body> </html>手っ取り早く動きを確認してみたい場合は、チャットアプリのサンプルが公式で提供されているので clone してすぐに動かすことができます
https://github.com/socketio/chat-exampleHomework
チュートリアルで Homework なるより学習を深めるためのお題が公開されているのでやってみました
コードは こちら に置いてます意訳も入っていますが、課題はこの6つです
- ユーザーが接続/切断した際にメッセージを送信する
- ニックネーム機能を追加する
- 送信者自身にはサーバを介してメッセージを送信せず、直接メッセージを追加する
- タイピング中の人を表示する
- オンラインの人を表示する
- プライベートメッセージ機能を追加する
個人的にポイントに感じたことだけ書きます(笑)
オンラインの人を表示する
オンラインのユーザを管理する配列を用意して、入退室時に配列に格納/削除する、というところまでは普通です
socket.id
で接続中のユーザのIDが取得できるのでこれを利用しますindex.js// 中略 var onlineUsers = [] io.on('connection', (socket) => { // 退室 socket.on('disconnect', () => { var index = onlineUsers.findIndex(item => item.id === socket.id); onlineUsers.splice(index, 1); io.emit('user update', onlineUsers); }); // 入室 socket.on('enter room', (nickname) => { onlineUsers.push({ id: socket.id, nickname: nickname }); io.emit('user update', onlineUsers); });詰まったところ
確認するときに、ブラウザのタブを複数開いて「リロード」していたのですが、なにやらうまくいかないぞ...
結論としては、リロードすると一度disconnect
イベントが発生してしまうので、それが原因のようでした
確認の際は、面倒ですが一度タブを閉じ、再度ページにアクセスするとよいと思いますプライベートメッセージ機能を追加する
クライアント側の実装などが面倒そうだったのでちゃんと作ってはないです(笑)
io.to({ソケットID}).emit('イベント名', {データ})
で特定の人にだけ送れます最後に
チュートリアルを通して Socket.IO の基本的な使い方を勉強してみました
room 機能などもあるみたいなのでまたいつか使ってみたいです
- 投稿日:2020-09-26T22:57:27+09:00
機械学習メモ - ブラウザ上でMNISTデータファイルをDrag&Dropで受け取って手書き数字をとりあえず表示する
やったこと
とりあえず、ニューラルネットワークで手書き数字認識を一からやってみようと思い、MNISTのデータの読み込みまでをやってみた。
できたもの
See the Pen MNIST handwritten digits viewer by kob58im (@kob58im) on CodePen.
■使いかた
事前にMNISTのサイトからgzファイルをダウンロードして解凍した結果の4ファイルをローカルに保存してください。
これらのファイルをDrag&Dropすると、ブラウザ上でデータを読み込んで表示します。(ファイル名のチェックのみ実施していますが、ファイルサイズやデータ内容の整合性チェックはしていません。)参考サイト
MNISTのデータ
- 配布元: http://yann.lecun.com/exdb/mnist/
- 解説: https://www.atmarkit.co.jp/ait/articles/2001/22/news012.html
File の Drag & Drop 読み込み
- https://www.html5rocks.com/ja/tutorials/dnd/basics/
- https://www.html5rocks.com/en/tutorials/file/dndfiles/#toc-selecting-files-dnd
- https://web.dev/read-files/#read-content
- https://developer.mozilla.org/ja/docs/Web/API/FileReader/readAsArrayBuffer
- https://lab.syncer.jp/Web/JavaScript/Reference/Global_Object/ArrayBuffer/
canvas画像のPixel単位操作
ニューラルネットワーク
すごい良さげな解説動画をみつけたので、これ見ながらニューラルネットワーク学習の実装までやってみたい。
- 投稿日:2020-09-26T22:37:42+09:00
typescript 型について
typescriptの型まとめ
// primitive const apple: number = 5 const speed: string = 'fast' const hasName: boolean = true const nothingMuch: null = null const nothing: undefined = undefined const now: Date = new Date() // array const colors: string[] = ['red', 'green', 'blue'] const myNumbers: number[] = [1, 2, 3] const truths: boolean[] = [true, false, true] // class class Car {} const car: Car = new Car() // object literal const point: { x: number, y: number } = { x: 10, y: 20 } const destinations = { Japan: { longitude: 153, latitude: 24 }, America: { longitude: 38, latitude: 97 } } const { Japan }: { Japan: { longitude: number; latitude: number } } = destinations //any const every: any[] = [10, 'Jon', true, null, undefined] // tuple type Drink = [string, boolean, number] const pepsi: Drink = ['brown', true, 40] const sprite: Drink = ['clear', true, 40] const tea: Drink = ['brown', false, 0]
- 投稿日:2020-09-26T22:22:45+09:00
ReactとPython flaskを使ってWebアプリを作りたい
はじめに
仕事でReactによるフロント開発に携わり始めたので、アウトプットの練習も兼ねて簡易webアプリを作成しました。
どんなアプリにするかアイデアはまったく思い浮かばなかったので、手元にあったmecabを使った分かち書きスクリプトを使って、フロントで受け取った入力テキストをサーバー側で分かち書きをし、その結果をフロントで表示するという非常にシンプルなアプリです。
(主目的はreactとflaskをつなぐ部分を勉強することだったため、アプリの見た目や機能は全然作り込んでいませんのであしからず。)表題の通り、フロント側はReact、サーバー側はpython flaskで実装しています。
今回実装したスクリプトはこちらで公開しています。
完成品
実装環境
OS: Ubuntu 18.04.2 LTS Python: 3.6 flask==1.0.2 npm: 6.14.7reactの環境構築については今回触れませんが、公式チュートリアルが日本語でも充実していて非常に参考になりました。
- https://ja.reactjs.org/こちらもすごくおすすめです。
- https://mae.chab.in/archives/2529実装する
構成図
今回実装したアプリの構成は以下のようになっています(主要部分のみ)。
サーバー側
サーバー側は以下のような構成になっています。
backend/ ├─ requirements.txt ├─ server.py └─ utils.py
server.py
はflaskサーバーを立ち上げるコードです。アドレスやポートは一番下、
app.run(host='127.0.0.1', port=5000)
で指定します。server.pyfrom flask import Flask from flask import request, make_response, jsonify from flask_cors import CORS from utils import wakati app = Flask(__name__, static_folder="./build/static", template_folder="./build") CORS(app) #Cross Origin Resource Sharing @app.route("/", methods=['GET']) def index(): return "text parser:)" @app.route("/wakati", methods=['GET','POST']) def parse(): #print(request.get_json()) # -> {'post_text': 'テストテストテスト'} data = request.get_json() text = data['post_text'] res = wakati(text) response = {'result': res} #print(response) return make_response(jsonify(response)) if __name__ == "__main__": app.debug = True app.run(host='127.0.0.1', port=5000)
@app.route("/wakati", methods=['GET','POST')
部分でフロントからテキストを受け取り、分かち書き処理した後、フロントへ返す処理をしています。
data = request.get_json()
によってフロントからポストされてきた内容をjson形式で取得します。
ここから必要なデータを取り出して、何らかの処理(関数にかけたり、DBに入れたりし)をし、response = {'result': res}
のようにjson形式にしてフロントに返します。(補足:CORSとは)
別リソースへアクセス(=クロスサイトHTTPリクエスト)できるようにするために必要なルールです。これがないとフロント側から立ち上げたflaskサーバへアクセスできません。
- 参考:https://aloerina01.github.io/blog/2016-10-13-1フロント側
今回は
create-react-app
の雛形を用いました。
(create-react-appの設定および使い方はこちらが非常にわかりやすいです!)フロント側は以下のような構成になっています(主要ファイルのみ掲載)。
frontend/app/ ├─ node_modules/ ├─ public/ ├─ src/ | ├─ App.css | ├─ App.js | ├─ index.js | └─ ... └─ ...自動生成された雛形の中の
App.js
を以下のように書き換えました。App.jsimport React from 'react'; import './App.css'; import Axios from 'axios'; //function App() { export class App extends React.Component { constructor(props) { super(props); this.state = {value: ''}; this.handleChange = this.handleChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); } render() { return ( <div className="App"> <header className="App-header"> <h1>text parser</h1> <form onSubmit={this.handleSubmit}> <label> <textarea name="text" cols="80" rows="4" value={this.state.value} onChange={this.handleChange} /> </label> <br/> <input type="submit" value="Parse" /> </form> </header> </div> ); } wakati = text => { //console.log("input text >>"+text) Axios.post('http://127.0.0.1:5000/wakati', { post_text: text }).then(function(res) { alert(res.data.result); }) }; handleSubmit = event => { this.wakati(this.state.value) event.preventDefault(); }; handleChange = event => { this.setState({ value: event.target.value }); }; } export default App;この中の以下の部分でサーバー側とのやり取りを行なっています。
wakati = text => { //console.log("input text >>"+text) Axios.post('http://127.0.0.1:5000/wakati', { post_text: text }).then(function(res) { alert(res.data.result); }) };
server.py
で立てたhttp://127.0.0.1:5000/wakati
にthis.state.value
の値をポストします。
サーバー側で処理された後、返ってきたresult
の値がalert(res.data.result);
によってブラウザに表示されます。動かす
フロントエンド/バックエンド用にそれぞれターミナルを立ち上げて以下のコマンドを実行します。
サーバー側
$ cd backend $ python server.pyフロント側
$ cd frontend/app $ yarn startブラウザから
localhost:3000
にアクセスすることでアプリを利用できます(yarn startで自動で立ち上がります)。おわりに
今回はReactとPython flaskを用いて簡易的なWebアプリを実装しました。
簡易的とはいえ、短時間で楽にWebアプリを実装できるので素晴らしいですね。フロント修行中の身なので、見た目や機能についてはまだまだなのでご意見、アドバイス等いただければ幸いです。
最後まで読んでいただきありがとうございました!
- 投稿日:2020-09-26T21:58:36+09:00
いきなりWebアプリケーションエンジニアになった未経験者に最低限知っておいてほしいこと
あらすじ
仕事でSpring Boot2を使ったWebアプリケーション開発をすることになりました。
自分が参画した段階で、SpringはおろかWebアプリケーションの仕組み自体を理解しているメンバーはゼロでした。
自分もSpringはおろかJava自体6年ぶりくらいだったのですが、Webの仕組みを知っているのが自分しかいなかったので教えるしかなかったのが、今回執筆しようと思ったきっかけです。Webアプリケーションとは
EdgeとかGoogle ChromeとかFirefoxとかSafariとかのウェブブラウザでアクセスしてログインしてポチポチして登録とか更新とか閲覧とか削除とかするソフトウェアです。
ヤフーにしろグーグルにしろアマゾンにしろ楽天市場にしろ、ウェブブラウザを使って利用するソフトウェアはWebアプリケーションです。
特徴としては、クライアント/サーバーシステム(いわゆるクラサバ)と違い、クライアント端末(パソコン)に特定のアプリケーションをインストールする必要がありません。Webアプリケーション概略図
めっちゃ適当に書くとこんな感じ。
Webアプリケーションを構成する技術には様々なものがあります。
難しい話は抜きにして、最低限理解しておきたいところを説明します。クライアントサイドとサーバサイド
Webアプリケーション開発でしっかりと意識しておきたいこととして、クライアントサイドとサーバサイドの技術の違いです。
まずはサーバサイドから。サーバサイド
JavaやC#やVB.NETやPHPやRubyやPythonやPerlやGoなどのプログラミング言語を使った開発は、サーバサイドになります。
データベースからデータを取り出したり保存したりする処理はこちらで行います。
サーバーは、クライアントからのリクエストがあった時に処理を実行してクライアントに結果を返します。
クライアントからのリクエストがなければ何もしません。クライアントサイド
ウェブブラウザでポチポチしてる時に見えているものがクライアントサイドになります。
HTML、CSS、JavaScriptを使います。
JavaScriptは基本的にクライアントサイドで動作します。
つまり、ユーザーのPCスペックの影響を受けます。
何かしらの操作によって発生したイベントによってプログラムが動作します。
これを、イベントドリブンといいます。HTTP通信
クライアント側からサーバー側にリクエストを送る方式がHTTP通信になります。
送る方法は、HTMLで記述されたsubmitもしくはJavaScriptで行います。同期通信と非同期通信
クライアントとサーバー間のHTTP通信において、同期通信と非同期通信があります。
同期通信では、クライアントからサーバーへリクエストを送信した際、サーバーからのレスポンスがあるまで待機します。
ウェブブラウザのタブがくるくる回っていたら同期通信です。
また、同期通信では画面がリフレッシュされます。
HTMLでいうsubmitは同期通信です。非同期通信では、クライアントからサーバーへリクエストを送ってもクライアント側は別の処理を実行できます。
レスポンスがあったらそれに応じて処理を続行します。
ウェブブラウザ上で動的にデータの取得や更新を行う際に非同期通信を利用します。
非同期通信では画面の一部のみを差し替えたりできます。
JavaScriptで実装します。メソッド
HTTP通信の際、メソッドというものがあります。
HTMLでは、formタグのmethod属性で指定します。GET
GETメソッドでは、URLの後ろにクエリストリング(クエリパラメータ)というものが付きます。
URLの後ろに?マークを付け、その後ろにキーと値の組み合わせで記述されます。
クエリストリングには長さ制限があったりするので大量のデータをGETメソッドで送ることはありません。
また、パラメータがURLに乗るので秘匿すべき情報はGETメソッドでは送らないのが一般的です。POST
POSTメソッドではHTTPリクエストのボディにデータが乗ります。
ウェブブラウザに表示すべきHTMLもレスポンスのHTTPボディに入っています。ヘッダーとボディ
話が前後しますが、HTTPにはヘッダーとボディがあります。
URLはヘッダーにあります。
POSTした際のデータはボディにあります。
データの形式がなんであるかは、ヘッダーで指定します。
リクエスト時もそうですが、レスポンス時もヘッダーで指定されています。終わり
端折りすぎたかもしれない。
ここから先は、より具体的なお話です。
Appendix
JavaScriptでの非同期通信とサーバサイドの処理
JavaScriptでform要素内のデータをsubmitすることもできますが、昨今は非同期通信を当たり前のように利用します。
Twitterをウェブブラウザで開いている際に下にスクロールするとその都度データを取得して表示していますよね。あれです。
JavaScriptでの非同期通信のことをAjaxと呼びます。
昔はXML形式でデータのやり取りをしていましたが、今はJSON形式でデータのやり取りをするのが一般的です。同期通信と非同期通信で、サーバサイドの実装も変わります。
同期通信ではウェブブラウザ上に描画するためのHTMLをレスポンスとして返す必要がありますが、非同期通信では必要なデータのみを返します。
そのため、JSON形式のデータを作成してレスポンスとして返すことになります。
サーバサイドで利用するプログラミング言語が何であっても、その言語のフレームワークを使用して開発するでしょうからそのフレームワークにJSON形式でレスポンスを返す機能が大抵あります。JavaScriptでの非同期通信における注意
JavaScriptで非同期通信を行う際、非同期処理のため、実行結果を受け取る前に処理が進んでいきます。
そのため、実行結果を受け取ってから処理する必要があるものについてはコールバック関数というもので処理したりします。
jQueryで説明すると、以下のような感じです。$.get('/foo/bar', function(data) { // /foo/bar というURLに対してリクエストを送った際に、レスポンスが data に入っている console.log(data); }); // こっちの処理は上のconsole.logより先に実行される console.log('hello, world!');上記の例で、
function(data)
の関数定義は無名関数というものです。
この無名関数内の処理が、非同期通信の結果を受け取った後に処理されます。
JavaScriptの関数は第一級関数というもので、関数自体を変数に代入したりすることが出来ます。
つまり、$.get
は以下のような実装イメージだと思うと理解できます。function get(arg1, arg2) { // 以下はただのイメージです var response = http.get(arg1); arg2(response); }第二引数で受け取った変数を関数実行し、その前に受け取っているレスポンスを引数としてセットしています。
無名関数として定義しているfunction(data)
のdata
はいわゆる仮引数なので、get
メソッド内で引数として渡しているresponse
がdata
として無名関数内で利用される、というイメージです。
このイメージが理解できていないと、非同期通信後に行う処理を先に実施してしまったりといった不具合を引き起こしてしまいます。
ひとまずこのあたりで。
後日追記するかもしれません。
- 投稿日:2020-09-26T21:07:12+09:00
GASからTwilioを動かしたい、条件分岐させたい
前提
- Twilioで電話番号取得済み
概要 & コード
電話の受け付け
参考文献をご覧になってください。とてもわかりやすい記事でした。
(ここで作成したGASアプリケーションを、以下「app1」と表記)条件分岐
GASアプリケーションをもう1つ用意してください。(以下「app2」と表記)
<Gather>
を以下のように少し工夫します。action
で、「app2」の「web app URL」を記載します。
そして、「app1」からのPOSTを「app2」で受け取ります。//「app1」の記述(一部) <Gather action='https://script.google.com/macros/s/{GASID}/exec' method='post' numDigits='1'> <Say> "ボタンを押してください。" </Say> </Gather>//「app2」の記述 function doPost(e) { var digits = e["parameters"]["parameter"]["Digits"]; //ボタン「1」を押していた場合 if(digits = 1){ var response_str = "<Response>\n <Say voice='woman' language='ja-jp'> 1が押されました。</Say>\n</Response>"; var out = ContentService.createTextOutput(response_str); out.setMimeType(ContentService.MimeType.XML); return out; }else{ var response_str = "<Response>\n <Say voice='woman' language='ja-jp'> 1以外のボタンが押されました。</Say>\n</Response>"; var out = ContentService.createTextOutput(response_str); out.setMimeType(ContentService.MimeType.XML); return out; } }参考文献
・Google Apps Script と Twilio で自作留守電サービスを構築してみた話
・twilioからGoogle Apps Scriptを経由して別サービスにつなぐ
- 投稿日:2020-09-26T21:07:12+09:00
GASからTwilioを動かしたい、条件分岐させたい、着信番号を取得したい
前提
- Twilioで電話番号取得済み
概要 & コード
・ 電話の受け付け
参考文献をご覧になってください。とてもわかりやすい記事でした。
(ここで作成したGASアプリケーションを、以下「app1」と表記)・ 条件分岐
GASアプリケーションをもう1つ用意してください。(以下「app2」と表記)
<Gather>
を以下のように少し工夫します。action
で、「app2」の「web app URL」を記載します。これによって、「app1」から「app2」にパラメーターを渡しますことができます!//「app1」の記述(一部) <Gather action='https://script.google.com/macros/s/{GASID}/exec' method='post' numDigits='1'> <Say> "ボタンを押してください。" </Say> </Gather>あとは、「app2」側でパラメーターから値を取得すればいいだけ!
//「app2」の記述 function doPost(e) { //押されたボタンを取得 var digits = e["parameter"]["Digits"]; //ボタン「1」を押していた場合 if(digits = 1){ var response_str = "<Response>\n <Say voice='woman' language='ja-jp'> 1が押されました。</Say>\n</Response>"; var out = ContentService.createTextOutput(response_str); out.setMimeType(ContentService.MimeType.XML); return out; }else{ var response_str = "<Response>\n <Say voice='woman' language='ja-jp'> 1以外のボタンが押されました。</Say>\n</Response>"; var out = ContentService.createTextOutput(response_str); out.setMimeType(ContentService.MimeType.XML); return out; } }・ 着信番号などその他
着信番号はe["parameter"]["From"]
で取得できます。その他いろいろe["parameter"]
に入ってます。参考文献
・Google Apps Script と Twilio で自作留守電サービスを構築してみた話
・twilioからGoogle Apps Scriptを経由して別サービスにつなぐ
- 投稿日:2020-09-26T20:16:32+09:00
with IE(JavaScriptのIE11対応)
はじめに
政府や行政サイトの「IE縛り」に代表されるように、IE汚染国である日本で仕事としてJavaScriptを書く場合、IEと共存するしなければならない場合が多いです。
with コロナならぬwith IEです。IE生活様式
- Transpile
- Bundle
- Polyfill
TranspileとBundle は、ES5の書き方で1つのファイルに書いていれば不要です。
エンジニアが不便を我慢すれば必要ない工程ではありますが、そんな我慢はしたくありません。
コロナで言えばマスク、消毒、手洗い、うがい、密を避ける、換気のような個人でできる対策に当たると思います。Polyfillは、もともとIE11が対応していない機能に対応させるもので、コロナでいえば治療薬ともいえるものだと思いますが、薬なのでリスクもあります。
コロナと違って、みんなでIEやめる合意が取れればIE対応せずに済む世界になるんですが。
河野大臣、ハンコ、書面、FAXに続いて、IE対応不要もお願いします。JavaScriptをIE11対応する
webpackとBabelのインストール
terminalnpm install -D webpack webpack-cli babel-loader @babel/core @babel/preset-env
パッケージ 内容 webpack webpack本体 webpack-cli webpackのコマンドラインツール babel-loader webpackでBabelを使えるようにする @babel/core Babel本体 @babel/preset-env 指定したブラウザ環境で動作するように変換するプラグイン 設定ファイルの準備
package.jsonの編集
package.jsonにビルドコマンドを追加します。
package.json{ "scripts": { "dev": "webpack --mode development", "build": "webpack --mode production" } }webpack.config.jsの作成
プロジェクトルートにwebpackの設定ファイルのwebpack.config.jsを作成します。
babelでtargetsを指定しないとbabel/preset-envがbrowserslistとの連携を行なってくれるので指定しません。webpack.config.jsmodule.exports = { module: { rules: [ { test: /\.js$/, use: [ { loader: "babel-loader" } ] } ] } };.babelrc
プロジェクトルートにBabelの設定ファイルの.babelrcファイルを作成します。
※webpack.config.js内に記述することもできます。.babelrc{ "presets": ["@babel/preset-env"] }.browserslistrc
プロジェクトルートに対象ブラウザを指定する.browserslistrcファイルを作成します。
.browserslistrcie 11Transpile
TranspileするJavaScriptの準備
src/index.jswindow.addEventListener("load", () => { alert("InternetExplorer"); });Transpile後のJavaScriptを読み込むHTMLの準備
dist/index.html<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <script src="main.js"></script> </body> </html>ビルド
terminalnpm run build動作確認
IE11でdist/index.htmlを開いて、JavaScriptが実行できているか確認します。
polyfillの追加
APIがサポートされていないなど、構文の変換だけでは対応できない機能への対応を行うpolyfillを追加できるようにします。
core-jsのインストール
terminalnpm install -S core-js.babelrcの変更
.babelrc{ "presets": [ [ "@babel/preset-env", { "useBuiltIns": "usage", "corejs": { "version": 3, "proposals": false } } ] ] }Polyfillを追加するJavaScriptの準備
src/index.jsalert(Array.from("InternetExplorer"));ビルド
terminalnpm run build動作確認
IE11でdist/index.htmlを開いて、JavaScriptが実行できているか確認します。
regenerator-runtime
必要に応じてregenerator-runtimeを使用します。
(ビルド時にCan't resolve 'regenerator-runtime/runtime'が表示されたとき)regenerator-runtimeのインストール
terminalnpm i -S regeneratorregenerator-runtimeのインポート
JavaScriptimport regeneratorRuntime from "regenerator-runtime" ; //もしくは //import "regenerator-runtime / runtime.js" ;TypeSctiptをIE11対応する
webpackとTypeScriptのインストール
terminalnpm install -D webpack webpack-cli typescript ts-loadertable:パッケージ
パッケージ 内容 webpack webpack本体 webpack-cli webpackのコマンドラインツール typescript TypeScript本体 ts-loader webpackでTypeScriptを読み込む 設定ファイルの準備
package.jsonの編集
package.jsonにビルドコマンドを追加します。
package.json{ "scripts": { "dev": "webpack --mode development", "build": "webpack --mode production" },tsconfig.jsonの作成
tsconfigでは.browserslistrcが使えないので、targetでJavaScriptのバージョンを指定します。
tsconfig.json{ "compilerOptions": { "target": "ES5", "lib": ["dom", "ES5","ScriptHost"] } }
target lib省略時に暗黙的に指定されているもの ES5 DOM,ES5,ScriptHost ES6 lib: DOM,ES6,DOM.Iterable,ScriptHost ※この時点ではlibの記述は不要ですが、後でlibを追加するので先にデフォルトのlibを記述しています。
webpack.config.jsの作成
webpack.config.jsmodule.exports = { module: { rules: [ { test: /\.ts$/, loader: 'ts-loader' } ] }, resolve: { extensions: ['.ts', '.js'] } }Transpile
TranspileするJavaScriptの準備
アロー関数のコードをIEで実行できるように変換
src/index.tswindow.addEventListener("load", () => { alert("InternetExplorer"); });Transpile後のJavaScriptを読み込むHTMLの準備
dist/index.html<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <script src="main.js"></script> </body> </html>ビルド
terminalnpm run buildIE11での動作確認
IE11でdist/index.htmlを開いて、JavaScriptが実行できているか確認します。
polyfillの追加
APIがサポートされていないなど、構文の変換だけでは対応できない機能への対応を行うpolyfillを追加できるようにします。
core-jsのインストール
terminalnpm install -S core-js動作確認用のコードの準備
src/index.jsimport "core-js"; alert(Array.from("InternetExplorer"));tsconfig.jsonの変更
libに追加します。
tsconfig.json{ "compilerOptions": { "target": "ES5", "lib": ["dom", "ES5","ScriptHost","ES2015.Core"] } }今回はlibに"ES2015.Core"を追加しましたが、コードによって追加する内容は変わります。
libの値
- es5
- es6
- es2015
- es7
- es2016
- es2017
- es2018
- es2019
- es2020
- esnext
- dom
- dom.iterable
- webworker
- webworker.importscripts
- scripthost
- es2015.core
- es2015.collection
- es2015.generator
- es2015.iterable
- es2015.promise
- es2015.proxy
- es2015.reflect
- es2015.symbol
- es2015.symbol.wellknown
- es2016.array.includees2017.object
- es2017.sharedmemory
- es2017.string
- es2017.intl
- es2017.typedarrays
- es2018.asyncgenerator
- es2018.asynciterable
- es2018.intl
- es2018.promise
- es2018.regexp
- es2019.array
- es2019.object
- es2019.string
- es2019.symbol
- es2020.bigint
- es2020.promise
- es2020.string
- es2020.symbol.wellknown
- es2020.intl
- esnext.array
- esnext.symbol
- esnext.asynciterable
- esnext.intl
- esnext.bigint
- esnext.string
- esnext.promise
https://github.com/microsoft/TypeScript/tree/master/lib
ビルド
terminalnpm run build動作確認
IE11でdist/index.htmlを開いて、JavaScriptが実行できているか確認します。
regenerator-runtime
必要に応じてregenerator-runtimeを使用します。
(ビルド時にCan't resolve 'regenerator-runtime/runtime'が表示されたとき)regenerator-runtimeのインストール
terminalnpm i -S regeneratorregenerator-runtimeのインポート
JavaScriptimport regeneratorRuntime from "regenerator-runtime" ; //もしくは //import "regenerator-runtime / runtime.js" ;環境を作らずにIE11対応する
あまり手をかけずにIE11対応をします。
Babel
https://babeljs.io/
https://babeljs.io/replアクセスしたブラウザ用にTranspileできるようですが、肝心のIE11が非対応のようです・・・・。
Polyfill
TranspileもBundleも不要で、Polyfillだけ使いたい場合はダウンロードしてきてHTMLから読み込めば使えるようになります。
例:intersectionObserver(https://github.com/w3c/IntersectionObserver/tree/master/polyfill)
index.html<script src="intersection-observer.js"></script>polyfill.io
https://polyfill.io/v3/
JavaScriptのPolyfillを配信しているサービスです。
※当然ですが、外部から読み込むので、polyfill.ioで障害が発生してしまったらPolyfillは無効になります。下記URLで必要な機能を検索して、そのPolyfillを使えるURLを取得できます。
https://polyfill.io/v3/url-builder/index.html//ES2019の全polyfill取得 <script src="https://polyfill.io/v3/polyfill.min.js?features=es2019"></script> //fetch機能だけのpolyfillを取得 <script src="https://polyfill.io/v3/polyfill.min.js?features=fetch"></script>
- 投稿日:2020-09-26T19:30:21+09:00
簡単電子レシート receiptline で Web フォントを使ってみた
前回は、変換ライブラリに手を加えて、紙レシートの行間隔を調整しました。
今回は、紙ではなく電子レシートのフォントを変更してみようと思います。標準のフォント
ReceiptLineをCLIでSVG変換のコマンドラインツールを使って SVG に変換します。
$ ./rltosvg 32 cp932 receipt.txt receipt.svg
入力データ
receipt.txt{image:iVBORw0KGgoAAAANSUhEUgAAAIAAAAAwAQMAAADjOuD9AAAABlBMVEUAAAD///+l2Z/dAAAAZklEQVQoz2P4jwYYRrrABwYGOwYG5gMMDBUMDPxAgQcMDDJAgQYGhgJcAv//yMj//9/8//+HerAZRAsAzUASAJoGMhRF4AC6ANCIAhQz8AkAXQoUOIDidBQBkG8hAj8gAqPJAa8AAGjulhOsX97yAAAAAElFTkSuQmCC} 市ヶ谷駅前店 東京都千代田区九段1-Y-X 2019年 2月19日(火) 19:00 {border:line} ^領 収 証 {border:space} {width:*,2,10} ビール | 2| ¥1,300 千鳥コース | 2| ¥17,280 ------------------------------------- {width:*,20} ^合計 | ^¥18,580 現 金 | ¥20,000 お 釣 り | ¥1,420出力データ
出力データのフォント指定部分です。
receipt.svg<g font-family="'MS Gothic', 'San Francisco', 'Osaka-Mono', 'Courier New', 'Courier', monospace" fill="#000" font-size="24" dominant-baseline="text-after-edge">Windows では「MS ゴシック」、Mac では「San Francisco」、他の環境ではデフォルトの等幅フォントで表示されると思います。
異なる環境で表示を合わせるには、ビットマップ画像に変換するか、フォントを手作業でインストールしなければなりません。Web フォントに変更
フォントのインストール作業を回避する方法があります。
そう、Web フォントです。ここでは Google Fonts を使います。
https://fonts.google.com/変換ライブラリ
lib/receiptline.js
を変更します。
- <style> を追加
- font-family を変更
- font-size を調整
以下は変更部分の抜粋です。フォントは「Kosugi Maru」を指定しています。
lib/receiptline.js// // SVG // const _svg = { ... // start printing: open: function (printer) { ... this.fontFamily = "'Kosugi Maru', monospace"; return ''; }, // finish printing: close: function () { const style = '<style>@import url("https://fonts.googleapis.com/css2?family=Kosugi+Maru&display=swap");</style>'; const fontSize = 24; return `<svg width="${this.svgWidth}px" height="${this.svgHeight}px" viewBox="0 0 ${this.svgWidth} ${this.svgHeight}" preserveAspectRatio="xMinYMin meet" ` + `xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">` + `<defs>${style}<filter id="receiptlineinvert" x="0" y="0" width="100%" height="100%"><feFlood flood-color="#000"/><feComposite in="SourceGraphic" operator="xor"/></filter></defs>` + `<g font-family="${this.fontFamily}" fill="#000" font-size="${fontSize}" dominant-baseline="text-after-edge">${this.svgContent}</g></svg>\n`; }, ... };フォントをいろいろ変えて、コマンドラインツールで変換してみます。
Kosugi Maru
- style
<style>@import url("https://fonts.googleapis.com/css2?family=Kosugi+Maru&display=swap");</style>
- font-family
'Kosugi Maru', monospace
- font-size
24
Kosugi
- style
<style>@import url("https://fonts.googleapis.com/css2?family=Kosugi&display=swap");</style>
- font-family
'Kosugi', monospace
- font-size
24
Sawarabi Mincho
- style
<style>@import url("https://fonts.googleapis.com/css2?family=Sawarabi+Mincho&display=swap");</style>
- font-family
'Sawarabi Mincho', monospace
- font-size
20
Ubuntu Mono & Kosugi Maru
- style
<style>@import url("https://fonts.googleapis.com/css2?family=Ubuntu+Mono&family=Kosugi+Maru&display=swap");</style>
- font-family
'Ubuntu Mono', 'Kosugi Maru', monospace
- font-size
24
Cutive Mono & Noto Serif JP (Extra-light 200)
- style
<style>@import url("https://fonts.googleapis.com/css2?family=Cutive+Mono&family=Noto+Serif+JP:wght@200&display=swap");</style>
- font-family
'Cutive Mono', 'Noto Serif JP', monospace
- font-size
22
Amatic SC (Bold 700) & Noto Sans JP (Thin 100)
- style
<style>@import url("https://fonts.googleapis.com/css2?family=Amatic+SC:wght@700&family=Noto+Sans+JP:wght@100&display=swap");</style>
- font-family
'Amatic SC', 'Noto Sans JP', monospace
- font-size
22
まとめ
最適なフォントは、日本語は「Kosugi Maru」「kosugi」、英語は「Ubuntu Mono」でした。
monospace であっても縦横比 2 : 1 のフォントは少ないです。等幅フォント・全角・半角・倍角は、今や絶滅危惧種。
レシートプリンターは、初期のワープロの生きた化石です。
- 投稿日:2020-09-26T19:25:25+09:00
大嫌いだったJavaScriptがプログラミングの楽しさを教えてくれた
この記事に技術的な話はありません。
ただ「プログラミングって楽しいなあ」と実感させてくれたのが、一番嫌いだったJavaScriptだったという話です。もし、自分にはプログラミングの才能がないと思っている人がいたら、「それでもプログラミングと一緒に人生を歩けるかもですよ?」という同じ初心者からの感想をここに残しておきたいと思います。つまり、爆速で○○ができなかった人間でも「プログラミングを楽しむことは平等に可能」なのかもと。
ただちょっと、時間と参考書の縁が必要なだけで。はじめに
手短に自己紹介させていただきますが、いま僕はSE系の会社でDjango/Angularメインでシステムとインターフェース関係をやってます。
プログラミングをはじめて2年強のアラフォーで、いまの会社は知人のツテで入りました。その頃は「HTMLとCSSをかろうじて触れます」レベルのひどいものでした。
いまもひどい部類なのによく会社も我慢してくれているもんだ。学習の歩みと反省
プログラミングの基礎部分を作るときこそ、学習の手順および、書籍・本・動画そしてメンターや講師といった情報源の巡り合わせが本当に重要だなと思います。
僕は当時、
Sのつくスクールで始めましたが講師との相性がおそらく相当に悪く、「これはお作法です」を鵜呑みできない自分の性分もあって、結局何も成果物がないまま半年が終わりました。それでもHTMLとCSSは多少触れるようになったので、友人知人の簡単なwebページを作ったりして小銭稼ぎをしていました。その際に、JavaScriptはよくわかんないけどコピペでちょっと入れる的な使い方をしてました。ただJavaScriptは構文エラーがあってもページ自体は表示して、でもクリックイベントは反映しない、という無機質な感じにイライラして、深く学ぶ気持ちは起きませんでした。避けられる限り避けてやると決めたくらいに嫌いでした。
おいおい地獄か? AngularとTypeScript
そんな状態でなぜか入れていただけた会社は初日に、
「じゃあこれ引き継いで」
と業務管理アプリの開発を丸投げされます。担当は私一人。Angularも知らなければもちろんTypeScriptなんぞ触ったことすらありません。
型定義もわからず、何に値を格納しているんだこれは・・・と混乱し、フレームワークのメリットもわからず、前任者もサーバーサイドはともかく、「Angularはこのひと月しか触ったことないから」と質問すらできない始末。なので一人、触ってはエラー。学んでは忘却。ひと月ふた月と過ぎていきます。途中、
「DjangoでAPI作ってJSONを非同期通信して、うんぬんかんぬんでよろしく」
と、JavaScriptでなにか作っていたらどうってことのないワードも、このときは日本語なのに意味わからん状態。帰ってから手当たり次第本を読むも、目は書いてある文字を滑り続けるし、写経しても翌日には忘れることの繰り返し。「自分、本当にプログラミングの才能ないんだな」と自己評価は地中深くに潜っていきます。とくにAngularは他のフレームワークに比べて利用者が少なく、そもそもの情報量が少ない&バージョンアップが早いので書籍は無いはAngularJSの情報も混ざるわで、本当に地獄の日々でした。
コード百遍、自ずから意通ず
読む。わからない。読む。わからない。
こんな繰り返しのある日、急に自分の中に仮説が立つようになりました。(……あれ? あのメソッドってこういう時に使うんじゃね?)
(あれをこうすれば意図通りのものが作れるんじゃね?)コードの基礎的な読み方や前任者の意図を読む精度は依然低いままでも、仮説を立てられるようになったところから、こう、主体的にコードを書けるようになった感覚が芽生えて、グッと面白さが湧いてきたのを覚えてます。
そのきっかけがJavaScriptだった
まあ正しくは触ってたTypeScriptなのですが、基礎的な部分の意でJavaScriptです。
構文のルール、()の意味、{}の役割などなどようやく線で繋がって、プロトタイプのメソッドとfunctuinで作るメソッド、Asyncの意味や注意点、デバッグの有効性、ようやく「あ、いまの自分なら教われば覚えられそう」という予感がしました。そこからは買ってホコリをかぶっていたJavaScript コードレシピ集や、JavaScriptリファレンス 第6版を片手に、UdemyのThe Complete JavaScript Course 2020を、へー! すげー! と進めてたりして、いまでもUdemyさんのコンテンツにはめちゃくちゃお世話になってます。
もしかしたらどこかで「ある日突然、コードが読める」という言葉を聞いたことあるかもしれませんが、人のターニングポイントはそれぞれで、僕の場合は「ある日突然、コードの仮説が立つようになる」がターニングポイントと思っています。
きっとこうすればこう動く。とか、これはバックエンド側の方で処理する方がスマート。と、パズルゲーム感覚のプログラミングは、ヘマもしますが本当に楽しいです!
そして、触れていない言語は山ほどありますしこの認識が正しいことかわかりませんが、本当に納期優先な最悪ケースでならJavaScriptでゴリゴリすればいいのかも、というJavaScriptの全能感はいまの僕に安心感を与えてくれています。
つまりJavaScript楽しい
現在だって僕はまともにプログラミングできてるなんて毛ほども思ってませんが、ようやく、使えるもの・世の中の役に立つものを作る人側のスタートラインに立とうとしているのかなー、なんていう成長感が嬉しくて仕方ありません。
いまではWebpack使いこなせたらいいなあとか、Reactスキル欲しいとか、その次はTypeScript押さえてやろう、みたいにJavaScriptを軸にもっと知らないことを知っていきたい、もっと使えるようになりたいと思うくらいです。あれだけ苦しめられたAngularもいまは嫌いじゃないですし、(というか一番触ってる)ゆくゆくはもっとサーバーサイド側の開発で挑戦したいとも思ってます。
長々と書いてしまいましたが、JavaScriptで動くものが作れるようになって、はじめてプログラミングの楽しさを知りました。(プログラミングの良さと楽しさは別物という意味で言っています)
もし人と比べて凹んでたり、挫折したりしてても、諦めずに(でもちゃんと手と頭を動かす時間を使って)いれば、プログラミングはある時一気に楽しくなると思います。
人生のお供にプログラミング、ってのもいいんじゃないかなあと。
仕事でも携わりながらも、僕は今それくらいの視点でいます。
せっかく触り始めたのだから、自分の中で良さがわかるところまで行けたら幸せだと思うんですよね、と素人が言ってみたり。とまあ、技術のギの字もない記事でしたが、ここまで読んでくださりありがとうございました。次回からは未熟なりに技術系記事を書いていこうと思います。
今後とも、どうぞよろしくお願いいたします。あの大嫌いだったJavaScriptに感謝と、これからもよろしくという挨拶を込めて。
console.log("Hello world.")
- 投稿日:2020-09-26T18:42:52+09:00
react (face api) 簡単なアプリケーション
はじめに
今回はazureのface apiを叩いて、結果をviewに表示するという簡単なプログラムをつくったのでここでアウトプットしておきたいと思います。
完成形
ご覧の通り、viewに関してはまだまだ改善点があり、私自身まだまだcssの勉強不足ですので暖かくみてください!!笑笑使ったパッケージ
主に使ったものは、
recharts
axios
@material-ui/coreとiconsになります。環境構築はもちろんcreate-react-appです。
api通信処理
blobへの変換
export const inputImage = (e, setFace, setIsFetched, setImage ) => { e.preventDefault(); const file = e.target.files[0] const reader = new FileReader() reader.onload = () => { fetchFace(reader.result) } reader.readAsDataURL(file) }FileReaderでは、Fileオブジェクトのファイルを実際に読み込みます。プレビューとして表示するというような動作はFileReaderを利用して行います
FileReaderの読み込みメソッドであるreadAsDataURL()を使いfileをDataURLとして読み込みます。
そしてその結果であるreader.resultを実際にapiを叩くfetchFaceに渡します。const bin = atob(image.split(',')[1]); const buffer = new Uint8Array(bin.length) for(let i = 0; i < bin.length; i++){ buffer[i] = bin.charCodeAt(i); } const blob = new Blob([buffer.buffer], {type: 'application/octet-stream'});ここでは先ほどのreader.resultをimageという引数として使っています。
このコードはblobの変換方法としてネットからそのまま引っ張ってきたコードになりますため、詳しい説明はできません。すいません。ちゃんと勉強します!!今回face apiはurlのapplication/json以外のリクエストだとこのapplication/octet-streamですかリクエストできないようなのでこちらのtypeで行いました。。
axios api
変換したblobを使い実際にapiを叩く処理はこちらになります。
import axios from 'axios' const apiKey = "apiKey" const url = "https://yuuki.cognitiveservices.azure.com/face/v1.0/detect"; var params = { "returnFaceId": "true", "returnFaceLandmarks": "false", "returnFaceAttributes": "age,gender,headPose,smile,facialHair,glasses,emotion," + "hair,makeup,occlusion,accessories,blur,exposure,noise" }; export const fetchFace = (blob) => { return axios({ method: 'POST', url: `${url}?`, params: params, headers:{ 'Content-Type': 'application/octet-stream', 'Ocp-Apim-Subscription-Key': apiKey, }, data: blob }) .then(response => response.data}) .catch(error => console.log(error.response)) }今回はjavascirptライブラリであるaxiosを使いました。
通常通りmethod,url,paramsを設定して、apiを叩きました。
このresponseとして返ってくるのが、こちらになります。
recharts pieグラフ
emotionは割合で返ってきていたので、rechartsをつかってグラフにしてみました。
こちらがグラフになります。
まずこの中にセットするため受け取ったdataを配列にして管理しますconst data = [ { emotion: '怒り', value: emotion.anger * 100, color: "#996633"}, { emotion: '混乱', value: emotion.contempt * 100, color: "#9933FF"}, { emotion: '嫌悪', value: emotion.disgust * 100, color: "#9900FF"}, { emotion: '無感情', value: emotion.neutral * 100, color: "#99FFFF"}, { emotion: '恐怖', value: emotion.fear * 100, color: "#999966"}, { emotion: '幸福', value: emotion.happiness * 100, color: "#99FF00"}, { emotion: '悲しみ', value: emotion.sadness * 100, color: "#99CCFF" }, { emotion: '驚き', value: emotion.surprise * 100, color: "#990033"}, ]; const newData = data.filter(item => item.value !== 0)dataのvalueが受け取った数値を100倍してパーセント表示できるようにしています。
そしてnewDataでは、0%の値を表示することがないようにfilterにかけて新しく配列作っています。
このnewDataをPieに渡して表示してもらいます。<ResponsiveContainer width="99%" height={370}> <PieChart textAncor="center" className="pie__chart"> <Pie data={newData} cy={180} label={renderCustomizedLabel} outerRadius={180} fillOpacity={0.7} dataKey="value" paddingAngle={1} > { data.map((data, index) => <Cell key={index} fill={data.color}/> )} </Pie> </PieChart> </ResponsiveContainer>ここで実際にrechartsを使っています。
renderCustomizedLabelは、const RADIAN = Math.PI / 180; const renderCustomizedLabel = ({ cx, cy, midAngle, innerRadius, outerRadius, emotion, value}) => { const radius = innerRadius + (outerRadius - innerRadius) * 0.5; const sin = Math.sin(-RADIAN * midAngle); const cos = Math.cos(-RADIAN * midAngle) const mx = cx + (outerRadius + 30) * cos; const ex = mx + (cos >= 0 ? 1 : -1) * 22; const my = cy + (outerRadius + 90) * sin; const ey = my; const val = value.toFixed(1) return ( <text textAnchor="middle" fill="black" x={ex + (cos >= 0 ? 1 : -1) * 12} y={ey} fontSize="12px" fontWeight={400}>{emotion}:{val}%</text> ) };ここでは円グラフの外のそれぞれの項目パーセンテージの設定を行っています。(幸福:81.4%)ここに関しても詳しくは理解できておりません。
ほとんどrechartsのドキュメント通りに書いたので深くは理解できていませんが簡単に実装できたので便利だなと感じました。まとめ
ローカルファイル(写真)をblob(バイナリーデータ)に変換することに時間がかかりました。。
私自身まだまだプログミングを学習し初めて日が浅いので、blobとは、何かから調べることになりました。今回はapiを叩いて取得したものただ羅列しただけのアプリケーションになるため、もっと何かのサービスに当てはめられるようにしたいと思っています。例えばazure face apiのfind similerでは顔の一致度などのapiもあるため、認証にも使えそうです。いずれは比較的大きめなサービスのごく一部としてもapiを利用できるようになりたいと感じています。
一応git hubにも公開しておりますので気になる方見ていただけると幸いです
こちらになりますazureのface apiはこちらです。
- 投稿日:2020-09-26T17:27:25+09:00
Node.jsでExcel⇒JSON、JSON⇒Excel変換するサンプル
やること
1.ExcelファイルをJSONで取得。
2.JSONの編集後、新しいExcelファイルに出力する。環境
Node.js
・インストール
https://nodejs.org/ja/
推奨版(執筆時点:12.18.4)・package.jsonの作成
作業用ディレクトリで以下を実行する。
node init -y
(-y:全てYesでデフォルト設定)xlsx(npm package)
・インストール
npm install xlsx
・公式
https://www.npmjs.com/package/xlsx実装
前提
・本の貸出履歴から、各ユーザの未返却履歴のみを抽出する状況を想定。
・履歴データはユーザIDと書籍コードで管理されているため、抽出データは氏名と書籍名を追加する。
・データは以下とする。(BookLendingHistory.xlsx)Excelファイルの読込
index.jslet XLSX = require('xlsx') let workbook = XLSX.readFile('BookLendingHistory.xlsx', {cellDates:true}) // cellDates(日付セルの保持形式を指定) // false:数値(シリアル値)[default] // true :日付フォーマットデータ取得(JSON)
シート毎にJSONで取得します。
index.jslet history, users, books workbook.SheetNames.forEach(sheet => { if("history" == sheet) history = XLSX.utils.sheet_to_json(workbook.Sheets[sheet]) if("users" == sheet) users = XLSX.utils.sheet_to_json(workbook.Sheets[sheet]) if("books" == sheet) books = XLSX.utils.sheet_to_json(workbook.Sheets[sheet]) })history(貸出履歴)
※データが無いセルのJSONプロパティは取得されません。(返却日時)
抽出
返却日時が無い履歴を抽出します。
index.jslet notReturned = history.filter(function(item) { return !("返却日時" in item) })加工
ユーザ名と書籍名を加えたJSONを作成します。
index.jslet notReturnedReport = [] notReturned.forEach(item => { item.ユーザ名 = getUserName(item) item.書籍名 = getBookName(item) notReturnedReport.push(item) }) /** * JSON内のユーザIDに一致するユーザ名を返す * @param {*} item */ function getUserName(item){ let userName = "" users.some(function(user) { if(user.ユーザID == item.ユーザID) userName = user.ユーザ名 }) return userName } /** * JSON内の貸出書籍コードに一致する書籍名を返す * @param {*} item */ function getBookName(item){ let bookName = "" books.some(function(book) { if(book.書籍コード == item.貸出書籍コード) bookName = book.書籍名 }) return bookName }出力(Excel)
新しいExcelファイルに書き出します。
index.jslet exportBook = XLSX.utils.book_new() let sexportSheet = XLSX.utils.json_to_sheet(notReturnedReport) XLSX.utils.book_append_sheet(exportBook, sexportSheet, "sheetName") XLSX.writeFile(exportBook, "NotReturnedReport.xlsx")Github
- 投稿日:2020-09-26T17:02:13+09:00
Seleniumの操作を音声付きで動画に記録する方法
目的
Seleniumの操作を動画で記録したい。
その際にブラウザの音声も記録したい。方法
以下のelgalu/seleniumのコンテナを使用すると簡単にできる。
https://github.com/elgalu/docker-selenium以下のサンプルはSeleniumでYoutubeの動画を30秒間再生して、その様子を動画に保存するサンプルである。
環境
macos Catalina 10.15.6
v14.10.1事前準備
npm init -y npm install --save selenium-webdriver mkdir videosテストコード
const {Builder, By, Key, until} = require('selenium-webdriver'); (async function example() { let driver = await new Builder() .forBrowser('chrome') .usingServer('http://localhost:4444/wd/hub') .build(); try { await driver.get('https://www.youtube.com/watch?v=WX6_koWVVpY'); // 再生をする await driver.findElement(By.className('ytp-play-button')).click(); const elTimeCurrent = await driver.findElement(By.className('ytp-time-current')); let bFlg = true; const timeId = setInterval(async ()=>{ // 一秒毎にマウスを動かして再生時間が隠れないようにする const rc = await elTimeCurrent.getRect(); let x = Number(rc.x); if(bFlg) x += 1; driver.actions().move({x: x, y:rc.y}).perform(); bFlg = !bFlg; }, 1000); // 30秒再生するまで待つ await driver.wait(until.elementTextContains(elTimeCurrent, '0:30'), 40*1000); clearInterval(timeId); } finally { await driver.quit(); } })();dockerコンテナの起動〜テストコードの起動するシェルスクリプト
sample.shdocker run -d --rm --name=grid -p 4444:24444 -p 5920:25900 \ --shm-size=2g -e VNC_PASSWORD=hola \ -e VIDEO=true \ -e AUDIO=true elgalu/selenium sleep 5 docker exec grid wait_all_done 30s node test.js docker cp grid:/videos/. videos docker exec grid stop-video docker stop gridこのスクリプトを実行するとvideosフォルダにvid_chrome_25550_firefox_25551.mkvが作成される。
このファイルはブラウザにドラッグ&ドロップすると再生して確認可能。参考
https://www.selenium.dev/selenium/docs/api/javascript/index.html
https://github.com/elgalu/docker-selenium/blob/master/docs/videos.md
- 投稿日:2020-09-26T16:39:22+09:00
【JS学習その⑧】コンストラクター関数 ~prototype・new演算子・instanceof~
JS学習シリーズの目的
このシリーズは、私ジャックが学んだJavaScriptのメカニズムについてアウトプットも兼ねて、
皆さんと知識や理解を共有するためのものです。
(理解に間違いがあればご指摘いただけると幸いです)コンストラクター関数とは
「新しくオブジェクトを作成するための雛形となる関数」
コンストラクター関数は、一般的な関数とは異なり、専用の
new
演算子を使ってオブジェクトを生成します。main.jsfunction A() { this.prop = 0; } const obj = new A();上記のコードのように、
new
演算子で作成したオブジェクトをインスタンスと言い、new
演算子でインスタンスを作成することをインスタンス化と言います。
※コンストラクター関数では、慣例として関数名の先頭を大文字で書きます。prototypeとは
「オブジェクトに存在する特別なプロパティー」
「コンストラクター関数と合わせて使用」main.jsfunction Person(name, age) { this.name = name; this.age = age; } Person.prototype.hello = function() { console.log('hello ' + this.name); } const bob = new Person('Bob', 18); const tom = new Person('Tom', 33); const sun = new Person('Sun', 20); bob.hello(); /*hello Bob*/ tom.hello(); /*hello Tom*/ console.log(bob);上記のコードでは、
prototype
プロパティにhello
メソッドを追加して、それをnew
演算子でインスタンス化したオブジェクトから呼び出しています。
よって、実行結果はそれぞれコメントした内容となります。ここで、
console.log(bob)
の結果を見てみると、
上記の画像のように、インスタンスの中に存在する__proto__
の中に、先ほどprototype
に追加した
hello
メソッドが格納されています。このことから、インスタンス化した際には
prototype
の参照が__proto__
にコピーされるということが分かります。↑は、非常に重要なので理解しておきましょう!
なぜprototypeを使うのか
前述したように、コンストラクター関数でメソッドを定義する際は、
prototype
を使って定義します。
ただ、prototype
を使わなくても直接コンストラクター関数に定義することはできます。
では、なぜprototype
を使って定義するのでしょうか?
それは、「メモリの効率化」のためです。
下記のコードを見てみましょう。main.jsfunction Person1(name, age) { this.name = name; this.age = age; this.hello = function() { console.log('hello ' + this.name); } } const bob = new Person1('Bob', 18); bob.hello(); /*hello Bob*/ function Person2(name, age) { this.name = name; this.age = age; } Person2.prototype.hello = function() { console.log('hello ' + this.name); } const tom = new Person2('Tom', 33); tom.hello(); /*hello Tom*/上記のコードでは、
Person1
ではコンストラクター関数に直接メソッドを定義して、Person2
ではprototype
にメソッドを定義しています。
どちらも、インスタンス化してメソッドを呼び出した際の実行結果は同じです。
しかし、この2つの違いは、
- Person1のインスタンスでは、メソッドそのものをメモリ空間にコピーして作成している
- Person2のインスタンスでは、
prototype
に定義したメソッドの参照を__proto__
コピーしているつまり、
Person1
の書き方だと、インスタンスを生成する度にhello
メソッドを追加する必要があるので、余分なメモリを消費することになります。
一方、'Person2'の書き方だと、インスタンスを生成した際にprototype
の参照をコピーするため、オブジェクトに格納されているメソッドの参照先は全て一致するので、余分なメモリを消費せずに効率的にプログラムを動かすことができます。そのため、この
prototype
というオブジェクトは、JavaScriptの仕組みを支える重要な技術となります。new演算子
まず、
new
演算子とは
「コンストラクター関数からインスタンスを作成するために使用する演算子」これまでも
new
演算子は使ってきましたが、new
演算子でコンストラクター関数からインスタンスを作成する際、
コンストラクター関数の戻り値によって動きが変わってきます。
- コンストラクター関数の戻り値がオブジェクトの場合は、コンストラクターが返す
return
のオブジェクトを新しいインスタンスオブジェクトとして呼び出し元に返す- コンストラクター関数の戻り値がオブジェクト以外、もしくは戻り値の
return
が定義されていない場合は、コンストラクターのprototype
の参照をインスタンスの__proto__
にコピーして、コンストラクターで使用している'this'を呼び出し元に返却する(※インスタンスを'this'の参照先としてコンストラクター関数を実行)main.jsfunction F1(a, b) { this.a = a; this.b = b; return {}; } const instance1 = new F1(1, 2); console.log(instance1); /*{}*/ function F2(a, b) { this.a = a; this.b = b; return 1; } const instance2 = new F2(1, 2); console.log(instance2); /*{a: 1, b: 2}*/上記のコードでは、
F1
では、戻り値がオブジェクトなので、new
演算子でインスタンス化した時、return
のオブジェクトをインスタンスオブジェクトとして返すので、結果は(今回の場合){}
(空のオブジェクト)となります。
F2
では、戻り値がオブジェクト以外のプリミティブ値なので、'new'演算子でインスタンス化した時、(今回はprototype
でメソッドを定義していませんが)'prototype'の参照を__proto__
にコピーして、'this'の参照するオブジェクトはインスタンス(今回の場合はinstance2
)になります。instanceofとは
「どのコンストラクターから生成されたオブジェクトかを確認する」
main.jsfunction F(a, b) { this.a = a; this.b = b; } F.prototype.c = function() {} const instance = new F(1,2); console.log(instance instanceof F); /*true*/ console.log(instance.__proto__ === F.prototype) /*true*/上記のコードでは、
console.log(instance instanceof Object)
で、instance
がF
のインスタンスなので、true
が返ってきます。
ちなみに、console.log(instance.__proto__ === F.prototype)
で、instance.__proto__
とF.prototype
が同じ参照先のオブジェクトなのでこれもtrue
が返ってきます。instanceofの使用例
instanceof
はプロトタイプチェーンをさかのぼって検証を行うので、例えば次のように関数に渡す引数が配列かオブジェクトかで条件分岐して関数を実行することもできます
(※プロトタイプチェーン・・・プロトタイプの多重形成をプロトタイプチェーンと言う)main.jsfunction fn(arg) { if(arg instanceof Array) { arg.push('value'); } else if (arg instanceof Object) { arg['key'] = 'value'; } console.log(arg) } fn([]) /*["value"]*/まとめ
いかがでしたでしょうか。
コンストラクター関数は、ES6から導入されたクラス構文の基礎となる部分なので、しっかり理解しておきましょう!
- 投稿日:2020-09-26T16:31:49+09:00
[Node.js]Express Validatorが使えない??
こちらの書籍を使ってNode.jsの学習をしてました。(ちなみに今回私が使用しているのは初版です)
Express Validatorが使えない??
そこで学習を進めていると、以下のエラーが吐かれました。
TypeError: validator is not a function at Object.<anonymous> (作業フォルダ/app.js:38:9)validatorが関数ではありませんと言われてしまいました。
そこで該当するソースコードをチェックしてみます。
app.jsvar validator = require('express-validator'); (省略) app.use(validator()); //エラーの該当部分validatorを利用する前には、requireでモジュールをロードして、変数validatorで定義しています。
スペルミスもないです。なぜエラーが???
とそこで、この書籍はメルカリで購入した初版だったこともあり、誤字や古い情報がちょこちょこあるのを思い出しました。
それで以下から
正誤表
を確認するとやっぱりあった!!
本書で使用している Express Validator は、現在 ver.6 となり、仕様が変更されているため、本書の記述の通りでは正常
に動作しなくなっています。
Express Validator をインストールする際、以下のようにして ver. 5 をインストールしてご利用下さい。
・既に最新版をインストールしている場合、アンインストール
npm uninstall express-validator
・ver.5 をインストール
npm install --save express-validator@5.3.1
引用:Node.js 超入門[第 2 版] 【正誤表】 - 秀和システムそもそもアプリにインストールしていた、Express Validatorに原因がありました。
正誤表の通りに再度、Express Validatorをインストールして無事エラーは解決しました。
なるべく新しい書籍を選ぶようにしたいけど、新品は高くて...
(もっと給料上げて~)
- 投稿日:2020-09-26T16:09:44+09:00
プロパティアクセスで無限にアクセスしてみる
前置き
注意:この記事はとりあえず書いてみたかったので書いただけで、特に深い意味もなければ学ぶ事も特にないと思います。それでも良い方はどうぞ
コードを書いてみる
a.jsconst a = {a:1,b:1}; a.a = a; console.log(a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.b);(このconsole.log内を書きたかったがために記事を書いてる)
コードの説明
これだけだとtwitterにでも投稿してろ、となるのでコードの説明。
一行目
a,bのキーを持つオブジェクト
a
を用意する。
この時、オブジェクトのa
と、キーのaは全く別物なので注意二行目
オブジェクト
a
のキーaに、オブジェクトa
を代入する。
なお、a.a
のようにドットでアクセスするのをプロパティアクセサーといい、オブジェクトのキーにアクセスする方法の一つ1。javascriptでこの記法を避けて通るのは不可能2なので記述方法自体は誰でも知っているはず。
元々int型の1
が入っていたのにオブジェクト型のa
を代入できるのは、javascriptに型の指定が無いから。
この代入により、オブジェクトa
のキーaには、元々のオブジェクトa
自体が入っている。ちなみに、javascriptは参照渡しのような動作3を行うため、
a===a.a
はtrueになる。
なので、a===a.a.a
もtrueになり、無限に辿ることが出来るようになる。三行目
無限にアクセスできるaを辿り、bを出力する。
もちろん最後のa.b
は、単純にa.b
を記述したのと同じなので、定義したb=1が出力される。
ただ、実際に無限に書くのは難しいものもあるので、ここでは20個で止めている。終わりに
実際にこのような使い方をすることはまず無いとは思いますが、参照渡しの言語だとこういう事もできるよというお話でした。
もう一つは
a["a"]
という方法。キーを文字列 で指定する必要がある。 ↩
document.getElementById
という表記で既に使っているし。 ↩曖昧表現の理由:JavaScriptに参照渡し/値渡しなど存在しない ↩
- 投稿日:2020-09-26T15:30:45+09:00
jQueryでセレクトボックスの選択要素を移動する
二つセレクトボックス(Selectタグ)があるサイトで、片方のセレクトボックスの選択要素を別のセレクトボックスに移動させるメモ
移動元の指定をoption:selectedにするのがミソ
$('#移動元SelectタグのID option:selected').appendTo($('#移動先SelectタグのID'));
移動ではなくコピーしたいときはclone()を挟むだけ
$('#コピー元SelectタグのID option:selected').clone().appendTo($('#コピー先SelectタグのID'));
Sample
html部分<!-- 移動元 Selct Tag--> <select id="selLeft" size="10"> <option value="1">りんご</option> <option value="2">みかん</option> <option value="3">なし</option> <option value="4">いちご</option> </select> <!-- 移動先 Selct Tag--> <select id="selRight" size="10"> <option value="5">ぶどう</option> </select> <!-- 実行ボタン--> <input type="button" id="toRight" value="->" />javascript部分<script type="text/javascript"> $(function () { $('#toRight').on('click', function () { // 移動元のSelectから移動先のSelectに移動 $('#selLeft option:selected').appendTo($('#selRight')); // 移動ではなくコピーしたいときはclone()を挟むだけ // $('#selLeft option:selected').clone().appendTo($('#selRight')); }); }); </script>
- 投稿日:2020-09-26T11:52:40+09:00
歌詞サイトから手軽にクリップボードにコピーするためのブックマークレット
歌詞サイトから歌詞をゲットしたい
転載対応なのか歌詞サイトの多くはコピペ禁止というか、古のテクニックで右クリック禁止をしている場合が多い。
ソースを開発者ツールでみると、要素自体は取りやすい模様。
ファイルとして保存してもいいが、とりあえずコピペできれば十分だとおもったので、ブックマークレットにすることにした。今回は検索でよくヒットするJ-Lyric.netをターゲットにする。
ブックマークレットの作成
ブラウザのブックマーク編集で、以下のjavascriptをURLとして入力する。
javascriptjavascript:function getLyric(){var a=document.createElement("textarea");var t = document.getElementById("Lyric").innerHTML.replace(/<br>/g, "\n").replace(/&/g, "&");a.textContent=t;var d=document.getElementsByTagName("body")[0];d.appendChild(a);a.select();var b=document.execCommand("copy");d.removeChild(a);return b};getLyric();使い方
- ブラウザのブックマーク編集で、ブックマークレットを作成。
- J-Lyric.netでなんらかの曲を表示。
- ブックマークレットを選択(クリップボードにコピーされる)
- メモ帳などにペースト
概説
元のソースは、こちらを参考にした。
単純にgetElementByID("Lyric")した内容をそのままクリップボードにもっていけなかったので、内部的にテキストエリアをつくってコピーさせているのがミソ。
- 投稿日:2020-09-26T08:57:23+09:00
FrontEndのいくつ技術の用途の取り纏め
FrontEndではいろいろな技術が創造されています。その中に、幅広く使ったものはHTML、JavaScript、CSS、DynamicHTML、DOM、AJAXです。
以下、用途により、説明します。HTMLの用途はなんですか
今はWEB開発が流行しています。WEB開発以前はデスクトップアプリケーションが流行でした。WEB開発に変わってから、何の技術でブラウザにコンポーネントを表示しますか、画面でどのような効果を表示しますか。
この場合、HTMLが登場しました。
HTMLでは、テキストコンポーネント、ボタンコンポーネントが定義されています。文字色、背景色が指定できます。script、cssも組み込めます。
つまり、以下の二点の用途があります。
・画面でデータの輸出と輸入及びユーザ体験のために効果の表示
・script、cssなどほかの技術のインターフェースを持つJavaScriptの用途はなんですか
HTMLで生成されたページが固定です。変えたい場合、JavaScriptを使ったほうがいいです。例えば、検索した結果、一般的に、テーブル見たいなフォーマットで結果を表示します。JavaScriptを使って、表示する結果が
変わられます、しかも、表示するタイトルも変わられます。
用途にて、以下があります。
・ブラウザ自体の状況をコントロール
・入力したコンポーネントの値をチェック
・変わる結果を表示
下の二点に対してはサーバ側でも実現できるんですが、JavaScriptのほうでサーバ側へ通信の回数が少なくなって、通信のデータ量が少なくなりました。CSSの用途はなんですか
画面の色、フォーマットなどstyleを設計する
DynamicHTMLの用途はなんですか
DynamicHTMLとは、DOM(Document Object Model)をサポートするHTMLです。ブラウザに動的な効果が要る場合使います。
ブラウザにDOMサポートしたら、DynamicHTMLのページを作れることは言えます。DOMの用途はなんですか
ブラウザに動的な効果が要る場合使います。
AJAXの用途はなんですか
画面全体の刷新ではなく、画面内の一部を刷新したい場合、使います。
Googleの検索機能、検索ボックスに文字を入力中につづりの候補が絞り
込まれ、検索ヒット数と共に表示される
- 投稿日:2020-09-26T08:43:02+09:00
Nuxt.jsで認証認可入り掲示板APIのフロントエンド部分を構築する #3 個別記事ページの作成
←Nuxt.jsで認証認可入り掲示板APIのフロントエンド部分を構築する #2 NuxtとRailsとの疎通テスト
個別記事ページの作成
$ mkdir pages/posts $ touch pages/posts/_id.vuepages/posts/_id.vue<template> <v-layout column justify-center align-center > <v-flex xs12 sm8 md6 > <v-card> <v-card-title class="headline"> {{ post.subject }} - {{ post.user.name }} </v-card-title> <v-card-text> {{ post.body }} </v-card-text> </v-card> </v-flex> </v-layout> </template> <script> export default { validate ({ params }) { return /^\d+$/.test(params.id) }, async asyncData ({ $axios, params }) { const response = await $axios.$get(`/v1/posts/${params.id}`) return { post: response.post } } } </script>Nuxtでは上記のように
pages/hoge/_id.vue
とすることで、hoge/{id}のような動的ページが作られます。バリデートは数値型のみ許容するように設定しています。以下、公式ドキュメント参照。
ルーティング - NuxtJSそして
asyncData
というSSR用のメソッドでv1/posts/${params.id}
とすることで、URLのid部分の数値から固定のpostを取得しているわけです。なお、今回は
post
の値をVuexのstoreに保存していません。
storeはなんでもかんでも保存を保存しておく場所ではなく、他のmoduleやサイト全体で使う必要があるか考え、そうでなければ不用意に突っ込まないほうがいいです。もしブラウザで以下の通り表示されたら、とりあえずOKです。
indexから個別ページにリンクを設置
indexから個別ページにリンクを貼ります。
pages/index.vue<v-card v-for="post in posts" :key="post.id"> <v-card-title class="headline"> - {{ post.subject }} - {{ post.user.name }} + <n-link :to="`/posts/${post.id}`"> + {{ post.subject }} - {{ post.user.name }} + </n-link> </v-card-title> <v-card-text>リンクになっていますね。
CORS対策
ではindexから個別ページに飛んでみましょう。
エラーになりましたか?
実はこれがエラーメッセージ読んでも原因が特定しづらく結構厄介なものです。
以前この問題の対応策を書き残していますので、詳細を知りたい方は以下の記事を。
Nuxt.jsでURL直叩きの時はページが表示されるのに、nuxt-linkやブラウザバックでAn error occurredになるさて上記記事にも書きましたが、原因はページ遷移した場合はSSR(サーバサイドレンダリング)ではなくCSR(クライアントサイドレンダリング)になるため、CORS(オリジン間リソース共有)の対策が必要です。
nuxt.config.js** Nuxt.js modules */ modules: [ - '@nuxtjs/axios' + '@nuxtjs/axios', + '@nuxtjs/proxy' ], - axios: {}, + axios: { + proxy: true + }, + proxy: { + '/v1/': { + target: 'https:/xxxxxxxxxxx.ap-northeast-1.amazonaws.com:8080/' + } + },targetはご自身のRails APIエンドポイントに置き換えてください。
この設定が済めば、indexも個別ページも、ブラウザバックやn-link(nuxt-link)による遷移時も正常に表示されるはずです。
続き
→
【連載目次へ】
- 投稿日:2020-09-26T08:42:09+09:00
Nuxt.jsで認証認可入り掲示板APIのフロントエンド部分を構築する #2 NuxtとRailsとの疎通テスト
←Nuxt.jsで認証認可入り掲示板APIのフロントエンド部分を構築する #1 環境構築
axiosの導入
URLのリクエストを送る際はaxiosを使います。
create_nuxt_appの時にaxiosを入れているはずですが、もし入れるのをミスっていた場合は以下の手順で追加します。$ yarn add @nuxtjs/axiosnuxt.config.jsexport default { ** Nuxt.js modules */ modules: [ + '@nuxtjs/axios' ],APIとの疎通テスト
pages/index.vueWelcome to the Vuetify + Nuxt.js template </v-card-title> <v-card-text> + <ul> + <li v-for="post in posts" :key="post.id"> + {{ post.subject }} + {{ post.body }} + </li> + </ul> ... components: { Logo, VuetifyLogo + }, + async asyncData({ $axios }) { + const response = await $axios.$get('/v1/posts'); + return { + posts: response.posts + }; } } </script>雑ではありますが、Railsのseedで作ったデータが無事に取得できましたね。
とりあえずこれでフロントエンドとバックエンドの連携確認は取れました。
Vuexで書き換える
特にルール無く記述していくことも可能なのですが、Vuexを利用して書いていきます。
Vuex は Vue.js アプリケーションのための 状態管理パターン + ライブラリです。 これは予測可能な方法によってのみ状態の変異を行うというルールを保証し、アプリケーション内の全てのコンポーネントのための集中型のストアとして機能します。 また Vue 公式の開発ツール拡張と連携し、設定なしでタイムトラベルデバッグやステートのスナップショットのエクスポートやインポートのような高度な機能を提供します。
文章・画像引用:Vuex とは何か? | Vuex
文章を読んだり画像をパッと見ただけではなかなか理解しづらいかもしれません。
actionsやらmutationsやらdispatch, commit…なにこれ?と思うかもしれませんが、
- storeに
state
、mutations
、actions
の3つ(+getters
の4つ)を持ってそれぞれを呼び出すというパターンstate
は値を持つだけmutations
はstateの値をセットするだけactions
で処理して、stateを変更する時は直接stateを呼ばずmutationsを呼ぶgetters
はstateの値を取得するだけmutations
を呼ぶことをcommit
と言うactions
を呼ぶことをdispatch
と言うとなります。
とりあえず以下のようにファイルを編集します。
store/posts.jsexport const state = () => ({ posts: [] }) export const getters = { posts (state) { return state.posts } } export const mutations = { setPosts (state, data) { state.posts = data } } export const actions = { async fetchPosts () { return await this.$axios.$get('/v1/posts') } }pages/index.vue<template> <v-layout column justify-center align-center > <v-flex xs12 sm8 md6 > <v-card v-for="post in posts" :key="post.id"> <v-card-title class="headline"> {{ post.subject }} - {{ post.user.name }} </v-card-title> <v-card-text> {{ post.body }} </v-card-text> </v-card> </v-flex> </v-layout> </template> <script> export default { async fetch ({ store }) { const posts = await store.dispatch('posts/fetchPosts') store.commit('posts/setPosts', posts.posts) }, computed: { posts () { return this.$store.getters['posts/posts'] } } } </script>storeディレクトリ以下に置いたファイルは、{ファイル名}/{関数名}で名前空間が切られます。
どのように処理されているかの流れは、上記2ファイルに1〜8でコメント記載しています。まずは
const posts = await store.dispatch('posts/fetchList') // 1
によってposts/fetchList
のdispatch
。
前述の通りdispatchはactionsを呼び出すので、posts.js
のactions
にあるfetchList
が呼ばれるわけですね。一度見方が分かれば、そこまで読み解くのは難しくないはずです。
Vuexはルールを破って書くことができてその場合エラーを投げたりしないのですが、結局そうなるとコードの保守性は下がります。ですので上記ルールに従って、storeの直接呼び出しや書き換えをせずに、actionsやmutationsで制御していきましょう。
続き
- 投稿日:2020-09-26T03:31:52+09:00
【JavaScript/初学者向け】打倒!ズンドコキヨシ(後半戦)
こんにちは!どいこです。
ズンドコキヨシ問題について、前回の記事で
「「ズン」「ドコ」のいずれかをランダムで出力」する所までできました。前回:【JavaScript/初学者向け】打倒!ズンドコキヨシ(前半戦)
しかし、まだキヨシは倒れていない・・・!
あきらめたらそこで試合終了なので、続きをやります。
ズンドコキヨシとは(再掲)
「ズン」「ドコ」のいずれかをランダムで出力し続けて
「ズン」「ズン」「ズン」「ズン」「ドコ」の配列が出たら
「キ・ヨ・シ!」って出力した後終了する手順その②
「ズン」「ズン」「ズン」「ズン」「ドコ」の配列が出たら
「キ・ヨ・シ!」って出力した後終了する・ まずは「この並びで出たら反応するよ~」の「この並び」を判定するための配列(
targetList
)と
「反応」の内容(answer
)を定義しておきます。const targetList = ['ズン', 'ズン', 'ズン', 'ズン', 'ドコ'] const answer = 'キ・ヨ・シ!'
・ 次は「並びが
targetList
と同じか」を判定する段階ですね。
勇者どいこはココでつまづきました。つまづいた所(キヨシが出てこない)
if (randomOutput === targetList) { console.log(answer) }要は「ランダムで作った配列の内容が
targetList
と同じ=
['ズン', 'ズン', 'ズン', 'ズン', 'ドコ']だったら
'キ・ヨ・シ!'って表示させようぜ!」
って意味なんですが「'ズン', 'ズン', 'ズン', 'ズン', 'ドコ'」って出力されてる時もキヨシが出てこない。
♪せつなさよりも遠くへ~
なぜ?
その理由とは・・・変数の中に直接配列が入っているわけではありません。
変数の中には、「参照」と呼ばれる「配列の場所」が記録されていて、
配列の実体は別の場所(メモリ上のどこか)にあります。
この事から、上のif文での比較においても
配列の中身の値を見比べているのではなく、参照先(保管場所)を見比べている事になります。
randomOutput
とtargetList
は、そもそも保管場所は異なりますよね。
(それぞれ別々に作られたものなので)中身の値ではなく「お互いの保管場所を比較している」ため
結果はelse
となり、我々は「キ・ヨ・シ!」と叫ぶ事もままならないわけです。解決策
JSON.stringify()
メソッドを使う事にしました。if (JSON.stringify(randomOutput) === JSON.stringify(targetList)) { console.log(answer) }JSON.stringify() メソッドは、ある JavaScript のオブジェクトや値を JSON 文字列に変換します。
JSON.stringify() - MDN web docs
JavaScriptで配列やオブジェクトを比較するときはJSONに変換
JSON.stringify()
効果で、むきだしの値同士を突き合わせる事ができ
randomOutput
の出力が「'ズン', 'ズン', 'ズン', 'ズン', 'ドコ'」の時に
無事「'キ・ヨ・シ!'」と表示されるようになりました。やったね
ちなみに「'キ・ヨ・シ!'」と叫んだら、ズンドコタイムは終了するのがお約束です。
forとは違って回数の決まっていないループ処理なので、while文を使います。
(条件式に当てはまる間だけ処理を繰り返し、当てはまらなくなったら終了させる)while (JSON.stringify(randomOutput) !== JSON.stringify(targetList)) { // 配列randomOutputを生成して表示させる処理 } // トリガー配列が現れたらループから抜けてanswerを表示させる console.log(answer)
回答まとめ(コード全体)
function kiyoshi() { const wordList = ['ズン','ドコ'] // ランダムで表示するワード候補2種 const elementsLength = 5 const randomOutput = [...Array(elementsLength)] // 未定義の要素 × 5個の配列 const targetList = ['ズン', 'ズン', 'ズン', 'ズン', 'ドコ'] // トリガーとなる配列 const answer = 'キ・ヨ・シ!' //トリガーとなる配列が現れたら表示させるワード // トリガーとなる配列が現れるまで処理をループ while (JSON.stringify(randomOutput) !== JSON.stringify(targetList)) { // wordListのインデックス[0] or [1]をランダムで生成し、結果をrandomOutputの要素へ代入する処理を // 要素数(5個)分ループさせる for (let i = 0; i < randomOutput.length; i++) { randomOutput[i] = wordList[Math.floor(Math.random() * wordList.length)] } console.log(randomOutput) // ランダムに生成された配列を表示 } // トリガー配列が現れたらループから抜けてanswerを表示させる console.log(answer) }もっと良い書き方も色々ありそうですが、
ひとまず自分で導きだした一つの解として置いておきます。俺たちの戦いはこれからだ!
ー最後までお読み頂き、とてもうれしく思います。
ありがとうございました。
- 投稿日:2020-09-26T00:54:51+09:00
【JavaScript/初学者向け】打倒!ズンドコキヨシ(前半戦)
こんにちは!どいこです。
初Qiita投稿です。
現在JavaScript絶賛勉強中のため
「fizzBuzz問題」をクリア後、「ズンドコキヨシ問題」にも手を出してみました。まさに今、キヨシと戦いながら書いています。
ズンドコキヨシとは
「ズン」「ドコ」のいずれかをランダムで出力し続けて
「ズン」「ズン」「ズン」「ズン」「ドコ」の配列が出たら
「キ・ヨ・シ!」って出力した後終了する手順その①
「ズン」「ドコ」のいずれかをランダムで出力し続ける
・ 「ズン」と「ドコ」を配列(
wordList
)にするconst wordList = ['ズン','ドコ']
・ 配列(
wordList
)のインデックス([0]
or[1]
)をランダムで出現させる
ランダムな値はMath.floor()
とMath.random()
を使って作るwordList[Math.floor(Math.random() * wordList.length)] //wordList[0]('ズン') またはwordList[1]('ドコ')
・ 出力する値は「ズン」「ズン」「ズン」「ズン」「ドコ」なので
要素数が5個の配列を用意const elementsLength = 5 const randomOutput = [...Array(elementsLength)] // [undefined, undefined, undefined, undefined, undefined] (未定義の要素×5の配列ができる)
・ この配列に先ほどのランダムな値(wordList
の[0]
or[1]
)
=すなわち「ズン」or「ドコ」を代入するrandomOutput[0] = wordList[Math.floor(Math.random() * wordList.length)]
・ 要素数5個の配列にしたいので、
for
文の繰り返しで入れていくfor (let i = 0; i < randomOutput.length; i++) { randomOutput[i] = wordList[Math.floor(Math.random() * wordList.length)] } // randomOutputのインデックス0~4それぞれについて // ランダムな値(「ズン」or「ドコ」)を代入
・ ここでいったん出力
console.log(randomOutput) // [ 'ズン', 'ドコ', 'ドコ', 'ズン', 'ズン' ]問題なくランダムな値が出力されるようになりました。
よし。あとはこのランダムな配列を
for
ループで出力して、
「ズン」「ズン」「ズン」「ズン」「ドコ」って出た時に
「キ・ヨ・シ!」と叫んであげたらええんや!と、この時の私は簡単に考えていた・・・。
次回、「城之内死す」次回予告目の前に立ちはだかるキヨシの灰色の牙城!!
ここはなんとかして「キ・ヨ・シ!」と叫ぶ機会を得たい!
さあ、スタンドのパワーを全開だッ!!
- 投稿日:2020-09-26T00:33:39+09:00
Safari で disable_with オプションを付けても変わらない場合
Railsでフォームを作る際、送信ボタンに disable_with オプションを付けることで、二重送信を防ぐことができる。
= f.button '送信する', data: { disable_with: '送信中…' }しかし、Safariに限って、なぜかボタンの文言が変化しなかった。GitHub上でも、多々報告されている。
disable_with doesn't work with link in Safari #306解決策
結論から書くと、JSで送信処理をあえて遅らせることで、とりいそぎは回避できた。
Issue内ではバニラJSで書かれた例が出てくるので、jQueryを使っていなけば、そちらを参考にした方が早いかもしれない。= f.button '送信する', data: { disable_with: '送信中…' }, class: 'disable_with_safari'$('.disable-with-safari').click(function (event) { if ($(this).data('disableWith')) { $(this).prop('disabled', true); $(this).text($(this).data('disableWith')); var form = $(this).closest('form'); if (form.length) { event.preventDefault(); setTimeout(() => form.submit(), 300); } } });備考
冒頭のIssueでも議論されていたが、Safariの独特な仕様で、一度submitが走った後はDOMの更新が行われなくなるようだ。(あくまでもIssue上でのコメントなので、正確な仕様は一次情報を参照してください)
そのため、disable_withをつけても、ボタンの文言は変わらなかった。ちなみに、今回の問題について検索すると以下の解決策が出てきていたが、わたしの場合は効果がなかった。
- cursor: pointer; をつける
- 空のclickイベントを設定する
- 空のtouchstartイベントを設定する
- バインドを$(document)に変更する
- 投稿日:2020-09-26T00:28:15+09:00
プロパティによってオブジェクトをグループ化したい
プロパティによってオブジェクトをグループ化したいです
オブジェクトの配列を、プロパティによってグループ化したいです。
想定するデータは以下のような感じです。const data = [ { type: '野菜', color: '赤', name: 'トマト' }, { type: '野菜', color: '赤', name: 'パプリカ' }, { type: '野菜', color: '緑', name: 'ブロッコリー' }, { type: '果物', color: '紫', name: 'ぶどう' }, ];最終的に望む形式は以下のような感じです。
const groupingData = { '野菜': { '赤': ['トマト', 'パプリカ'], '緑': ['ブロッコリー'], }, '果物': { '紫': ['ぶどう'], }, };MDNにやりたいことが載ってた
まさにずばりやりたいことがそのまま載ってました。
Array.prototype.reduce()
を使います。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce#Grouping_objects_by_a_property// MDNに載っていたコードそのまま抜粋 return objectArray.reduce(function (acc, obj) { let key = obj[property] if (!acc[key]) { acc[key] = [] } acc[key].push(obj) return acc }, {});今回の形式バージョンを考えました
// MDNに載っていたコードを少し改造 const groupBy = (objectArray, property, optionProperty = '') => { return objectArray.reduce((acc, obj) => { const key = obj[property]; if (!acc[key]) { acc[key] = []; } if (optionProperty && obj.hasOwnProperty(optionProperty)) { acc[key].push(obj[optionProperty]); } else { acc[key].push(obj); } return acc; }, {}); }; // 階層構造にする const createGroupingData = (objectArray, property1, property2, property3) => { const groupingData = groupBy(objectArray, property1); Object.keys(groupingData).forEach((key) => { groupingData[key] = groupBy(groupingData[key], property2, property3); }); return groupingData; }; // 使い方 const groupingData = createGroupingData(data, 'type', 'color', 'name');もっと良い方法を教えて下さい。
再帰的な感じにしたいです。
- 投稿日:2020-09-26T00:08:08+09:00
styled-componentsでレスポンシブを楽に書く
はじめに
今回はstyled-componentsを使ってレスポンシブデザインをできるだけ楽にする方法を紹介します。
他のstyled-componentsの記事はこちら
* styled-componentsを使ってみる
* styled-componentsでJavascriptの値を使う
* styled-componentsのThemeを使ってみる実際に描いてみる
今回のは一回書いてしまえばつかいまわせるので簡単に書きます。
src直下にmedia.tsを作成し、以下のコードを書きます。Javascriptで書く場合はtypeの部分は全部消します。
src/media.tsimport { css, CSSObject, FlattenSimpleInterpolation, SimpleInterpolation, } from 'styled-components'; export const sp = ( first: CSSObject | TemplateStringsArray, ...interpolations: SimpleInterpolation[] ): FlattenSimpleInterpolation => css` @media (max-width: 560px) { ${css(first, ...interpolations)} } `; export const tab = ( first: CSSObject | TemplateStringsArray, ...interpolations: SimpleInterpolation[] ): FlattenSimpleInterpolation => css` @media (min-width: 561px) and (max-width: 1024px) { ${css(first, ...interpolations)} } `; export const pc = ( first: CSSObject | TemplateStringsArray, ...interpolations: SimpleInterpolation[] ): FlattenSimpleInterpolation => css` @media (min-width: 1025px) { ${css(first, ...interpolations)} } `;styled-componentsからインポートしたcssは、関数みたいにも使えます。sp(スマートフォン)、tab(タブレット)、pc(パソコン)という関数を作って、それぞれのサイズでスタイルを当てたい時に呼び出して使います。
引数のタイプはVSCodeの型推論と同じになるようにしただけです。
次にApp.tsxで以下のコードを書いて、使ってみましょう。
src/App.tsximport React from 'react'; import styled from 'styled-components'; import { pc, sp, tab } from './media'; export const App = () => <Box>レスポンシブ</Box>; const Box = styled.div` background-color: red; ${sp` width: 20px; height: 20px; `} ${tab` width: 50px; height: 50px; `} ${pc` width: 100px; height: 100px; `} `;ブラウザで確認するとそれぞれのサイズでちゃんと赤い正方形のサイズが変わると思います。
ブレイクポイントなど自由に変えて使ってみてください。終わりに
ここまで読んで頂きありがとうございます!現在、PHP(Laravel)を中心に勉強しているのでそちらの方の記事を多く投稿していくと思います。感想やリクエストなどどんどん送ってくれると嬉しいです!
参考記事
- 投稿日:2020-09-26T00:06:07+09:00
styled-componentsのThemeを使ってみる
はじめに
今回はstyled-componentsのThemeに焦点を当てていきたいと思います。
themeに関しては実務で使った事がなく、あまり理解できていないところもあるので、詳しい方コメントで教えていただけると幸いです^_^他のstyled-componentsの記事はこちら
themeとは?
ReduxみたいにProviderで囲ったコンポーネント内のどこからでもアクセスできる値みたいなイメージです。
styled-componentsドキュメント引用↓
// Define our button, but with the use of props.theme this time const Button = styled.button` color: ${props => props.theme.fg}; border: 2px solid ${props => props.theme.fg}; background: ${props => props.theme.bg}; font-size: 1em; margin: 1em; padding: 0.25em 1em; border-radius: 3px; `; // Define our `fg` and `bg` on the theme const theme = { fg: "palevioletred", bg: "white" }; // This theme swaps `fg` and `bg` const invertTheme = ({ fg, bg }) => ({ fg: bg, bg: fg }); render( <ThemeProvider theme={theme}> <div> <Button>Default Theme</Button> <ThemeProvider theme={invertTheme}> <Button>Inverted Theme</Button> </ThemeProvider> </div> </ThemeProvider> );themeのメリット
themeというぐらいなので、色を全体的に変えたりなどスタイルの雰囲気をガラッと変えたりするのに使いやすそうです。
実際に使ってみる
ドキュメントの例でも十分ですが、せっかくなので使ってみましょう!
index.tsx
にAppProviderコンポーネントを作って、ボタンを押したらthemeが切り替わる感じにします。AppコンポーネントはThemeProviderで挟んで、themeを渡します。これでAppコンポーネント内ではt hemeが使えます。
src/index.tsximport React, { useState } from 'react'; import ReactDOM from 'react-dom'; import { ThemeProvider } from 'styled-components'; import { App } from './App'; const AppProvider = () => { const [is_theme_dark, set_is_theme_dark] = useState(false); const default_theme = { text_color: 'black', background_color: 'white', } const dark_theme = { text_color: 'white', background_color: 'black' } const toggle_theme = () => { set_is_theme_dark(!is_theme_dark); }; return ( <> <button onClick={toggle_theme}>Change!</button> <ThemeProvider theme={is_theme_dark ? dark_theme : default_theme}> <App /> </ThemeProvider> </> ); }; ReactDOM.render(<AppProvider />, document.getElementById('app'));次にAppコンポーネントこと
App.tsx
に以下のコードを書きます。src/App.tsximport React, { useContext } from 'react'; import styled, { ThemeContext } from 'styled-components'; export const App = () => { const theme = useContext(ThemeContext); return ( <TitleWrapper> <h1>Hello World!</h1> <Button>No Event</Button> </TitleWrapper> ); }; const TitleWrapper = styled.div` width: 100vw; height: 100vh; background-color: ${(props) => props.theme.background_color}; text-align: center; h1 { color: ${(props) => props.theme.text_color}; } `; const Button = styled.button` color: ${(props) => props.theme.text_color}; background-color: ${(props) => props.theme.background_color}; `;themeはhooksのuseContextにstyled-componentsからインポートしたThemeContextを渡してあげるとindex.tsxで定義したthemeの値が入ります。そしてテーマの値をスタイルに使っています。
ブラウザを見てみると下の画像のようにdefault_themeが表示されると思います。
左上のボタンを押すとdark_themeに変わります。
使ってみた感想
今回は簡単な物を作ったのであまり恩賜を受けられませんでしたが、グローバルステートと組み合わせて、サイト全体のテーマを変えたりするのに使えそうな感じがしました。
ここまで読んでいただきありがとうございます!今後もいろいろな記事を書いていきたいと思っているので感想や要望などいただけたら、モチベーションにもつながります。
参考記事
- 投稿日:2020-09-26T00:03:46+09:00
styled-componentsでJavascriptの値を使う
はじめに
一応、下の記事の続きとして書いているのでインストールなどはお手数ですが下の記事をご覧ください。
他のstyled-componentsの記事
* styled-componentsのThemeを使ってみる
* styled-componentsでレスポンシブを楽に書く今回はstyled-componentsでJavascriptの値を使う方法を試していきます。
実際にやってみる
連想配列で定義した値を使う
まず、srcディレクトリ直下にstyle.tsファイルを作り、以下のコードを書きます。
src/style.tsexport const COLOR = { RED: '#FF0000', ORANGE: '#FFA500', YELLOW: '#FFFF00', GREENYELLOW: '#ADFF2F', GREEN: '#008000', BLUE: '#0000FF', SKYBLUE: '#87CEEB', PURPLE: '#800080', PINK: '#FFC0CB', BROWN: '#A52A2A', WHITE: '#FFFFFF', GRAY: '#808080', BLACK: '#000000', };上のコードを見ると、色の名前とカラーコードの連想配列になっています。
App.tsxに以下のようなコードを書いてみましょう。App.tsxはsrc直下にあり、index.tsxにインポートする感じで書いています。
src/App.tsximport React from 'react'; import styled from 'styled-components'; import { COLOR } from './style'; export const App = () => { return ( <TitleWrapper> <h1>Hello World!</h1> <Button>Click</Button> </TitleWrapper> ); }; const TitleWrapper = styled.div` text-align: center; h1 { color: ${COLOR.RED}; } `; const Button = styled.button` color: ${COLOR.WHITE}; background-color: ${COLOR.BLUE}; &:hover { background-color: ${COLOR.SKYBLUE}; } `;先ほど作った
style.ts
をインポートしてスタイルに使っています。このように、色や大きさの値をどこかのファイルにまとめて、インポートして使う感じにすると共同開発でもわかりやすくなります。propsを渡して、その値を使う(例1)
App.tsxを以下のように書き換えてみましょう。Typescriptを使わない場合は、interfaceやの部分は消しましょう。
src/App.tsximport React, { useState } from 'react'; import styled from 'styled-components'; export const App = () => { const [is_red, set_is_red] = useState(true); const handleClick = () => { set_is_red(!is_red); }; return ( <TitleWrapper is_red={is_red}> <h1>Hello World!</h1> <Button onClick={handleClick}>Click</Button> </TitleWrapper> ); }; interface ITitleWrapper { is_red: boolean; } const TitleWrapper = styled.div<ITitleWrapper>` text-align: center; h1 { color: ${({ is_red }) => is_red ? 'red' : 'blue'}; } `; const Button = styled.button` color: white; background-color: blue; &:hover { background-color: skyblue; } `;上から見ていきましょう。まず、is_redという初期値がtrueのstateを作り、is_redはButtonをクリックするたびにtrue<->falseで切り替わる事がわかります。また、TitleWrapperにはis_redを渡している事がわかります。
そして、下の方のTitleWrapperを見てみるとis_redの値が使われています。ここでは三項演算子を使って、h1のテキストの色を変えています。
is_redの状態 色 true 赤 false 青 propsを渡して、その値を使う(例2)
また、下のようにスタイルの連想配列をそのまま渡す方法もあります。
src/App.tsximport React, { useState } from 'react'; import styled from 'styled-components'; export const App = () => { const [text_color, set_text_color] = useState({ color: 'blue' }); const handleClick = () => { set_text_color({ color: 'red' }); }; return ( <TitleWrapper text_color={text_color}> <h1>Hello World!</h1> <Button onClick={handleClick}>Click</Button> </TitleWrapper> ); }; interface ITitleWrapper { text_color: { color: string; }; } const TitleWrapper = styled.div<ITitleWrapper>` text-align: center; h1 { ${({ text_color }) => text_color}; } `; const Button = styled.button` color: white; background-color: blue; &:hover { background-color: skyblue; } `;※この場合はクリックしたら青から赤になり、その後にボタンを押しても色は変わりません
渡された連想配列がそのままスタイルとして適用されています。
うまく説明できたかわかりませんが。styled-componentsでは今回説明したJavascriptの値を使えるという部分が個人的にかなりいいなと思っています。次はthemeについて書きたいと思います。
ここまで読んでいただきありがとうございます!少しでもお役に立てれば幸いです!
参考記事
- 投稿日:2020-09-26T00:01:37+09:00
styled-componentsを使ってみる
はじめに
今回はstyled-componentsの簡単な使い方をやります。
他のstyled-componentsの記事
* styled-componentsでJavascriptの値を使う
* styled-componentsのThemeを使ってみる
* styled-componentsでレスポンシブを楽に書くstyled-componentsとは?
styled-componentsはReactにおけるCSSの当て方の一つで、Reactのコンポーネントのようにjsの値を渡したりでき、コンポーネントのようにスコープが作られるため、使いやすいです。
インストール
Reactの環境ができている方は下のコマンドはスルーしてください。
できていない方は以下のコマンドを打つか、記事を見ながら作ってみてください。
typescriptを使わない場合は下のコマンドの--typescript
は必要ありません。ターミナルnpx create-react-app --typescript [アプリ名]webpackでReact+Typescriptの環境構築をする
VSCodeで開き、ターミナルで以下のコマンドを打ちます。Typescriptを使わない場合は
@types/styled-components
は必要ありません。VSCodeのターミナル//npm npm install --save styled-components npm install --save-dev @types/styled-components //yarn yarn add styled-components yarn add -D @types/styled-components準備完了!
実際に使ってみる
早速使ってみましょう!
普通にスタイルを当てる
まずは
App.tsx
に以下のコードを書いてブラウザで見てみましょう。src/App.tsximport React from 'react'; export const App = () => { return <h1>Hello World!</h1>; };ちなみに
index.tsx
は以下のようにしています。src/index.tsximport React from 'react'; import ReactDOM from 'react-dom'; import { App } from './App'; ReactDOM.render(<App/>, document.getElementById('app'));次にstyled-componentsを使って、スタイルを当ててみましょう。
以下のような感じで使います。VSCodeの拡張機能の
vscode-styled-components
を入れるとシンタックスハイライトが効いてみやすくなります。const [コンポーネントとして使う名前] = styled.[タグ名]` //style `;
App.tsx
にstyled-componentsをインポートして、h1にスタイルを当てています。src/App.tsximport React from 'react'; import styled from 'styled-components' export const App = () => { return <Title>Hello World!</Title>; }; const Title = styled.h1` color: red; `;補足:下のコードように連想配列で書く書き方もあるみたいです。詳しくはドキュメント
const Box = styled.div({ background: 'palevioletred', height: '50px', width: '50px' });コンポーネント内の要素にスタイルを当てる
App.tsxを以下のように書き換えてみましょう。
src/App.tsximport React from 'react'; import styled from 'styled-components'; export const App = () => { return ( <TitleWrapper> <h1>Hello World!</h1> </TitleWrapper> ); }; const TitleWrapper = styled.div` text-align: center; h1 { color: red; } `;中心に赤く
Hello World!
が表示されるはずです。
上のようにある要素の中の要素にスタイルを当てるといった使い方もできます。擬似要素を使う
App.tsxを以下のように書き換えてみましょう。
src/App.tsximport React from 'react'; import styled from 'styled-components'; export const App = () => { return ( <TitleWrapper> <h1>Hello World!</h1> <Button>Hover</Button> </TitleWrapper> ); }; const TitleWrapper = styled.div` text-align: center; h1 { color: red; } `; const Button = styled.button` color: white; background-color: blue; &:hover { background-color: skyblue; } `;先ほどに加えて、hoverで水色になるボタンが表示されます(hover前は青)。
&の後に擬似要素を書くことで使えます。
コンポーネントにスタイルを当てる
App.tsxを以下のように書き換えてみましょう。
src/App.tsximport React from 'react'; import styled from 'styled-components'; export const App = () => { return ( <TitleWrapper> <h1>Hello World!</h1> <Button>Hover</Button> <StyledButton>Hover</StyledButton> </TitleWrapper> ); }; const TitleWrapper = styled.div` text-align: center; h1 { color: red; } `; const Button = styled.button` color: white; background-color: blue; &:hover { background-color: skyblue; } `; const StyledButton = styled(Button)` color: black; background-color: white; `;上では色だけを変えたボタンを新しく作っています。このように、作ったコンポーネントに上書きする形でスタイルを当てる事ができます。
MaterialUIなどにも使う事ができて便利です。ここまで読んでいただきありがとうございます!少しでもお役に立てれば幸いです!
参考記事