- 投稿日:2019-11-27T23:47:49+09:00
JavaScriptの基本文法
はじめに
この記事は、2019/11/1からプログラミング学習を始めた学生がプロのプログラマーになるまでのアウトプットする為の書き込みの場である。
JavaScriptを呼び出そう
HTMLのコード中にJavaScriptのコードを記入しJavaScriptを呼び出す
<script src="script.js"></script>基本文法
- 出力 console.log
- 変数 変数の前にvarを入れる
- 変数 変数を宣言する場合、前にletとconstを入れる必要があります。1
- letは、後で書き換えることのできる変数宣言
- constは、後で書き換えることのできない変数宣言
- 条件分岐(ES6バージョン)
- 条件式は()でくくる必要がある
- Ifの終わりに{}で囲む必要がある
- rubyでは、elsifであるが、else ifと記入する
- 条件分岐(ES5バージョン)
- 宣言する変数の前にvarをつける!!
- 関数
- rubyの場合、関数の前にdefがつきますが、jsの場合、functionと記入する
今日の感想 明日から
インプットをする機関を取りすぎてしまいアウトプットする時間が作る事ができない
今回のように今日インプットした、もの全てをアウトプットする事ができない
明日からは、今は、インプットする事に専念し、アウトプットについては簡単に端的にまとめたアウトプットをしていこうと思います終わりに
もっとこうした方が良いよなどご指摘頂けると幸いです
- 投稿日:2019-11-27T23:12:21+09:00
HTML フォームの部品をまとめてみる(JavaScriptでの関連処理も)
概要
Jsの学習を進める中で、フォームの部品に関しても学習する機会があったので、この際軽くまとめてみようと思う。本当に軽くね。
テキスト
text.html<input type="text" name="text_name" value="text1" id="text" />text.js//テキストの値を取得 $("#text").val(); //テキストのvalueの設定 $("#text").val("設定値");チェックボックス
check.html<input type="checkbox" name="check" value="check1" id="check1" checked /> <input type="checkbox" name="check" value="check2" id="check2" />
- 複数の部品のname属性に同じ値を設定すると、1つのグループを作れる。
- 「checked」を設定することで最初からチェックされている状態になる。
check.js//チェックされているかを取得 var check1 = $("#check1").prop("checked"); //チェックを設定 var check2 = $("#check2").prop("checked", check1); //値を取得するだけ $("#check1").val(); //同じグループのチェックボックスをまとめて扱いたい場合のセレクタの書き方 //チェックされている値を取得 //チェックされている値がなければ、「undefined」を取得する $("input[name=check]:checked").val();ラジオボタン
radio.html<input type="radio" name="radio_name" value="radioBtn1" id="radio1" /> <input type="radio" name="radio_name" value="radioBtn2" id="radio2" />
- 複数の部品のname属性に同じ値を設定すると、1つのグループを作れる。
- Jsについてはチェックボックスに書いてあるもので十分なので割愛。
セレクトボックス
select.html<select name="select_name" id="list" multiple size=3> <option value="リスト1" selected>リスト1</option> <option value="リスト2">リスト2</option> <option value="リスト3">リスト3</option> <option value="リスト4">リスト4</option> <option value="リスト5">リスト5</option> </select>-「selected」を設定することで最初から選択されている状態になる。
- multiple size属性では同時選択できる数の最大値を設定できるselect.js//選択されている値の取得 //選択されている値がなければ「null」が取得される $("#list").val();テキストエリア
textarea.html<textarea name="textarea_name" cols="20" rows="3" id="textarea"></textarea>
- rows属性では入力欄の高さを行数で指定できる
- cols属性では入力欄の幅を文字数で指定できる
SUBMITボタン
submit.html<button type="submit" id="submit">提出</button>submit.js//submitボタンにイベントを追加 $("#submit").submit(function() {});おまけ
alert.js//アラートを出力 alert("アラート");
- 投稿日:2019-11-27T23:02:12+09:00
エンジニアスタンプラリー~フロントエンド編#16
企画主旨
Frontend Developer Roadmapをひたすら巡回する企画
詳しくはこちら今回の実施内容
モバイルアプリ
Desktop Applications
Electron
Getting Startedを参考に導入及び環境構築を実施。
今まで作成してきたデザインや機能をそのまま移植する。App.tsximport React from 'react'; import { StyleSheet, Text, View } from 'react-native'; import Form from './Form' import List from './List' import Bottom from './Bottom' type P = { } type S = { page: string, front_skill: string[], back_skill: string[], inputText: string } export default class App extends React.Component<P, S> { constructor(props) { super(props) this.state = { page: 'front', front_skill: [], back_skill: [], inputText: '' } this._onPushButton = this._onPushButton.bind(this) this._onChangeText = this._onChangeText.bind(this) this._onChangePage = this._onChangePage.bind(this) } _onPushButton(): void { let { inputText, page, front_skill, back_skill } = this.state if (inputText !== '') { if (page === 'front'){ front_skill.push(inputText) } else { back_skill.push(inputText) } inputText = '' this.setState({ front_skill, back_skill, inputText }) } } _onChangeText(inputText: string): void { this.setState({ inputText }) } _onChangePage(page: string): void { this.setState({ page }) } render() { return ( <View style={styles.container}> <View style={styles.form}> <Form inputText={this.state.inputText} _onPushButton={this._onPushButton} _onChangeText={this._onChangeText} /> </View> <View style={styles.list}> <List skills={this.state.page === 'front' ? this.state.front_skill : this.state.back_skill} /> </View> <View style={styles.bottom}> <Bottom _onChangePage={this._onChangePage} /> </View> </View> ) } }成果物
あまりコードの流用ができず、同じJavaScriptでも全く別物の印象。
https://github.com/tonchan1216/WDR-frontend-reactNative
- 投稿日:2019-11-27T22:48:47+09:00
ChromeならiPadでコンソール出力を確認できる
Chrome for iOSがv.74からjavascriptのコンソールのログ出力に対応していました。
使い方
通常のChromeのように開発者ツールを開くのではなく、アドレスバーに chrome://inspect と入力してアクセスすると以下のページが表示されます。
"ログ記録を開始"ボタンを押して、他のタブでページを開くとこのページにログが記載されていきます。
便利!開発者ツールが使えるようになったわけではありませんが、ログ出力が見れるようになっただけでも大助かりです。
参考
- https://blog.chromium.org/2019/03/debugging-websites-in-chrome-for-ios.html
- https://www.softantenna.com/wp/software/chrome-for-ios-inspect/
- 投稿日:2019-11-27T22:39:12+09:00
Axiosの前処理でAPIレスポンスのスネークケースをキャメルケースに変換する
はじめに
サーバサイドからのAPIレスポンスが
some_id
のようにスネークケースで定義されていると、JS側で毎回キャメルケースに変換することになりますよね。{ some_id: 1, some_name: "name", }今回はそんな変換処理をAxiosの共通処理でまとめてみました。
レスポンスをキャメルケースに変換する
import { camelCase, snakeCase } from 'change-case'; const isObject = (target: any): boolean => Object.prototype.toString.call(target).slice(8, -1) .toLowerCase() === 'object'; const convertSnakeToCamel = (target: any): void => { if (Array.isArray(target)) { target.forEach((t) => convertSnakeToCamel(t)); } if (isObject(target)) { Object.keys(target).forEach((key) => { if (isObject(target[key]) || Array.isArray(target[key])) { convertSnakeToCamel(target[key]); } // スネークケースをキャメルケースに変換する if (key.includes('_')) { target[camelCase(key)] = target[key]; delete target[key]; } }); } };キャメル変換する部分は
change-case
というライブラリを入れましたが、そこまで複雑ではないので自作でもよさそう参考:javascriptでキャメルケースとスネークケースの相互変換
Axiosに組み込む
import axios, { AxiosInstance } from 'axios'; const Axios: AxiosInstance = axios.create(); Axios.interceptors.request.use((request) => { // リクエストの共通処理 return request; }); Axios.interceptors.response.use((response) => { if (response.data) { convertSnakeToCamel(response.data); } return response; }); export default Axios;interceptorsを使うことでできました。interceptorsを使いこなせば、他にも、リクエストに共通のヘッダを毎回差し込んだり、レスポンスでエラー処理をしたりと、何かと便利にできそうです。
- 投稿日:2019-11-27T21:55:51+09:00
Phina.jsテキストのスクロール実装について
Phina.jsのテキストスクロールについて
- Phina.jsを使用してアプリ開発をしていたところテキストのスクロールが必要な場面に遭遇したのですが
ネットで調べてもそれらしい記事が少なかったこととライブラリのコードを調べたときに実装途中のコードが 見つかりどうやら機能自体はあるようですがまだ未実装なようなのでそれを自分用に改良した
コードを役に立つかどうかは分かりませんが掲載しようと思います。(勘違いだったらすみません)
テキストのスクロール自体はLabelAreaクラスの要素scrollYのパラメータをいじることで可能なようです。以下コード
//シーンクラス内で this.group = DisplayElement().addChildTo(this); var self = this; this.labelArea = LabelArea({ text: "スクロール", width: 580, height: 320, x:350, y:780, fill: "white", stroke: null, fontSize: 30, }).addChildTo(this.group); this.labelArea.setInteractive(true); var physical = phina.accessory.Physical(); physical.friction = 0.8; var lastForce = 0; var lastMove = 0; this.labelArea.on('pointstart', function(e){ lastForce = physical.velocity.y; lastMove = 0; physical.force(0, 0); }); this.labelArea.on('pointmove', function(e){ var p = e.pointer.deltaPosition; lastMove = p.y; self.labelArea.scrollY -= 3*lastMove; }); this.labelArea.on('pointend', function(e){ physical.force(0, lastForce + lastMove); }); window.onmousewheel = function(e){ //chrome限定です self.labelArea.scrollY -= 0.9*e.wheelDelta ; }
- 投稿日:2019-11-27T21:26:32+09:00
vue2-leafletでGeoJsonデータを表示・操作する
はじめに
ウェブ上で簡単に地図を表示する人気ライブラリ「Leaflet.js」をVue.js環境でお手軽に使えるラッパーライブラリ「vue2-leaflet」を5日くらい色々いじって得た知見を本記事にまとめます。いじった結果のゴール地点は以下の画像のようなウェブアプリケーションです。①地図エリアに.geojsonファイルをドラッグドロップすると地図上に地物を表示、②読み込んだGeoJsonレイヤを一覧表示、③地物をクリックするとその地物の属性をすべて表示、という以上の三機能をVue.jsの特性を活かし実装しました。本記事ではvue2-leafletの導入・実装と、機能①に焦点を絞って解説したいと思います。
環境
- npm 6.12.0
- @vue/cli 4.0.5
- leaflet 1.6.0
- vue2-leaflet 2.2.1
「vue cli」環境で開発しました。「vue cli」環境構築については本記事では掲載しません(以下の記事が詳しいです)。
Vue.js #001 – Vue CLI 3で環境構築導入
npm install leaflet vue2-leafletインストール後、main.jsにてcssを読み込ませます
main.jsimport Vue from 'vue' import App from './App.vue' //ここから import { Icon } from 'leaflet' import 'leaflet/dist/leaflet.css' //ここまで // this part resolve an issue where the markers would not appear delete Icon.Default.prototype._getIconUrl; Icon.Default.mergeOptions({ iconRetinaUrl: require('leaflet/dist/images/marker-icon-2x.png'), iconUrl: require('leaflet/dist/images/marker-icon.png'), shadowUrl: require('leaflet/dist/images/marker-shadow.png') }); Vue.config.productionTip = false new Vue({ render: h => h(App), }).$mount('#app')以上で導入は完了です。
実装
すべて単一ファイルコンポーネントとして記載しています。
script
<script> import { LMap, LTileLayer, LControlLayers, LControlScale, LGeoJson, } from 'vue2-leaflet'; export default { name: 'MapPane', components: { LMap, LTileLayer, LControlLayers, LControlScale, LGeoJson, }, data() { return { center: [38, 140], zoom:5, options: { onEachFeature: function(feature, layer) { layer.options.smoothFactor = 2; } }, geojson: null, } }, </script>template
<template> <div class="mapPane" @dragover.prevent="dragover" @drop.prevent="drop" > <l-map :zoom="zoom" :center="center" :preferCanvas="true" > <l-control-layers position="topright" :collapsed="false" ></l-control-layers> <l-control-scale position="bottomleft" :imperial="false" :metric="true" ></l-control-scale> <l-tile-layer name="MIERUNE MONO" visible="true" url="https://tile.mierune.co.jp/mierune_mono/{z}/{x}/{y}.png" attribution="Maptiles by <a href='http://mierune.co.jp/' target='_blank'>MIERUNE</a>, under CC BY. Data by <a href='http://osm.org/copyright' target='_blank'>OpenStreetMap</a> contributors, under ODbL." layer-type="base" ></l-tile-layer> <l-geo-json :geojson="geojson" :options="options" :options-style="styleFunction" @click="onFeatureClick" ></l-geo-json> </l-map> </div> </template>長いので分解してみましょう。
まずLeafletマップ本体はl-mapです。l-mapの内側に、必要なUIパーツや表示したいレイヤーを記述します。<l-map :zoom="zoom" :center="center" :preferCanvas="true" > <!-- ここにタイルレイヤーだとかコントロールだとかを追加する --> </l-map>素のl-mapだけだと背景地図すら表示されません。なのでまずタイルレイヤーを追加してみましょう。タイルレイヤーはl-tile-layerをl-map内に記述する事で追加されます。
<l-tile-layer name="MIERUNE MONO" visible="true" url="https://tile.mierune.co.jp/mierune_mono/{z}/{x}/{y}.png" attribution="Maptiles by <a href='http://mierune.co.jp/' target='_blank'>MIERUNE</a>, under CC BY. Data by <a href='http://osm.org/copyright' target='_blank'>OpenStreetMap</a> contributors, under ODbL." layer-type="base" ></l-tile-layer>このとおり、l-map内に各種コンポーネントを追加記述して、望む機能をもつ地図をつくれる訳ですね。おなじみのレイヤーコントロール(レイヤー一覧、L.control.layers)は、前述の例のとおりl-control-layersで実装できたりと、基本的な機能をvueの記法で実装出来ます。そういったビルトインのUIパーツなどはおそらく実装には困らないと思いますので、本記事ではGeoJsonレイヤーの取扱いについて掘り下げていきたいと思います。
GeoJsonレイヤーの取扱
実装
<l-geo-json :geojson="geojson" :options="options" :options-style="styleFunction" @click="onFeatureClick" ></l-geo-json>l-geo-jsonをl-map内に追加します。v-bindでgeojsonオブジェクトを渡してやる必要があります。ここで、このgeojsonはリアクティブです。つまりdata内のgeojsonの変更がマップに即時反映されます。vue2-leafletの前にvue-mapboxで遊んでいて、同様にgeojsonレイヤーのコンポーネントはあるのですが、リアクティブではありませんでした。この一点だけでもLeafletを優先して使う価値があると思います(GeoJsonビューア愛好家として)。
という訳でまずは.geojsonファイルのドラッグドロップ機能を実装しましょう。
<div class="mapPane" @dragover.prevent="dragover" @drop.prevent="drop" > <!-- l-mapなどなど --> </div>vueでドラッグドロップイベントを実装する場合はdragoverとdropをv-onで書きます。ここではドロップ時にdropというメソッドを実行せよ、という意味になります。.preventはブラウザの基本機能の実行を防ぐ構文です(例:ファイルをドロップすると、ブラウザ自体がそのファイルを開こうとするため)。さてここで、ドロップイベントだけ監視したいのだから、dragoverは不要ではないか?と考えると思います。しかしながらそれでは動作しません。おそらくdragover時にブラウザ処理が先行してしまうから(.preventが走らないから)だと思います。
さて、dropメソッドは、script内のmethodsにて宣言します。
methods: { drop: function(event) { let fileList = event.dataTransfer.files; let vm = this for ( let i = 0; i < fileList.length; i++ ) { let reader=new FileReader() reader.onload=function(e){ let geojson = JSON.parse(reader.result) vm.geojson = geojson } reader.readAsText(fileList[i]) } }, }drop内の無名関数の引数eventにはドロップされたファイルの情報などが含まれています。File APIにより、ドロップされた.geojsonファイルからgeojson形式のオブジェクトを取得します。File APIについての解説はここでは省きます。
さて、ここでlet vm = thisこの文の意味ですが、本当ならfor文内でもvueコンポーネントをthisで呼び出したい訳ですが、スコープが(良い言葉が思いつきませんがイメージ的には)一段深くなっており、thisで参照出来ません。そこで、thisでコンポーネントを参照出来るうちにvmという変数で保持している訳です。
さて、FileAPIでの読み込みが完了するとlet geojson = JSON.parse(reader.result) vm.geojson = geojsonこのとおり、vueコンポーネント内の、data内の、geojsonに、たった今File APIで取得したGeoJson型オブジェクトを突っ込みます。するとl-geo-jsonのgeojsonはリアクティブなので地図に地物が追加されます。
地物ごとの処理(onEachFeature)
l-geo-jsonのoptionsは、その他のコンポーネントと異なり、v-bindで:optionsに、オブジェクトをまとめて渡してやらなければなりません。
<!-- l-geo-json内 --> :options="options"
//data()内 options: { onEachFeature: function(feature, layer) { layer.options.smoothFactor = 2; } },この例では、LeafletにおけるL.GeoJSONでおなじみのonEachFeature()を設定しています。地物ごとに個別の処理を行える関数です。ここでは、各地物の描画を簡素化しています。
まとめ
とてもとても長くなってしまいましたが、vue2-leafletの使い方を色々まとめました。どうやら私は、MapboxでもなんでもGISフレームワークの勉強の際は、とりあえず手持ちのGeoJsonやらを表示させるまでをチュートリアルと考えているフシがあります。vue.jsは使い始めですが、すげぇ便利だなって…。ここ数ヶ月はコードを書くばかりで知識のアウトプットもとい備忘録の作成を怠っていたため、来たるアドベントカレンダーへ向け、溜まっている下書きを清書していきたいです。
- 投稿日:2019-11-27T21:26:32+09:00
vue2-leafletの使い方、GeoJsonデータの表示・操作など
はじめに
ウェブ上で簡単に地図を表示する人気ライブラリ「Leaflet.js」をVue.js環境でお手軽に使えるラッパーライブラリ「vue2-leaflet」を5日くらい色々いじって得た知見を本記事にまとめます。いじった結果のゴール地点は以下の画像のようなウェブアプリケーションです。①地図エリアに.geojsonファイルをドラッグドロップすると地図上に地物を表示、②読み込んだGeoJsonレイヤを一覧表示、③地物をクリックするとその地物の属性をすべて表示、という以上の三機能をVue.jsの特性を活かし実装しました。本記事ではvue2-leafletの導入・実装と、機能①に焦点を絞って解説したいと思います。
環境
- npm 6.12.0
- @vue/cli 4.0.5
- leaflet 1.6.0
- vue2-leaflet 2.2.1
「vue cli」環境で開発しました。「vue cli」環境構築については本記事では掲載しません(以下の記事が詳しいです)。
Vue.js #001 – Vue CLI 3で環境構築導入
npm install leaflet vue2-leafletインストール後、main.jsにてcssを読み込ませます
main.jsimport Vue from 'vue' import App from './App.vue' //ここから import { Icon } from 'leaflet' import 'leaflet/dist/leaflet.css' //ここまで // this part resolve an issue where the markers would not appear delete Icon.Default.prototype._getIconUrl; Icon.Default.mergeOptions({ iconRetinaUrl: require('leaflet/dist/images/marker-icon-2x.png'), iconUrl: require('leaflet/dist/images/marker-icon.png'), shadowUrl: require('leaflet/dist/images/marker-shadow.png') }); Vue.config.productionTip = false new Vue({ render: h => h(App), }).$mount('#app')以上で導入は完了です。
実装
すべて単一ファイルコンポーネントとして記載しています。
script
<script> import { LMap, LTileLayer, LControlLayers, LControlScale, LGeoJson, } from 'vue2-leaflet'; export default { name: 'MapPane', components: { LMap, LTileLayer, LControlLayers, LControlScale, LGeoJson, }, data() { return { center: [38, 140], zoom:5, options: { onEachFeature: function(feature, layer) { layer.options.smoothFactor = 2; } }, geojson: null, } }, </script>template
<template> <div class="mapPane" @dragover.prevent="dragover" @drop.prevent="drop" > <l-map :zoom="zoom" :center="center" :preferCanvas="true" > <l-control-layers position="topright" :collapsed="false" ></l-control-layers> <l-control-scale position="bottomleft" :imperial="false" :metric="true" ></l-control-scale> <l-tile-layer name="MIERUNE MONO" visible="true" url="https://tile.mierune.co.jp/mierune_mono/{z}/{x}/{y}.png" attribution="Maptiles by <a href='http://mierune.co.jp/' target='_blank'>MIERUNE</a>, under CC BY. Data by <a href='http://osm.org/copyright' target='_blank'>OpenStreetMap</a> contributors, under ODbL." layer-type="base" ></l-tile-layer> <l-geo-json :geojson="geojson" :options="options" :options-style="styleFunction" @click="onFeatureClick" ></l-geo-json> </l-map> </div> </template>長いので分解してみましょう。
まずLeafletマップ本体はl-mapです。l-mapの内側に、必要なUIパーツや表示したいレイヤーを記述します。<l-map :zoom="zoom" :center="center" :preferCanvas="true" > <!-- ここにタイルレイヤーだとかコントロールだとかを追加する --> </l-map>素のl-mapだけだと背景地図すら表示されません。なのでまずタイルレイヤーを追加してみましょう。タイルレイヤーはl-tile-layerをl-map内に記述する事で追加されます。
<l-tile-layer name="MIERUNE MONO" visible="true" url="https://tile.mierune.co.jp/mierune_mono/{z}/{x}/{y}.png" attribution="Maptiles by <a href='http://mierune.co.jp/' target='_blank'>MIERUNE</a>, under CC BY. Data by <a href='http://osm.org/copyright' target='_blank'>OpenStreetMap</a> contributors, under ODbL." layer-type="base" ></l-tile-layer>このとおり、l-map内に各種コンポーネントを追加記述して、望む機能をもつ地図をつくれる訳ですね。おなじみのレイヤーコントロール(レイヤー一覧、L.control.layers)は、前述の例のとおりl-control-layersで実装できたりと、基本的な機能をvueの記法で実装出来ます。そういったビルトインのUIパーツなどはおそらく実装には困らないと思いますので、本記事ではGeoJsonレイヤーの取扱いについて掘り下げていきたいと思います。
GeoJsonレイヤーの取扱
実装
<l-geo-json :geojson="geojson" :options="options" :options-style="styleFunction" @click="onFeatureClick" ></l-geo-json>l-geo-jsonをl-map内に追加します。v-bindでgeojsonオブジェクトを渡してやる必要があります。ここで、このgeojsonはリアクティブです。つまりdata内のgeojsonの変更がマップに即時反映されます。vue2-leafletの前にvue-mapboxで遊んでいて、同様にgeojsonレイヤーのコンポーネントはあるのですが、リアクティブではありませんでした。この一点だけでもLeafletを優先して使う価値があると思います(GeoJsonビューア愛好家として)。
という訳でまずは.geojsonファイルのドラッグドロップ機能を実装しましょう。
<div class="mapPane" @dragover.prevent="dragover" @drop.prevent="drop" > <!-- l-mapなどなど --> </div>vueでドラッグドロップイベントを実装する場合はdragoverとdropをv-onで書きます。ここではドロップ時にdropというメソッドを実行せよ、という意味になります。.preventはブラウザの基本機能の実行を防ぐ構文です(例:ファイルをドロップすると、ブラウザ自体がそのファイルを開こうとするため)。さてここで、ドロップイベントだけ監視したいのだから、dragoverは不要ではないか?と考えると思います。しかしながらそれでは動作しません。おそらくdragover時にブラウザ処理が先行してしまうから(.preventが走らないから)だと思います。
さて、dropメソッドは、script内のmethodsにて宣言します。
methods: { drop: function(event) { let fileList = event.dataTransfer.files; let vm = this for ( let i = 0; i < fileList.length; i++ ) { let reader=new FileReader() reader.onload=function(e){ let geojson = JSON.parse(reader.result) vm.geojson = geojson } reader.readAsText(fileList[i]) } }, }drop内の無名関数の引数eventにはドロップされたファイルの情報などが含まれています。File APIにより、ドロップされた.geojsonファイルからgeojson形式のオブジェクトを取得します。File APIについての解説はここでは省きます。
さて、ここでlet vm = thisこの文の意味ですが、本当ならfor文内でもvueコンポーネントをthisで呼び出したい訳ですが、スコープが(良い言葉が思いつきませんがイメージ的には)一段深くなっており、thisで参照出来ません。そこで、thisでコンポーネントを参照出来るうちにvmという変数で保持している訳です。
さて、FileAPIでの読み込みが完了するとlet geojson = JSON.parse(reader.result) vm.geojson = geojsonこのとおり、vueコンポーネント内の、data内の、geojsonに、たった今File APIで取得したGeoJson型オブジェクトを突っ込みます。するとl-geo-jsonのgeojsonはリアクティブなので地図に地物が追加されます。
地物ごとの処理(onEachFeature)
l-geo-jsonのoptionsは、その他のコンポーネントと異なり、v-bindで:optionsに、オブジェクトをまとめて渡してやらなければなりません。
<!-- l-geo-json内 --> :options="options"
//data()内 options: { onEachFeature: function(feature, layer) { layer.options.smoothFactor = 2; } },この例では、LeafletにおけるL.GeoJSONでおなじみのonEachFeature()を設定しています。地物ごとに個別の処理を行える関数です。ここでは、各地物の描画を簡素化しています。
参考
公式ドキュメントですが、あまり詳しい事は書いてありません。あんまり複雑な事しないなら早いか。
ソースですが、ここにexampleが多数ありとてもかなり非常に参考になります。
まとめ
とてもとても長くなってしまいましたが、vue2-leafletの使い方を色々まとめました。どうやら私は、MapboxでもなんでもGISフレームワークの勉強の際は、とりあえず手持ちのGeoJsonやらを表示させるまでをチュートリアルと考えているフシがあります。vue.jsは使い始めですが、すげぇ便利だなって…。ここ数ヶ月はコードを書くばかりで知識のアウトプットもとい備忘録の作成を怠っていたため、来たるアドベントカレンダーへ向け、溜まっている下書きを清書していきたいです。
- 投稿日:2019-11-27T21:00:19+09:00
【初心者チャレンジ】BMIを計算する Herokuで実装
axios と javascript を使ってサイトをHerokuで実装
今週はHerokuを使ってサイトの実装をしてみたいと思います。
参考資料
【資料1】【喉頭がんの治療プロトコルをNode.jsでVueに表示しHerokuにデプロイ
環境
Node.js v10.16.3
Windows 10 pro
Visual Studio Code v1.39.1概要
フォルダを作成し、中に >node_modules:node.jsのデータが入っているフォルダ
>public:htmlデータを入れるフォルダ
>index.html:サイトを構成する静的ファイル
>index.js:作成したpublicの静的ファイルをexpressで表示させるコード
>gitignore:herokuで実装する時に不要なデータを送らないよう指定するファイル
>package-lock.json:npm init -yで作成される
>package.json:npm init -yで作成される
インストールしたライブラリデータ等のパッケージが登録されている
>Procfile:Herokuを起動するのに必要なファイル上記のファイルを作成し、Herokuで実装。
なんだかフォルダ名が楽天となっているのは本当は別のものを実装しようとしていて心折れた形跡です。今回のコードを起動必要なライブラリは
npm init -y npm i body-parser expressをそれぞれターミナルに入力し、インストール。
index.html
<!DOCTYPE html> <html> <head> <title>Step 01</title> <script src="https://unpkg.com/vue"></script> </head> <body> <h1>あなたのBMI</h1> <script> // 体重の数値を得る let weight; weight = prompt(`BMIを測定します。まずはあなたの体重(kg)を入力して下さい`); // 身長の数値を得る let height; height = prompt(`BMIを測定します。次にあなたの身長(m)を入力して下さい`); //体重と身長からBMIを計算して、警告ダイアログに表示する let bmi = weight / (height * height); let message = `あなたのBMIは「` + bmi + `」です。`; alert(message); console.log(message); </script> </body> </html>gitignore
# Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* # Runtime data pids *.pid *.seed *.pid.lock # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage # nyc test coverage .nyc_output # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) .grunt # Bower dependency directory (https://bower.io/) bower_components # node-waf configuration .lock-wscript # Compiled binary addons (https://nodejs.org/api/addons.html) build/Release # Dependency directories node_modules/ jspm_packages/ # TypeScript v1 declaration files typings/ # Optional npm cache directory .npm # Optional eslint cache .eslintcache # Optional REPL history .node_repl_history # Output of 'npm pack' *.tgz # Yarn Integrity file .yarn-integrity # dotenv environment variables file .env # next.js build output .nextindex.js
var express = require('express'); var app = express(); // public というフォルダに入れられた静的ファイルはそのまま表示 app.use(express.static(__dirname + '/public')); // bodyParser var bodyParser = require('body-parser'); app.use(bodyParser.json()); app.post('/post', function(req, res) { for (key in req.body) { console.log(key, '=', req.body[key]); } res.end(); }); app.listen(process.env.PORT || 8080); console.log("server start! (heroku)");Procfile
web: node index.jsこちら、Procfileをindex.jsと同じフォルダに置かなければHerokuをうまく使えないようですので、作成。
Herokuの実行
あとはHerokuを実行して繋げる。
完成
リアルな数字はみっともないので適当な数字を、、、
身長も体重も盛り盛りです。
良ければ一度自分のBMIを試してみてくださいw完成
、、、と見せかけて。
実は先程しれっと飛ばしたherokuの実行、出来ておりません。
既にばれていたかもしれませんが
しっかりローカルで起動しております。
エラーばっかりで諦めて後まわしていたのですがひとつうまく行ったので自白を。。。まずherokuの起動にあたって次を順番にターミナルに入力。
①git init②git add --a③git commit -m "commit"④heroku create s191127-sample //heroku create 自分のアプリ名⑤git push heroku master①~⑤の手順で順番に入力すればうまくURLを取得できるはずが④でエラー。
コミットできず。。。Creating ⬢ s191127-sample... !
! You've reached the limit of 5 apps for unverified accounts. Delete some apps or add a credit card to verify your account.英語は読めん、、、が何やらクレジットカード的なサムシング。。。
色々調べると
【heroku create のエラー】
なんと無料herokuでは5つまでしか使えないらしい!!
そもそも5つもアプリ作った覚えない…と思いつつ疑いながらもリンクサイトを参考に下記をターミナルに入れて試してみるとheroku apps //自分の全アプリが表示される作った覚えのないアプリがきっちり5つ表示されました(笑)
heroku destroy --app 消したいアプリ名もう一度しつこく聞いてくるので
消したいアプリ名だけを再入力し削除!やったね!!
本当に完成!!!
https://s191127-sample.herokuapp.comありがとうございました!!
- 投稿日:2019-11-27T20:41:46+09:00
知ってるとかっこよくなれるJS小技集2019
はじめに
コードレビューでさらっと
「こんな書き方もできますよ」って言ってくれる先輩ってかっこいいですよね。
いつかそんな先輩になれるように、メモ化しておこうという試みです。クリスマスのお供にどうぞ。記法
変数名が同じなら省略できる
const params = { id: id name: name }const params = { id, name }{}で分割代入
似てるものでこういう省略系もあります。分割代入と言うらしいです。
const id = response.id;const {id} = response;複数もOK
const {id, name} = response;実務でよくみる使い方だと、Reactでpropsから値取り出すときとかでしょうか。
const {width, height} = props; return ( <div style={{ width, height }} /> )スプレッド演算子...で展開できる
const hoge = { id: fuga.id, name: fuga.name, type: fuga.type }const hoge = { ...fuga }展開してさらに追加してもOK
const hoge = { ...fuga, date: Date.now() }実例だと、ReduxでStateを一部更新するときなんかに使えますね
[HogeAction.SOMETHING]: (state: IHogeState, action: any): IHogeState => ({ ...state, fuga: action.payload })オブジェクトをコピーするためにも使えますが、Object.assign同様ディープコピーではないので注意です。
const original = { id: 1, obj: { name: "shwan" } }; const copy = { ...original }; console.log(copy.obj.name); // "shwan" original.obj.name = "changed"; console.log(copy.obj.name); // "changed"オブジェクトのディープコピー技だとこんなのもありますが、undefindを扱えない罠があったりするので、できるだけ避ける人生を歩みたいですね。
const copy = JSON.parse(JSON.stringify(original));スプレッド演算子に話をもどすと、引数でも展開できたりします。使う場面はあまりなさそうですが。
doSomething(fuga.id, fuga.name, fuga.type);doSomething(...fuga);条件分岐
論理演算子 || で条件分岐
let name; if (response.name) { name = response.name } else { name = 'Guest'; }const name = response.name || 'Guest';nullかもしれない配列を空配列にするときによく使います
response.names.forEach() // response.namesがnullやundefindだとエラーconst arr = response.names || []; arr.forEach()論理演算子 && で条件分岐
if (isGuest) { doSomething(); }isGuest && doSomething();この記法、
if( A && B )
if( A || B )
なときと違う動きをしてるようにも見えますが
&&
は 左が偽なら左の値を、真なら右の値を返す
||
は 左が真なら左の値を、偽なら右の値を返す
という共通の仕組みです。const name = response.name || 'Guest';の例では
response.name
が'shwan'
などの文字列であれば左が真とみなせるので、左の値が返り、''
やnull
であれば、偽なのでGuest
が返ります。キャスト
!! でのbooleanキャスト
const isSelected = this.state.type ? true : false;const isSelected = !!this.state.type;この記法も!を2つつなげると
if(!isSelected)
なときと違う動きをしているようにみえますが、
!
は 値を真偽値とみなしたうえで真偽を反転する という動きでどちらも共通です。+ でのnumberキャスト
const id = '1' console.log(+id); // 1console.log(new Date()); // Wed Nov 27 2019 21:00:38 GMT+0900 (日本標準時) console.log(+new Date()); // 1574856056863キャストできなければ
NaN
になるので使い所は見きわめましょう。
ちなみにnew DateはDate.now()を使えばキャストせずとも数値で出せます。console.log(Date.now()); // 1574856056863letしない
三項演算子でletの回避
let type = 'a'; if (isB) { type = 'b'; }const type = isB ? 'b' :'a';即時関数でletの回避
複雑な分岐なら即時関数を使って回避することもできますね。このあたりは可読性とトレードオフでしょうか。
const type = ((flag) => { if (flag) { return 'a'; } else { return fukuzatuNaSomething(); } })(isB);その他Tips
関数の引数に初期値を設定する
初期値をつけておけば条件分岐を減らせます
const doSomething = (a, b = 1) => a * b; doSomething(1); // 1 doSomething(1, 2); // 2someで繰り返し処理を途中で抜ける
arr.forEach((obj) => { doSomething if(shouldBreak){ //break; ←できない } })arr.some((obj) => { doSomething if (shouldBreak) { return true; } return false; })someで配列の中を調べる
includes代わりに使えます
const arr = ['a', 'b', 'c']; arr.includes('b') // trueconst arr = [{ name: 'shwan' },{ name: 'taro' }]; arr.some((person) => person.name === 'shwan') // true対象が配列なのか調べる
Array.isArray(target)対象がオブジェクトなのか調べる
Object.prototype.toString.call(target).slice(8, -1).toLowerCase() === 'object';ここでいうオブジェクトは
{id: 1, name:'a'}
こういうのです。配列のようにスッキリは書けないので一工夫必要になります。そろそろネタがなくなって極小ネタになってきたので、また来年に向けて溜め直してきます!
- 投稿日:2019-11-27T20:36:13+09:00
関数型言語に爪先レベルで入門したので、まずはJavaScriptで値をイミュータブル(不変)に扱ってみる
Mikatus Advent Calendar 2019 3日目の記事。
最初に
こんにちは。
普段はTypeScriptでVue.jsを書いているのですが、関数型言語を勉強しようと、最近社内で
すごいHaskellたのしく学ぼう!
通称すごいH本
の勉強会を立ち上げました。Haskellの特徴は色々あると思いますが、カリー化とか部分適用とか、JavaScriptに活かすのはなかなか難しそうだなと感じるものが多いので、わかりやすく効果がありそうな、
値をイミュータブルに扱う方法
について紹介していきます。私自身JavaScriptも入門レベルなので、紹介する内容は基礎的なものですが、同じように最近JavaScriptに入門した人がいたら参考にしてみてください。
では参ります。
イミュータブルのメリット
まずイミュータブルとは、Wikipediaさんによると、
イミュータブル (英: immutable) なオブジェクトとは、作成後にその状態を変えることのできないオブジェクトのことである。
イミュータブル - Wikipediaらしいです。
プログラミング関係の言葉をWikipediaで調べると難解なことが多いのですが、これは比較的わかりやすいですね。
この定義ではわかりづらいという場合でも、後に出てくる実際のコードを見ればなんとなく理解できるのではないかと思います。
で、イミュータブルにプログラミングするメリットですが、
- 不測の値が混入するのを防ぐ
- 値が置き換えられてないかいちいち確認する手間や心理的負荷がなくなる
- プログラムの保守性が上がる
あたりが考えられ、特にチームで開発する上では大切なことだと感じています。
私自身独学期間を経て、今年エンジニアになったばかりなのですが、一人で開発する時には意識することもなかった、他のメンバー・将来のメンバーの負荷を減らすコードの必要性を実感しています。
というわけで、例外はあると思いますが、基本的に値はイミュータブルな方が良いのではないでしょうか。
では、これらのメリットを享受するため、JavaScriptでは実際どう書くのかを3つ見ていきましょう。
再代入しない
第一に、一度変数を宣言したら、再代入しないということです。
JavaScriptでは変数を宣言する時に、
var
、let
もしくはconst
が使えます。
var
はよっぽどの例外がない限り、使わないという方向で問題ないでしょう。
私にはよっぽどの例外が思いつきません。残るは、
let
とconst
ということになりますが、
- letは再代入可能
- constは再代入不可
になります。
実際のコードで見ていきましょう。
まずは
let
で変数を宣言した場合です。let.jslet firstName = 'Ai' let lastName = 'Katou' firstName = 'Kai' lastName = 'Atou' let fullName = `${firstName} ${lastName}` console.log(fullName) // Kai Atou // 書き換えられている!加藤あいが阿藤快に書き換えられてしまっています。
次に
const
です。const.jsconst firstName = 'Lewis' const lastName = 'Ann' firstName = 'Rice' // TypeError: Assignment to constant variable. // 再代入しようとするとTypeErrorに const fullName = `${firstName} ${lastName}` console.log(fullName) // Lewis Annが必ず表示される const obj = { number: 11, firstName: 'Hinata', lastName: 'Kashiwagi' } obj.number = 100 console.log(obj) // {number: 100, firstName: 'Hinata', lastName: 'Kashiwagi'} // あくまで再代入の禁止で、オブジェクトのプロパティ変更は可能なため注意アン・ルイスを半ライスに書き換えようと思ったのですが、できませんでした。
というわけで変数の宣言には
const
を使っていきましょう。
JavaScriptを本格的に書き始めて2ヶ月弱ですが、let
での宣言が必要になる機会はほぼないという印象です。なお、
const
でもあくまで再代入ができなくなるだけであって、オブジェクトのプロパティは変更できてしまうので注意しましょう。次行きます。
配列をイミュータブルに扱う
今度は配列をイミュータブルに扱う方法です。
配列の操作には
破壊的操作
と非破壊的操作
が存在します。配列に対して
破壊的操作
を行うと、操作を行なった配列が直接変更されてしまい、変更前の値を参照することができなくなります。対して、
非破壊的操作
は元の配列はそのままに新しい配列を返すので、変更前の値も参照することができます。実際のコードで見ていきましょう。
array.jsconst array = [3, 5, 6, 10, 11, 12] const sortedArray = array.sort((a, b) => b - a) console.log(array) // [ 12, 11, 10, 6, 5, 3 ] // 元の配列もソートされてしまっている!sortメソッドは
破壊的操作
なので元の配列もソートされてしまいます。
sortメソッドは便利なので、ぜひ使いたいところですが、このままではイミュータブルな状態を実現できません。なので下記のように書いていきましょう。
array.jsconst array = [3, 5, 6, 10, 11, 12] const copiedArray = [...array] // 元のarrayを変更しないようにスプレッド構文で一旦コピーする const sortedArray = copiedArray.sort((a, b) => b - a) console.log(array) // [ 3, 5, 6, 10, 11, 12] // ソート前の配列も呼び出せる console.log(sortedArray) // [ 12, 11, 10, 6, 5, 3 ]sortメソッドを適用する前に配列をコピーしました。
一手間かかるだけのように思えますが、こうすることで上に挙げたメリットを享受できます。array.jsconst array = [3, 5, 6, 10, 11, 12] array.push(13) // pushも元の配列を変更する const addedArray = [...array, 13] // スプレッド構文を使って要素が追加された新しい配列を作るここでは
sort
とpush
メソッドを例に挙げていますが、破壊的操作は他にもあります。配列にメソッドを適用しようと思ったら、それが
破壊的
か非破壊的
かを確認してみましょう。次行きます。
オブジェクトをイミュータブルに扱う
配列に続いて今度はオブジェクトです。
再代入しないの項目でも少し触れましたが、オブジェクトについては
const
で宣言しようとプロパティの変更ができてしまいます。ここもイミュータブルに扱いたいところです。
実際のコードで見ていきましょう。
object.jsconst obj = { number: 11, firstName: 'Hinata', lastName: 'Kashiwagi' } obj.color = 'orange' // obj[color] = 'orange' も同様 console.log(obj) // { number: 11, firstName: 'Hinata', lastName: 'Kashiwagi', color: 'orange' } // 変更されてしまっていて、変更前の状態が参照できない const addedObj = { ...obj, color: 'orange' } // スプレッド構文を使って追加する console.log(addedObj) // { number: 11, firstName: 'Hinata', lastName: 'Kashiwagi', color: 'orange' } console.log(obj) // { number: 11, firstName: 'Hinata', lastName: 'Kashiwagi' } // 元の状態も参照できる元のオブジェクトのプロパティを直接変更するのではなく、
スプレッド構文
を使って新しいオブジェクトを作っています。
スプレッド構文
便利ですね。ただ、この例については、TypeScriptを導入しているなら、型をちゃんと定義することで、後からプロパティの追加とかができないようにする方がいいと思います。
最後に
値をイミュータブルに扱う方法を3つ紹介しました。
意識したことがなかった人がいたら、ぜひ意識して今回紹介した方法などを試してみてください。
すごいH本勉強会では、まだモナド等には触れていないので、万が一モナドを理解できたらJavaScriptを書く際にも活かせないか模索したいと思います。
それでは!
- 投稿日:2019-11-27T20:02:20+09:00
[FULL STATIC] Nuxt generate<4つのアプローチまとめ>
最近ContentfulとNetlify、Nuxt.jsを利用してブログを立ち上げました。
ヘッドレスCMSとNuxt generateで静的なサイトを!と思ったのですが、実際にはクライアントからのアクセスのたびにContentfulへの通信が発生していました。
理由は明らかです。
asyncDataでContentfulからのデータ取得処理を行っているからです。サーバー側だけでなく、クライアント側でも再度取得処理が実行されてしまっているのですね...。Contentfulの記事更新時にNetlify上で generate⇒デプロイ まで自動で行うように設定しているので、記事は常に最新です。クライアントが新着問い合わせを行う必要はありません。「不要なアクセスがあるのは気持ち悪いな〜」と思って調べてみました。
この記事のまとめ
はじめに結論をまとめておきます。
通信を減らす・無くす手法は大きく4つに分けられます
A.nuxtServerInitで全データ取得&JSONに保存。
非常に直感的な手法です。完全に0にはできませんが、そこそこ減らせるはずです。個人ブログなどの場合はこれでも良いかもしれません。tagやキーワード検索などの機能が豊富なCMSも多いのですが、vueのfilterなどでリアルタイム検索すれば十分そうです。ただし、不要な通信は依然として残ります。
B.window.__NUXT__からJSONに保存。
nuxt generateで吐き出されたhtmlには window.__NUXT__ からはじまる箇所にJavaScriptが記載されています。また、サーバーサイドで取得したデータも含まれています。そのデータをJSONにキャッシュして利用しようという考え方です。
こちらのアプローチを取っているのはnuxt-payload-extractorです。下記で軽く触れますが、詳しくは実装を参考ください。asyncDataで取得している場合はシンプルに導入できます。ただし、fetchやstore(ひとまとめにvuex)では不具合が起こる可能性があるとreadmeに記載されています。実は、full staticなgenerateはネイティブでの実装も検討されています。その1つの方策としても魅力的なようですが、課題もあるようです。
C.APIリクエスト時にJSONに保存。
こちらも直感的ですね。個人でのアプローチ例も多い手法です。
nuxt-staticではプラグインとしてaxiosリクエスト時にキャッシュしています。
また、下記で触れますがQiitaでも2例紹介されています。
トラブル時にも原因を突き止めやすそうで安心ですD.htmlからJavaScriptを削除。
正反対かつド直球なアプローチ。htmlからscriptを削除してしまいます。セキュリティの観点からがきっかけですが、以下のブログ記事でcheerioを使用した方法が紹介されています。
Nuxt.js Generate後の<script>window.__NUXT__=を消したいさらにパフォーマンスを求めるなら
JSONデータをキャッシュする場合(A,B,C)
⇒下記Qiita記事で紹介されているように、webworkerでクライアント側にもキャッシュさせる。
Firebase、Flamelink、Nuxt、Netlify、PWAを使ってJAMstackなブログを作るhtmlからJavaScriptを削除する場合
⇒AMPの導入。
以降ではGithub Issue、Qiita、ブログ記事の3つのソースから上記の4つの方法をもう少し詳細に紹介します。
目次
- 1.Github Issueから(generate時にJSONに保存)
- 2.Qiitaから(generate時にJSONに保存)
- 3.ブログ記事から(htmlからJavaScriptを削除)
1.JSONに保存(Github Issueから)
Full static generated mode #22で静的なgenerateについて議論されています。
別の形で解決しましたが、以前にもnuxt generateには不要なJavaScriptがあるというIssueは上がっていました。現在は上記のIssueが中心のようです。(以前のIssue:Lots of unnecessary JavaScript in generated Nuxt static build)
Full static generated mode #22では
nuxt generate --full-staticという新しいオプションも提案されています。
ただし、queryの処理など課題も多いようです。Issueでは個人での対処例が2つ紹介されています。
どちらもJSONファイルにデータを保存し、
if(process.static && process.client)で条件分岐し、クライアントサイドでの通信であれば、JSONファイルからのフェッチに変更しています。
仕組みは非常にシンプルなので、それぞれ簡単に紹介します。
stursby/nuxt-static
axiosでの通信時に条件分岐。JSONとして保存しておき、ベースURLを変更。
src/plugins/axios.jsのファイルがメインの処理部分です。以下Githubからの引用にコメントを追記したものです。src/plugins/axios.jsimport axios from 'axios' let baseURL = 'https://jsonplaceholder.typicode.com' if (process.browser && process.static) { baseURL = '/data' //クライアントサイドではaxiosのbaseurlを変更していますね! } const api = axios.create({ baseURL }) //クライアントでの実行時 if (process.browser && process.static) { //interceptorsでリクエスト前にリクエスト先のurlを書き換えています api.interceptors.request.use(config => { config.url = config.url + '.json' return config }) } //サーバーサイドでの実行時 if (process.server && process.static) { const mkdirp = require('mkdirp-promise') const { join, dirname } = require('path') const { writeFileSync } = require('fs') api.interceptors.response.use( async function(response) { // Do something with response data //envファイルで予め格納先を指定しておいて、リクエストのパス名で取得データを保存 const path = join(process.env.dataDir, response.request.path + '.json') console.log('Save', path) await mkdirp(dirname(path)) writeFileSync(path, JSON.stringify(response.data)) return response }, function(error) { // Do something with response error return Promise.reject(error) } ) } export { api }nuxt-payload-extractor
こちらはgenerate時にデータをタイムスタンプ付きでJSONファイルに保存しています。
以下moduleから抜粋nuxt-payload-extractor/lib/module.js//略 //生成されたhtmlから取得したデータが格納されている箇所を抜き出して保存 let extractPayload = function(html, route, base, timestamp){ let chunks = html.split('<script>window.__NUXT__=') let pre = chunks[0] let payload = chunks[1].split('</script>').shift() let post = chunks[1].split('</script>').slice(1).join('</script>') let path = route === '/' ? '' : route return { html: pre + '<script defer src="' + base + path + '/payload' + timestamp + '.js"></script>' + post, payload } } //略 //nuxt.hook this.nuxt.hook('generate:page', async page => { if(!this.nuxt.options.generate.subFolders) throw new Error('generate.subFolders should be true for nuxt-payload-extractor') if(blacklist && blacklist.includes(page.route)) return page //上記処理extractPayloadをgenerate時に呼び出しています let { html, payload } = extractPayload(page.html, page.route, base, timestamp) writePayload(payload, page.route, distDir, timestamp) page.html = html return page }) //略その後、asyncData内で処理を分岐しています。(※$payloadURLはmodule読み込み時に指定するblacklistです)
以下nuxt-payload-extractorのサンプルからです。
nuxt-payload-extractor/example/pages/extracted.vue//略 <script> export default { async asyncData({ $axios, $payloadURL, route, error }){ try { if(process.static && process.client && route.path !== '/'){ //route.path !== '/' - because this route is blacklisted for nuxt-payload-extractor let {data} = await $axios.get($payloadURL(route)) return data } let post = await $axios.$get(`/post.json`) return { post } //Or alternative way // let payload = {}; // if(process.static && process.client && route.path !== '/') // payload = await app.$axios.$get(document.location.origin + route.path + '/payload.json') // else // payload.post = await app.$axios.$get(`/post.json`) // // return payload } catch (e) { //略どちらも導入も簡単ですが、nuxt-payload-extractorを利用される際はreadmeの以下の注意点もご確認ください。
Caveats
There may be issues with vuex data requests and nested routes. Keep in mind that payload.json has not hash in its name, so it shouldn't be cached in browser.翻訳
注意事項
vuexデータリクエストとネストされたルートに問題がある可能性があります。 payload.jsonの名前にはハッシュがないため、ブラウザーにキャッシュしないでください。workerでブラウザキャッシュしたい場合は次項のQiitaが参考になるかと思います。
2.JSONに保存(Qiitaから)
上記2つの例と同様、JSONファイルへの保存というアプローチを紹介しているQiita記事があります。
1つめの記事はFirebase向けのCMS Flamelinkの紹介のほか、JAMstackの説明なども丁寧に解説されています。素敵な記事ですね。その一環でJSONファイルへの保存も紹介しつつ、また、webworkerでキャッシュすることによって更にパフォーマンスを上げていることが紹介されています。先述したnuxt-payload-extractorはブラウザにキャッシュできませんでしたが、パフォーマンスを求めるならこちらを参考にしたほうが良さそうですね。
2つめの記事は1つめの記事が参照されています。JSONへの保存・読み込み処理部分だけの記事なので参考にしやすいと思います。(なくすト.js)
3.直接htmlから削除する(ブログ記事から)
nuxt generateで生成されたhtmlには
<script>window.__NUXT__=からはじまる箇所にJavaScriptの処理が記載されています。先述した、nuxt-payload-extractorはこの記載箇所からデータを取得しているので、そちらのソースも参照するとわかりやすいかもしれません。(そのほかの例はaxiosから直接データを取得しています)
単純ですが、htmlからこの記載を削除してしまえば通信も発生しません。
サクッと導入するには以下の記事で紹介されているcheerioを利用した方法が手軽かと思います。Nuxt.js Generate後の<script>window.__NUXT__=を消したい
以下上記ブログ記事からの引用です。
nuxt.config.jsconst cheerio = require('cheerio') export default { // 省略 hooks: { 'generate:page': page => { const doc = cheerio.load(page.html); doc(`body script`).remove(); page.html = doc.html(); }, }, // 省略 }行っているのはnuxt.config.jsにcheerioで直接該当箇所を削除する処理を書くことだけです。アニメーションなどのために、クライアントサイドでJavaScriptを使う場合はscript全部ではなく、payload部分のみを削除すれば良さそうですね。(テストなどはしていません)
ケースごとのまとめ
リンクも再掲します。
case1 サクッと手軽になくしたい
htmlを軽くしたい。クライアントサイドのJavaScriptはいらない。既存のプロジェクトから不要なリクエストをとにかくサクッとなくしたい。セキュリティも気になる。
⇒D.直接htmlから削除する が手軽です。
Nuxt.js Generate後の<script>window.__NUXT__=を消したいcase2 webworkerやPWAは使わない
サクッと手軽になくしたい。webworkerやPWAは使わない。一番多そうなケースです。
⇒A.nuxtServerInitで全データ取得&JSONに保存。
⇒B.window.__NUXT__からJSONに保存。
⇒C.APIリクエスト時にJSONに保存。中でもB.nuxt-payload-extractorか、C.「Nuxtでビルド時にAPIを静的化して、完全にサーバーへのリクエストをなくすト」が導入しやすそうだなと思います。
case3 パフォーマンスを向上させたい
サイトのパフォーマンスを向上させたい。
⇒A.nuxtServerInitで全データ取得&JSONに保存。
⇒C.APIリクエスト時にJSONに保存。加えてwebworkerでキャッシュ。更にhtmlから不要な処理は削除しても良いかもしれません。
Firebase、Flamelink、Nuxt、Netlify、PWAを使ってJAMstackなブログを作る⇒更に更に、軽さを追求する場合は、AMPの導入を検討してみても良いかもしれません。
Nuxtは公式でAMPのサンプルもあげられています。nuxt.js/examples/with-amp/以上です。
至らない点あるかもしれません。不備などありましたら、ご指摘いただけるとありがたいです。
- 投稿日:2019-11-27T19:26:53+09:00
Primitives vs Objects
What is the difference between primitives and objects?
Variables holding primitives actually holds the value of the primitive inside the variable.
Variables associated to an object doesn't hold the object but holds a reference to where the object is stored. It points to the object.
// Primitives var a = 23; var b = a; a = 46; console.log(a); // 46 console.log(b); // 23 // Objects var obj1 = { name: 'John', age: 26 }; // New object is not created but rather a new reference to the same object is created var obj2 = obj1; obj1.age = 30; console.log(obj1); // 30 console.log(obj2); // 30 // Functions var age = 27; var obj = { name: 'Jonas', city: 'Lisbon' }; function change(a,b){ a = 30; b.city = 'San Francisco'; } change(age,obj); console.log(age); // 27 console.log(obj.city); // San FranciscoWhen a primitive is passed to a function as an argument, rather than the actual variable, a copy will be passed. This means that the variable passed will not be affected.
When an object is passed, it's actually the reference to the object that is being passed and that is why the change is made to the object.
- 投稿日:2019-11-27T18:45:49+09:00
競馬の回収率、ディープラーニングを使わなくても100%をいくらでも超えられる
煽ってすみません。まぁ後出しでいいんだったらですが。
例えば、今年の 11/24 までの、種牡馬、馬主、調教師、騎手別の単勝の配当を集計して、出走数>100、回収率>100、勝率>10% で抜き出して回収率の高い順に並べたのが以下の表です。
データは JRA-VAN のデータを使っています。(残念ながら有料ですが、1ヶ月は無料期間があるようです)
データを以下のアプリを使って mongodb に落としたあと、javascript で集計しています。無料です。私作です。手前味噌ですみません(汗;
ちなみに1カラム目の意味は以下のとおりです。
- hn は種牡馬
- bn は馬主
- ch は調教師
- ks は騎手
上の表にあるエピファネイアと松永幹夫調教師の回収率のグラフもつけておきます。
ちなみに上のアプリを使って構成した mongodb のデータを集計しやすい json に変換するためのスクリプトは以下のようになります。よかったら参考にしてください。
main.jsconst MongoClient = require( 'mongodb' ).MongoClient; const assert = require('assert'); function IsSiba( _ ) { switch ( _ ) { case "10" : case "11" : case "12" : case "13" : case "14" : case "15" : case "16" : case "17" : case "18" : case "19" : case "20" : case "21" : case "22" : case "51" : case "53" : case "54" : case "55" : case "56" : case "57" : case "58" : case "59" : return true default : return false } } const fs=require( 'fs' ) const masUM=JSON.parse( fs.readFileSync( 'um.json', 'utf8' ) ) function Get( db, p, cb ) { const pRA = Object.assign( { 'head.DataKubun': '7' }, p ) const pSE = Object.assign( { 'head.DataKubun': '7' }, p ) const pHR = Object.assign( { 'head.DataKubun': '2' }, p ) Promise.all( [ db.collection( 'RA' ).find( pRA ).sort( { 'head.MakeDate.Year': -1, 'head.MakeDate.Month': -1, 'head.MakeDate.Day': -1 } ).toArray() , db.collection( 'SE' ).find( pSE ).sort( { 'head.MakeDate.Year': -1, 'head.MakeDate.Month': -1, 'head.MakeDate.Day': -1 } ).toArray() , db.collection( 'HR' ).find( pHR ).sort( { 'head.MakeDate.Year': -1, 'head.MakeDate.Month': -1, 'head.MakeDate.Day': -1 } ).toArray() ] ).then( _ => { let RA = {} { _[ 0 ].map( _ => ({ Year : _.jvid.Year , MonthDay : _.jvid.MonthDay , JyoCD : _.jvid.JyoCD , Kaiji : _.jvid.Kaiji , Nichiji : _.jvid.Nichiji , RaceNum : _.jvid.RaceNum , GradeCD : _.GradeCD , SyubetuCD : _.JyokenInfo.SyubetuCD , KigoCD : _.JyokenInfo.KigoCD , JyuryoCD : _.JyokenInfo.JyuryoCD , JyokenCD : _.JyokenInfo.JyokenCD[ 4 ] , Kyori : _.Kyori , TrackCD : _.TrackCD , TenkoCD : _.TenkoBaba.TenkoCD , BabaCD : IsSiba( _.TrackCD ) ? _.TenkoBaba.SibaBabaCD : _.TenkoBaba.DirtBabaCD }) ).forEach( _ => { const key = _.Year + _.JyoCD + _.Kaiji + _.Nichiji + _.RaceNum if ( RA[ key ] ) console.error( 'RA DUP', key ) else { _[ 'SE' ] = [] RA[ key ] = _ } } ) } { let w = {} // Checking purpose only _[ 1 ].forEach( _ => { const raKey = _.jvid.Year + _.jvid.JyoCD + _.jvid.Kaiji + _.jvid.Nichiji + _.jvid.RaceNum const key = raKey + _.Umaban const data = { Umaban : _.Umaban , KettoNum : _.KettoNum , Sire : masUM[ _.KettoNum ].Sire , ChokyosiCode : _.ChokyosiCode , BanusiCode : _.BanusiCode , KisyuCode : _.KisyuCode , KakuteiJyuni : _.KakuteiJyuni , Odds : _.Odds } if ( w[ key ] ) console.error( 'SE DUP', key ) else { switch ( _.IJyoCD ) { case 1: case 2: case 3: break default: w[ key ] = data RA[ raKey ].SE.push( data ) break } } } ) } { let w = {} // Checking purpose only _[ 2 ].forEach( _ => { const key = _.jvid.Year + _.jvid.JyoCD + _.jvid.Kaiji + _.jvid.Nichiji + _.jvid.RaceNum if ( w[ key ] ) console.error( 'HR DUP', key ) else { RA[ key ].HR = _ } } ) } cb( Object.values( RA ).sort( ( p, q ) => p.Year == q.Year ? p.MonthDay == q.MonthDay ? p.RaceNum == q.RaceNum ? 0 : p.RaceNum < q.RaceNum ? -1 : 1 : p.MonthDay < q.MonthDay ? -1 : 1 : p.Year < q.Year ? -1 : 1 ) ) } ) } function Main( db ) { Get( db , { 'jvid.Year': { '$gte': '2016' } } , _ => { console.log( JSON.stringify( _, null, '\t' ) ) process.exit( 0 ) } ) } MongoClient.connect( 'mongodb://192.168.1.23:27017' , { useNewUrlParser : true , useUnifiedTopology : true } , ( err, client ) => { assert.equal( err, null ) Main( client.db( 'JRA-VAN' ) ) } )上で使う um.json を作成するための、Mongo Shell スクリプト
um.msconst _ = db.UM.find( null , { 'head.MakeDate': true, 'KettoNum': true, 'Bamei': true, 'Ketto3Info': true } ).sort( { 'head.MakeDate.Year': -1, 'head.MakeDate.Month': -1, 'head.MakeDate.Day': -1 } ); const dict = {} _.forEach( _ => { if ( ! dict[ _.KettoNum ] ) dict[ _.KettoNum ] = { Bamei : _.Bamei , Sire : _.Ketto3Info[ 0 ].HansyokuNum , Broodmare : _.Ketto3Info[ 1 ].HansyokuNum , BroodmareSire : _.Ketto3Info[ 2 ].HansyokuNum } } ); ObjectId.prototype.tojson = function() { return '"' + this.valueOf() + '"'; }; Date.prototype.tojson = function() { return '"' + this.toISOString() + '"'; }; printjson( dict );参考
- 投稿日:2019-11-27T18:36:14+09:00
Objects and Functions in Javascript
There is a saying that everything is an object in JavaScript...
Primitives
- Numbers
- Strings
- Booleans
- Undefined
- Null
Objects
- Arrays
- Functions
- Objects
- Dates
- and more...
Almost everything is an object in JavaScript and this is what differentiates JavaScript.
Object-Oritented Programming
Object Oriented Programming is a programming paradigm which heavily relies on usage of objects.
- Objects interact with one another with methods and properties
- Used to store data and structure applications into modules in order keep code clean
In JavaScript, classes might be reffered to as constructor or prototype
Inheritance
Inheritance is when one object is based on another object and has access to other object's properties and methods.
Prototypes
Prototype property of an object is where we put methods and properties that we want other objects to inherit.
Inheritance is possible in JavaScript thanks to the existance of Prototypes. **Every JavaScript object has a Prototype property. **
All objects in JavaScript is an instance of Object constructor / prototype.
Instance -> Occurance of an object.
Think of prototype as a blueprint or a class
Prototype Chain
Chain of prototypes. When an object doesn't have a method or a property, it will look at the parent prototype. If the Object prototype doesn't have it, it simply returns null.
Implementation
Function Constructor
var Person = function(name, yearOfBirth, job) { this.name = name; this.yearOfBirth = yearOfBirth; this.job = job; }; // Instatiation var john = new Person('John',1990,'teacher');The 'new' operator points the 'this' variable to the empty object created when the new operator was called instead of the global object. This is why we can have multiple instances of the same Prototype.
Prototype property
Person.prototype.calculateAge = function(){ console.log(2019-this.yearOfBirth); } john.calculateAge() // Outputs '29''calculateAge' method is attached to constructor function's prototype property.
A property can also be attached to prototype.
john.hasOwnProperty('job'); // true john.hasOwnProperty('calculateAge'); //false john instanceof Person; // true^ Checking if an instance has a specified property
Property for objects
Arrays
Arrays have array prototype
var x = [1,2,3]; // x has property of length x.length; // 3Another way to create an object : Object.create
This is another way to create an object. Also inherits from a prototype
Create an object that will act as a prototype
Create an object based on the prototype object
var personProto = { calculateAge: function() { console.log(2019 - this.yearOfBirth); } }; // Pass in the object that will act as the prototype. // Method 1 var john = Object.create(personProto); john.name = 'John'; john.yearOfBirth = 1990; john.job = 'teacher'; // Method 2 var jane = Object.create(personProto, { name: { value: "Jane" }, yearOfBirth: { value: 1969 }, job: { value: "designer" } });Object.create inherits from the prototype passed in as an argument whereas the function constructor inherits from the constructor's prototype property.
Object.create allows more complex inheritance.
Function constructor is more popular
- 投稿日:2019-11-27T17:34:13+09:00
Vuetifyのv-messagesのmin-heightを0pxにしたい
- 投稿日:2019-11-27T17:11:25+09:00
画像変更なPull Requestでちょっと便利なTips
Diverse Advent Calendar 2019 13日目の記事です。
突然ですが、Pull Requestちゃんとレビューできてますか?
先日私はやらかしました
本日はそのやらかし話とそれに対しての対策したお話しします。
序章 ~罠との遭遇~
まず、下記の図はお馴染みのGitHubのPull Requestのdiff画面です。
GitHubには画像差分を比較するために強い機能1を持っています。
しかし、デフォルトで画像の差分は展開されません。
見るためには、指定のボタン を押す必要があります。すると、削除された画像は、
Binary file not shown.
からDeleted file not shown.
に変わったことがわかります。本題はここからです。
変更画像がたくさんあった場合のPull Requestのとき、どうしましょう。
ボタン押さないと差分はわかりません。3こ、4こ程度ならボタンポチポチで大丈夫だと思います。
10こ辺りから悩ましくなります...意を決してポチポチ作戦を試みたものの、上記の画像みたいにファイル削除が続いた後に、「あー、全部削除なのねー」って気を抜いた次の瞬間、
やらかしましたね
第1章 ~罠に立ち向かう術~
画像変更差分が多いPull Requestが突きつけられたとき、ポチポチはめんどくさい。
そこで気づきました。
JSあるじゃん
<button class="btn btn-sm BtnGroup-item tooltipped tooltipped-w rendered js-rendered selected" aria-label="Display the rich diff" type="submit" data-disable-with="" aria-current="true">...</button>すると、
js-rendered
クラスが該当ボタン に割り当てられてそうな臭いを察知早速ボタンポチポチスクリプトを作成。
Array.from(document.getElementsByClassName('js-rendered')).forEach((b) => b.click());これをconsoleで実行すると、
第2章 ~それでもボク(自動化)はやってない~
なんとかボタンポチポチマンからスクリプトポチーマンまで進化しました。
しかし、便利になったとはいえPull Requestの都度スクリプトポチーはだるい...
そこで、 Tampermonkey の登場です。
Tampermonkeyとは
Tampermonkeyは、指定のページを表示時にユーザが組んだスクリプトを実行してくれる無料で使えるGoogle Chromeの拡張機能2です。
環境
- Google Chrome (78.0.3904.108)
- Tampermonkey (v4.9)
ユーザスクリプト
実行させるスクリプトを定義します。
// ==UserScript== // @name 画像差分を表示するくん // @namespace http://tampermonkey.net/ // @version 0.1 // @description try to take over the world! // @author You // @match https://github.com/*/*/*/files* // @grant none // ==/UserScript== window.onload = () => { Array.from(document.getElementsByClassName('js-rendered')).forEach((b) => b.click()); };スクリプト本体に関しては、先ほどのスクリプトを
window.onload
で実行するようにしました。そしてポイントは、ヘッダー内の
@match
に、スクリプト実行したいサイトのURLを定義します。34すると、GitHub File Changedの画面を開くと、自動で画像差分を表示してくれるようになりました!
最後に
今回、Tampermonkeyをご紹介しましたが、これ以外に似たような拡張機能を使っても同様のことは実現できると思います。
また今回実装したスクリプトは必要最低限の実装のみのため、変更ファイル数が多いPRだと遅延読み込みが発生して全ての差分が表示できない場合があるので、自分にあったスクリプトを書いて快適なレビューライフを送りましょう!参考
https://help.github.com/en/github/managing-files-in-a-repository/rendering-and-diffing-images#viewing-differences ↩
Google Chrome以外に、Firefox, Safari, Microsoft Edgeなどの主要ブラウザ用のプラグインもあります。 ↩
- 投稿日:2019-11-27T17:06:31+09:00
仮想DOMの時代にDOMを返すAPIを扱う(CSR編)
DMMグループ Advent Calendar 2019 12日目の記事です。
アドカレ映えしない、地味~な内容ですが、よろしくお願いします。
TL;DR
Angular / React / Vue製サイトにWebAPI経由で返されたDOMを挿入する
Angular React Vue 前書き
大規模Webサイトの各ページへ横断的に要素を挿入したい要件があるとします。
- ブランドロゴ、ナビゲーション、トラッキングタグなど
- 大規模なので、ページ間で管轄が分かれていたり、別々のアーキテクチャで構築されていたりする
- 要素の管轄は一部署に集約したい
これらの要件を満たす手段として、 DOM Stringを返すようなAPIが提供される ことがあります。
他の手段としては「DOMを構築・挿入するスクリプトを配布する」という方法もありますが、それと比べて以下のようなメリットが期待できます。
- 呼び出しタイミングや組み込み位置をある程度制御できる
- サーバサイド・クライアントサイドのどちらからでも呼び出せる
「クライアントサイドからでも呼び出せる」とはいえ、必ずしもすんなりできるとは限らず、特に昨今のJSフレームワークで開発されたWebサービスに組み込むにはハマりどころが色々あります。
今回はAngular, React, Vue製でクライアントサイドレンダリングするようなWebサイトにそのようなAPIを組み込む際の方法や注意を纏めています。
[全般]DomStringをDOMとしてrenderされるようにinnerHtmlラッパを使う
いずれのフレームワークもXSS対策のため、テンプレートやJSX中で使用される変数はHTMLエスケープされるようになっています。
そのため、APIレスポンスのDOM stringをそのままテンプレートやJSXへ書き出そうとしても、HTMLがプレーンテキストとして表示されるだけでHTML要素としては読み込まれません。
そこで、変数中のHTMLをHTML要素として表示するための機能が提供されておりますので、そちらを利用します。
大前提としてAPI側のXSS対策はなされているものとします。
Angular React Vue innerHTML dangerouslySetInnerHTML v-html [linkタグ]Angularの場合はlinkタグの扱いに注意
APIレスポンスの中に、スタイルシートを読み込むためのlinkタグが含まれる場合の話です。
React, Vueは上記の方法でlinkタグを出力することが出来るのですが、AngularではinnerHTMLでHTML要素を出力する際に、やはりXSS対策のためlinkタグやscriptタグが削除されるようになっています。
これを回避する方法も提供されており、DomSanitizerのbypassSecurityTrustHtmlメソッドを利用することでlinkタグを出力することができます。
[scriptタグ]innerHTMLではJSを実行出来ない
APIレスポンスにscriptタグが含まれる場合は厄介です。
innerHTML(やこれまで挙げたFWのinnerHTMLラッパ)を利用することで、DOMやスタイルを反映させることができますが、scriptタグ経由のJSを呼び出すことは出来ません。
これはW3CのHTML5 scriptタグの仕様として記述されています。
When inserted using the document.write() method, script elements execute (typically synchronously), but when inserted using innerHTML and outerHTML attributes, they do not execute at all.
これについては、フレームワーク側のサポートはなく、動的にJSを実行する方法などに記載されているように、
document.createElement('script');
でscript要素を作成、- domStringをパースしてscript要素を組み立てていき、
- 最終的に
document.body.appendChild
などで完成したscript要素を挿入するという、骨の折れる作業をする必要がありそうです。
[実践]ヘッダー・フッターでコンテンツを挟み込む
ここで少し具体的な話になります。
APIのレスポンスとして、複数のUI、例えばヘッダーとフッターが一遍に返されるような場合はどのように実装すればよいでしょうか。
この場合、ヘッダー・フッターを1つのコンポーネントとして扱うのが良さそうです。
各フレームワークとも、似たようなかたちで任意のコンテンツを挟み込むようなコンポーネントを実装出来ます。
Angular React Vue ng-content props.children slot 実装例 / Angular
app.component.html<navigation> <!-- サイトコンテンツ --> </navigation>navigation.component.html<div [innerHTML]='header'></div> <ng-content></ng-content> <div [innerHTML]='footer'></div>実装例 / React
App.js(内のJSX)<Navigation> <!-- サイトコンテンツ --> </Navigation>Navigation.js(内のJSX)<div dangerouslySetInnerHTML={{ __html: header }} /> {this.props.children} <div dangerouslySetInnerHTML={{ __html: footer }} />実装例 / Vue
index.html<navigation> <!-- サイトコンテンツ --> </navigation>template<div v-html="header"></div> <slot></slot> <div v-html="footer"></div>おわり
- 本記事はDOMを返すようなAPIをmanageする、というのがテーマであり、共通UIを提供する手段としてDOMを返すAPIの設計を推奨するものではありません。
- アプリケーションをSSRする場合は更なる課題がありそうですので、それはまた次の機会に。
- 明日は @uruha さんより、Virtual DOMの時代の楽しい話が聞けるんじゃないかと思うので、乞うご期待!
サンプルアプリの元ネタ
- 投稿日:2019-11-27T16:39:52+09:00
Emacs 26.3 で vue-mode の js パートのインデントが効かなくなる件の対処法
syntax-ppss-table
に何かが設定されているのが原因のようで、Issueにあった次の設定を追加したら直りました。(setq mmm-js-mode-enter-hook (lambda () (setq syntax-ppss-table nil))) (setq mmm-typescript-mode-enter-hook (lambda () (setq syntax-ppss-table nil)))参照
Indentation problems in <script> tags #74
https://github.com/AdamNiederer/vue-mode/issues/74#issuecomment-539711083
- 投稿日:2019-11-27T16:00:00+09:00
01. 「パタトクカシーー」
01. 「パタトクカシーー」
「パタトクカシーー」という文字列の1,3,5,7文字目を取り出して連結した文字列を得よ.
Go
package main import "fmt" func main() { var src string = "パタトクカシーー"; var des string = ""; // rune 型へ変換 msrc := []rune(src); // 1.ループで処理 for i := 0; i <= 7; i++ { // 問題では 1,3,5,7 指定だが奇数判定で対応 if (i % 2) != 0 { des += string(msrc[i]); } } fmt.Println(des); }python
# -*- coding: utf-8 -*- src = u"パタトクカシーー" des = "" # 1.ループで処理(range) reverse = "" for i in range(len(src)): # 問題では 1, 3, 5, 7 指定だが奇数判定で対応 if i % 2 != 0 : des += src[i] print(des) # -*- coding: utf-8 -*- src = u"パタトクカシーー" des = "" # 1.ループで処理(range) reverse = "" for i in range(len(src)): # 1, 3, 5, 7 指定だが奇数判定で対応 if i % 2 != 0 : des += src[i] print(des) # 2.スライスで処理(ステップを指定) print(src[1::2])Javascript
var src = "パタトクカシーー"; var dsc = "" // 1.ループで処理 for (var i = 0; i < src.length; i++) { // 問題では 1, 3, 5, 7 指定だが奇数判定で対応 if (i % 2 != 0) { dsc += src[i]; } } console.log(dsc);まとめ
単純なループ処理しか思いつかない。
他のロジックって有るのかな?。100本ノックの記事探してみる。
1,3,5,7 を渡すと、切り出してくれる標準関数とか有るのかな?。
- 投稿日:2019-11-27T13:54:45+09:00
jQueryで顔認識機能を実装できる「jQuery Face Detection Plugin」を試してみた
jQuery Face Detection Pluginを試してみた
いつの間にかJavaScriptで顔認識を実装できるような時代になっていたのでjQuery Face Detection PluginというjQueryライブラリを使ってみました。
ダウンロード
まず、公式サイトよりjQuery Face Detection Pluginをダウンロードします。
ダウンロードしたらjquery.facedetection.zipというファイルが落ちてきます。
解凍したら、jquery.facedetection.min.jsとjquery.facedetection.jsが入っているので好きな方を
bodyの閉じタグの上あたりに読み込ませます。読み込み
bodyの閉じタグの上に以下のスクリプトを読み込ませます。
HTML<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script> <script src="js/jquery.facedetection.min.js"></script> <script> // 顔認識の処理を書いていく場所 </script>プラグインで取得できる値
取得できる値についてはこちらのブログで丁寧に解説されていますが主に取得できる値は以下になります。
値 内容 x 画像の中の顔範囲のX座標 y 画像の中の顔範囲のY座標 width 顔範囲の横幅 height 顔範囲の縦幅 positionX 親要素内の顔範囲の横位置 positionY 親要素内の顔範囲の縦位置 offsetX ドキュメント内の顔範囲の横位置 offsetY ドキュメント内の顔範囲の縦位置 confidence 顔認識の信頼性を示すレベル値 顔認識のサンプルを試してみた
HTML
HTML<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>faceDetection</title> <style> .picture-container { position: relative; } .face { border: solid 3px #fff; } </style> </head> <body> <div class="picture-container"> <!-- 認識させたい顔の画像 --> <img id="picture" src="img/face.jpg"> </div> <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script> <script src="js/jquery.facedetection.min.js"></script> <script src="js/script.js"></script> </body> </html>JavaScript
JavaScript$('#picture').faceDetection({ complete: function (data) { for (var i=0; i<data.length; i++) { /*顔認識したところをボーダーで囲む*/ $('<div>', { 'class':'face', 'css': { 'position': 'absolute', 'left' : data[i].x * data[i].scaleX + 'px', 'top' : data[i].y * data[i].scaleY + 'px', 'width' : data[i].width * data[i].scaleX + 'px', 'height' : data[i].height * data[i].scaleY + 'px' } }).insertAfter(this); } }, error: function (code, message) { alert('Error: ' + message); } });検証結果
複数人でも認識するか?
なぜか一人だけ認識されない結果になりました…。
複数人いた場合、認識の精度はイマイチのようです。まとめ
実務で取り入れるには若干精度に不安が有りそうですが、遊びでやるには面白いかなと思いました。
ちなみにJavaScriptで顔認識のできるライブラリは他にもあってclmtrackr.jsというライブラリも良く使われており、そちらは顔の目や口などパーツ単位での認識も可能なようです。
- 投稿日:2019-11-27T13:33:16+09:00
WebRTCでストリームの音量を変更する
やりたかったこと
音声通話をするときに、マイクの音量を変更したい。
そのときにストリームの音量を変更して、通信相手にも変更した音量で音声が聞こえるようにしたい。MediaStreamの音量を変更する方法
- getUserMediaでMediaStreamを取得する
AudioContextを作成する。
AudioContextはユーザーの操作(クリックなど)があったときに生成しないとエラーやWarningがでたりする。audioContext = new (window.AudioContext || window.webkitAudioContext)();1で取得したMediaStreamを指定して、MediaStreamAudioSourceNodeを生成する。
MediaStreamAudioSourceNodeオブジェクトからの音声は再生や編集が可能になる。source = audioContext.createMediaStreamSource(stream);AudioContextからMediaStreamAudioDestinationNodeを生成する。
MediaStreamAudioDestinationNodeのstreamプロパティはMediaStreamと同じような使い方ができる。
このstreamをRTCPeerConnectionで送信することができる。audioDestination = audioContext.createMediaStreamDestination();AudioContextからGainNodeを生成する。
GainNodeで音声のボリュームを操作できる。gainNode = audioContext.createGain();3〜5で生成したNodeをconnectで接続する
audioDestination(MediaStreamAudioDestinationNode)のstreamで音量が変更されたstreamにアクセスすることができる。source.connect(gainNode); gainNode.connect(audioDestination);↓ Sample(スライダーで音量変更するだけ)
See the Pen gainNode-test by mossan245 (@mossan245) on CodePen.
WebRTCで通信相手に音量変更したStreamを送信する
送信しているMediaStreamの音声のトラックをMediaStreamAudioDestinationNodeのstreamのAudioTrackで置き換えることで実現できる。
Trackの置き換えにはRTCRtpSender.replaceTrack()を利用する。しかし、FFやSafariだとうまく音量が変更されたStramの送信がうまいくが、Chromeから送信すると受信側で音声が再生されなくなってしまう。
どうやらChromeにバグがあるらしく現状のバージョンだとうまく動かない。
そのため、Chromeの場合は別途音量変更を通知する仕組みを用意して、通信相手側で直接音量を変更する必要がある。参考
- 投稿日:2019-11-27T12:23:49+09:00
React + TypeScript でTodoリストを作りながら入門
昔Vueを勉強する前にチラっとReactをやったものの挫折して、それ以来触って無かった者です。
最近Reactは分かりやすくなってるとか、TypeScriptとの相性が良いとか聞いたので少しやってみることにしました。
今回は色々な資料を当たりながら最終的にシンプルなTodoリストを作成しました。
React用の型が追加される「@types/react」も使ってみます。
何か間違ってたら指摘お願いします。作るもの
追加と削除だけのとてもシンプルなTODOリスト。
コードを見てみる
Todoの型を決める
import * as React from "react"; import * as ReactDOM from "react-dom"; // *************************************** // Todoの型定義 // *************************************** interface Todo { id: number; name: string; }Todoは、idとnameというプロパティを持つ型だと決めている。
idはnumber型であり、nameはstring型である。Todoコンポーネントを作る
Todoリスト1つづつの部分である。
HTMLタグで言うと ”li” の部分に当たる。// *************************************** // Todoコンポーネント // *************************************** // TodoListItemPropsの型定義 interface TodoListItemProps { todo: Todo; onDelete: (todo: Todo) => void; } const TodoListItem: React.FunctionComponent<TodoListItemProps> = ({ todo, onDelete }) => { const onClick = () => { onDelete(todo); }; return ( <li> {todo.name} <button onClick={onClick}>Delete</button> </li> ); };TodoListItemコンポーネントは、「React.FunctionComponent」という型に従いそのPropsは「TodoListItemProps」という型に従うと決める。
「FunctionComponent」は自分では定義していない。
「@types/react」が提供してくれる型。interface FunctionComponent<P = {}> { (props: PropsWithChildren<P>, context?: any): ReactElement | null; propTypes?: WeakValidationMap<P>; contextTypes?: ValidationMap<any>; defaultProps?: Partial<P>; displayName?: string; }Macなら「Option + クリック」、winなら「ctrl + クリック」で定義されている型のところへ飛べるので、見てみるとこんな感じらしい。
うん、よく分からんけど関数のコンポーネントなんやろ(適当)
Todoリストコンポーネントを作る
フォームを含まないTodoリスト全体の部分。
HTMLタグで言うと ”ul” の部分に当たる。上記の「TodoListItem」の親になる。
// *************************************** // Todoリストの一覧表示部分 // *************************************** // Propsの型定義 interface TodosListProps { todos: Todo[]; onDelete: (todo: Todo) => void; } const TodosList: React.FunctionComponent<TodosListProps> = ({ todos, onDelete }) => ( <ul> {todos.map(todo => ( <TodoListItem todo={todo} key={todo.id} onDelete={onDelete} /> ))} </ul> );フォーム部分のコンポーネントを作る
ユーザーが入力するフォームや、追加ボタンの部分。
// *************************************** // Todo追加用フォーム // *************************************** // Propsの型定義 interface NewTodoFormProps { onChange: (event: React.ChangeEvent<HTMLInputElement>) => void; onAdd: (event: React.FormEvent<HTMLFormElement>) => void; todo: Todo; } const NewTodoForm: React.FunctionComponent<NewTodoFormProps> = ({ onChange, onAdd, todo }) => ( <form onSubmit={onAdd}> <input onChange={onChange} value={todo.name} /> <button type="submit">Add a todo</button> </form> );特筆するところはないけど、「React.ChangeEvent」「React.FormEvent」なども上記のように「Option + クリック」「ctrl + クリック」でどういう型なので見れるので興味があれば見てみると良いかもしれない。
Todoアプリ全体のコンポーネント
一番親の部分
// *************************************** // Todoリスト本体部分 // *************************************** // 状態(State)の型定義 interface State { newTodo: Todo; todos: Todo[]; } class App extends React.Component<{}, State> { state = { newTodo: { id: 1, name: "" }, todos: [] }; render() { return ( <div> <h2>React + TypeScript Todoリスト</h2> <NewTodoForm todo={this.state.newTodo} onAdd={this.addTodo} onChange={this.handleTodoChange} /> <TodosList todos={this.state.todos} onDelete={this.deleteTodo} /> </div> ); } // Todoの追加 private addTodo = (event: React.FormEvent<HTMLFormElement>) => { // デフォルトの送信機能を使わない event.preventDefault(); // 空なら処理を止める if (!this.state.newTodo.name.length) { return; } // TodoリストStateの更新、引数には現在(更新前)のStateが入ってくる this.setState(previousState => ({ newTodo: { id: previousState.newTodo.id + 1, name: "" }, // 現在のTODOの配列と、新しいTODOを結合する todos: [...previousState.todos, previousState.newTodo] })); }; // フォームの内容が変わったらnewTodoの内容も変える private handleTodoChange = (event: React.ChangeEvent<HTMLInputElement>) => { const inputedValue = event.target.value; this.setState({ newTodo: { ...this.state.newTodo, name: inputedValue } }); }; // Todoの削除 private deleteTodo = (todoToDelete: Todo) => { this.setState(previousState => ({ todos: [ ...previousState.todos.filter(todo => todo.id !== todoToDelete.id) ] })); }; } ReactDOM.render(<App />, document.getElementById("root"));まとめ
何だかReactは凄く難しいイメージがあったのですが、意外とシンプルで基本普通のJavaScriptに近い形で書けるのが良いと思いました。
またTypeScriptも趣味でチョロっとしか触ったこと無かったのですが、補完やコードジャンプやエラー通知が便利だなと実感出来ました!
ここ数ヶ月Swiftを勉強していたのですが、結構TypeScriptと似ている印象です。
以上
- 投稿日:2019-11-27T11:55:53+09:00
言語処理100本ノック 2015 をやってみた 00
言語処理100本ノック 2015 をやってみた
動機と目的
新たな言語を勉強してみるが使わないと忘れていく。
Qiita で 100本ノックの記事を見た。なるほど。やってみよう。
言語処理100本ノック 2015目標
毎日(たぶん)、数問を解くことでそれぞれの言語に慣れるようにしてみる。
Qiita に書くことで Markdown に慣れる。言語
- Go
- Python
- Javascript
を主にやってみる。
00.文字列の逆順
Go
package main import_ "fmt" func main() { var data string = "stressed" var reverse string // 1.ループ for i := len(data) - 1; i >= 0; i-- { reverse += string(data[i]) } fmt.Println(reverse) }python
# -*- coding: utf-8 -*- data = "stressed" reverse = "" # 1.逆カウントのループ i = len(data) - 1 while i >= 0: reverse += data[i] i-=1 print(reverse) # 2.range->reversed reverse = "" for i in reversed(range(len(data))): reverse += data[i] print(reverse) # 3.reversed->join print(''.join(reversed(data))) # 4.スライス print(data[::-1])Javascript
var data = "stressed"; var reverse = "" // 1.逆カウントのループ for (var i = data.length - 1; i >= 0; i--) { reverse += data[i]; } console.log(reverse); // 2.split->reverse->join console.debug(data.split("").reverse().join(""));まとめ
Javascript以外は日本語非対応。
後でマルチバイト対応予定。
- 投稿日:2019-11-27T11:55:53+09:00
00. 文字列の逆順
00.文字列の逆順
文字列"stressed"の文字を逆に(末尾から先頭に向かって)並べた文字列を得よ.
Go
package main import_ "fmt" func main() { var data string = "stressed" var reverse string // 1.ループ for i := len(data) - 1; i >= 0; i-- { reverse += string(data[i]) } fmt.Println(reverse) }python
# -*- coding: utf-8 -*- data = "stressed" reverse = "" # 1.逆カウントのループ i = len(data) - 1 while i >= 0: reverse += data[i] i-=1 print(reverse) # 2.range->reversed reverse = "" for i in reversed(range(len(data))): reverse += data[i] print(reverse) # 3.reversed->join print(''.join(reversed(data))) # 4.スライス print(data[::-1])Javascript
var data = "stressed"; var reverse = "" // 1.逆カウントのループ for (var i = data.length - 1; i >= 0; i--) { reverse += data[i]; } console.log(reverse); // 2.split->reverse->join console.debug(data.split("").reverse().join(""));まとめ
goは日本語非対応。
問題ページとトップページ分割。
- 投稿日:2019-11-27T11:49:29+09:00
Google広告コンバージョンタグのソースコードでどうクリックIDを処理しているのか見てみる
概要
Google広告のコンバージョンタグのソースコードで2種類の広告クリックIDのCookieを処理している部分を見てみました。
背景
【Google広告】クロスドメインのコンバージョン計測-実装パターンと検証でcookieを調べたところ、Google広告に送るためのクリックIDをセットしたcookieとして以下の2種類存在することが確認できました:
-_gcl_aw
-_gac_UA-***
この2種類のCookieをGoogle広告のコンバージョンタグではどのようにハンドリングしているのか見てみたいと思いました。※なお、本動機は以下の記事に影響を受けています。
Google AnalyticsのCookieの仕組みを解説
Googleアナリティクスのトラッカー生成処理を徹底解説前提
コンバージョンタグを仕込んだページで
conversion_async.js
を見てみます。
https://www.googleadservices.com/pagead/conversion_async.js
↓
ソースコードを整形。詳細
途中で力尽きてざっくりと見ただけになりましたが、2つのCookieをハンドリングし分けているコードの箇所を確認することができました。何か誤りがあればコメントで教えてください_(..)_
conversion_async.js(function() { ... //718行目:URLパラメータ(a=b)を作る function T(a, b) { b = S(b); return "" != b && (a = S(a), "" != a) ? "&".concat(a, "=", b) : "" } //792行目:cookieからクリックID(※)を作る //※ "a=b" つまり "_gcl_aw=***"または"_gac_UA-***=***" function Kb(a, b) { ... var d = ""; //797行目:cookie"_gcl_aw"にマッチする値を"gclaw"にセットしてリターン if (b.google_gcl_cookie_prefix && /^[a-zA-Z0-9_]+$/.test(b.google_gcl_cookie_prefix) && "_gcl" != b.google_gcl_cookie_prefix) return d = mb(a, b.google_gcl_cookie_prefix), T("gclaw", d); ... if ... //804行目:"_gac_UA-***"にマッチするcookieの値をセット else { ... b = []; a = a.cookie.split(";"); for (var e = /^\s*_gac_(UA-\d+-\d+)=\s*(.+?)\s*$/, f = 0; f < a.length; f++) { //808行目:"_gac_UA-***"にマッチするcookieがあれば値を取得する var g = a[f].match(e); g && b.push({ c: g[1], ... } ... } //829行目:cookie"_gac_UA-***"にマッチする値を"gac"にセットしてリターン return d + (c ? T("gac", c) : "") } //893行目:Google広告へのリクエストURLを作る function V(a, b, d, c, e, f) { ... switch (e) { default: return ""; case 2: case 3: var g = "googleads.g.doubleclick.net/pagead/viewthroughconversion/"; break; case 1: g = "www.google.com/pagead/1p-conversion/"; break; case 0: g = (c.google_conversion_domain || "www.googleadservices.com") + "/pagead/conversion/" } g = ["https://", g, S(c.google_conversion_id),...,"?random=",S(c.google_conversion_time)].join(""); ... //909行目コンバージョンラベルやクリックIDを含む各種パラメータを設定 a = [...,T("label", c.google_conversion_label),...Kb(d, c),...].join(""); ... g += a; ... return g } ... //1140行目 }).call(this);
- 投稿日:2019-11-27T11:49:29+09:00
【Google広告コンバージョンタグのソースコードでどうクリックIDを処理しているのか見てみる
概要
Google広告のコンバージョンタグのソースコードで2種類の広告クリックIDのCookieを処理している部分を見てみました。
背景
【Google広告】クロスドメインのコンバージョン計測-実装パターンと検証でcookieを調べたところ、Google広告に送るためのクリックIDをセットしたcookieとして以下の2種類存在することが確認できました:
-_gcl_aw
-_gac_UA-***
この2種類のCookieをGoogle広告のコンバージョンタグではどのようにハンドリングしているのか見てみたいと思いました。※なお、本動機は以下の記事に影響を受けています。
Google AnalyticsのCookieの仕組みを解説
Googleアナリティクスのトラッカー生成処理を徹底解説前提
コンバージョンタグを仕込んだページで
conversion_async.js
を見てみます。
https://www.googleadservices.com/pagead/conversion_async.js
↓
ソースコードを整形。詳細
途中で力尽きてざっくりと見ただけになりましたが、2つのCookieをハンドリングし分けているコードの箇所を確認することができました。何か誤りがあればコメントで教えてください_(..)_
conversion_async.js(function() { ... //718行目:URLパラメータ(a=b)を作る function T(a, b) { b = S(b); return "" != b && (a = S(a), "" != a) ? "&".concat(a, "=", b) : "" } //792行目:cookieからクリックID(※)を作る //※ "a=b" つまり "_gcl_aw=***"または"_gac_UA-***=***" function Kb(a, b) { ... var d = ""; //797行目:cookie"_gcl_aw"にマッチする値を"gclaw"にセットしてリターン if (b.google_gcl_cookie_prefix && /^[a-zA-Z0-9_]+$/.test(b.google_gcl_cookie_prefix) && "_gcl" != b.google_gcl_cookie_prefix) return d = mb(a, b.google_gcl_cookie_prefix), T("gclaw", d); ... if ... //804行目:"_gac_UA-***"にマッチするcookieの値をセット else { ... b = []; a = a.cookie.split(";"); for (var e = /^\s*_gac_(UA-\d+-\d+)=\s*(.+?)\s*$/, f = 0; f < a.length; f++) { //808行目:"_gac_UA-***"にマッチするcookieがあれば値を取得する var g = a[f].match(e); g && b.push({ c: g[1], ... } ... } //829行目:cookie"_gac_UA-***"にマッチする値を"gac"にセットしてリターン return d + (c ? T("gac", c) : "") } //893行目:Google広告へのリクエストURLを作る function V(a, b, d, c, e, f) { ... switch (e) { default: return ""; case 2: case 3: var g = "googleads.g.doubleclick.net/pagead/viewthroughconversion/"; break; case 1: g = "www.google.com/pagead/1p-conversion/"; break; case 0: g = (c.google_conversion_domain || "www.googleadservices.com") + "/pagead/conversion/" } g = ["https://", g, S(c.google_conversion_id),...,"?random=",S(c.google_conversion_time)].join(""); ... //909行目コンバージョンラベルやクリックIDを含む各種パラメータを設定 a = [...,T("label", c.google_conversion_label),...Kb(d, c),...].join(""); ... g += a; ... return g } ... //1140行目 }).call(this);
- 投稿日:2019-11-27T10:51:31+09:00
Android Chrome/78.0.3904.108 にて起こっているpreload問題とその暫定的対応
何が起こっているのか
内容は上記の記事を見てもらうとすごくわかりやすくまとめてくれています。(同僚さんの記事です)
おそらく、ページ内の行動履歴?に関係する関連の高いaタグのhrefの中身を「自動で」preloadしてしまっているという問題で、フロントとしてどうしたのか
まず、被害の大きい箇所、ページを調べて
a href="/hogehoge/bar"を
span data-href="/hogehoge/bar"という風に書き換えた上で、あとはJS側でdata-hrefの中身をclickでlocation.hrefに入れて飛ばす方法で暫定対応。
それかa data-href="/hogehoge/bar"という風にして、load時にdata-hrefの中身を抜き取ってhrefに入れるとかでも行けそうな。
雑感
ひとまず、被害の大きい箇所だけの暫定対応とはいえ、まだ対応ページがそこまでではなかったのでできる技かなと。
これでも完璧ではないので、追っかけ対応になることもありえる。
サイト規模の大きいところはその限りではないだろうから大変かと思います。早くchrome側のアップデートなりで修正されることを望むばかり
- 投稿日:2019-11-27T10:10:19+09:00
A-FRAME: 物理演算でボーリングっぽい動きを実現してみる6(ピンの形状)
A-Frameをつかって物理演算ができるようにしてみます。
ピンに物理的な形状を設定してみて、振る舞いの違いをみてみます。ピンは細長いひょうたん型で、プリミティブにはそのような形状はありません。
cylinderがふさわしいような気がしますが、自動的に割り当てた場合にどのような形状が割当たるのでしょうか。例1)shape=auto
autoは使用可能な形状から自動的に選択されます。
demo
boxが選択されたようです。例2)shape=primitive
primitiveは平面/円柱/球の内、対応するAフレームプリミティブが自動的に使用されます。
demo
boxが選択されたようです。
説明にはboxは候補に書かれていないですが、置いておきましょう。例3)shape=box
boxを設定します。
demo
ピンの形状にマッチしたboxが設定されました。例4)shape=hull
hullはシュリンクラップのようなモデルをラップします。
hullという単語には殻や外皮といった意味があります。
demo
レーンをすり抜けていきました。
ボールにhullを設定した時はレーンを転がっていったので、思っていたのと違う感じがします。例5)shape=mesh
meshはJS物理エンジンでモデル化するのが難しく、特定の他のシェイプを「フォールスルー」し、パフォーマンスに重大な制限があります。
demo
hullと同じように、レーンすり抜けていきました。
ボールにmeshを設定した時もレーンをすり抜けたので、この振る舞いはボールと同じように感じます。例6)shape=none
noneは衝突ジオメトリを追加しません。
demo
ボールにnoneを設定した時と同様、レーンをすり抜けていきました。例7)shape=sphere
sphereを設定します。
demo
ピンは球体とは異なるので、違和感が大きいです。
あと、sphereの中心とピンの中心がずれているように見えます。
boxの場合はboxの中心とピンの中心は一致していました。例8)shape=cylinder
最後にもっとも有力に思っていたcylinderを設定します。
demo
cylinderもsphereと同様に、sphereの中心とピンの中心が一致していません。まとめ
ピンの物理的な形状は、自動で設定するとboxが選択されてcylinderにはなりませんでした。
そして、今回設定した形状はどれもしっくりきませんでした。今回みえた課題としては、
- sphereとcylinderを設定した時に、ピンと物理的な形状の中心が一致しない原因が何か
- 複合シェイプを利用する事で、中心の不一致に対応する事もできそう(noneの本来の使い方っぽい)
- hullを設定した時の振る舞いがボールとピンで異なるがあるので、次以降で見ていきます。