- 投稿日:2020-01-23T23:26:37+09:00
Vue.js のディレクティブの記述を膨らませないために
概要
本記事では、Vue.jsのディレクティブって便利だけどなんかかさばって可読性が・・という方に
改善する方法をv-bindを例にして紹介していきます。元のコード
aタグに対して2つのv-bindが設定されています。
2つくらいなら・・と思うかもしれませんがこれが膨張していくと恐ろしいことになります。index.html<div id="app"> <a :href="url" :id="number">リンク</a> </div>index.jsnew Vue({ el: '#app', data: { url: 'https://sample.com', number: 23 } })改善
ひとつのオブジェクトにまとめることができます。
画面上の表示はなにも変わりません。index.html<div id="app"> <a v-bind="{href: url, id: number}">リンク</a> </div>index.jsnew Vue({ el: '#app', data: { url: 'https://sample.com', number: 23, } })さらに改善
dataの中にオブジェクトとして持たせておいて、html上ではbindObjectとバインドさせているだけになりました。こちらも画面上の表示はなにも変わりません。
こちらの方がhtmlに記載する量が少ないのでVue.jsを書く上での理想の形です。そして管理もしやすくなりますね。index.html<div id="app"> <a v-bind="bindObject">リンク</a> </div>index.jsnew Vue({ el: '#app', data: { bindObject: { url: 'https://sample.com', number: 23, } } })最後に
可読性を下げず、チームでも個人でも管理しやすい形でコーディングできるように自分ももっと気をつけていこうと思います。
- 投稿日:2020-01-23T23:08:26+09:00
Vue.jsでイベントオブジェクトを取得する
概要
本記事では、Vueのディレクティブである v-on を使ってイベントオブジェクトを取得する方法を紹介します。
イベントオブジェクトとは
イベントオブジェクトは、イベントハンドラーおよびイベントリスナーにおいて実行される関数の引数として受け取ることのできるオブジェクトです。 そのイベントオブジェクトから、発生したイベントに関わる様々な情報(プロパティ)を知ることができ、またそのイベントを制御するメソッドを活用することができます。
詳しくは下記記事を参考にしてください。
https://phpjavascriptroom.com/?t=js&p=event_object実際のコード
sampleTextというidを持たせたp要素の上にマウスを乗せた時、そのマウスの位置を取得し
その下のresultというidを持たせたp要素に表示させるという流れ。index.html<div id="app"> <p id="sampleText" v-on:mousemove="mousePosition">ここにマウスを載せると下のX、Yの値が変わるよ</p> <p id="result">X:{{x}}, Y:{{y}}</p> </div>index.jsnew Vue({ el: '#app', data: { x: 0, y: 0 }, methods: { mousePosition: function(event) { this.x = event.clientX; this.y = event.clientY; // eventの中を見てみると、全てのイベントオブジェクトが入っている console.log(event); } } })補足
今回はclientX(Y)を取得したが、eventの中をconsole.logで見ると全ての情報が入っている。
補足2
引数に渡しているeventは好きなものに変えてもちゃんと動く。
- 投稿日:2020-01-23T22:57:41+09:00
[Node.js]モジュール定義について整理(exports-require / export-import)
はじめに
あるファイルに定義した関数等を別のファイルで使いたいときにどうするか。
Node.jsでは二つのやり方がある。
- exportsで公開してrequireで読み込む(CommonJS)
- exportで公開してimportで読み込む(ES2015)
常に使えるのは1の方法。Babelを使うなら2の方法でも可能。
どちらを選んでもメリット・デメリットがあるわけではない(と思う)ので、お好きなほうで。個人的には、常に使える1の方法がいいような気がしている。
使用例
exportsで公開してrequireで読み込む(CommonJS)
一つの関数だけをエクスポート
module.js// 関数を定義 const f = () => { console.log('Hello!') } // 関数を公開 module.exports = fclient.jsconst f = require('./module') f() // Hello!エクスポートするのは関数に限らず、文字列や数値でも良い。
複数の関数をエクスポート
module.js// 関数を定義 const f1 = () => console.log('Hello!') const f2 = () => console.log('World!') // 別名をつけて、二つの関数を公開 module.exports = { sayHello: f1, sayWorld: f2 }client.jsconst mod = require('./module') mod.sayHello() // Hello! mod.sayWorld() // World!クラスをエクスポート
module.js// クラスを定義 class Person { constructor(name) { this.name = name } greet(params) { console.log(`Hello, ${this.name}!`) } } // 公開 module.exports = Personclient.jsconst Person = require('./module') p = new Person('aki') p.greet() // Hello, aki!おまけ:選択してインポート
module.jsconst f1 = () => console.log('Hello!') const f2 = () => console.log('World!') module.exports = { sayHello: f1, sayWorld: f2 }client.js// インポートする対象を選択する const {sayHello} = require('./module') sayHello() // Hello!exportで公開してimportで読み込む(ES2015)
下記の参考資料を参照。いつか書く。
参考資料
1. exports - require(CommonJS)
- Node.js v13.7.0 Documentation
- 説明が難しめ
- 『Node.jsデザインパターン』(オライリー)
- P.25~
2. export - import((ES2015))
- 投稿日:2020-01-23T22:49:56+09:00
JavaScriptの多次元ハッシュ(連想配列)を一括でURIエンコードする
一括処理したいこと
HTTP通信において、次のような多次元のハッシュ(連想配列)を送信する場合、
sampleHash.jslet hash = { text: "こんにちは", user: { name: "山田太郎", email: "taro@yamada.com" } };サーバへのリクエストは、次のようにURIエンコードして送信することになります。
URIエンコード(UTF-8)text=%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF&user[name]=%E5%B1%B1%E7%94%B0%E5%A4%AA%E9%83%8E&user[email]=taro%40yamada.comこの処理を、関数を使って一括でURIエンコードすることがこの記事の目的です。
一括変換する関数
一括変換する関数は、次のとおりです。
sample.js// テスト用のハッシュ(連想配列) let hash = { text: "こんにちは", user: { name: "山田太郎", email: "taro@yamada.com" } }; // 一括変換する関数 function encodeHashToURI(data, stockKey="") { if (typeof(data) == "object") { let resultStr = ""; // エンコードした文字列を格納する変数 let bracketOpen = stockKey == "" ? "&" : "["; // 開き角括弧(一番上の階層は"&") let bracketClose = stockKey == "" ? "" : "]"; // 閉じ角括弧(一番上の階層は空文字列) Object.keys(data).forEach(function(key) { let newKey = stockKey + bracketOpen + encodeURIComponent(key) + bracketClose; let newVal = data[key]; if (typeof(newVal) == "object") { resultStr += encodeHashToURI(newVal, newKey); // 値がオブジェクトであれば再帰処理 } else { resultStr += newKey + "=" + encodeURIComponent(newVal); // 値が文字列や数値の場合は、処理を確定して要素として追加 } }); if (stockKey == "" && resultStr[0] == "&") { resultStr = resultStr.replace("&", ""); //1文字目の"&"を削除 } return resultStr; } else { return data; } } // 処理結果の表示 console.log(encodeHashToURI(hash)); // 出力結果: text=%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF&user[name]=%E5%B1%B1%E7%94%B0%E5%A4%AA%E9%83%8E&user[email]=taro%40yamada.comこれでHTTP送信可能なデータが生成できます。
難しいことはしてないので、コードとコメントを読んでいただければ、大体わかると思います。階層が深い場合の例
なお、関数を再帰的に利用しているので、階層の深さに関わらず使用できます。
例えば、次のような入り組んだハッシュ(連想配列)でも変換できます。sampleHash2.jslet hash = { post: { text: "こんにちは", id: 348, other: { url: "https://www.yahoo.co.jp/", style: { layer1: "階層1", layer2: "階層2" } } }, user: { name: "山田太郎", email: "taro@yamada.com", } };出力結果は、次のようになります。
sample.jslet ans = encodeHashToURI(hash); // 関数で変換 console.log(ans); // 変換結果を表示 // 出力結果: post[text]=%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF&post[id]=348&post[other][url]=https%3A%2F%2Fwww.yahoo.co.jp%2F&post[other][style][layer1]=%E9%9A%8E%E5%B1%A4%EF%BC%91&post[other][style][layer2]=%E9%9A%8E%E5%B1%A4%EF%BC%92&user[name]=%E5%B1%B1%E7%94%B0%E5%A4%AA%E9%83%8E&user[email]=taro%40yamada.com console.log(decodeURIComponent(ans)); // 変換結果をデコード // 出力結果: post[text]=こんにちは&post[id]=348&post[other][url]=https://www.yahoo.co.jp/&post[other][style][layer1]=階層1&post[other][style][layer2]=階層2&user[name]=山田太郎&user[email]=taro@yamada.comデータ加工のイメージ
多次元配列をURIエンコードで送信する場合のルールは次のような感じです。
- ネストしている要素は1つずつ独立させる
- 下の階層のキーは、
[ ]
で括って繋げて1つのキーとするイメージtext: こんにちは user[name]: 山田太郎 user[email]: taro@yamada.comそして、URIの形式なので、次の加工も必要です。
- キーと値は
=
で関連付ける- 要素と要素は
&
で接続するイメージtext=こんにちは&user[name]=山田太郎&user[email]=taro@yamada.com最後に、URIエンコード(UTF-8)で変換します(再掲)。
text=%E3%81%93%E3%82%93%E3%81%AB%E3%81%A1%E3%81%AF&user[name]=%E5%B1%B1%E7%94%B0%E5%A4%AA%E9%83%8E&user[email]=taro%40yamada.com最後に
挙動を確認しているのは、文字列型、数値型、Boolean型までです。
何らかの参考となれば幸いです。
- 投稿日:2020-01-23T22:49:01+09:00
javascript入門
javascriptを勉強した内容をまとめます
ブラウザで使う場合
htmlのscriptタグ内に書く
index.html<!DOCTYPE html> <html lang="jp"> <head> <meta charset="utf-8"> </head> <body> <script> const a = 50 const b = 17 const c = a * b console.log(c) </script> </body> </html>jsファイルに書いておいてscriptタグでインポートする
以下はindex.htmlと同じディレクトリにtest.jsを置いた場合です
index.html<!DOCTYPE html> <html lang="jp"> <head> <meta charset="utf-8"> </head> <body> <script type="text/javascript" src="test.js"></script> </body> </html>test.jsconst a = 50; const b = 17; const c = a * b; console.log(c);変数と定数
動的型付言語なので型を明示的に宣言しなくても動きます
定数const a = 'hoge'; const b = 300; const c = 2.9; console.log(a); console.log(b); console.log(c);変数var d = 'hoge'; var e = 300; var f = 2.9; console.log(d); console.log(e); console.log(f); d = 'fuga'; e = 123; f = 1.8; console.log(d); console.log(e); console.log(f);配列
javascriptvar arr = [1, 2, 3]; console.log(arr); console.log(arr[1]); arr = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]; console.log(arr); console.log(arr[1][1]);論理演算
等号が「==」、NOTが「!」、ANDが「&&」、ORが「||」になります。
javascriptconst a = true && true; const b = true && false; console.log(a); console.log(b); const c = true || false; const d = false || false; console.log(c); console.log(d); console.log(!c); console.log(!d);if文
javascriptconst flag = true if(flag){ console.log('input value:' + flag + ' is True'); }else{ console.log('input value:' + flag + ' is False'); } const value = 300 if(value < 200){ console.log('input value:' + value + ' is less than 200'); }else{ console.log('input value:' + value + ' is more than 200'); }for文
javascriptfor(i=0; i<10; i++){ console.log(i); }switch文
javascriptconst place = '東京' switch (place) { case "東京": console.log('Tokyo'); break; case "大阪": console.log('Osaka'); break; default: console.log("不明"); break; }関数の書き方
3つ書き方があるみたいです。
python勢的には1つに統一してほしいですが、いろんな書き方が出来ることを是とするカルチャーなんでしょうか。返り値のない関数function normalFunc1(x) { console.log(x); } let normalFunc2 = function(x){ console.log(x); } let arrowFunc = (x) => { console.log(x); } normalFunc1('ふつうの関数1'); normalFunc2('ふつうの関数2'); arrowFunc('アロー関数式');返り値のある関数function normalFunc3(x) { console.log(x); return x * 2; } let normalFunc4 = function(x){ console.log(x); return x * 2; } let arrowFunc2 = (x) => { console.log(x); return x * 2; } console.log(normalFunc3(3)); console.log(normalFunc4(3)); console.log(arrowFunc2(3));sleep動作
javascriptはsleep関数が用意されていないようです。これはそもそもjavascriptではsleep()をするべきでないという事みたいなんですが、待機後に実行するsetTimeout()を使えばできます。第2引数ミリ秒待機してから第1引数の内容を実行しますので、例えば2秒待ってから「2秒待機しました」と表示する場合は以下で出来ます。
javascriptsetTimeout(() => { console.log('2秒待機しました'); }, 2000);async/awaitによる非同期処理
以下のような仕組みで非同期処理を実施します
1. Promiseをreturnする関数をasync function内で呼ぶと非同期処理を開始する
2. PromiseをawaitするとPromiseを発行したfunctionが終了するまで待機する複数の関数を非同期で開始しておいて、最後にまとめてPromiseをawaitするような使い方になると思います。
こちらのコードをお借りしました。
https://webbibouroku.com/Blog/Article/js-async-await実行するとf2()が先に終了して、f1()の結果が後から返ってきます。
javascript// 2秒後に値を返す function getNum(num) { return new Promise((resolve, reject) => { setTimeout(() => { resolve(num); }, 2000); }); } // 6秒後に出力 async function f1(value) { const n1 = await getNum(value*1); const n2 = await getNum(value*2); const n3 = await getNum(value*3); console.log(n1, n2, n3); } // 2秒後に出力 async function f2(value) { const p1 = getNum(value*1); const p2 = getNum(value*2); const p3 = getNum(value*3); const n1 = await p1; const n2 = await p2; const n3 = await p3; console.log(n1, n2, n3); } f1(1); f2(2);
- 投稿日:2020-01-23T22:18:56+09:00
トラがエシレバターになるWebComponents
WebComponentsとは
Web Developer Roadmap でも今年からリスト入りした
コンポーネントを作るための ブラウザ標準の技術 の総称。HTML, JS, CSSが一体となった非常に再利用性の高いコンポーネントを作ることができます。
オリジナルのHTMLタグを作れるだけじゃない
トラがエシレバターになるコンポーネントは、
いつもみんなが使っている<img>
タグにis="my-img"
を指定しただけです。これがWebComponentsのすごいところで、ただオリジナルのHTMLタグを作るだけじゃなく
ネイティブの機能を活かして 独自に拡張(e.g. クリックすると回転してエシレバター)することができます。customElements.define('my-img', MyImg, { extends: 'img' });実装時に重要なのはこの一行だけで、
継承したいネイティブのタグ{ extends: 'img' }
を指定して
customElements.define
にぶちこむだけです。また、
is属性
を設定しなければ通常の<img>
タグを利用することができます。ソースコード
my-img.jsimport butter from '../assets/butter.png'; /** * <img>タグの拡張 - HTMLImageElementを継承 */ class MyImg extends HTMLImageElement { constructor() { super(); this.times = 0; this.type = ''; this.innerHTML = ` <style> :host { display: block; cursor: pointer; } .rotate1 { animation: rotate 0.75s linear infinite; } .rotate2 { animation-duration: 0.5s; } .rotate3 { animation-duration: 0.25s; } .rotate4 { animation-duration: 0.125s; } .rotate5 { animation-name: none; } @keyframes rotate { 0% { transform: rotate(0deg) translate(-50px) rotate(0deg); } 100% { transform: rotate(-360deg) translate(-50px) rotate(360deg); } } </style> `; this.addEventListener('click', this.rotate); } rotate() { this.times++; this.classList.add(`rotate${this.times}`); // バターになる if (this.times === 5) { this.src = butter; } } } customElements.define('my-img', MyImg, { extends: 'img' });index.html<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width,initial-scale=1" /> <title>WebComponents Sample</title> </head> <body> <h1>トラがエシレバターになるWebComponents</h1> <div style="text-align: center; margin: 50px auto;"> <img src="./assets/tiger.png" alt="トラ" is="my-img" width="240" /> </div> <script src="./my-img.js"></script> </body> </html>トラがエシレバターになるPR:
https://github.com/cc822jp/saturday-hackathon/pull/1/filesその他こだわった点
普通に回転させるとバター感が薄れてしまうため
半円周上に動くように調整しました。実際の案件に耐えうるのか?
MS Edgeのブラウザエンジンがchromiumに置き換わることで、
全てのメジャーブラウザでpolyfill無しで利用することができます。An effort 9 years in the making, but we’ve finally made it.
— Polymer Project (@polymer) January 15, 2020
WebComponents are supported natively in every major browser pic.twitter.com/6yPoTXno27※Googleが推進するPolymer Projectでも歓喜してました
※polyfillを使うことで旧バージョンのEdgeでも利用することはできます。まとめ
動画でも解説しているので良ければ見てみてくださいm(__)m
- 投稿日:2020-01-23T20:34:24+09:00
[JavaScript] Submit without submit button
submit ボタン無しで javascript から submit するYO!
<style> textarea { vertical-align: top; } </style> <form id='inputs'> <p> <label for='name'>Name</label> <input id='name'> </p> <p> <label for='content'>Content</label> <textarea id='content'></textarea> </p> </form> <script> // Meta + Enter で submit しますねん inputs.onkeydown = e => { // Meta + Enter で処理する if (e.key != 'Enter' || !e.metaKey) return; // フォーム (HTMLFormElement) を得る const form = e.currentTarget; // 空の Event を作る const form_event = document.createEvent('HTMLEvents') // Event 初期化 (submit 機能。イベントチェーンを浮上。キャンセル可) form_event.initEvent('submit', true, true); // イベントを dispatch できたら…… if (form.dispatchEvent(form_event)) { // submit() メソッドを使える form.submit(); } } </script>
createEvent()
のHTMLEvents
ってのがよく分からない。
「取り得るイベント型は "UIEvents", "MouseEvents", "MutationEvents", "HTMLEvents" のいずれかです」で submit に関するコトだから、HTMLEvents
なのかなあ、とあと
dispatchEvent()
も、どうしてこれでうまくいくのか説明できないorz
- 投稿日:2020-01-23T19:58:26+09:00
LaravelでUncaught domexception: failed to execute '***' on 'element': ',' is not a valid attribute name.を吐き出したときに確認したこと
Laravelにてアプリを作成していたところコンソールに下記のようなエラーが発生
Uncaught domexception: failed to execute 'setattribute' on 'element': ',' is not a valid attribute name.簡単に訳すと「属性名に','(カンマ)は使えないから'setattribute'は実行できないよ!」
と怒られてしまってます。考えたこと
コンソールのエラーだからJavaScriptのファイルに不備があるのか?
右側のapp.js:41954から該当エラー個所をみてみるが何もおかしいところはなさそう。
そもそも","(カンマ)がその行にない...「属性名」に異常?つまりviewファイル側におかしいところがあるのか?
index.blade.php<li> <a href="{{ action('WordsController@show', $word )}}">{{ $word->en }}</a> <a href="">{{ $word->ja }}</a> <a href="#", data-id="{{ $word->id }}">[×]</a> <form method="post", action=" {{ url('/words', $word->id) }} " id="form_{{ $word->id }}" > {{ csrf_field() }} {{ method_field('delete') }} </form> </li>よーくみると
4行目のhref="#"の後ろのカンマ...
5行目のmethod="post"の後ろのカンマ...いりません!!!
これを消したらエラーも消えました。
おわりに
認証機能を追加するべく、Laravel6のuiパッケージを入れ込んでからこのエラーがで始めたので、色々と考え込んでしまったけれど、結局は小さなミスでした。
直前までUIも問題なく動いていたのも時間を食ってしまいました。
しかしながら探しても日本語の記事が全然ない...
同じようなことが起こった方と自分への忘備録として残しておきます。参考リンク
- 投稿日:2020-01-23T18:14:23+09:00
moment.jsのsubtract(減算)で時間の分(m)がうまく行かなかった件
問題定義
moment.jsで30分減算した時間を出したかったが
subtract(int, 'm')
でブラウザがフリーズしたimport moment from 'moment' const time = moment('22:00', 'HH:mm'); // これを実装するとブラウザが固まる const reducedTime = time.subtract(30, 'm');解決
subtract(int, 'm')
をsubtract(int, 'minutes')
に変えると解決したimport moment from 'moment' const time = moment('22:00', 'HH:mm'); // これを実装するとブラウザが固まる const reducedTime = time.subtract(30, 'minutes');コメント
なぜかはわからないので原因がわかる方はコメントで教えてくださると嬉しいです。
- 投稿日:2020-01-23T17:59:52+09:00
jsPDFでHTML上の日本語表記のテーブルをサクッとPDF化
概要
- クライアントサイドで手軽にPDFを作成したい
- HTML上の特定テーブル(日本語表記)をそのままPDF化
jsPDFとは?
- JavaScriptを用いてPDFを作成するライブラリ
jsPDF-AutoTableとは?
- jsPDFのテーブル作成プラグイン
How to
- 1. 使用したい日本語フォント(.ttf)をダウンロード
- ここは個人の好きなフォントでOK
- 今回は Koruri という軽量日本語フォントを利用
- 2. ここからフォントをJSファイルに変換する
- fontNameに適当な名前を入力
- fontStyleはダウンロードしたフォントのスタイルを指定
- ファイルにダウンロードしたフォントファイルttfを指定
- 3. 各種JSをHTMLにて読み込み、jsPDFを用いてPDF化処理を記述
- jspdf.min.js
- jspdf.plugin.autotable.js
- Koruri-Regular-normal.js ← 2でフォントファイルから変換したJS
<html> <head> <script type="text/javascript" src="assets/js/jspdf.min.js"></script> <script type="text/javascript" src="assets/js/jspdf.plugin.autotable.js"></script> <script type="text/javascript" src="assets/js/Koruri-Regular-normal.js"></script> <script> $(function() { // 縦表示が'p'、横表示が'l' // 用紙サイズはA4 let doc = new jsPDF('l', 'pt', 'a4', false); // ここでセットしたフォントはjsPDFのtext()で指定したテキストにのみ反映 doc.setFont('Koruri-Regular', 'normal'); doc.text(40, 30, 'HTMLテーブルをPDF化'); doc.autoTable({ theme: 'grid', html: '#table', // ここでフォントの指定をしないとテーブル内部の文字が化ける styles: {font: 'Koruri-Regular', fontStyle: 'normal', fontSize: 12} }); doc.save('table.pdf'); }); </script> </head> <body> <table id="table"> <tr> <th>名前</th> <th>年齢</th> </tr> <tr> <td>ほげ</td> <td>20</td> </tr> <tr> <td>ふが</td> <td>30</td> </tr> </table> </body> </html>まとめ
- クライアントサイドでPDF化する方法はいくつかあるが、いまいち日本語化対応になっていなかったり、日本語対応するやり方が複雑だったりで情報も少ない。今回のやり方が一番サクッとできそうな方法かなーと。
参考
- jsPDF
- jsPDF-AutoTable
- Koruri
- 投稿日:2020-01-23T17:03:16+09:00
【Nuxt.js】TypeScript基礎編:Vue.extendでシンプルなコードを書こう
前置き
今回はNuxt.jsでTypeScript??
大まかに3つに分けて書いています✍️
・TSのメリット
・TSの書き方3つ
・Vue.extendコード例TSのメリット
すごく簡単に言うと、
安全な開発がしやすくなります??♀️
ここが参考になります!
https://qiita.com/SoraKumo/items/43fba2ad2d10336a665TSの書き方
TSを入れた場合の書き方は3パターン
・Vue.extend
・vue-class-component
・vue-property-decoratorシンプルで書きやすいのがVue.extendです?
Vue.extendのメリット
簡単⭕️
1番とっつきやすい書き方?何故なら!
通常と書き方がほとんど変わらないから!
Vue、Nuxtらしさを保ったままTSが使えます。TSなしの場合と変わるのは3点のみ!!!
・script langをtsに変更
・Vueモジュールをimport/extend
・コンポーネント、ページ自体にクラス名を付与他は通常と変わらない
これがVue.extendの良さ?他の書き方は?
@Componentとかになるあれ。
書き方が結構変わりますよね〜!
・vue-class-component
・vue-property-decoratornuxtの場合はこちら
・nuxt-class-component
https://github.com/nuxt-community/nuxt-class-component
・nuxt-property-decorator
https://github.com/nuxt-community/nuxt-property-decorator詳しい書き方はこちらが参考になります!
https://qiita.com/potato4d/items/c9c0c8e674f20c85948aVue.extend
【基礎構文】
通常と比較しても
シンプルで非常に分かりやすいですね!?公式はこちら
https://typescript.nuxtjs.org/ja/
https://jp.vuejs.org/v2/guide/typescript.html#基本的な使い方index.vue<script lang="ts"> import Vue from 'vue' export default Vue.extend({ // コンポーネント・ページ自体にクラスを付与 name: 'Component', components: { }, props: { }, data() { return { } }, computed: { }, mounted () { }, methods: { }, created () { console.log('CLICK!!!')// eslint-disable-line }, }) </script>【通常】
index.vue<script> export default { components: { }, props: { }, data() { return { } }, computed: { }, mounted () { }, methods: { }, created () { console.log('CLICK!!!')// eslint-disable-line }, } </script>data
Stringの場合は
" "(double quote)ではなく
' ' (single quote)のみ⭕️index.vue<script lang="ts"> import Vue from 'vue' export default Vue.extend({ name: 'Component', data () { return { userName: '', } }, }) </script>props
TSなしの場合と変わりません。
・type
・required
・validator
全てそのまま書けます✍️index.vue<script lang="ts"> import Vue from 'vue' export default Vue.extend({ name: 'Component', props: { status: { type: String, required: false, validator (value) { return [ 'default', ].includes(value) }, }, }, }) </script>methods
こちらも同様
$emitなども通常通り記載可能です。index.vue<script lang="ts"> import Vue from 'vue' export default Vue.extend({ name: 'Component', methods: { alert () { this.$emit('componentAlert') }, }, }) </script>console.log()
methodsやライフサイクルで
console.log()を使用する場合ESlintにひっかかります。
その場合は// eslint-disable-lineを追記すれば⭕️
https://eslint.org/docs/rules/no-consoleindex.vue<script lang="ts"> import Vue from 'vue' export default Vue.extend({ name: 'Component', created () { console.log('created!!!')// eslint-disable-line }, }) </script>
- 投稿日:2020-01-23T16:51:02+09:00
[JavaScript][Node.js]メモ:アロー関数の文法
アロー関数(ES2015)の書き方。
f = () => console.log('Hello!') f() // Hello!評価した値を返す// 式(Expressions)の評価結果が戻り値になる sum = (a, b) => a + b r = sum(1, 2) console.log(r) // 3returnで値を返す// {}で文(Statements)を作り、returnで値を返す sum = (a, b) => { return a + b } r = sum(1, 2) console.log(r) // 3引数のカッコを省略// 引数が一つの時のみカッコを省略可能(0個もしくは2つ以上のときは省略不可) say = word => console.log(word + '!') say('Yeah') // Yeah!即時関数(() => console.log('Hello!'))()引数としてのアロー関数arr = [1,2,3,4,5] // かっこよくない console.log(arr.map(function(e) { return e + 1 })) // [ 2, 3, 4, 5, 6 ] // かっこいい console.log(arr.map(e => e + 1)) // [ 2, 3, 4, 5, 6 ]リンク(mozilla)
- 投稿日:2020-01-23T16:51:02+09:00
[JavaScript][Node.js]アロー関数のサンプル集
アロー関数(ES2015)ってかっこいいですよね。
ふつうにf = () => (console.log('Hello!')) f() // Hello!カッコは省略可能f = () => console.log('Hello!') f() // Hello!returnの省略// 一行の時だけ sum = (a, b) => a + b r = sum(1, 2) console.log(r) // 3returnの省略(NGパターン)// {}で括ると省略できない。()ならOK。 sum = (a, b) => { a + b } r = sum(1, 2) console.log(r) // undefined複数行// returnは必須 sum = (a, b) => { return a + b } r = sum(1, 2) console.log(r) // 3引数のカッコを省略// 引数が一つの時のみカッコを省略可能(0個もしくは2つ以上のときは省略不可) say = word => console.log(word + '!') say('Yeah') // Yeah!即時関数(() => console.log('Hello!'))()引数としてのアロー関数arr = [1,2,3,4,5] // かっこよくない console.log(arr.map(function(e) { return e + 1 })) // [ 2, 3, 4, 5, 6 ] // かっこいい console.log(arr.map(e => e + 1)) // [ 2, 3, 4, 5, 6 ]リンク(mozilla)
- 投稿日:2020-01-23T16:49:46+09:00
[JavaScript][Node.js]アロー関数のサンプル集
アロー関数(ES2015)ってかっこいいですよね。
ふつうにf = () => (console.log('Hello!')) f() // Hello!カッコは省略可能f = () => console.log('Hello!') f() // Hello!returnの省略// 一行の時だけ sum = (a, b) => a + b r = sum(1, 2) console.log(r) // 3returnの省略(NGパターン)// {}で括ると省略できない。()ならOK。 sum = (a, b) => { a + b } r = sum(1, 2) console.log(r) // undefined複数行// returnは必須 sum = (a, b) => { return a + b } r = sum(1, 2) console.log(r) // 3引数のカッコを省略// 引数が一つの時のみカッコを省略可能(0個もしくは2つ以上のときは省略不可) say = word => console.log(word + '!') say('Yeah') // Yeah!即時関数(() => console.log('Hello!'))()引数としてのアロー関数arr = [1,2,3,4,5] // かっこよくない console.log(arr.map(function(e) { return e + 1 })) // [ 2, 3, 4, 5, 6 ] // かっこいい console.log(arr.map(e => e + 1)) // [ 2, 3, 4, 5, 6 ]リンク(mozilla)
- 投稿日:2020-01-23T15:50:24+09:00
TypeScriptで FileReaderのonloadした後、resultに対してエラーが出る時の暫定対処
FileReader
https://developer.mozilla.org/ja/docs/Web/API/FileReader/result
なんかおかしいらしい。
TS2339: Property 'result' does not exist on type 'EventTarget'
なぜか怒られる
https://github.com/Microsoft/TypeScript/issues/4163
なんかおかしいらしい。
※翻訳してまで読んでないので興味がある方は調べてください。const reader = new FileReader(); reader.onload = (e) => { //(e.target as any)でany型に変換する console.log((e.target as any).result); }; reader.readAsDataURL(file);
- 投稿日:2020-01-23T15:42:23+09:00
npmとは yarnとは
この記事の目的
yarnとは何か、npmとは何かという概念を理解することを目的としています。
yarnとは npmとは・・・
Node.jsで動作するパッケージマネージャー
Node.jsとは
Node.jsというのは、JavaScriptでサーバーサイドの処理を可能にさせるプログラム
パッケージマネーシャーとは
「パッケージマネージャとは、コンピュータに何のソフトウェアがインストールされたかを記録し、新しいソフトウェアのインストール・新しいバージョンへのソフトウェアの更新・以前インストールしたソフトウェアの削除を容易に行えるようにするプログラム」
【Debian公式ページ】パッケージマネージャとは
つまり
yarnとnpmはJavaScriptでサーバーサイドの処理を行うために使うプログラム(Node.js)上で使うパッケージマネージャーということです。
下記で一つずつ確認していきましょう。
JavaScriptはもともとフロントエンド言語
- ブラウザでの画面表示部分、つまりフロントエンドで使用されることを前提に作られたプログラム言語。
- データベースへのアクセスなどサーバーサイドの処理はできなかった。
Node.jsの誕生でJavaScriptでサーバーサイドの処理が可能に
- Node.jsが開発されたことで、JavaScriptでサーバーサイドの処理ができるようになった。
- それに伴い、JavaScriptのフレームワークが開発された。(React、Express、)
フレームワークを使うためにはインストールを簡単にする必要がある
- 1000個のプログラムで出来ているフレームワークをインストールするとして、そのフレームワークが動くために必要なプログラムを全て手作業でサーバーの適切な場所にコピーし、特定の設定ファイルを書き換え、正常にインストールできたかを確認し、バージョン管理をするのはかなり困難。
パッケージマネージャーがインストールや管理を簡単にする
- 簡単なコマンドの入力でインストールを完了することができる。
Node.jsで動作するパッケージマネージャー、それがyarnとnpm
:)
npmは
- Node.jsをインストールすれば一緒にインストールされる。
- 2009年にNode.jsがリリースされた翌年にnpmがリリースされた。
yarnは
- 2016年にリリース。npmと互換性があり、npmで使用していたプロジェクト設定ファイル(package.json)がそのまま使える。
- npmと比べてインストールが速い、セキュリティが高いという特徴がある。
- セキュリティが高いというのは、インストール時にパッケージが不正に変更されていないかなどをチェックサムを用いて検証することができ、安全なパッケージのインストールが可能であるということ。
- バージョン管理についても優れていて、yarnではプログラムのインストール後に、yarn.lockというファイルが作成され、それにはインストールしたプログラムが使用している別のプログラム(依存プログラム/パッケージ)のバージョンが明確に書き込まれている。
- 依存プログラム/パッケージをその後再度インストールしてもバージョンの整合性が保たれるので、バージョン不一致でプロジェクトが動かなくなる危険性が無くなる。
- npm、homebrew、MacPortsからインストールできる。
npmとyarnの違い
依存プログラム/パッケージのケア
npm:バージョン違いの依存プログラム/パッケージをインストールしてしまう可能性あり
yarn:yarn.lockファイルにより、バージョン違いの依存プログラム/パッケージのインストールは起こらないインストール速度
npm:遅い
yarn:早い参考
【Samurai Blog】JavaScriptのパッケージマネージャーnpmとYarnについて解説します!
【Samurai Blog】【Node.js】初心者も理解できる言語の特徴を体系的に解説!
- 投稿日:2020-01-23T15:19:22+09:00
Nuxt.jsのbeforeDestroyed()でイベントリスナーを削除できなかった時の対処法。
発端
偉い人「このページは横画面でしか表示したくない」
蝦「はい」環境
OS: windows10 64bit
node.js v10.16.3
Nuxt.js v2.10.2イベントリスナーを追加
蝦「画面の向きが変わるたびにVueファイル内のdata()の値を変えてやればおk」
とりあえずMDNのサンプルをそのままつかう。
Window: orientationchange イベント - Web API | MDNebi.js<script> //中略 beforeMount() { console.log("beforeMount"); window.addEventListener("orientationchange", this.checkRotate, false); } </script>しかし
イベントリスナーが消えない。
「後はbeforeDestroy()でイベントリスナーを削除するように書けばおk」
kani.js<script> //中略 beforeDestroy() { console.log("beforeDestroy"); window.removeEventListener("orientationchange", this.checkRotate, false); }, </script>消えない。
追加はされるが削除はされないので、該当ページに遷移するたびにイベントリスナーが増える。やばい原因: beforeDestroy()もDestroy()も実行されてない
プリントデバッグしてみると、ページ離脱時にbeforeDestroy()もDestroy()も実行されてない。
再起動してもキャッシュを消しても実行されないという不思議。解決策: ナビゲーションガードを使う。
どうにもならなくなったらbeforeRouteLeave(to, from, next)で削除すればおk。
ナビゲーションガード | Vue Router
Vue-Routerのナビゲーションガードを使ってみる - Qiitauni.js<script> //中略 beforeRouteLeave(to, from, next) { if (to.name) { next(); window.removeEventListener("orientationchange", this.checkRotate, false); return; } return; }, </script>別のプロジェクトでは再現しなかったので、多分もう使うことはほぼないだろうけど備忘録代わりに書いておく。
原因がわかる人がいたらだれか教えてください。
- 投稿日:2020-01-23T14:07:28+09:00
ReactNative(Expo)でオススメしたいライブラリ
ReactNativeを開発していて、しばらくしてからにこんなライブラリあったんだ!!
ということがしばしばあったので、ReactNativeの開発を始めようと思っている方向けに、オススメのライブラリを紹介していこうと思います。採用したライブラリ
native-base
当初、react-native-elementを導入していたが、
styleを書く手間を省けない。
アクションシートやツールチップなど、OS間の差分を吸収してくれるUIがなかっためにnative-baseに移行した。
githubのスターではreact-native-elementが上回っているが、
工数削減を目的とした場合は、native-baseの方が良いと思う。react-native-swiper
intro系のライブラリと比較してもswiperで事足りそうなので実装が手軽な点が魅 a了。
そのほかにも紹介予定のライブラリー・・・
lottie-react-native
react-navigation-redux-helpers
sentry-expo
expo-analytics-amplitude
expo-analytics
- 投稿日:2020-01-23T14:02:11+09:00
ReactNative(Expo)でイベントトラッキング 〜expo-analyticsの使い方をざっくりまとめる〜
ReactNativeでイベントトラッキングする時に候補に上がるのは、
- firebase
- amplitude
- expo-analytics
の3つだと思います。
今回は、そのうちの1つexpo-analyticsの紹介です。expo-analyticsはWeb版GoogleAnalyticsのイベントトラッキングをReactNativeで簡単に利用できるようにしてくれているライブラリです。
正直、WebサイトでGAのイベントトラッキングをしたことがある人は、
使い方がほぼ同じなのでこの記事を見る必要はないです笑リポジトリURL
https://github.com/ryanvanderpol/expo-analyticsEvents [イベント]
GoogleAnalyticsでの説明
https://developers.google.com/analytics/devguides/collection/analyticsjs/events?hl=jaanalytics.event(new Event('eventCategory', 'eventAction', 'eventLabel', ‘eventValue’)) 例) analytics.event(new Event('Video', 'Play', 'The Big Lebowski', 123)) .then(() => console.log("success")) .catch(e => console.log(e.message));引数の対応表
引数 フィールド名 値の型 必須 説明 第1引数 eventCategory テキスト はい 通常はインタラクションに使用されたオブジェクト(例: 'Video') 第2引数 eventAction テキスト はい インタラクションの種類(例: 'play') 第3引数 eventLabel テキスト いいえ イベントを分類する際に使用(例: 'Fall Campaign') 第4引数 eventValue 整数 いいえ イベントに関連する数値(例: 42) Custom Dimensions [カスタムディメンション]
GoogleAnalyticsでの説明
https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters#cd_ログイン状態や有料ユーザーなど独自で分析軸を定義したい時に使用する。
※最大20個までなのでトラッキングクラスでインデックスを管理する必要がある
Google analytics版の解説
https://webtan.impress.co.jp/e/2017/09/21/26869GooleAnalytics管理画面の設定手順
GA管理画面で管理→プロパティ→カスタム定義にて、カスタムディメンションを作成。
「ユーザー」「セッション」「ヒット」「商品」のどれに紐づくかを設定。実装
analytics.addCustomDimension(1(作成したディメンションのインデックス番号), 'Comedy’(ディメンション名)) 使いところわからないけど、削除も可能。 analytics.removeCustomDimension(1);Custom Metrics [カスタム指標]
GoogleAnalyticsでの説明
https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters#cm_数値の独自定義をしたい時に使用する、
こちらも最大20個までなのでトラッキングクラスでインデックスを管理する必要がある想定イメージ
- 電話のコール数
- アプリの起動回数
- 表示時間
フォーマット
- 整数
- 通過
- 時間
実装例
analytics.addCustomMetric(1, 15); analytics.removeCustomMetric(1);デバックモード
デバックモードをtrueにするとconsole.logに表示される。
const analytics = new Analytics('UA-XXXXXX-Y', null, { debug: true })
- 投稿日:2020-01-23T13:41:20+09:00
1時間でChrome拡張作ってみた!
なぜか急にChrome拡張というものを作ってみたくなったので、作ってみました!
作ったもの
Instagramってログインしてないと、全部の写真見えないんです...。
アカウント作ってログインすればいいって話なんですけど、見るためだけにアカウント作るのがなんかあれだったので、ログインを促すポップアップが表示されないようにしました!
ソースコードはGithubリポジトリを参照してください ↓
https://github.com/Michin0suke/instagram-extension-pack1. マニフェストファイルをつくる!
所要時間:15分
Chrome拡張にはマニフェストファイル(
manifest.json
)というのが必須らしいです。この記事を参考にさせていただきました!{ "manifest_version": 2, "name": "Instagram without login", "version": "1.0.0", "description": "Chrome extension to remove login prompts on Instagram sites", "icons": { "16": "icon16.png", "48": "icon48.png", "128": "icon128.png" }, "content_scripts": [ { "matches": [ "https://www.instagram.com/*" ], "js": [ "script.js" ] } ], }こんな感じで完成!
description
は英語書けないので、Google翻訳にやってもらいました笑無理に英語を使わなくても、日本語でも大丈夫です。
2. アイコンを用意!
所要時間:15分
Illustratorでテキトーに作りました笑
なんかアンチインスタグラムみたいなアイコンになっちゃったけど配信はしないのでOK!
マニフェストに指定した通り、16px*16px、19px*19px、48px*48px、128px*128pxを用意しました。
3. スクリプト書くよ!
所要時間:20分
script.js
に以下を書き込みます。document.querySelector('.ctQZg').remove() const interval = setInterval(() => { if (document.querySelector('.RnEpo.Yx5HN')) { document.querySelector('.RnEpo.Yx5HN').remove(); setTimeout(() => document.body.style.overflow = 'visible', 50); clearInterval(interval); } }, 100);今回のスクリプトは数行なので簡単!
JavaScriptの簡単な説明をすると、0.1秒毎にログインポップアップが表示されているか確認して、表示されていれば消して、
body
に付与されるスクロール禁止も外すというものです。ポップアップを消したあとは、clearInterval()
で繰り返し実行を止めてます。4. Chromeで実際に使ってみる!
所要時間:10分
『パッケージ化されていない拡張機能を読み込む』でもいいんですが、毎回警告が表示されるらしいので、chrome://extensions/から『拡張機能をパッケージ化』してみます。
下の画像だとダウンロード直下にソースファイルを含むディレクトリを置いていますが、この記事の最後にあるファイル構成のように2重のディレクトリ構成にしておいたほうが、
.pem
や.crx
が管理しやすくて良さそうです。ポチポチして鍵を作って、生成された
.crx
ファイルをChromeにドラッグ&ドロップ!Before
After
ポップアップは一瞬表示されるけど、すぐ無くなって快適!
これでインスタのアカウントなくてもインスタグラムを楽しめます!
(なぜそこまでしてアカウントを作ろうとしないのか)
ファイル構成
instagram-extension-pack ├── instagram-extension │ ├── icon128.png │ ├── icon16.png │ ├── icon19.png │ ├── icon48.png │ ├── manifest.json │ └── script.js ├── instagram-extension.crx └── instagram-extension.pem感想
思ったより簡単に作れた!
- 投稿日:2020-01-23T13:08:20+09:00
Javascript アロー関数を簡単にまとめて学ぶ
はじめに
アロー関数?書けるよ。あまり理解してないけどって人向けに具体的な使用例の一部をお伝えする記事です。
アロー関数とは、アロー関数式は、より短く記述できる、通常の function 式の代替構文です。また、this, arguments, super, new.target を束縛しません。アロー関数式は、メソッドでない関数に最適で、コンストラクタとして使うことはできません。
つまり従来より簡潔に書けるし明瞭な構文で書けるってこと。
ちなみに関数とメソッドの違いは、
- 単独で 呼び出せる のが関数
myfunc(); <- こんな形の奴
- 単独で 呼び出せない のが関数
var str = 'user1,user2,user3'; var result = str.split(','); <- メソッド従来の関数の書き方との書き換え方
引数の値を2倍にして返り値で返す関数で例えると、
- 従来の書き方
index.jsfunction doubleUp(num){ return num * 2; }
- アロー関数で書き換えた書き方 その1
index.jsconst doubleUp = (num) => { return num * 2; }
- アロー関数で書き換えた書き方 その2.0 (引数が一つの場合に限る)
index.jsconst doubleUp = num => { return num * 2; }
- アロー関数で書き換えた書き方 その2.1 (引数が一つ以上の場合)
index.jsconst doubleUp = (a, b) => a * b;
- アロー関数で書き換えた書き方 その3.0 (関数の処理が returnで一行の場合に限る)
index.jsconst doubleUp = num => num * 2;
- アロー関数で書き換えた書き方 その3.1 (関数の処理が returnで一行以上の場合)
index.jsconst doubleUp = num => { alert('hoge'); return num * 2; }アロー関数と無名関数の挙動って一緒なの?
結論から言うと一緒じゃない。
関数内で this を使う場合は挙動が変わってくるんじゃい。
以下の関数を見るんじゃい。index.jslet person = { age: "30", callName: function(){ console.log(this.age); //"30" が出力される window.setTimeout(function() { console.log(this.age) // undefined が出力される }, 1000); } };console.log(this)で両方とも出力してもらえればわかると思うんじゃい。
window に関してはそもそも callName というオブジェクトがないんじゃい。
なので参照しようとすると undefined になるんじゃい。しかし上記のようなオブジェクトにおいてageプロパティをwindow.setTimeout...内で使用したいというケースはよくあるケース。
ではどうするかというと、こうする。
- やり方 その1
index.jslet person = { age: "30", callName: function(){ console.log(this.age); // "30" が出力される window.setTimeout(function() { console.log(this.age) // "30" が出力される }.bind(this), 1000); } };bind()は引数とされている this と callName直下のスコープ内にある this を同じものとする機能があるんじゃい。
- やり方 その2index.jslet person = { age: "30", callName: function(){ console.log(this.age); //"30" が出力される let hoge = this; window.setTimeout(function() { console.log(hoge.age) //"30" が出力される }, 1000); } };hoge変数にpersonオブジェクトのスコープを代入してあげるんじゃい。
- やり方 その3
index.jslet person = { age: "30", callName: function(){ console.log(this.age); //"30" が出力される window.setTimeout(function() { console.log(this.age) //"30" が出力される }.call(this), 1000); } };callメソッドを使うんじゃい。
- やり方 その4
index.jslet person = { age: "30", callName: function(){ console.log(this.age); //"30" が出力される window.setTimeout(() => { console.log(this.age) //"30" が出力される }, 1000); } };アロー関数を使うんじゃい。
アロー関数内を使うと、アロー関数内でthisが定義されないんじゃい。
javascriptの性質上, アロー関数内でthisを見つけられない場合スコープチェーンを辿って
上の階層に同じ変数名のものを探すんじゃい。最終的にグローバル変数まで探して見つかれなければ undefined を返すんじゃい。
こういう使い方があるんじゃい。以上じゃい。
ps:
こういうやり方もあるよ〜」とかあれば教えて欲しいんじゃい。
勉強になるんじゃい。
- 投稿日:2020-01-23T13:07:14+09:00
VisualForce で rerender すると4バイト文字が化ける
問題点
SalesForce の VisualForce で rerender すると
4バイト文字が文字化けするサンプル
単純にボタン押したら再描画するだけの画面を作成
Test.vfp<apex:page standardController="User" extensions="TestController" title="Test"> <div>rerender範囲外:?野家</div> <apex:form id="frm"> <div>rerender範囲内:?野家</div> <apex:commandButton value="rerender" action="{!act}" status="reloadStatus" rerender="frm" /> </apex:form> </apex:page>TestController.apxcpublic class TestController { public TestController(ApexPages.StandardController controller) { } public void act(){ return; } }これで
rerender
ボタンを押下すると
rerender
範囲内の「?野家
」が「ஷ野家
」になってしまう
※範囲外に関しては変更なし調査
文字コード
とりあえず「
?
」と「ஷ
」のコードポイント調べたTest.jsconsole.log('ஷ'.codePointAt().toString(16)); // 0x0bb7 console.log('?'.codePointAt().toString(16)); // 0x20bb7絶対これや
サロゲートペア文字(4バイト文字)がおかしくなる模様欠損箇所
rerender
の範囲外は正常なことからapex:commandButton
が怪しい
ボタン押下時は Ajax で Apex にアクセスしてるようなのでレスポンス内容を見てみるResponse<?xml version="1.0"?> <html lang="en_US" xmlns="http://www.w3.org/1999/xhtml"><head> <!--省略--> </head> <body> <form id="j_id0:frm" name="j_id0:frm" method="post" action="https://hogehoge.visual.force.com/apex/Test?core.apexpages.request.devconsole=1" enctype="application/x-www-form-urlencoded"> <input type="hidden" name="j_id0:frm" value="j_id0:frm" /> <div>rerender範囲内:ஷ野家</div> <!--省略--> </body> </html>もう化けとる
このへん見る限り XML ではサロゲートペア使えないっぽい数値文字参照
𠮷
にすればいけるかな~と思ったけど
状況変わらず Ajax のレスポンス時点で文字化けしてた。対策
苦肉の策としてエスケープを挟むことにした
画面側でデコードできればURLエンコードとか他のでもいいと思うTest.vfp<apex:page standardController="User" extensions="TestController" title="Test"> <div>rerender範囲外:?野家</div> <apex:form id="frm"> <div id="yoshi" style="visibility:hidden;">%uD842%uDFB7</div> <apex:commandButton value="rerender" action="{!act}" status="reloadStatus" rerender="frm" /> <script> var ysdiv = document.getElementById('yoshi') ysdiv.innerHTML = unescape(ysdiv.innerHTML); ysdiv.style.visibility = "visible"; </script> </apex:form> </apex:page>エスケープ後の文字列を表示してJSで無理矢理元に戻す感じ
<script>
をrerender
内に置かないと動かないので注意もうちょいきれいな対策したい
- 投稿日:2020-01-23T11:49:46+09:00
あっさり読むrails④(非同期通信)
はじめに
JSを使った非同期通信を簡単に書いてみます。
前提
使用するのは、
Ruby on rails
Haml
jQuery
です。
CSSは特に指定しません。実行
次のファイルを用意します。
sample.haml.html= form_with model:@sample, local: true do |f| = f.text_area :name, placeholder: "サンプル", class: "sample-form" .add-textこれがビューデータとなります。
sample.js$(function(){ $(".sample-form").on("change" functon(){ var sampletext = $(this).val; $(".add-text").text(sampletext): }) });これが非同期処理の中身になります。
これで、テキストエリアに文章を入力すれば、同じ文章が
.add-text
の部分に表示されます。※随時更新します
- 投稿日:2020-01-23T11:49:46+09:00
あっさり読むrails④(非同期処理)
はじめに
JSを使った非同期処理を簡単に書いてみます。
前提
使用するのは、
Ruby on rails
Haml
jQuery
です。
CSSは特に指定しません。実行
次のファイルを用意します。
Gemfilegem 'jquery-railsこれを記述して、
bundle install
します。
(ディレクトリが、アプリのディレクトリになっている事を確認。pwd
というコマンドで確認可能)application.js//= require jqueryこの記述を忘れるとエラーになります(
$とは何ですか?
という感じのエラー)sample.haml.html= form_with model:@sample, local: true do |f| = f.text_area :name, placeholder: "サンプル", class: "sample-form" .add-textこれがビューデータとなります。
sample.js$(function(){ $(".sample-form").on("change" functon(){ var sampletext = $(this).val; $(".add-text").text(sampletext): }) });これが非同期処理の中身になります。
これで、テキストエリアに文章を入力すれば、同じ文章が
.add-text
の部分に表示されます。※随時更新します
- 投稿日:2020-01-23T08:56:45+09:00
Node.js、Web Speech APIを使って音声認識を出力
概要
WebSpeechAPIを使用して聞き取った音声の文字おこしをブラウザ上に表示させます。
作成方法
1.WebSpeechAPIを含むhtmlフォルダの作成
新規フォルダを作成し、その中にindex.htmlを作成。
index.html<!-- index_voice.html --> <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>音声認識サンプル</title> </head> <body> <h2>音声認識サンプル</h2> <button id="btn">start</button> <div id="content"></div> <script> //ここに音声認識の処理を書いていく </script> <body> <html>音声認識をするための処理をscriptタグ内に書いていきます。
index.html//ここに音声認識の処理を書いていく const speech = new webkitSpeechRecognition(); speech.lang = 'ja-JP'; </script>
音声認識を実行する準備はこの2行で完了です。
webkitSpeechRecognition()を定義し、langをja-JPにすることで日本語に対応したWeb Speech APIが使えるようになります。
そしてこれをイベントで実行出来るようにしていきます。index.html//ここに音声認識の処理を書いていく const speech = new webkitSpeechRecognition(); speech.lang = 'ja-JP'; //---------------追記---------------// const btn = document.getElementById('btn'); const content = document.getElementById('content'); btn.addEventListener('click' , function() { // 音声認識をスタート speech.start(); }); speech.addEventListener('result' , function(e) { // 音声認識で取得した情報を、コンソール画面に表示 console.log(e); //---------------追記---------------// // 音声認識で取得した情報を、HTMLに表示 const text = e.results[0][0].transcript; content.innerText = text; //--------------------------------// }); //--------------------------------// </script>
2.サーバー立ち上げ
node.js Expressを使ってローカルサーバーの立ち上げを行います。
Web speech Api-sampleというフォルダー作成します。初期設定します。
npm init -y必要なライブラリをインストールします。
npm i body-parser expressindex.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)");こちらをindex.jsで保存します。
index.js と同じ階層に public フォルダを作りその中に 先ほど作成したindex.html を格納します。node index.js実行します。
http://localhost:8080/ でアクセス。
スタートボタンをおして話し、終わりにまたボタンを押すと。
これでひとまず完了です。次に使用してHerokuにデプロイしていきます。(続く)
考察
まずは音声認識APIをつかってアウトプットすることができました。音声の聞き取りの精度もパソコンを目の前にあるような状況、会議とかだったら問題なく拾える制度でした。
次は、Vue.jsでデザインを整えていきます。
また、いまのままではただの文字の羅列になるので、これをどう編集させていくか、プログラムで機能を追加できていければと思います。
- 投稿日:2020-01-23T08:13:35+09:00
【Rails】 DataTables のテーブルに関連付けされたモデルのデータを表示する方法
はじめに
DataTableへデータを渡すときに json 形式に変換する必要があります。
関連付けされたモデルのデータを簡単に json 形式に変換することができます。関連リンク
関連リンクを下記に載せておくので、必要であれば参考にしてください。。
- 【Rails】 DataTables 実装方法
- 【Rails】 DataTables 動的にカラムを変更する方法
- 【Rails】 DataTables 検索結果の保持方法
関連付けされたモデルのデータを表示する方法
as_json の include をすることで user に関連付けされた post のデータを一緒に json のデータとして作成してくれます。
app/datatables/users_datatable.rbclass UsersDatatable # *** 省略 *** def as_json(options = {}) { recordsTotal: User.count, # 取得件数 recordsFiltered: users.total_count, # フィルター前の全件数 # user モデルに関連付けされた post モデルを include して json ファイルを作成する。 data: users.as_json(include: :post), # 表データ } end # *** 省略 *** endまとめ
こちらは突き詰めると、 DataTable 特有の使い方ではなく、 json ファイルの作り方の記事になってしまっています。
使い方が全くわからないという方には少しは参考になるかなーと思い投稿させていただきました。
- 投稿日:2020-01-23T06:46:34+09:00
ブログカードを支える技術
ブログカードとは以下のようにリンクをちょっとリッチに表示してくれる機能のことです。以前はこれってどうやって実現しているんだろうと不思議に思っていました。
上記のようなブログカードは「はてなブログ」や「WordPress」等のブログサービスでよく見かけますが、基本的にこれらのブログカードはリンク先のURLを指定するだけで自動的に生成されています。本記事では上記のようなブログカードを支える技術について解説します。(本記事は自分のブログからの転載記事です。)
はじめに
本記事では、ブログカード1の表示に使われる一般的な技術の解説およびJavaScriptによる実装を行います。普段何気なく見たり使ったりしているブログカードの技術に興味がある人におすすめします。
ブログカードの要素技術
まずはブログカードを実現するための要素技術について解説します。
ブログカードの構成
ブログカードは主に「タイトル」、「説明」、「画像」から成ります。オプションで「favicon」、「サイト名」、「ソーシャルカウント」を表示する場合もあります。
ブログカードの主な情報源
ブログカードの主な情報源は以下の3つになりますが、メインの「タイトル」、「説明」、「画像」といった情報を提供しているのはOpen Graph Protocolになります。
- Open Graph Protocol(OGP)
- favicon
- ソーシャルカウント
Open Graph Protocol
Open Graph Protocolの説明の前提知識として、まずはソーシャルグラフについて説明します。ソーシャルグラフとはFacebookやTwitter等のSNS(ソーシャル・ネットワーク・サービス)において、人と人の繋がりである「ソーシャル・ネットワーク」を点と線で可視化したものです。「Graph」の由来は、点と点の結びつきに関する数学理論であるグラフ理論から来ています。
上記のようなソーシャルグラフの構築には点と点をつないで線にする仕組みが必要です。そのために考案されたのがOpen Graph Protocol(OGP)です。ソーシャルグラフにおける「点」は「人」を表していますが、Webの世界ではWebページを「人」とみなしてソーシャルグラフを構築します。WebページはHTMLで記述されHTTPプロトコルを用いてやり取りされますが、基本的にはOGPもその仕組みの上に成り立っています。具体的には以下の図のように単純な仕組みでメタデータのフォーマットのみがOGPで規定されていて、それ以外は既存のHTTPやHTMLの仕組みをそのまま利用しています。メタデータに関してはHTMLのヘッダに記述されているのでHTTPのHEAD
メソッドで取得でき、Webページ全体を取得しなくても済むようになっています。OGPで必須とされているメタデータは
og:title
、og:type
、og:image
、og:url
の4つですが、og:site_name
やog:description
もよく利用されます。具体的なHTMLのヘッダに埋め込まれたメタデータの例は以下のようになります。<meta property="og:type" content="article"> <meta property="og:title" content="とにかくかっこいいブログの作り方"> <meta property="og:url" content="https://tonikaku-kakkoii-blog.com/articles/how_to_create_kakkoii_blog/index.html"> <meta property="og:site_name" content="tonikaku-kakkoii-blog.com"> <meta property="og:description" content="とにかくかっこいいブログサイトを作るにはどうすればいいのかお悩みの方も多くいると思います。本記事ではデザイン、技術、記事内容の3つの視点で解説します。"> <meta property="og:image" content="https://https://tonikaku-kakkoii-blog.com/ore_kakkoii.png">favicon
「favicon」はWebサイトのシンボルとして表示される画像のことでブラウザのタブやブックマークで表示されます。もともとInternet Explorer 5で「お気に入り」に画像を表示するための技術で、「favicon」の由来は「favarite icon」だとされています。
faviconの画像形式には特に決まりがなくブラウザ依存ですが、伝統的にはICO形式です。ICO形式はWindowsのアイコン形式で、以下のように正方形の任意の画像サイズを複数格納できるようになっています2。
Webサイトにfaviconを設定する伝統的な手法はWebサイトのルートディレクトリにfavicon.ico
というファイル名でICO形式のファイルを配置することです。しかし近年のブラウザではそれ以外にも以下のようにHTMLのヘッダでfaviconを指定することもできます。<link rel="icon" href="tonikaku-kakkoii-blog.com/favicon.ico" />MIMEタイプを指定することによってgifやpngといった画像形式にも対応できます。
<link rel="icon" type="image/png" href="tonikaku-kakkoii-blog.com/favicon.png" />favicon取得用API
faviconはこれまで説明してきたとおり画像形式も配置場所もばらばらなので、単純にブログカードに表示させることはできません。クライアント側のJavaScriptでもある程度はできるかもしれませんがICO形式等のマルチ画像のフォーマットがあると厳しいです。そこでブログカード用にfavicon取得用のAPIをサーバサイドで実装するのが一般的です。favicon取得用APIではfaviconを取得してブログカードの表示に適切は画像フォーマットとサイズに変換してクライアントに返却します。このようなAPIは自作することも公開されているサービスを利用することもできます。以下にfavicon取得用APIの例を掲載します3。
- GoogleのAPI
- HatenaのAPI
ソーシャルカウント
ソーシャルカウントは一般的にSNSにおける「人気」を表す指標のことで、例えばはてなブックマークの数とかFacebookのいいねの数になります。ソーシャルカウントを取得する方法は、サービスによってそれぞれ異なります。具体例として以下にはてなブックマーク数を取得するAPIを掲載します。
- はてなブックマークの取得API
ブログカードの実装
今回は以下のようなブログカードをサーバサイドJavaScript(Node.js)で実装してみたいと思います。
JavaScriptでHTMLを出力するイメージです。今回は
getTag
関数を実装し、戻り値はPromiseとします。利用方法は以下のとおりですgetTag({url: "https://hinastory.github.io/cats-cats-cats/2019/12/29/visualize_ruby_development_by_file/"}).then(e => console.log(e))HTMLの骨組み
まずはHTMLを出力するにあたって骨組みを考えます。最初は出力するイメージを再現できる素直な入れ子構造を考えます。ポイントはブログカードをクリックしたらリンク先に飛びたいのでリンクを示す
aタグ
でなるべく広く囲むことです。次にスタイル(CSS)を当てることを考えて不足しているレイヤーがあれば調整します。最終的にできた骨組みは以下になりました。aタグと書かれた箇所以外は全てdivタグで、class属性の骨格を示しています。
- hbc-blog-card
- hbc-link-wrap
- hbc-link(aタグ)
- hbc-card
- hbc-info
- hbc-favicon
- hbc-site-name
- hbc-contents
- hbc-thumbnail
- hbc-text
- hbc-title
- hbc-url
- hbc-description
JavaScriptにおける実装
実装は以下のとおりです。基本的には上記の骨格どうりにHTMLタグを組み立てているだけです。今回はソーシャルリンクの実装は行っていませんが、実装はそれほど難しくはないはずです。
blog_card.jsconst util = require('hexo-util'); const ogs = require('open-graph-scraper'); // Open Graph Protocol解析用 const escapeHTML = require('escape-html'); const url = require('url'); const descriptionLength = 140; const className = 'blog-card'; const faviconAPI = 'http://favicon.hatena.ne.jp/?url=$URL'; function getTag(options){ return ogs(options) .then(function (result) { const ogp = result.data; const info = getInfo(options, ogp); const contents = getContents(options, ogp); const card = util.htmlTag('div', { class: 'hbc-card' }, info + contents, false); const link = util.htmlTag('a', { class: 'hbc-link', href: options.url, target: options.target, rel: options.rel }, card, false); const linkWrap = util.htmlTag('div', { class: 'hbc-link-wrap' }, link, false); const tag = util.htmlTag('div', { class: className }, linkWrap, false); return tag; }) .catch(function (error) { console.log('error:', error); return ''; }); } function getInfo(options, ogp) { let name = ''; const urlParsed = url.parse(options.url); // ogSiteNameがなかった場合にホスト名を表示 if (ogp.hasOwnProperty('ogSiteName')) { name = ogp.ogSiteName; } else { name = urlParsed.hostname; } const siteName = util.htmlTag('div', { class: 'hbc-site-name' }, name); let api = faviconAPI.replace('$DOMAIN', encodeURIComponent(urlParsed.hostname)); api = api.replace('$URL', options.url); const favicon = util.htmlTag('img', { class: 'hbc-favicon', src: api } , ''); return util.htmlTag('div', { class: 'hbc-info' }, favicon + siteName, false); } function getContents(options, ogp) { let contents = ''; let text = ''; if (ogp.hasOwnProperty('ogImage')) { const image = util.htmlTag('img', { src: ogp.ogImage.url } , ''); contents = util.htmlTag('div', { class: 'hbc-thumbnail' }, image, false); } text += util.htmlTag('div', { class: 'hbc-title' }, escapeHTML(ogp.ogTitle), false); text += util.htmlTag('div', { class: 'hbc-url' }, options.url, false); if (ogp.hasOwnProperty('ogDescription')) { const description = adjustLength(ogp.ogDescription); text += util.htmlTag('div', { class: 'hbc-description' }, description); } contents += util.htmlTag('div', { class: 'hbc-text' }, text, false); return util.htmlTag('div', { class: 'hbc-contents' }, contents, false); } // 内容が長い場合に切り詰める function adjustLength(description) { if (description && description.length > descriptionLength) { description = description.slice(0, descriptionLength) + '…'; } return description; } // 実行したい場合は以下のようにする // getTag({url: "https://hinastory.github.io/cats-cats-cats/2019/12/29/visualize_ruby_development_by_file/"}).then(e => console.log(e))実行結果
上記のプログラムの実行にはnode.jsとnpmによるライブラリ(
hexo-util
とopen-graph-scraper
)のインストールが必要ですが、興味がある方は実行してみてください。実行結果(HTML)は以下のとおりです。適当にスタイルを当てています。See the Pen blog_card by hinastory (@hinastory) on CodePen.
まとめ
本記事ではブログカードで使われている技術として以下の3つの技術について解説を行い、JavaScriptによるブログカードの実装例を紹介しました。
- Open Graph Protocol
- favicon
- ソーシャルカウント
本記事がブログカードを支える技術の理解の一助となれば幸いです。
参考文献
- 投稿日:2020-01-23T01:01:13+09:00
Windows 10のChromeで文字認識(OCR)
Shape Detection APIのTextDetectorで Text Detection not implemented. というエラーが出たのでメモ。
【重要】Windows 10にEnglishの言語機能(光学式文字認識)をインストール
日本語のWindowsには、Englishの言語機能は入ってないので注意が必要。
設定を開く
時刻と言語を選択
言語タブを選択し、優先する言語を追加する
United Statesを選択し、次へ
光学式文字認識をインストール
★再☆起★動☆
Englishの言語が追加されたことを確認して、Windowsを再起動。
これで、英語版のWindows.Media.Ocrが使えるようになりました。
ChromeのExperimental Web Platform featuresを有効化
chrome://flags/#enable-experimental-web-platform-featuresにアクセスし、Enabledに変更し、ChromeをRelaunch
画像からOCR
画像を選択すると自動でOCRを行います。
See the Pen Windows 10 Chrome OCR by John Doe (@04) on CodePen.
WebカメラからリアルタイムOCR
WebカメラからOCRを行います。リアルタイムでBoundingBoxが付きます。
[Violation] Feature policy violation: camera is not allowed in this document. で動かないので右上の EDIT ON CODEPEN を押して動かしてください。
See the Pen Windows 10 Chrome Realtime OCR by John Doe (@04) on CodePen.
FullHD、フルスクリーンでOCRするサンプルもあります。
https://codepen.io/04/pen/wvBRQYgまとめ
- 英数字や記号しか認識できませんが、Tesseract.jsよりも高速で精度が高いので追跡番号などを認識するのにおすすめです。
- オフラインで動作するので、Google VisionやAmazon Textractが使えない環境にもおすすめです。画像をクラウドにアップすると時間がかかりますが、エッジでやれば、高速でお金もかかりません。
- 投稿日:2020-01-23T00:52:54+09:00
html2canvasを使ってVue.js のサイトを画像で切り取る
html2canvasを使いたい
今回はVue.jsで作ったhtmlをhtml2canvasで画像にしたいと思います
参考資料
【2019年度版】Windowsでhtmlを画像化する方法(html2canvasの使い方)
【資料2】html2canvasとVue.jsでつくるコラ画像ジェネレータ
【エラー用資料】Vue.jsの初歩的なミス環境
Windows 10
Visual Studio Code: 1.40.1 (system setup)
Chrome: 76.0.3809.146
Node.js: 12.4.0
V8: 7.6.303.31-electron.0
OS: Windows_NT x64 10.0.18362index.htmlを作成
基本コード
<!DOCTYPE html> <html lang="ja" dir="ltr"> <head> <meta charset="utf-8"> <title>"html2canvas" </title> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <!--Vue.jsの適用headに入れると全部に適応される--> <script src="https://cdn.jsdelivr.net/npm/html2canvas@1.0.0-alpha.12/dist/html2canvas.min.js"></script> <!--html2canvasの適用headに入れると全部に適応される--> <style type="text/css"> /* コラ画像用のCSS */ #preview { overflow: auto; width: 100%; } /* #preview_inner { background: url(https://3.bp.blogspot.com/-cPqdLavQBXA/UZNyKhdm8RI/AAAAAAAASiM/NQy6g-muUK0/s800/syougatsu2_omijikuji2.png) no-repeat left bottom; background-size: 200px auto; position: relative; padding-bottom: 250px; width: 600px; }*/ #baloon { position: relative; border: 4px solid #ff8888; background: #fff; border-radius: 8px; display: inline-block; font-weight: bold; font-size: 1.2rem; color: #444; padding: 10px; min-width: 200px; white-space: pre-wrap; } #baloon:before, #baloon:after { content: ''; display: block; width: 0; height: 0; border-style: solid; border-width: 50px 15px 0 15px; border-color: #fff transparent transparent transparent; position: absolute; left: 20px; bottom: -50px; } #baloon:before { left: 16px; bottom: -60px; border-width: 58px 19px 0 19px; border-color: #ff8888 transparent transparent transparent; } </style> </head> <body> <div id="preview"> <!--<div id="preview_inner">--> <div id="baloon"> <p>{{ message }}</p> </div> <!--</div>--> <p> <img width="200" src="https://3.bp.blogspot.com/-cPqdLavQBXA/UZNyKhdm8RI/AAAAAAAASiM/NQy6g-muUK0/s800/syougatsu2_omijikuji2.png"> </p> <textarea class="form-control" v-model="message"></textarea> </div> <div> <button class="btn btn-primary btn-block" v-on:click="generate">画像を生成</button> </div> <script> var app = new Vue({ el: '#preview', data: { message: '' }, methods: { } }) </script> <script> var generate = html2canvas(document.querySelector("#preview")).then(canvas => { document.body.appendChild(canvas) }); </script> </body> </html>HTML
にアクセス。一部は画像に変換できたものの、
ターミナルとブラウザで検証をして見つかるエラーは消せたのですが
画像を含めて生成はできませんでした。
文字とかは画像化できるのですが、、、あと、画像を生成を押す前からずっと出ています。
せっかく作ったボタンの意味!!
ここの原因はたぶんここ<script> var generate = html2canvas(document.querySelector("#preview")).then(canvas => { document.body.appendChild(canvas) }); </script>見るからにうまく組めていない怪しいコード。
ボタンを押した関数のgenerateで動いて欲しい、かつhtml2canvasを使いたい。
色々試して書いたのですが思いついたものはどれも駄目でした(´;ω;`)あとはおみくじ画像がうまく画像化されない件。
こちらもstyleに埋め込んでいるから駄目なのかとimgタグでdivの中に持ってきたりしましたがどちらも駄目でした。【エラー】Uncaught ReferenceError: Vue is not defined
これはそもそもHTMLとVue.jsの仕組みがきちんと分かっていない為に起きたエラーでした。
備忘録的に分かった事を書いておこうと思います。
下記コードの黄色線を引いてる部分を/bodyの直前にもってきていたのが失敗で、
読み込めていませんでした。完成品
html2canvasとVue.jsは使えたものの目指していた形にはなりませんでした。
アドバイスありましたら随時募集中です。もしくは万が一少しでも参考になりましたら幸いです。
ありがとうございました。
- 投稿日:2020-01-23T00:46:12+09:00
俺的PWAの振り返り
はじめに
PWAに手を出したものの、手を出してからほったらかして1年近く。
ちょっと復習しておこうと思います。
理解が充分でないところはこれから深めていきます。
もし間違っていることがあればどんどん突っ込んでくださいw拙作アプリの「LTタイマー」を題材に進めていこうかと思います。
LTタイマーを作ったのが1年以上前なのでもしかしたらやり方が古いかもしれないので、気づいた時点でアップデートしていきたい。
参考サイトのソースをベースとして、少し手を加えています。参考
PWA: ServiceWorkerを使って、キャッシュをコントロールする(オフラインハンドリング)
MDN ウェブアプリマニフェスト
僕の考えた最強のService Workerキャッシュ戦略で爆速サービスを作ったPWAとは
PWAとはProgressive Web Appsの略です。
ネイティブアプリのような感じのWebアプリを作成できます。
あくまでネイティブアプリのような感じなので、ネイティブアプリにはできるけどPWAではできないこともあります。PWAでアプリを作成・動作させるためには、PWAに対応したWebブラウザが必要になります。
PWAに対応していないブラウザでは、ただのWebサイトとして扱われるので心配はいりません。
また、localhostまたはHTTPSでないと、PWAは利用できません。PWA自体は、ウェブアプリマニフェスト(manifest.json) + ServiceWorker + Casch API の組み合わせで成り立っています。
ServiceWorker
Service Workerはブラウザにインストールされ、バックグランドで常駐します。
まだやったことはありませんが、Service Workerでプッシュ通知を扱うことができるとのこと。
あと、ServiceWorkerはDOMにアクセスできません。manifest.json
manifest.jsonはPWAの設定をJSON形式で記載したものです。
アプリの名前、表示方法、アイコンなどなどが設定できます。Casch API
Casch APIをつかって、ローカルストレージに読み込んだコンテンツを保存したり、読み込んだりします。
ここではローカルストレージをメインで紹介していますが、実はローカルストレージのほかにSession Strage、IndexedDB、WebSQLなども扱えるとのことです、この辺も、調べないとね。LTタイマーの構成
GitHubを見てもらうとわかりますが、こんな感じです。
ちなみに、GitHubの内容は古い場合がありますので注意してください。
manifest.jsonがマニフェスト
sw.jsがサービスワーカーの実装になります。処理の概要
まずはServiceWorkerの登録処理になります。
登録処理はindex.htmlでおこなっていますindex.htmlif ('serviceWorker' in navigator) { navigator.serviceWorker.register('./sw.js') .then(function(registration){ console.log('Service Worker install success.'); registration.onupdatefound = function() { // 更新があると呼び出される console.log('Update : ServiceWorker'); registration.update(); } }) .catch((error) => { // registration failed console.log('register faild : ', error); }); }まず、ServiceWorkerに対応しているかどうかのチェックが必要になります。
それを最初のif文で行っています。
次に、ServiceWorkerで動かすJavaScriptファイルを登録します。index.htmlnavigator.serviceWorker.register('./sw.js')ServiceWorkerに変更があった場合の対応として、registration.onupdatefoundメソッドを実装しています。
index.htmlregistration.onupdatefound = function() { // 更新があると呼び出される console.log('Update : ServiceWorker'); registration.update(); }sw.jsの内容です。
まず、ServiceWorkerではファイルのキャッシュを行います。
キャッシュするファイルの一覧を配列で宣言しています。
CACHE_NAMEとVERSIONを結合してるのは、ServiceWorkerの更新が発生した際に、キャッシュの更新を行うことを目的としているためです。その辺は、後で説明します。sw.jsvar CACHE_NAME = 'lttimer'; var VERSION="1.0.1" var CACHE_FILE = [ './index.html' ,'./css/DSEG7Classic-Regular.woff' ,'./css/PixelMplus10-Regular.woff' ,'./js/jquery-1.9.1.min.js' ,'./sound/Zihou01-mp3/Zihou01-1.mp3' ,'./sound/Zihou01-mp3/Zihou01-1.ogg' ,'./sound/silent.mp3' ,'./sound/silent.ogg' ]; const CACHE_KEYS = [ CACHE_NAME + VERSION ];次にServiceWorkerのインストールイベントでキャッシュへの登録処理を行います。
installイベントは1回しか呼び出されません。sw.jsself.addEventListener('install', function(e) { e.waitUntil( caches.open(CACHE_NAME.then(function(cache) { return cache.addAll(CACHE_FILE); }) ); });installイベントが終わるとactivateイベントが呼び出されます。
acticateの中では、キャッシュのキーと一致しないキャッシュの削除処理をおこなっています。sw.jsself.addEventListener('activate', event => { event.waitUntil( caches.keys().then(keys => { return Promise.all( keys.filter(key => { return !CACHE_KEYS.includes(key); }).map(key => { // 不要なキャッシュを削除 if(key.indexOf(CACHE_NAME) == 0){ console.log("ServiceWorker : " + key+ " remove"); return caches.delete(key); }else{ console.log("ServiceWorker : " + key+ "no remove"); return true; } }) ); }) ); });次にfetchイベントでリクエストを処理します。
ここでリクエストをも元にキャッシュから取り出したり、キャッシュにないファイルを登録します。
場合によっては古いキャッシュを削除し、新しいファイルをキャッシュします。
(ここもほぼコピペであまり理解していない・・・^^;)fetchイベントの中はオンライン、オフラインで処理を分けます。
オンラインオフラインの切り分けは navigator.onLine でできるとのこと。sw.jsself.addEventListener('fetch', function(event) { //ブラウザが回線に接続しているかをboolで返してくれる var online = navigator.onLine; if(online){ //オンラインのときの制御 }else{ //オフラインのときの制御 } });オンラインの処理はこんな感じ
まだ、処理を完全に理解しきっていないのですが、たぶんこのコメント通りでいいはず。sw.jsevent.respondWith( caches.match(event.request).then( function (response) { if (response) { // キャッシュを返す return response; } return fetch(event.request).then(function(response){ // キャッシュにないので追加 // Responseはストリームなのでキャッシュなので複製 cloneResponse = response.clone(); if(!response || response.status != 200){ //正常に取得できなかったときにハンドリングしてもよい console.log("ServiceWorker : request faild " + response.status); }else{ //現行のキャッシュに追加 caches.open(CACHE_NAME + VERSION).then(function(cache){ cache.put(event.request, cloneResponse).then(function(){ //正常にキャッシュ追加できたときの処理(必要であれば) console.log("casshed"); }); }); } return response; }).catch(function(error) { //デバッグ用 return console.log(error); }); } ) );event.respondWith()の中で、matchメソッドでキャッシュの検索結果を処理します。
一致すればそれを返却し、一致しなければ、キャッシュに登録します。オフラインの時はこんな感じ。
sw.jsevent.respondWith( caches.match(event.request).then( function(response) { // キャッシュがあったのでそのレスポンスを返す if (response) { return response; } //オフラインでキャッシュもなかったパターン return caches.match("offline.html").then(function(responseNodata){ //適当な変数にオフラインのときに渡すリソースを入れて返却 //今回はoffline.htmlを返しています return responseNodata; }); } ) );offline.htmlを返却するようにしているけど、offline.htmlを用意していない・・・w
基本的にはオンラインの時と同じでmatchの結果で処理を行っています。ここまでで最低限と思われる処理になります。
manifest
manifestファイルには、アプリケーションのアイコンや、名称などの情報を設定します。
設定項目が多くあるので、MDNのウェブアプリマニフェストのページを参考にするとよいと思います。manifest.json{ "name": "LT Timer", "orientation": "landscape", "display": "standalone", "start_url": "./", "short_name": "LT Timer", "description": "LT Timer", "background_color": "#000020", "theme_color": "#000020", "icons": [ { "src": "./img/icon_48.png", "type": "image/png", "sizes": "48x48" }, { "src": "./img/icon_96.png", "type": "image/png", "sizes": "96x96" }, { "src": "./img/icon_192.png", "type": "image/png", "sizes": "192x192" } ] }とりあえず、使っている設定の概要を
設定名 概要 name アプリケーション名 orientation アプリケーションの向き display standalone start_url アプリケーションの開始URL short_name アプリケーションの短縮名 description アプリケーションの説明 background_color 背景色 theme_color アプリケーションのテーマ色 icons アプリケーションのアイコン デバッグ
デバッグはChromeのデベロッパーツールを使うのが良いと思います。
SafariとかFirefoxとかは使わないのでよくわからないけど。デベロッパーツールをですが、通常のWebの開発で利用する機能は当然ですが、PWAに関するところで重要なのが以下の2点。
- 保存したコンテンツを削除
- Service Workerの登録解除
- オンライン・オフラインの切り替え
これができないとデバッグが大変。というか無理でしょう。
上記の内容は、デベロッパーツールのApplicationタブで確認できます。また、console.logでログを出力ができます。
というか、デベロッパーツールでApplicationタブのサービスワーカーを見るとエラーがカウントされているけど、確認ができません(自分だけ?)
そのため、ログに出さないとサービスワーカーでエラーが発生しても、エラーがでいるかの把握が困難になります。最後に
この内容はYoutubeのチャンネルや日本Androidの会 浜松支部の2月の定例会でも扱いたいなぁと思っています。