20190523のJavaScriptに関する記事は22件です。

Magic Leap MagicScript Landscape Application. UiTab

Prepare
Magic Leap One
https://www.magicleap.com/magic-leap-one

mlsdk v.0.20.0
https://creator.magicleap.com/downloads/lumin-sdk/overview

magic-script-cli v2.0.1
https://www.npmjs.com/package/magic-script-cli

magic-script-polyfills v2.2.0
https://www.npmjs.com/package/magic-script-polyfills

image file
https://pixabay.com/ja/illustrations/%E3%83%80%E3%82%A4%E3%83%A4%E3%83%A2%E3%83%B3%E3%83%89-%E5%88%86%E9%9B%A2-%E9%80%8F%E6%98%8E-1857736/

3d model file
https://poly.google.com/view/esWz3QtmDCX

Create Project

magic-script init my-tab org.magicscript.tab "Tab"
cd my-tab
mkdir res

place imgae file and 3d model file in res folder.

Code

Change app.package

DATAS = "digest.sha512.signed" : "." \
        "bin/" : "bin/" \
        "res/" : "res/"
OPTIONS = package/minApiLevel/2

Change app.js

import { LandscapeApp, ui } from 'lumin';
const { 
  UiTab,
  UiText,
  UiImage,
  UiLinearLayout, 
  EclipseLabelType, 
  Orientation, 
  Alignment} = ui;

export class App extends LandscapeApp {
  onAppStart () {
    const prism = this.requestNewPrism([0.9, 0.3, 0.5]);
    const topLayout = UiLinearLayout.Create(prism);
    const text = UiText.Create(
      prism, "Standard Tabs:", EclipseLabelType.kT7);

    text.setTextSize(0.0303);
    topLayout.addItem(text, 
        [0.04, 0.04, 0, 0], Alignment.CENTER_LEFT);
    topLayout.setOrientation(Orientation.kHorizontal);

    const tabLayout = UiLinearLayout.Create(prism);
    tabLayout.setOrientation(Orientation.kVertical);

    const tab1 = UiTab.Create(prism, "Tab1");
    tab1.setTextColor([1, 1, 1, 0.4]);
    tabLayout.addItem(tab1,
        [0, 0, 0.02, 0.02], Alignment.CENTER_LEFT);

    const tab2 = UiTab.Create(prism, "Tab2");
    tab2.setTextColor([1, 1, 1, 0.4]);
    tabLayout.addItem(tab2, 
        [0, 0, 0.02, 0.02], Alignment.CENTER_LEFT);

    topLayout.addItem(tabLayout, 
        [0.04, 0.04, 0, 0], Alignment.CENTER_LEFT);

    const resource = prism.createModelResourceId(
        "res/ar-poop-vroom.glb", 0.1);
    const model = prism.createModelNode(resource);
    const image = UiImage.Create(prism, 
        "res/diamond-1857736_640.png", 0.1, 0.1);
    topLayout.addItem(image, 
        [0.04, 0.04, 0, 0], Alignment.CENTER_LEFT);
    tab1.onActivateSub(function(event)
      {
        topLayout.removeItem(2);
        topLayout.addItem(image, 
          [0.04, 0.04, 0, 0], Alignment.CENTER_LEFT);
      }  
    );
    tab2.onActivateSub(function(event)
      {
        topLayout.removeItem(2);
        topLayout.addItem(model, 
          [0.04, 0.04, 0, 0], Alignment.CENTER_LEFT);
      }  
    );
    prism.getRootNode().addChild(topLayout);
  }
}

Build

magic-script build -i

Run

magic-script run --port=10000

Reference
UiTab(Magic Script API Doc)
https://docs.magicscript.org/lumin.ui.UiTab.html

Tabs (UiTab)(Guide C++)
https://creator.magicleap.com/learn/guides/luminrt-uitab

magicscript
https://www.magicscript.org/

Thanks!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

フロントエンド未経験のSEがポートフォリオ作ってみた

はじめに

普段はSEとしてJavaでバックエンドの開発を行っているのですが、最近フロントエンドの学習を始めました。
学習の一環として自身のポートフォリオを作成しました!
私と同じ様にフロントエンドの経験が浅い方で、何か作ってみたいと考えている方の参考になれば幸いです。

作ったもの

https://masayuki777.github.io/
portfolio.png

作り方

以下の記事を参考にしてテンプレートベースに作成を行いました!
・エンジニアでポートフォリオ作りました
・うちゅういちかんたんなポートフォリオのつくりかた

↓テンプレートはこちら
・テンプレート
スクリーンショット 2019-05-23 22.39.57.png

編集したのはhtml(index.html)とcss(style.css)で後は画像の差替えなどです。

アイコンは以下のサイトのもを利用しました。
https://fontawesome.com/
スクリーンショット 2019-05-23 22.37.20.png
無料で利用できるのと登録などが不要な点が良かったです。

また、ヘッダーのグラデーションに関しては以下のサイトのCSSを利用しました。
https://uigradients.com/#Lunada
右上のGet cssからCSSをコピーして利用できる点が良かったです。

ホスティングに関してはgithub.ioを利用しました。
公式サイトは以下ですが、他にも分かりやすくまとめられている記事はたくさんあるので簡単に公開できました。
https://pages.github.com/

まとめ

今回初めてWEBサイトを作成したのですが、学ぶことが多くとてもいい経験になりました。
中身はスカスカですし、実力不足でデザインを諦めた部分もあるので、継続的にアップデートしていきたいと思います。(小さく作って大きく育てるの精神大事)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【jQuery】【Ajax】iTunes APIを使って音楽情報の検索フォームを作り、Ajaxを使って表示させる

iTunesAPIを使って音楽情報を検索したい!

はじめまして。記念すべき初めてのQiita投稿です。
私はRailsチュートリアルから勉強を初めて2〜3ヶ月の:hatching_chick:エンジニア転職志望者でございます。
ポートフォリオ制作として現在音楽のSNSアプリを作っています。

その過程で、このwebアプリになくてはならない
音楽CDのアーティスト名とかタイトルとかジャケット情報をフォームから検索して表示
させる部分をド初心者なりに必死こいて作りなんとか動く形になったため、はじめてのアウトプットとしてまとめます!!

動けば万々歳精神で作ったものなので、もっとこうすれば綺麗にできるよ!
というアドバイスありましたらバシバシご指摘いただけると嬉しいです:bow_tone4:

使ったもの

当初、音楽系APIで使えるものを「音楽系API(主に音楽配信サービス)まとめ」等から探していました。
Apple Music APIを使おうとしたのですが、理解が全く追いつかず断念。いつかリベンジしたい...

途方にくれていたところこんなものを発見。

なんとhttps://itunes.apple.com/searchの後ろに色々検索パラメータをつけるだけでJSON形式でデータが取得できるとのこと!

リクエストで返ってくるデータはこんな感じらしいです。

result
{
"resultCount": 2,
-"results": [
-{
"wrapperType": "track",
"kind": "song",
"artistId": 94949844,
"collectionId": 317186212,
"trackId": 317186341,
"artistName": "the pillows",
"collectionName": "Rock stock & too smoking the pillows",
"trackName": "Funny Bunny",
"collectionCensoredName": "Rock stock & too smoking the pillows",
"trackCensoredName": "Funny Bunny (Rock Stock Version)",
"artistViewUrl": "https://itunes.apple.com/jp/artist/the-pillows/id94949844?uo=4",
"collectionViewUrl": "https://itunes.apple.com/jp/album/funny-bunny-rock-stock-version/id317186212?i=317186341&uo=4",
"trackViewUrl": "https://itunes.apple.com/jp/album/funny-bunny-rock-stock-version/id317186212?i=317186341&uo=4",
"previewUrl": "http://a744.phobos.apple.com/us/r20/Music/v4/2f/53/63/2f536341-4614-7453-1275-d68fdc0df2e2/mzaf_7461742241849893593.aac.m4a",
"artworkUrl30": "http://is2.mzstatic.com/image/pf/us/r30/Music/5f/13/2f/mzi.jmusphex.30x30-50.jpg",
"artworkUrl60": "http://is1.mzstatic.com/image/pf/us/r30/Music/5f/13/2f/mzi.jmusphex.60x60-50.jpg",
"artworkUrl100": "http://is4.mzstatic.com/image/pf/us/r30/Music/5f/13/2f/mzi.jmusphex.100x100-75.jpg",
"collectionPrice": -1,
"trackPrice": 250,
"releaseDate": "2009-06-03T07:00:00Z",
"collectionExplicitness": "notExplicit",
"trackExplicitness": "notExplicit",
"discCount": 1,
"discNumber": 1,
"trackCount": 14,
"trackNumber": 10,
"trackTimeMillis": 206600,
"country": "JPN",
"currency": "JPY",
"primaryGenreName": "Rock"
}, (以下略)

詳しい使い方は記事を参照してください。
ちなみにiTunesで扱っているコンテンツであれば音楽以外も取得できるようです。

ということで、今回こちらのAPIを使って検索フォームを実装してみました。

こんな感じでできた

検索キーワードとして

  • アーティスト名
  • アルバムタイトル

の2つでCD情報を検索したいと思います。

search.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script>
$(function () {
  $('#search_button').on('click', function () {
//入力したアーティスト名とアルバム名を検索フォームから取得。
   var artistname = $('#my-form [name=artistterm]').val();
   var albumname = $('#my-form [name=albumterm]').val();
//変数termに検索キーワードをがっちゃんこしていれます
  var term = artistname + albumname;
 //ajax通信を行い、クエリパラメーターをAPI側に送信
    $.ajax({
      url: 'https://itunes.apple.com/search',
      type: 'GET',
      data: {
        term: term,
        country: 'jp',
        media: 'music',
        entity: 'album',
        lang: 'ja_jp',
        limit: '10'
      },
      dataType: 'json',
      timespan: 1000
    }).done(function (data) { //通信が成功したときの動き
      var result = data.results[0];
      if (result == null) { //検索結果がなかった場合
        $("#image-box").children("img").attr({ 'src': '' });
        $('#searchresult').text("検索結果が見つかりません");
      }
      else { //検索結果が存在した場合
        $("#searchresult").remove();
        $('#artistname').text(result.artistName);
        $('#albumtitle').text(result.collectionName);
        $('#artwork').text(result.artworkUrl100);
        var url = result.artworkUrl100;
        $("#image-box").children("img").attr({ 'src': url });
      }
    });
  }).fail(function (jqXHR, textStatus, errorThrown) { //通信失敗したときの動き
    $("#span1").text(jqXHR.status); //例:404
    $("#span2").text(textStatus); //例:error
    $("#span3").text(errorThrown); //例:NOT FOUND
  });
});
</script>
</head>
<body>
<!--以下、検索フォームなどの表示部分-->
<p>アーティスト名:<span id="artistname"></span></p>
<p>トラック名:<span id="albumtitle"></span></p>
<p>アルバム画像URL:<p><span id="artwork"></span></p>
<div id="image-box"><img /></div>
<p id="searchresult"></p>
<form id="my-form">
    <input type="text" name="artistterm" value="" />
    <input type="text" name="albumterm" value="" />
</form>
<input type="button" id="search_button" value="検索">
<p>エラー原因</p>
<p><span id="span1"></span></p>
<p><span id="span2"></span></p>
<p><span id="span3"></span></p>
</body>
</html>

jQueryやAjaxの基本的な使い方の解説などは、
私が正直全然ここで説明できるほど身につけていないので他の記事におまかせいたします。。。

自分のRailsアプリで使いたいとき

自身のアプリのapp/assets/javascript配下に

search.js
$(function () {
  $('#search_button').on('click', function () {
//入力したアーティスト名とアルバム名を検索フォームから取得。
   var artistname = $('#my-form [name=artistterm]').val();
   var albumname = $('#my-form [name=albumterm]').val();
//変数termに検索キーワードをがっちゃんこしていれます
  var term = artistname + albumname; 
 //ajax通信を行い、クエリパラメーターをAPI側に送信
    $.ajax({
      url: 'https://itunes.apple.com/search',
      type: 'GET',
      data: {
        term: term,
        country: 'jp',
        media: 'music',
        entity: 'album',
        lang: 'ja_jp',
        limit: '10'
      },
      dataType: 'json',
      timespan: 1000
    }).done(function (data) { //通信が成功したときの動き
      var result = data.results[0];
      if (result == null) { //検索結果がなかった場合
        $("#image-box").children("img").attr({ 'src': '' });
        $('#searchresult').text("検索結果が見つかりません");
      }
      else { //検索結果が存在した場合
        $("#searchresult").remove();
        $('#artistname').text(result.artistName);
        $('#albumtitle').text(result.collectionName);
        $('#artwork').text(result.artworkUrl100);
        var url = result.artworkUrl100;
        $("#image-box").children("img").attr({ 'src': url });
      }
    });
  }).fail(function (jqXHR, textStatus, errorThrown) { //通信失敗したときの動き
    $("#span1").text(jqXHR.status); //例:404
    $("#span2").text(textStatus); //例:error
    $("#span3").text(errorThrown); //例:NOT FOUND
  });
});

上記を「○○.js」の名前でファイルを作って格納。

またRails.5.x以降からjQueryを使えるように設定しないといけないらしいので、以下の記事などを参考に設定。
Rails 5.x標準で Ajax+(jQuery+Partial) でHTML部分更新する世界一シンプルなサンプル

あとは<body>以下の検索フォーム部分をお好きなビューファイルに入れるだけで動くと思います。

参考記事

上記を作るにあたって参考にした記事を以下に挙げます。
私のように「Railsチュートリアルは一通りやったけど、jQueryとかAjaxとか全くわからーん:sob:」という人は以下を参考にするとなんとなくやっていることが分かると思います。

まずこの記事のコードを基本としてアレンジしました。

その他大いに参考にしたもの

最後に個人的感想

初めて記事を書いたので、
節目としてここまで勉強してきた中で個人的な感想を記録しておきます:walking_tone3:

初ポートフォリオ制作はインスタの音楽ジャケ版的なアプリを作りたいなぁ〜と思い(そういうサービス既にありますが)、
作る前はなんとなく「音楽系のAPIあるだろうし、理想形はそこからデータ取ってきてそれをそのまま投稿できたらいいなぁ」とぼんやりイメージしていた程度でした。

いざ作り始めてからは、必死に調べては参考記事のコードをアレンジし、試行錯誤して
あれよあれよと言う間に気づいたら結果的に理想と同じ機能を作ることができました。

どのAPIを使うか?の段階でそもそもAPIの知識が足りず、若干壁にぶち当たりましたが
粘り強くググっていたら初心者にも簡易に使えるものを発見できたのが、今回の超ラッキーでした:metal:そしてAPIの使い方の丁寧なまとめがあったことも:metal:ありがたや。
とりあえず今回学んだことは粘り強くググることの大切さでした

まだアプリは完成していませんが、この機能を実装できたことがちょっとした自信とモチベーションにつながりました。今後もがんばるぞ!!
そして私と同じくエンジニア目指して勉強に勤んでいる方々、一緒に頑張りましょう:octopus:

ここまで読んでいただいてありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

value を変えずに input に3桁カンマを振る

value を変えずに input に3桁カンマを振る

「数値に3桁カンマを振りたい」って要望は結構あって、ググると js で value 自体をゴリゴリいじる実装が多いですが、今回はちょっと違ったアプローチをしてみます。

コード

<html lang="ja">
<head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>

<style>
    /* number-input と number-overlap をまとめるコンテナ */
    .number-container {
        position: relative;
    }

    /* カンマ区切りにしたい input 要素 */
    .number-input {
        position: relative;
    }
    /* 普段は z-index:1 で number-overlap の後ろに居る。色は被ってしまうので transparent */
    .number-input:not(:focus) {
        z-index: 1;
        color: transparent;
    }
    /* フォーカス中は z-index:3 で number-overlap の前に来る。色も戻す */
    .number-input:focus {
        z-index: 3;
        color: #555;
    }

    /* number-input に覆い被さる要素。 pointer-events:none でクリックを透過させる */
    .number-overlap {
        position: absolute;
        top: 1px;
        left: 0px;
        display: inline-block;
        background: transparent;
        width: 100% !important;

        pointer-events: none;
        z-index: 2;
    }
</style>
<script>
    $(function () {
        // フォーカスを抜けたり値が変わったりしたら覆い被さっているテキストをカンマ区切りにする
        $('#hoge').on('blur change input', function () {
            const $this = $(this);
            if ($this.val() !== '') {
                $this.next().text((+$this.val()).toLocaleString());
            }
        });
    });
</script>

<form class="form-inline">
    <div class="form-group">
        <div class="number-container">
            <input type="text" id="hoge" name="hoge" class="form-control number-input" placeholder="自動でカンマが付きます">
            <span class="number-overlap form-control"></span>
        </div>
    </div>
</form>

</body>
</html>

殴り書きしたのでかなり荒いですが、コメントを読めば何をしているかは分かると思います。
一応説明を加えると下記のような感じです。

.number-container

position で要素を重ね合わせる必要があるので、そのためのコンテナです。
それ以上の意味は特にありません。

.number-input

input に付与します。
普段は z-index で後述の .number-overlap の後ろに隠れていますが、フォーカスすると前面に出てきます。

.number-overlap

実際に3桁カンマの文字列が設定される要素です。
普段は z-index で前述の .number-input に被さっていますが、フォーカスされると後ろに引っ込みます。
pointer-events:none なのでクリックを透過します。これが今回のキモです。これによって「被さっているんだけど後ろの要素がクリック(フォーカス)できる」が実現できます。

javascript

フォーカスとか見え方とかは css だけで完結しますが、「カンマを振る処理そのもの」はどうしても js の力を借りる必要があるのでそれをしています。
できれば html + css だけでやりたい・・・。

注意点

ただし上記はかなりサボっています。実際はいろいろ調整しないと使い物になりません。

まず .number-overlap のスタイルは number-input に完全に合わせる必要があります(padding や font-size など多岐にわたる。合わせないとすぐズレる)。
上記は bootstrap を使っていますが form-controlinput に限定されないので流用してるだけです。しっかりやろうとすると結構めんどくさいです。
実際、上記は chrome で確認しましたが、他のブラウザではズレたりします。そういった微調整が必須になります。

また、個人的に数値入力は type=number を使うことが多いです。さらに右寄せにしたいですが、スピンボタンがくっつくのでブラウザごとの調整が必要です(幅がブラウザごとに全く異なります)。
正直かなり大変なので、いっその事スピンボタンは消したほうが良いです。

js サイドも上のコードだと数値以外を入れると NaN になるので、もっといろいろな分岐が必要です。
NaN は type=number でサボったり出来ますが e の罠があったりして、正直「ロケール指定ができる type=number があればいいのに・・・」と思います。

まとめ

注意点で記載した所が調整・クリアできれば変換の面倒をまったく見なくて済むようになるので結構有用だと思います。
(js で値をゴリゴリいじるのはなんか好かん)。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javascriptにおける 論理演算子 || の動きについて

本記事について

おそらく初心者向けですが、
Javascriptで、a || b
という記述があった時の論理演算子の動きについて共有します。

Javascriptの開発の中で、下記のような記述を読み解くのに少し時間がかかったので、自分用のメモとしても残しておきます。

※こちらはService Worker+Cache APIを実装した際のコードですが、Service Workerについては別の機会にまた解説したいと思います。

self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.open(CACHE_NAME)
    .then(function(cache) {
      return cache.match(event.request)
      .then(function (response) {
        return response || fetch(event.request)
        .then(function(response) {
          cache.put(event.request, response.clone());
          return response;
        });
      });
    })
  );
});

Javascriptの処理

この処理を順番に整理すると
caches.open(CACHE_NAME)でCACHE_NAMEと一致したかどうかを確認し、Promiseを返す(成功していればthen内部の処理へ)
cache.match(event.request)でcacheの中身がevent.requestにマッチしていればPromiseを返す(そして2つ目のthen内部の処理へ)

return response || fetch・・・・

と続いていくわけですが、

ここの処理は、

レスポンスを返すか、fetchでリクエストを送るか
ということになります。(当たり前だ)

a || bの処理の仕様

・・・なのですが、処理の中で
a || b(aまたはb)となった時に

え、じゃあ結局どっちの処理をするの?

ということを疑問に思う方もいるかもしれません。

この「または」を表す || という論理演算子の考え方としては

処理a || 処理b

となっている場合は

処理aがtrueの場合、処理aのみを実行
処理aがfalseの場合、処理bを実行

となります。

なんか、if文でやたらと

if(a || b) {
  //aまたはbなら処理
}

と刷り込まれてしまっていると、「あれ?aかbかが実行されると書いてあるけど、それって何で決まるんだっけ?」
とふと思ったのでググって自分なりにコードの意味を噛み砕いていくことができました。

一応先ほどのコードの意味を順番に書いておきます。
caches.open(CACHE_NAME)でCACHE_NAMEと一致したかどうかを確認し、Promiseを返す(成功していればthen内部の処理へ)
cache.match(event.request)でキャッシュの中身がevent.requestにマッチしていればPromiseを返す(そして2つ目のthen内部の処理へ)
return responseがtrueならばそこでブラウザに処理を返す
return responseがfalseならばfetchの処理に進む
response.clone()でレスポンスをクローンして、cache.put()キャッシュを更新する

という感じになります。

a || bをちゃんと調べてまとめてみましたとさ。

参考:MDN

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

BinaryASTでJavaScriptのロードを高速化

TL; DR

JavaScriptファイルをプリコンパイルしておける機能。
ただし、まだ実験段階なので2019年5月時点で実用は出来ません。
サポートしているのはFirefox Nightly版のみ。

BinaryASTとは

BinaryASTは、JavaScriptの初回ロードを高速化します。
初回ロードの高速化というと、ダウンロード速度の方をイメージしてしまいますが、
ダウンロード後(あるいはキャッシュ読み込み後)のブラウザでのパース作業を高速化します。

ASTについて

ブラウザで実行する通常のJavaScriptコードの場合、ソースはコードの構文構造を記述する抽象構文木(Abstract Syntax Tree, AST)と呼ばれる中間表現に解析されます。
この表現はバイトコードまたはネイティブのマシンコードにコンパイルして実行することができます。

生のJavaScript
const hoge = 3 + 4;
AST表現されたJavaScript
{
    "type": "Program",
    "body": [
        {
            "type": "VariableDeclaration",
            "declarations": [
                {
                    "type": "VariableDeclarator",
                    "id": {
                        "type": "Identifier",
                        "name": "hoge"
                    },
                    "init": {
                        "type": "BinaryExpression",
                        "operator": "+",
                        "left": {
                            "type": "Literal",
                            "value": 3,
                            "raw": "3"
                        },
                        "right": {
                            "type": "Literal",
                            "value": 4,
                            "raw": "4"
                        }
                    }
                }
            ],
            "kind": "const"
        }
    ],
    "sourceType": "script"
}

JavaScriptコードからASTへの変換は以下のページで試せます。
http://esprima.org/demo/parse.html

高速化の仕組み

BinaryASTは、元のJavaScriptコードとそれに関連付けられたデータ構造をバイナリ形式で表現します。
これによりクライアントでの上記パース作業を簡略化しようという仕組みです。
モバイル端末などのローエンドなクライアントほど効果が出ます。

  • 適用前
    image.png

  • 適用後
    image.png

pre-encode

エンコーダーはRustで実装されているようです。試しにエンコードしてみます。
お試しなのでサクッとDockerで実行します。

Rustインストール
docker run --rm -it ubuntu
apt-get update
apt-get upgrade
apt-get install curl
curl https://sh.rustup.rs -sSf | sh
1を選択
1) Proceed with installation (default)
cargoにパスを通す
source ~/.cargo/env
nodejsとnpmをインストール
apt-get install nodejs npm
binastを取ってくる
apt-get install git
git clone https://github.com/binast/binjs-ref
エンコーダーとデコーダー
cd binjs-ref
cargo run --bin binjs_encode -- --help
cargo run --bin binjs_decode -- --help

ここまでで環境準備は整いました。
エンコードを試してみます。

hoge.js
const hoge = 3 + 4;
console.log(hoge);
エンコード実行
cargo run --bin binjs_encode -- -i hoge.js -o output
cd output
ll
total 28
drwxr-xr-x  2 root root 4096 May 19 04:33 ./
drwxr-xr-x 15 root root 4096 May 19 04:33 ../
-rw-r--r--  1 root root  379 May 19 04:33 hoge.binjs
-rw-r--r--  1 root root   10 May 19 04:33 hoge.grammar
-rw-r--r--  1 root root   39 May 19 04:33 hoge.js
-rw-r--r--  1 root root   10 May 19 04:33 hoge.strings
-rw-r--r--  1 root root   10 May 19 04:33 hoge.tree

binjsファイルが出来ていますね。

hoge.binjs
BINJS[GRAMMAR]identity;(IdentifierExpression0LiteralNumericExpression(AssertedDeclaredName2AssertedScriptGlobalScope BinaryExpression"BindingIdentifier CallExpression&ExpressionStatement
    Script,StaticMemberExpression&VariableDeclaration$VariableDeclarator[STRINGS]identity;P
                                                                                          hogeconsolelog+
constconst lexical[TREE]identity;Z                                                                                                                                                  @@

デコーダーも試してみましょう。

デコード実行
cargo run --bin binjs_decode -- ./output/hoge.binjs decode_hoge.js

スペースや改行は消されましたが、戻っていますね。
行末のセミコロンも消されているので、minifyされているんでしょうか。

decode_hoge.js
const hoge=3+4;console.log(hoge)

ついでにダンプも実行してみます。

ダンプ実行
cargo run --bin binjs_dump -- ./output/hoge.binjs

ダンプ結果
stdout
Attempting to decode as multipart.

    0 : 10                      # Script {
      :                         # .scope
    1 : 06                      # AssertedScriptGlobalScope {
      :                         # .declaredNames
    2 : 02                      # list (length=1) [
    3 : 04                      # AssertedDeclaredName {
      :                         # .name
    4 : 00                      # string="hoge"
      :                         # .kind
    5 : 0a                      # string="const lexical"
      :                         # .isCaptured
    6 : 00                      # bool=false
      :                         # }
      :                         # ]
      :                         # .hasDirectEval
    7 : 00                      # bool=false
      :                         # }
      :                         # .directives
    8 : 00                      # list (length=0) []
      :                         # .statements
    9 : 04                      # list (length=2) [
   10 : 14                      # VariableDeclaration {
      :                         # .kind
   11 : 08                      # string="const"
      :                         # .declarators
   12 : 02                      # list (length=1) [
   13 : 16                      # VariableDeclarator {
      :                         # .binding
   14 : 0a                      # BindingIdentifier {
      :                         # .name
   15 : 00                      # string="hoge"
      :                         # }
      :                         # .init
   16 : 08                      # BinaryExpression {
      :                         # .operator
   17 : 06                      # string="+"
      :                         # .left
   18 : 02                      # LiteralNumericExpression {
      :                         # .value
   19 : 00 00 00 00 00 00 08 40 # float=3
      :                         # }
      :                         # .right
   27 : 02                      # LiteralNumericExpression {
      :                         # .value
   28 : 00 00 00 00 00 00 10 40 # float=4
      :                         # }
      :                         # }
      :                         # }
      :                         # ]
      :                         # }
   36 : 0e                      # ExpressionStatement {
      :                         # .expression
   37 : 0c                      # CallExpression {
      :                         # .callee
   38 : 12                      # StaticMemberExpression {
      :                         # .object
   39 : 00                      # IdentifierExpression {
      :                         # .name
   40 : 02                      # string="console"
      :                         # }
      :                         # .property
   41 : 04                      # string="log"
      :                         # }
      :                         # .arguments
   42 : 02                      # list (length=1) [
   43 : 00                      # IdentifierExpression {
      :                         # .name
   44 : 00                      # string="hoge"
      :                         # }
      :                         # ]
      :                         # }
      :                         # }
      :                         # ]
      :                         # }

サーバーへの配置

binast-cf-worker を見る感じ、リクエストヘッダーのAcceptapplication/javascript-binastが含まれていれば、Content-Typeapplication/javascript-binastを指定した.binjsファイルを返せば良さそうです。

上記のサンプルでは、クライアント判定NGの場合は通常の.jsファイルを返すようにしていますが、今回はお試しなので、.binjsファイルなら無理やりapplication/javascript-binastにしてみます。

また、httpsでないとbinASTは有効にならないようですので、ローカルでテストするときは注意。
とりあえずオレオレ証明書で回避しておきます。

docker run --rm --name binjstest -v ~/output:/usr/share/nginx/html:ro -d -p 8080:80 nginx
docker exec -it binjstest bash

apt-get update
apt-get install openssl
openssl genrsa 2048 > server.key
openssl req -new -key server.key > server.csr
openssl x509 -days 3650 -req -signkey server.key < server.csr > server.crt
mkdir /etc/nginx/ssl
mv server* /etc/nginx/ssl

apt-get install vim
vim /etc/nginx/conf.d/default.conf

vim /etc/nginx/mime.types

nginx -s reload
/etc/nginx/conf.d/default.conf
server {
    listen 80 ssl;
    server_name localhost;

    ssl_certificate_key /etc/nginx/ssl/server.key;
    ssl_certificate /etc/nginx/ssl/server.crt;
         :
/etc/nginx/mime.types
types {
    text/html                                        html htm shtml;
    text/css                                         css;
    text/xml                                         xml;
    image/gif                                        gif;
    image/jpeg                                       jpeg jpg;
    application/javascript                           js;
    application/javascript-binast                    binjs;
         :
~/output/hoge.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8">
    <script type="text/javascript" src="hoge.binjs"></script>
  </head>
  <body>
  </body>
</html>

クライアント準備

現在BinaryASTをサポートしているのはFirefox Nightly版のみとのことで、インストールします。

about:configにて以下を設定します。
dom.script_loader.binast_encoding.domain.restrictfalse
dom.script_loader.binast_encoding.enabledtrue
image.png

hoge.htmlをブラウザで表示

以下のエラー。えぇ、プレビュー版はconstは使えないのかよ!

SyntaxError: BinAST Parsing Error: Const is not supported in this preview release     hoge.binjs

constをvarに変えて再度エンコード。デプロイして再確認。
やっとできました。ちゃんとBinary表現になっていますね。結果も正しく動いています。
image.png

image.png

image.png

まとめ

情報が少ない中、何とかここまでたどり着けました。
今回は短いソースなので、ファイルサイズも逆に大きくなってしまいましたが、次回は大きなファイルでどれだけ変わるかやってみようと思います。(そこが重要)

ちなみに、公式デモサイトである下記は、正しくmime-typeを返していない為binjsになっていないようです。(2019/05/23)
https://serve-binjs.that-test.site/

image.png

image.png

参考元: https://blog.cloudflare.com/binary-ast/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Map Objectのイディオム集

Map Objectは JavaScriptで連想配列を実現するための機能です。
今まではObjectを連想配列として使っていたわけですが、ES6から、専用のクラスが用意されるようになりました。

今まであまり使ってこなかったですが、せっかくなので、よく使う表現をメモしておきます

使い方はざっと以下のような感じ、

const user1: User = {
 userId: 1,
 userName: "user1"
}

const user2: User = {
 userId: 2,
 userName: "user2"
}

const userMap = new Map<number, User>() // Map Objectを作成。キーとバリューになる型をジェネリックを使って定義できる。また、定義しなくても型推論してくれる

userMap.set(user1.userId, user1) // UserオブジェクトをMapに登録
userMap.set(user2.userId, user2)

userMap.get(1) // userIdが1のuserを取得
userMap.delete(2) // userIdが2のuserを削除

基本

上で紹介したもの以外に以下のようなメソッドが用意されています。

.has(key)

そのキーのメンバが存在するかどうかをチェックします。

.entries() / .keys() / .values()

それぞれ、Mapのメンバ、キー、バリューのイテレータを返します。
単体で使うことは少ないですが、後述する配列への変換処理だったり、マージ処理するときに活躍します。

.forEach()

Arrayで使えるforEachと同じような感じですね。Mapオブジェクトのメンバーを回しながら順番に処理してくれます。

userMap.forEach((val,key,self)=>{
    console.log(key, val,self)
})

.size

Arrayで言うところの.lengthですね。Map Objectに入っている要素数を返してくれます。

userMap.size // => 2

配列への変換

Map Objectは便利なのですが、いかんせん繰り返し処理に使えるメソッドがforEachしかなく、map()filter() reduce() などが使えるArrayが恋しくなるときが多々あります。

以下のように書けば、Map ObjectをArrayに変換することができます。

const userArray = [...userMap.values()]    // => [user1, user2 ....]

MDNのサイトで紹介されているのは以下のような方法ですね。
こちらでも同様です

const userArray = Array.from(userMap.values())    // => [user1, user2 ....]

もしもkeyだけの配列がほしいのであれば以下のような感じです

const userArray = [...userMap.keys()]    // => [1, 2 ....]

配列からMap Objectへの変換

なにか処理をするためにArrayに変換するのであれば、処理が終わったあとはもう一度 Map Objectに戻したくなるでしょう。
もしくはAPIから取得した値は配列で帰ってくるかもしれません。
その場合は以下のように書くことでMap Objectに変換することができます。
Map Object → Arrayのときよりも若干面倒ですね

const userMap = new Map(userArray.map(user => [user.userId, user])) 
// => Map<number,User>へ変換

Map Object同士をマージする

配列ではなくMap Objectを使う主な理由は、キーを使った値へのアクセスが容易(かつ高速)なことに加えて、常にキーが一意になるようにObjectの塊を取り扱うことができることです。

例えば、同じユーザーIDをを持つUserが存在する可能性のある2つの配列(usersArray1usersArray2)を一つに結合して、ユーザーIDが一意になるように重複を取り除く事を考えてみましょう。

配列に格納されている値がプリミティブな値であれば、Setを用いて一意な配列を作る事はできます。
しかし今回扱いたいObjectの場合は、以下のようにうまく一意になってくれません。

const user2: User = {
    userId: 2,
    userName: "user2"
}

const sameUser2: User = {
    userId: 2,
    userName: "user2"
}

const duplicateUserArray = [user2, sameUser2]

// user2とsameUser2は、全く同じメンバーを持っているが、インスタンスが違うので、重複しているとはみなされない
console.log([...(new Set(duplicateUserArray))]) 
// => [ { userId: 2, userName: 'user2' }, { userId: 2, userName: 'user2' } ]

しかし、こんな時もMap Objectであれば解決です。以下のように書くことで、キーが重複した値を除去しつつ、2つのMap Objectをマージすることができます。
重複したキーが有る場合は、後に指定したMap(この場合はuserMap2)が優先されることに注意してください。

// userMap1とuserMap2をマージ
const mergeUserMap = new Map([...userMap1.entries(),...userMap2.entries()])
例.ts
const userMap1 = new Map()  // Map Objectを作成
userMap1.set(user1.userId, user1)  // user1を追加
userMap1.set(user2.userId, user2)  // user2を追加

const userMap2 = new Map()  // Map Objectを作成
userMap2.set(user1.userId, user1)  // user1を追加
userMap2.set(user3.userId, user3)  // user3を追加

// userMap1とuserMap2をマージ
const mergeUserMap = new Map([...userMap1.entries(),...userMap2.entries()])
// => Map {
//    1 => { userId: 1, userName: 'user1' },
//    2 => { userId: 2, userName: 'user2' },
//    3 => { userId: 3, userName: 'user3' } }

Objectで言うところのObject.assign()と同じような動きですね

// userObjectとuserObject2をマージ
const mergeUserObject = Object.assign(userObject1, userObject2)

キーの差し替え

あまり頻度は多くないですが、Map Objectに設定したキーを別の値に置き換えたMap Objectを作りたくなるときがあります。
そんなときは以下のようにかけます。

// キーをuserIdからuserNameに変更
const nameMap = new Map([...userMap2.values()].map(user => [user.userName, user]))

一度配列に変換してからMapに戻すというめんどくさいことをやっているのであまり良くないですね。ちなみに一意じゃない値をキーに設定すると、例によって重複削除されてしまうので注意してください。

オブジェクトへの変換

2019/05/24追記: コメントで、より良い書き方を頂きました。感謝!

Object.fromEntries()を使う方法です。今はStage4の状態みたいですが、IE、Edge,Node.js以外の主要な環境では使えるみたいです。

TypeScript側での対応はまだみたいですが、2019/05/24時点でmasterにマージされているようなので、次のリリースでは使えるようになるのではないかと思います。

const userObject = Object.fromEntries(userMap)  // userMapをuserObjectに変換

以前のやり方。Object.fromEntries()が使えるなら読む必要はない

 

JSON.stringify()したいときなど、どうしてもObjectにしたいときがあります。
そういうときは以下のコードでオブジェクトに変換することができます。

本当は一文で書きたかったんですが、難しくなりすぎるのでやめました。

const userObject:any = {}
userMap.forEach((u,uId)=>{
    userObject[uId] = u
})

いっそ関数化してしまっても良いかもしれません。

/**
 * Map => Objectに変換する
 */
function mapToObject(map: Map<any,any>):any{
    const object: any = {}
    map.forEach((val, key) => {
        userObject[key] = val
    })
    return object
}

参考

オブジェクトからMap Objectへの変換

2019/05/24追記: コメントでより良い書き方を頂きました。感謝!

Object.entries()を使うことでObject→Map Objectへ変換することができます。

const userMap = new Map(Object.entries(userObject)); // userObjectをuserMapに変換

ただし Symbol のキーは処理の過程で除かれてしまうので、それも含めるなら、以下のように書きます

const userMap = new Map()
for (const key of [...Object.keys(userObject), ...Object.getOwnPropertySymbols(userObject)]) {
  userMap.set(key, userObject[key])
}

以上、

後半考えると、別にObjectでもいいのでは?とも思っちゃいますね。ただ、配列→オブジェクトへの変換はMapのほうが楽かなという印象があります。全体的に、Array FriendlyなObjectって印象ですね。

Object ⇔ Map Objectの変換も簡単にかけるので、連想配列にMap Objectを使わない理由はなさそうです。
これからはもっと使っていきたいと思います。

もっとこんな便利な書き方あるよって人がいれば教えてください

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaScript】Stringの数字をNumberに変換方法

Stringの数字をNumberに型変換したい時

const num = "12345";

console.log(typeof(num)) // String

console.log(typeof(Number(num))) // Number

console.log(typeof(parseInt(num,10))) // Number 第二引数は10進数ということ

console.log(typeof(+num)) // Number

いろんなやり方がありますね。
ただ、主流は+numをよく使うみたいです。

その前に、typeScriptで型を定めたほうがいいってことか。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

VueでJsの中に静的と動的に外部Jsを読み込む書き方

Vueプロジェクトを開発する際に、使っているJsの中に、外部の第三者のJsファイルのファンクションやオブジェクトを読み込んで利用することがよくあります。外部Jsを読み込むときには、静的にと動的に二つの方法があります。以下で簡単に説明してみます。

例文

index.vue
<template>
  <div>
  <p>{{ msg }}</p>
  </div>
</template>
<script>
import { t3m1 } from './t3'
export default {
  name: 'test',
  data () {
    return {
      msg: 'you are ok.'
    }
  },
  mounted () {
    t3m1()
  }
}
</script>
t1.js
const t1m1 = function () {
  console.log('t1m1 ok')
}
function t1m2 () {
  console.log('t1m2 ok')
}
export { t1m1, t1m2 }
t2.js
const t2 = {}
t2.t2m1 = function () {
  console.log('t2m1 ok')
}
t2.t2m2 = function () {
  console.log('t2m2 ok')
}
export default { t2 }
t3.js
// 外部Jsを静的に読み込む書き方
import t2 from './t2'
function t3m1 () {
  console.log('t3m1 ok')
  // 外部Jsを動的に読み込む書き方
  import('./t1.js').then((t1) => {
    t1.t1m1()
  })
  t2.t2.t2m1()
}
export { t3m1 }

実行結果

無題.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Good Good Study,Day Day Up.

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ajaxで非同期通信とかを頑張った話

この記事について

Javascriptがぜんぜん解らない()駆け出しフロントエンジニアが、勉強目的で触ったajaxのまとめです。

対象者

  1. これからJavascriptを触る人
  2. pureなJavascriptで非同期通信処理をやろうとしている人
  3. 未来に存在しているであろう、ど忘れしている私へ

そもそもAjaxとはなんぞや

[Asynchronous JavaScript + XML]

その名の通り、JavascriptとXMLを使った非同期通信。
XMLに送るデータ内容を記述し、JavascriptのAPIであるXMLHttpRequestを使って、サーバー側にデータ送信する処理の事です。
ただ、最近はもうXMLを使わずにJSONにデータを記述することが多い。

pureなJavascriptだとどうコーディングするの?

main.js
        let xhr = new XMLHttpRequest();
        xhr.open('POST','hogehoge.php',true);
        xhr.setRequestHeader('content-type','application/x-www-form-urlencoded;charset=UTF-8');
        xhr.send(
            //送りたい内容を記述
        );
        xhr.onreadystatechange = function()
        {
           //サーバーからの応答に応じた処理
        }

各メソッドに対しての説明は省略しちゃいます。
詳しくはMDNの方で。

これを、初期読み込み時にサーバーからデータを取得してきたり、
更新ボタンを押下したときのイベント処理の中に組み込んだり…と使っていきます。

ただし、問題が…

JavaScript はシングルスレッドで動くため、当然ながらsendメソッドでデータをサーバー側に送信した後は、後続の処理を引き続き行われます。

main.js
        let xhr = new XMLHttpRequest();
        xhr.open('POST','hogehoge.php',true);
        xhr.setRequestHeader('content-type','application/x-www-form-urlencoded;charset=UTF-8');
        xhr.send(
            //送りたい内容を記述
        );

        xhr.onreadystatechange = function()
        {
           //サーバーからの応答に応じた処理
        }

        alert('りんご')

このような場合、sendメソッド実行後はalertが実行され、りんごがアラートで表示されちゃいます。
当然ながら、そこでHTTP通信で返されてきた結果を扱いたいとしても、まだonreadystatechange内に結果は返されていないはずなので、クリティカルなことになっちゃいます。
えらいこっちゃ…。
なので、こうしちゃいます。

main.js
        xhr.onreadystatechange = function()
        {
            if(xhr.readyState = 4 && xhr.status == 200)
            {
               alert(xhr.responseText)
            }
        }

これで、サーバーから帰ってきたデータを扱うことができます。
やったね!

ただ…

この非同期処理を連続で行って行きたい場合はどうなるかというと…

main.js
let xhrA = new XMLHttpRequest();
//設定とかの処理
xhrA.onreadystatechange = function()
{
    if(xhrA.readyState = 4 && xhrA.status == 200)
    {
        let xhrB = new XMLHttpRequest();
        //設定とかの処理
        xhrB.onreadystatechange = function()
        {
            if(xhrB.readyState = 4 && xhrB.status == 200)
            {
                let xhrC = new XMLHttpRequest();
                //設定とかの処理
                xhrC.onreadystatechange = function()
                {
                    if(xhrC.readyState = 4 && xhrC.status == 200)
                    {
                        //続く…
                    }
                }
            }
        }
    }
}

わぁ、ネストの宝石箱やー。
これはもう見るだけで拒否感が出てしまいますね。
当然ながら、色々処理を省いてこれなので実際に運用するとさらに可読性が下がっていっちゃいます。
ヤバイですね☆

解決方法

その1 Callback

callbackで実装する。
ES6(ES2015)以前で主に使われていた手法です。
問題のコードよりかはわかりやすくなりますが、callbackもコールバック地獄というネストが深くなる問題を抱えてるので、
ブラウザ側も最新のJS仕様に対応してきているので、最近では使用されないことが多いのと思います。

その2 Promise

今回私が使ったのがこれでした。
ES6(ES2015)で実装された仕様。
Promiseを使う

Promiseは非同期処理の最終的な完了もしくは失敗を表すオブジェクトです。

main.js
function promiseFunction()
{
    return new Promise((resolve,reject) =>
    {
         //非同期で行いたい処理をここに記述する
    })
}

成功した場合はresolveメソッドを呼び出すことで処理が終わったことを、呼び出し元に伝えることができます。
また、promiseではpromiseチェーンを使うことで、非同期処理を連続して行うことができます。

main.js
promiseFunction().then(() =>
{
    return promiseFunction();
})
.then((result) => 
{
    return promiseFunction();
})
.then((result) => 
{
    return promiseFunction();
})
.catch((err) =>
{
   console.log('エラー');
});

thenの中で次に行いたいpromiseオブジェクトを呼び出すことで、処理をつなげることができます。
callbackやpureな実装時よりも、かなり直感的でシンプルになりました。

その3 async/await

ES2017で実装された仕様。
Promiseよりチェーンをなくし、よりシンプルでわかりやすくなった非同期処理です。
async/awaitで非同期処理はシンプルになる
async/awaitを使ったモダンな非同期処理

とはいえ全く別物というわけではなく、非同期処理の部分ではPromiseを使用しています。

どれを使えばいいの?

async/awaitを使えばいいと思います。
私は先にPromiseを見つけた関係でそちらを使いましたが、よりモダンにするならasync/await一択と思われます。
ただ、Promiseも使う場合があると思いますので、どっちも使えるようになっておくといいかな。

終わりに

長くなってしまいましたが、pureなajaxを頑張ってみた記事でした。
これからpureな非同期処理をやる方に、少しでも情報を伝えることができたなら幸いです。

参考記事

MDN
Promiseを使う
async/awaitで非同期処理はシンプルになる
async/awaitを使ったモダンな非同期処理

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

devDependenciesを削除してAWS Lambdaに高速アップロード

*この記事はA JavaScript user’s First AWS Lambda functionからの一部転用です。

TL;DR

npm pruneコマンドでdevDependenciesを一旦削除したらいいよね。

はじめに

Wataruです。カナダのバンクーバーというところでCivic Tech関連の小さな会社でソフトウェアデベロッパーとして働いています。なぜカナダで働いているか、もし興味があったらnote書きましたので読んで頂けると嬉しいです。
仕事の半分以上はフロントエンドを担当しています。Firebaseを触って以来、サーバレスアーキテクチャに興味があり、AWSの勉強を始めました。日本で購入したAWSによるサーバーレスアーキテクチャを参考に学習を進めています。

本題

上記の書籍の第3章にて、node.jsアプリケーションのAWS Lambdaへのデプロイについて触れられています。デプロイするアプリケーションのpackage.json(一部)は以下の通りです。

// package.json
{
...
  "scripts": {
    ...
    "deploy": "aws lambda update-function-code --function-name arn:aws:lambda:us-east-1:000000000000:function:transcode-video --zip-file fileb://Lambda-Deployment.zip",
    "predeploy": "zip -r Lambda-Deployment.zip * -x *.zip *.log"
  }
...
}

デプロイは以下のnpmコマンドで行なっています。

$ npm run deploy

コマンドが実行されると、package.jsonに書かれたpredeployが実行され、deployが実行される、という流れになっています。

  1. predeploy: 作業ディレクトリの全てを"Lambda-Deployment.zip"という名前で圧縮。
  2. deploy: AWSサーバーに圧縮したzipファイルをアップロード。

書籍で紹介されているサンプルプログラム程度ならこのままでいいのですが、unit testを書いたりESLintやprettierなんかを入れたりしていると、devDependenciesが増えてしまい、zipファイルのサイズが大きくなってしまいます。大きくなればなるほど、アップロードに時間もかかってしまいますね。devDependenciesはアップロードする必要が無いので、いっそのこと消してしまいましょう。

npm prune

npm pruneはnpmのCLIコマンドの一つです。ドキュメントを引用すると:

This command removes “extraneous” packages. If a package name is provided, then only packages matching one of the supplied names are removed.

Extraneous packages are packages that are not listed on the parent package’s dependencies list.

翻訳してみます。

このコマンドは"余計な"パッケージを削除します。パッケージ名がコマンド実行時に与えられていたら、その名前に一致したものだけが削除されます。

余計なパッケージとは、そのパッケージを使っているpackage.json内のdependenciesの一覧にないパッケージのことです。

npm pruneには--productionフラグというものがあります。このフラグをつけてコマンドを実行すると、devDependenciesで指定された全てのファイルが消去されます。

devDependenciesを削除できればzipファイルを軽量化できそうですね。しかし、このコマンドで削除したパッケージは開発には必要なものばかりです。削除した後でまたダウンロードするのも時間がかかりそうですよね。

npm install --offline

npm installにはドキュメントには記載されていない、おそらく非公式の--offlineというフラグがあります。npm CLIのデベロッパー@zkatがそのフラグについてnpmのissueの中で言及しています。以下、部分的に引用します。

  • npm@5 will automatically install from cache if it doesn't detect network, or if your router lacks external access. So if you want to use npm on a plane, you can just use it. You don't need --offline. It'll Just Work™.
  • --offline will ONLY be useful when you want to guarantee that no network access is done, and you want npm to crash when it doesn't have something in cache (this does not include git dependencies right now). So, this is what you use if you've hit your data cap and want to make damn sure npm doesn't suck up more data. Or, like, if you're temporarily tethering off your phone.

翻訳してみます。

  • npm@5はネットワークが検知されなかったら自動的にキャッシュからインストールします。飛行機などwifiがない環境でnpmを使いたかったら普通に使うことができます。--offlineフラグを使う必要はありません。
  • --offlineフラグは、あなたがどうしてもネットワーク接続なしでnpmを使いたい時"だけ"便利です。キャッシュにパッケージがなかったらnpmコマンドはクラッシュするでしょう(git dependenciesは2017年9月時点でキャッシュサポートされていません)。あなたがインターネットのデータ使用量を超過してしまった時や、電話を使ってテザリングしているときなどに使えます。

--offlineフラグを使って明示的にキャッシュからインストールするようにすれば、削除したdevDependenciesの再ダウンロードの時間を抑えることができそうです。

これらのnpmコマンドを使ったpackage.jsonに書き換えてみます。

// package.json
{
...
  "scripts": {
    ...
    "deploy": "aws lambda update-function-code --function-name arn:aws:lambda:us-east-1:000000000000:function:transcode-video --zip-file fileb://Lambda-Deployment.zip",
    "predeploy": "rm Lambda-Deployment.zip > /dev/null && npm prune --production && zip -r Lambda-Deployment.zip * -x *.zip *.log",
    "postdeploy": "npm install --offline"
  }
...
}

先程と少し異なり、postdeployを追加しました。

  1. predeploy:
    1. 今ある"Lambda-Deployment.zip"を削除。
    2. npm prune --productionでdevDependenciesを削除。
    3. 作業ディレクトリの全てを"Lambda-Deployment.zip"という名前で圧縮。
  2. deploy: AWSサーバーに圧縮したzipファイルをアップロード。
  3. postdeploy: キャッシュから全てのパッケージをインストール。

結果

devDependenciesを削除することで、私の場合はアップロードするzipファイルのサイズを26.3 MBから5.6 MBまで抑えることに成功しました。

おわりに

削除したり再インストールしたりするのでもしかしたら余計に時間がかかるかも、と思っていましたが、意外と時間をかけることなくデプロイすることができました。このあたりはdependenciesの量で変わったりするかもしれません。
元の記事では ESLint + prettier や husky + lint-staged + jest についても触れています。Qiitaではたくさんの方が書いているようですので省略しましたが、もし興味があれば教えてください。学び始めたばかりですが、共有できることもあるかと思います。

読んでいただきありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

jsonpの基本

 はじめに

jsonpって言葉は聞いたことあるけど、jsonと何が違うのか全く理解していませんでした。
jsonと同じようにデータ取得できるだろうと思っていたら、全く取得できず、詰みました。
初めて違いを理解し、実際に使ったので、その備忘録です。

仕組み

jsonpとはjson with paddingの略だそうです。jsonpのurlを叩くと、下記のように返ってきます。

callback({"りんご":1,"ぶどう":2,"もも":3});

jsonpのurlを叩くと、scriptが実行されます。そのcallbackとして、データが返ってきます。

jsonとの違い

・jsonはjavascriptから生まれたデータ用フォーマットで、下記のような感じのデータを保持するものです。

{
  "りんご":1,
  "ぶどう":2,
  "もも":3
}

つまり、jsonはデータそのものだが、jsonpはjsの関数です。
jsonとjsonpはちょっと記述が違うだけ、という認識をすると混乱します。(私がそうでした。)

データ取得方法

違いはわかったけど、じゃあデータをどうやって取得するのがいいのでしょうか。
方法はいくつかあると思います。
ライブラリを使用して取得する方法が一番簡単で良いのかもしれませんが、今回はライブラリを使用しない方法を記載します。
(モジュールの一部を記載するので、もし参考にされる場合は、適宜classの中とかに入れる必要があります。)

ConfirmationAPI.js
let index = 0;
confirmSerialCode(url) {
  index++;
  const callbackName = `confirm${index}`;
  const random = Math.round(Math.random() * 10000000);
  return new Promise((resolve, reject) => {
    const $base = document.getElementsByTagName('script')[0];
    const $script = document.createElement('script');

    window[callbackName] = function (res) {
      resolve(res);
      document.body.removeChild($script);
    };

    $script.id = `jsonp${index}`;
    $script.async = true;
    $script.src = `${url}?callback=${callbackName}&cache=${random}`;
    $script.onerror = function () {
      reject('Request Error');
    };

    $base.parentNode.insertBefore($script, $base);
  });
}

index.js
this.confirmationAPI
  .confirmSerialCode(url)
  .then((res) => {
    console.log(res);
  })
  .catch((err) => {
    console.log(err);
  });

今回は、この関数が走って、callbackから返ってきた値をconsoleに出力しています。
また、2回走っても大丈夫なように、indexの連番を付与しています。
(連番をつける理由は、2回関数が実行されてしまって、コールバックが一緒だった時にうまく動かない可能性があるためです。)

最後に

これはjsonpで値をどう取得すればよいかわかっていなかった私自身の備忘録です。
もし使いやすいライブラリをご存知の方いらっしゃいましたら、教えていただきたいです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

位置情報を取得するAPIを解説してみた

完成形はこちらになります↓


See the Pen
NVXQYE
by Yuji Yakena (@yuji-yakena)
on CodePen.


1.まずは簡単なhtmlを作成

index.html
<!DOCTYP html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Geolocation</title>
</head>
<body>
  <h1>位置情報サンプル</h1>
  <button onclick="getPosition();">位置情報を取得する</button>
  <script src="app.js"></script>
</body>
</html>

ポイントは2箇所あります

<button onclick="getPosition();">
ボタンをクリックすることで、jsファイルで定義したgetPosition()メソッドを呼び出して実行します。

<script src="app.js"></script>
jsファイルを読み込みます。

2.jsファイルにてメソッドの定義

app.js
function getPosition(){
  navigator.geolocation.getCurrentPosition(
    function(position){
      let nowLatitude = position.coords.latitude;
      let nowLongitude = position.coords.longitude;

      alert("緯度:"+nowLatitude+"、経度"+nowLongitude);
    },
  );
}

function 関数名()
任意の関数名を定義し、{ }内に実際の動作を記述します。
html側では、この関数名で呼び出します。

navigator.geolocation.getCurrentPosition()
デバイスの現在位置を取得するメソッドです。
引数として、success・error が使用されます。

メソッドの構文・定義など詳細はこちら↓
https://developer.mozilla.org/ja/docs/Web/API/Geolocation/getCurrentPosition

function(position)
先程引用した位置取得メソッドの引数として認識されます。

let 変数名
任意の変数を定義して、緯度・経度プロパティを格納します。

position.coords.latitude
position.coords.longitude
それぞれ緯度・軽度プロパティとして取得できます。

alert(引数)
先程定義した変数をアラート表示します。

まとめ

APIを理解する最初のステップとして、参考になれれば幸いです。。

参考元

MDN JavaScript リファレンス
https://developer.mozilla.org/ja/docs/Web/JavaScript

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Vue.js] vue.jsでアンカーリンクを作成する

やりたいこと

vue.jsでアンカーリンク(ページ内リンク)の実現

手順

[その1] vue-smooth-scrollのインストール

cd *** // プロジェクトのディレクトリに移動 
npm i -D vue-smooth-scroll

[その2] main.jsファイルにプラグインの使用を宣言

import vueSmoothScroll from 'vue-smooth-scroll'
Vue.use(vueSmoothScroll);

[その3] 挿入

<a href="#section1" v-smooth-scroll="{ duration: 1000, offset: -50 }">セクション1</a>

<div id="section1">
  <!-- セクション1のコンテンツ -->
</div>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TensorFlow.jsでDeepLearning(学習用画像データURLの収集)

こんにちわ。Electric Blue Industries Ltd.という、ITで美を追求するファンキーでマニアックなIT企業のマッツと申します。TensorFlow.jsでDeepLearningをする際に学習データ用の画像を収集することがあると思いますが、Google画像検索を使って簡単におこなう小ネタです。

Google画像検索で取得したい画像がリスト表示されたところで、下記のjavascriptをブラウザ(Google Chrome推奨)の開発者ツールのコンソールで実行すると、表示された画像のソースURLがCSVファイルとしてダウンロードできます。

ggl_image_urls.js
// 検索結果のHTMLソースから各検索結果のアイテムごとにソースURLを取得し配列化
urls = Array.from(document.querySelectorAll('.rg_di .rg_meta')).map(el=>JSON.parse(el.textContent).ou);

// 改行コードを各行末尾に付けたCSVとして吐き出す
window.open('data:text/csv;charset=utf-8,' + escape(urls.join('\n')));

追伸: Machine Learning Tokyoと言うMachine Learningの日本最大のグループに参加しています。作業系の少人数会合を中心に顔を出しています。基本的に英語でのコミュニケーションとなっていますが、能力的にも人間的にもトップレベルの素晴らしい方々が参加されておられるので、機会がありましたら参加されることをオススメします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JsDocをもとにドキュメントを自動生成する

Documentation.js

コマンドひとつで、ドキュメントを自動生成してくれる!

まずは、JsDocで記述

JsDocの概要については、おググりください。
VSCodeとかだと、関数の上の行で/** + EnterでJsDocテンプレートが挿入される

import { TweenMax } from 'gsap';

/**
 * TweenMax の基本操作
 */
export default class TweenAnimation {
  /**
   * @param {any} target
   * @param {number} duration
   * @param {gsap.TweenConfig} vars
   */
  constructor(target, duration, vars) {
    this._tween = TweenMax.to(target, duration, vars);
  }

  /**
   * アニメーションを止める
   * @param {any} [atTime]
   * @param {boolean} [suppressEvents]
   */
  pause(atTime, suppressEvents) {
    this._tween.pause(atTime, suppressEvents);
  }
}

生成されたドキュメント (HTML形式の場合) の一部

スクリーンショット 2019-05-23 11.38.02.png

使い方

インストール

yarn add -D documentation
(or npm i -D documentation)

HTML形式でドキュメント生成

documentation build path/to/your/javascript.js -f html
documentation build src/** -f html -o docs

MarkDown形式でドキュメント生成

documentation build path/to/your/javascript.js -f md
documentation build src/** -f md -o docs.md

オプションについて

  • -f: フォーマット (html, md)
  • -o: アウトプット(出力)
    • HTML形式の場合、生成するディレクトリ名を指定
      • index.htmlassets/が格納される
    • MarkDown形式の場合、ファイル名.md

番外編: ファイルに変更があったら、ドキュメントを更新

  • さらに、HTMLでもMarkDownでも生成したい場合の例

yarn add -D documentation chokidar-cli npm-run-all
(or npm i -D documentation chokidar-cli npm-run-all)

package.json
"scripts": {
  "watch": "chokidar 'src/es6/**' -c 'npm run docs'",
  "docs": "npm-run-all -p docs:*",
  "docs:html": "documentation build src/es6/** -f html -o docs/es6",
  "docs:md": "documentation build src/es6/** -f md -o docs/es6/README.md"
}

実行
yarn watch

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript: オブジェクトの分割代入とスプレッド構文を使ってみる

ECMAScript 2015の分割代入は、配列のほかオブジェクトに用いることもできます。また、ECMAScript 2018では、オブジェクトにスプレッド構文が採り入れられました。これらの構文の使い方を、簡単にご説明します。なお、配列の場合については、「JavaScript: ECMAScript 2015のスプレッド構文・残余引数・分割代入を使ってみる」をお読みください。

スプレッド構文を使う

スプレッド構文(...)をオブジェクトに用いると、プロパティと値の組みが展開されて取り出せます。つぎのコードは、オブジェクトを複製する例です。

const rect = {type: 'rectangle', width: 50, height: 20};
const rectClone = {...rect};
console.log(rectClone);  // {type: "rectangle", width: 50, height: 20}

ただし、つくられるのは浅いコピーです。値がオブジェクトのときは、参照が渡されることになります1

const rect = {type: 'rectangle', coords: {width: 50, height: 20}};
const rectClone = {...rect};
rect.coords.height = 25;
console.log(rectClone);  // {type: "rectangle", coords: {width: 50, height: 25}}

複数のオブジェクトを展開すれば、ひとつにまとめられます。ただし、同じプロパティがあると、あとの値で上書きされることにご注意ください。

const rect = {type: 'rectangle', width: 50, height: 20};
const circle = {type: 'circle', radius: 25};
const union = {...rect, ...circle};
console.log(union);  // {width: 50, height: 20, type: "circle", radius: 25}

展開したオブジェクトは、入れ子にしてまとめることもできます。

const type = {type: 'rectangle'};
const coords = {x: 20, y: 10};
const size = {width: 50, height: 20};
const rect = {...type, coords: {...coords, ...size}};
console.log(rect);  // {type: "rectangle", coords: {x: 20, y: 10, width: 50, height: 20}}

同じかたちの構文でも、関数の残余引数は配列です。任意の数のオブジェクトをまとめようとしたとき、つぎのように定めた関数では意図した結果が得られません。配列インデックスをプロパティとして、オブジェクトにまとめられてしまうからです。

function mergedObjects(...objects) {
  return {...objects};
}
console.log(mergedObjects(type, coords, size));
// {0: {type: "rectangle"}, 1: {x: 20, y: 10}, 2: {width: 50, height: 20}}

関数として定めるなら、残余引数の配列からオブジェクトをひとつずつ取り出してまとめなければなりません。つぎのコードは、Array​.prototype​.reduce()メソッドを使った例です(「配列要素の平均を求める」参照)。

function mergedObjects(...objects) {
  // return {...objects};
  return objects.reduce(
    (objects, object) => ({...objects, ...object})
  , {});
}
console.log(mergedObjects(type, coords, size));
// {type: "rectangle", x: 20, y: 10, width: 50, height: 20}

分割代入を使う

オブジェクトの分割代入は、オブジェクトからプロパティを取り出して対応する変数に代入する構文です。

let {a, b} = {a: 0, b: 1};
console.log(a, b);  // 0 1

対応するプロパティがない変数には、undefinedが入ります。また、すべてのプロパティを取り出す必要はありません。

let {a, b, x} = {a: 0, x: 10, y: 20};
console.log(a, b, x);  // 0 undefined 10

取り出したプロパティ値を、名前の異なる変数に割り当てることもできます。また、変数にデフォルト値を与えれば、対応するプロパティがない場合にその値が用いられます。

let {x, a: y, z = 0} = {a: 1, x: 10, y: 20};
console.log(x, y, z);  // 10 1 0

また、オブジェクトの残余プロバティ(Rest Properties)が、ECMAScript提案のステージ4の段階になりました。関数の残余引数とは異なり、オブジェクトにまとまります。

let {a, b, ...rest} = {a: 0, b: 1, x: 10, y: 20};
console.log(a, b, rest);  // 0 1 {x: 10, y: 20}

つぎのコードは、関数の引数にオブジェクトの分割代入を用いた例です。入れ子のオブジェクトから、プロパティを取り出すこともできます。

function getRectArea({type, coords: {width, height}}) {
  if (type === 'rectangle') {
    return width * height;
  }
}
console.log(getRectArea({
  type: 'rectangle',
  coords: {x: 20, y: 10, width: 50, height: 20}
}));  // 1000

ただし、入れ子の親に対応するプロパティがないとエラーになります。

console.log(getRectArea({type: 'rectangle'}));
// TypeError: Right side of assignment cannot be destructured

つぎの関数は、このエラーを避けるため、引数とプロパティにデフォルト値を用いた例です。

// function getRectArea({type, coords: {width, height}}) {
function getRectArea({type, coords = {}}) {
  if (type === 'rectangle') {
    const {width = 0, height = 0} = coords;
    return width * height;
  }
}
console.log(getRectArea({
  type: 'rectangle',
  coords: {x: 20, y: 10, width: 50}
}));  // 0
console.log(getRectArea({type: 'rectangle'}));  // 0

つぎの関数は引数にオブジェクトの分割代入を用い、さらに残余プロパティも使ってみました。

function getArea({type, ...coords}) {
  switch (type) {
    case 'rectangle':
      const {width, height} = coords;
      return width * height;
    case 'circle':
      const {radius} = coords;
      return radius ** 2 * Math.PI;
  }
}
console.log(getArea({
  type: 'rectangle',
  width: 50,
  height: 20
}));  // 1000
console.log(getArea({
  type: 'circle',
  radius: 10
}));  // 314.1592653589793

オブジェクトの種類(type)をもうひとつ増やしてみましょう。ここで注意したいのは、caseはブロックをつくらず、switch文がひとつのブロックなので、その中で変数の重複が許されないことです。プロパティ名がかぶってしまうときは、変数名を変えることが考えられます。

function getArea({type, ...coords}) {
  switch (type) {
    case 'rectangle':
      const {width, height} = coords;
      return width * height;
    case 'triangle':
      const {base, height: _height} = coords;  // 変数名を変える
      return base * _height / 2;
    case 'circle':
      const {radius} = coords;
      return radius ** 2 * Math.PI;
  }
}
console.log(getArea({
  type: 'triangle',
  base: 50,
  height: 20
}));  // 500

あるいは、switch文の外で変数宣言する手もあります。このときは、また別の注意をしなければなりません。分割代入を変数宣言で始めないときは、左辺がブロック{}とみなされ、代入演算子=が続けられないことです。そのため、代入文はかっこ()でくくらなければなりません。

function getArea({type, ...coords}) {
  let width, height, radius;
  switch (type) {
    case 'rectangle':
      ({width, height} = coords);
      return width * height;
    case 'triangle':
      ({base, height} = coords);
      return base * height / 2;
    case 'circle':
      ({radius} = coords);
      return radius ** 2 * Math.PI;
  }
}

オブジェクトを展開してまとめるスプレッド構文と、プロパティが簡単に取り出せる分割代入、少し注意すべき点はあるものの、コードがすっきり書けます。いろいろと工夫できそうです。


  1. 深いコピーをつくるには、つぎのようなやり方が考えられます。

    const rectClone = JSON.parse(JSON.stringify(rect));
     
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

vuejsが反映されない

解決コマンド

npm install -g expo-cli
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Shape Detection APIでQRコード検出を試してみました

はじめに

初めまして、k.s.ロジャースの西谷です。

直近でPCカメラからQRコードの読込を行いたい話がありました。
利用者の環境が未定であったことから、ブラウザ上で動作するQRコードリーダがあれば良い話があり、調査しておりました。
こちらの記事を紹介頂き、非常に参考になりました。

しかし、Shape Detection APIが全てのブラウザで非対応は「何かあるのでは?」と考え調査と検証を行いました。

間違い・助言等があればコメントにてお知らせいただけたらと思います。

Shape Detection APIとは

顔検出、ORコード読込、テキスト認識の3つのAPIを提供します。
これらはハードウェアアクセラレーションに対応しており、既存のWebライブラリに比べて低い負荷で実行することができます。

また、本APIは2019/05時点では草案で、試験的な機能となります。

本来上記3つの機能がありますが、今回は要件がQRコード読み取りであるため、QRコード周りの検証を行います。

実装

chromeの以下URLからExperimental Web Platform featuresをEnabledにすることで利用できるようになります。

chrome://flags/#enable-experimental-web-platform-features

QRコード識別の部分に関してはdetector.detect(image)をするだけで、後は識別した後のデータを返してくれます。
検証はこちらのコードを利用して進めました。
https://qiita.com/massie_g/items/a203cf2071f96d910743

動作状況

今回の一番の嵌りどころでした。
本APIはハードウェアアクセラレーションを利用するため、利用デバイスによって挙動が異なります

私の手元のデバイスではそれぞれ次のようになりました。

OS バージョン等 検証結果
Android Nexus 5X 正常動作
iOS iPhone8 Chromeのフラグが存在せず
macOS Mojave 10.14.1 動作するがQRコードを検出せず
Windows windows10 1809 非対応

Androidについて

こちらは正常動作しました。

image.png

QRコード以外にDataMatrixなども検出できます。
image.png

macOSについて

macOSはサポート済み判定されており、detect関数もエラーなく動作していました。
しかし、公式サンプルの固定画像でもQRコードを認識出来ませんでした。

スクリーンショット 2019-05-23 4.47.53.png

その他のデバイス

Windowsは公式で非対応との記載があり、QRコードは対象外になります。
iOSのChromeはenable-experimental-web-platform-featuresがなく、非対応となっています。

参考資料

https://wicg.github.io/shape-detection-api/#introduction
https://www.chromestatus.com/feature/4757990523535360

おわりに

今回はWebブラウザでQRコードを認識する予定でしたが、現状のShape Detection APIはかなりデバイスに依存することが分かりました。
標準化後に依存周りが良くなれば、開発コストやパフォーマンスを改善に期待できると思います。

今回は知見がほぼないため、間違いや改善案などがあれば是非教えて頂けたらと思います!

Wantedlyでもブログ投稿してます

Techブログに加えて会社ブログなどもやっているので、気になった方はぜひ覗いてみてください。
https://www.wantedly.com/companies/ks-rogers

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Shape Detection APIでQRコード検出を試しました

はじめに

初めまして、k.s.ロジャースの西谷です。

直近でPCカメラからQRコードの読込を行いたい話がありました。
利用者の環境が未定であったことから、ブラウザ上で動作するQRコードリーダがあれば良い話があり、調査しておりました。
こちらの記事を紹介頂き、非常に参考になりました。

しかし、Shape Detection APIが全てのブラウザで非対応は「何かあるのでは?」と考え調査と検証を行いました。

間違い・助言等があればコメントにてお知らせいただけたらと思います。

Shape Detection APIとは

顔検出、ORコード読込、テキスト認識の3つのAPIを提供します。
これらはハードウェアアクセラレーションに対応しており、既存のWebライブラリに比べて低い負荷で実行することができます。

また、本APIは2019/05時点では草案で、試験的な機能となります。

本来上記3つの機能がありますが、今回は要件がQRコード読み取りであるため、QRコード周りの検証を行います。

実装

chromeの以下URLからExperimental Web Platform featuresをEnabledにすることで利用できるようになります。

chrome://flags/#enable-experimental-web-platform-features

QRコード識別の部分に関してはdetector.detect(image)をするだけで、後は識別した後のデータを返してくれます。
検証はこちらのコードを利用して進めました。
https://qiita.com/massie_g/items/a203cf2071f96d910743

動作状況

今回の一番の嵌りどころでした。
本APIはハードウェアアクセラレーションを利用するため、利用デバイスによって挙動が異なります

私の手元のデバイスではそれぞれ次のようになりました。

OS バージョン等 検証結果
Android Nexus 5X 正常動作
iOS iPhone8 Chromeのフラグが存在せず
macOS Mojave 10.14.1 動作するがQRコードを検出せず
Windows windows10 1809 非対応

Androidについて

こちらは正常動作しました。

image.png

QRコード以外にDataMatrixなども検出できます。
image.png

macOSについて

macOSはサポート済み判定されており、detect関数もエラーなく動作していました。
しかし、公式サンプルの固定画像でもQRコードを認識出来ませんでした。

スクリーンショット 2019-05-23 4.47.53.png

その他のデバイス

Windowsは公式で非対応との記載があり、QRコードは対象外になります。
iOSのChromeはenable-experimental-web-platform-featuresがなく、非対応となっています。

参考資料

https://wicg.github.io/shape-detection-api/#introduction
https://www.chromestatus.com/feature/4757990523535360

おわりに

今回はWebブラウザでQRコードを認識する予定でしたが、現状のShape Detection APIはかなりデバイスに依存することが分かりました。
標準化後に依存周りが良くなれば、開発コストやパフォーマンスを改善に期待できると思います。

今回は知見がほぼないため、間違いや改善案などがあれば是非教えて頂けたらと思います!

Wantedlyでもブログ投稿してます

Techブログに加えて会社ブログなどもやっているので、気になった方はぜひ覗いてみてください。
https://www.wantedly.com/companies/ks-rogers

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【2分でイメージをつかむ】fsオブジェクト

なんでこれを書いたか

  • Node.jsでサーバーを立てた後、やることはこれ
  • ファイル読み込みをどうやってやるかのイメージをつかみたかったから

fsオブジェクトとは

  • file system オブジェクト
  • ファイルを扱うオブジェクトでHTMLファイルなど読み込める!

使い方

男は黙って、変数に読み込む

var fs = require('fs');

まずはこれだけ使えるようになればOK

fs.readFile( './hello.html', 'UTF-8', コールバック関数 );
  • 第1引数にファイルパスを指定(例えば・・'./hello.html')
  • 第2引数には'UTF-8'を指定(一旦はこういうもんだと思っておけばOK)
  • 第3引数には、読み込み完了後の処理を行うコールバック関数をかく

コールバック関数

  • コールバック関数は、このファイルの読み込みが完了したら、発動します!
  • 仮にこのファイルが、読み込みに1時間を要する超膨大ファイルだったとしましょう
  • 1時間後、コールバック関数が発動し、そのあとに書いてるソースコードはようやく読まれるようになります

 

例えるなら、お会計をしている人が財布を探していて、なかなか列が進まない・・
images.jpeg

しかし

  • そんなことしてたら、ずーっと待ちっぱなしで列に並ぶ気が失せる
  • というか財布探すのに1時間もかけて、店員もずっと待ってるって・・・店としてどうなんだ!

というご意見がありましたので、、

お財布探している間、次の方どうぞ〜

  • に変更しました
  • お財布が見つかり次第、お会計(コールバック関数)に移らせていただきます。
  • 探している間は、次のお客様(readFile以降の処理)からお会計させていただきます。

簡単なコード例

function doRequest(req, res) {
    //hello.htmlを読み込もう!
    fs.readFile('./hello.html', 'UTF-8', 
        // 読み込みが完了したら、コールバック関数が発動
        function(err, data) {
            res.writeHead(200, {'Content-Type': 'text/html'});
            // 読み込んだファイル(data)を書き込む
            res.write(data);
            res.end();
        });

    console.log('readFileのファイル読み込みが完了するまで、お待ちください');
    console.log('おや、このログを見ているということは、まだファイルの読み込みが完了していないようですね');
}
  • hello.htmlのファイル読み込みがおそーーかった場合、コールバック関数は発動せずに、readFile関数の下にある、console.logが呼び出される
  • ファイル読み込みが完了した時点で、コールバック関数が発動する

こっから脱線

  • コールバック関数は、「非同期処理」になります
  • つまり、割り込みができるということです!
  • 財布見つかった時点で、最後尾に列びなおさなくても、お会計に割り込めるというイメージです

じゃあ、それって並列処理なんですか?

  • 違います
  • 並列処理は、複数の作業を同時に実施します。
  • レジいっぱいあるから財布探している人が割り込んでくる心配はない。同時にお会計ができるということです

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む