20190524のJavaScriptに関する記事は21件です。

JS初心者の自分がつまづいた、「プロトタイプベースのオブジェクト指向」について調べてみた

概要

JavaScript初心者だった私が、 『改訂新版JavaScript本格入門』にあたり、
つまづいた「プロトタイプベースのオブジェクト指向」を調べてみました。

この記事を書いた人間の状況(2019/5 現在)

  • PHPを中心に開発してきました(書いた時で3年半くらい)
  • クラスベースのオブジェクト指向はある程度理解しているつもりです
  • JavaScript(JS)は正直本格的にやってこなかったため、勉強をはじめました
  • 『改訂新版JavaScript本格入門』 の「プロトタイプベース」という言葉に馴染みがなく、つまづきました
  • なので調べてみた、というのが今回の記事です

留意点

  • あくまで「調べてみた」なので、完璧に理解できているわけではありません
  • いろんなものを調べた結果、どう自分は理解したかを書いていきます
  • 様々な本や記事を参考にしました(できる限りリンクを貼ったつもりです)
  • 皆様の解釈と異なるかもしれませんが、多めに見てください(明らかに違う場合はご指摘お願いします)

本題

以下の通りです。

  1. そもそもオブジェクト指向とは?
  2. プロトタイプベースはクラスタイプベースとどう違うのか?
  3. プロトタイプベースでは、(クラスベースと違って)何ができるの?
  4. プロトタイプベースは今後どうなるのか?

1. そもそもオブジェクト指向とは?

  • これだけでご飯3杯はいける内容です(のでここでは割愛)
  • 個人的には『オブジェクト指向でなぜつくるのか』 という書籍がおすすめです
  • Qiitaだと、 オブジェクト指向と10年戦ってわかったことオブジェクト指向のいろは が有名です(私も勉強させていただきました)
  • 私の解釈としては、「オブジェクト指向で実装すると、コードの重複を防いで修正漏れを無くしてくれたり、コードを一文一文全て読まなくても、メソッドとプロパティでどういうことができるかを理解できたり、他のオブジェクトとの結合箇所だけ注意すればバグが起こりにくかったり・・・と言った(手続き型と比べて)利点がたくさんあるよね」と思っています(なげえよ)

2. プロトタイプベースはクラスタイプベースとどう違うのか?

Javascriptでオブジェクト指向するときに覚えておくべきこと という記事に、以下の通り書かれていました

クラスベース (Java):クラスとインスタンスは異なる実体です。
プロトタイプベース (JavaScript):すべてのオブジェクトはインスタンスです。

『改訂新版JavaScript本格入門』にも似た話が記載されていました。
「クラスという名の抽象的な設計図(雛形)がなくて、すべては実体なんだなー」と私は理解しました。
(ES6からクラスの概念が導入されたことは、話がややこしくなるので一旦ここでは無視しています)

3. プロトタイプベースでは、(クラスベースと違って)何ができるの?

ここが特に興味深いところでした。まだまだあるかもしれませんが、以下の2点が書かれていました。

  • インスタンスにプロパティを追加・削除ができる(関数リテラルも対象なので、メソッドも追加できる)
  • prototypeプロパティを使って、全てのオブジェクトの元になるプロトタイプオブジェクトにメソッドを追加し、さらにそれを別のインスタンスから参照することができる

※2019/5/25 以下追記
↓↓↓↓↓
こちらコメントにてご指摘をいただいた通り、オープンクラスを採用している言語では、
既存のクラスにメソッド等を追加が可能とのことでした。
あくまでこのクラスベースとは「インスタンスにメソッド等を追加できない」言語を対象にしています。
↑↑↑↑↑

この「できる」は、非常に「怖い」なあと私は思ってしまいました。
「このオブジェクトはこのメソッドが定義されているから使えるはず」
「このオブジェクトで何ができるのかは定義(クラス)を見にいけばいい」
というのが一切通用しなくなるわけですから、ソースコードを注意深く読まなければいけません。
(もっともsealメソッドを使うなど、その辺りの懸念を低減するものもあるとのことですが)

またこういったインスタンスにプロパティを追加/削除できるというのが、
「JSのオブジェクトは連想配列みたいなもん」と言われる所以なんだろうなあと思いました。

4. プロトタイプベースは今後どうなるのか?

あくまで私の感想ですが、プロトタイプベースで今後JSを書くのは少なくなると感じています。

実際先ほど触れた通りES6からクラスの概念が導入され、
またTypeScriptなどAltJSで他の言語と同じように、クラスベースで簡単に書くことができます。

そもそもバックエンド側のメジャーな言語は、クラスベースのオブジェクト指向が使われていることからも、
馴染みの薄いプロトタイプベースで実装する理由はあまり見つかりません。

ただだからと言って、プロトタイプベースを理解しなくていいというわけにはいかないとも思っています。
上記事項を理解してないと、既存のソースコードがわかりにくい可能性が高いです。(実際私はここでつまづきました)

特にクラスに精通している方こそ、「なんでインスタンスにメソッド追加してるの?」とか思ってしまうかもしれません。
そうではなく「JSではこういう書き方もできるんだ」と覚えておいても損ではないかなあ、と思っています。

参考にした書籍、リンク

『改訂新版JavaScript本格入門』のChapter 5
Javascriptでオブジェクト指向するときに覚えておくべきこと
クラスベースとプロトタイプベース
JavaScriptをプロトタイプベースのオブジェクト指向言語と言うべきではない

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

ElmとWebAssemblyで迷路生成Webアプリを作った話

ElmとWebAssemblyで迷路生成Webアプリを作った話

はじめに

東京大学計数工学科は毎年物理工学科と一緒に五月祭で工学博覧会という企画を行っています。
今回、「アルゴリズム」を紹介するということで迷路生成アルゴリズムを展示しました。
そのためのwebアプリをElmとWebAssemblyで作って公開したので紹介します。

https://tomabou.github.io/meyfes-front/

機能

お絵かきをして
Screenshot_20190524-111819~01.png
格子グラフを作って(頂点をタップすると格子グラフが編集できます)
Screenshot_20190524-111833~01.png
Submitすると迷路が出来ます
Screenshot_20190524-111840~01.png
Screenshot_20190524-111847~01.png
いろんな迷路を作ってみてください!

採用理由

Elm

ElmはUI作成に特化した言語、アーキテクチャです。

今まで自分はWebフロントを書いたことが全くありませんでした。ブラウザで実行できる以外に良い点が無さそうなJavaScript(偏見)を勉強するモチベも、技術の流れが早く数年後には廃れているであろう色々なフレームワーク(偏見)を勉強するモチベも全く湧かなかったのですが、ElmはJavaScriptを書かなくて済みそうで、かっこよさそうなのにそんなに流行っていなさそうだったので使ってみることにしました。

まあ純粋関数型言語って響きがカッコイイよね。

WebAssembly

迷路生成アルゴリズムはもともとC++で書いてありました。

五月祭当日はGoogle Compute Engineを借りてその上で実行する形を取っていたのですが、出来ればクライアント側で完結させたいです。そうするとお金も掛からないし、遅延も少なくなります。JavaScript等に書き直せば話は簡単ですが、JavaSciptのことはよく知りません。
WebAssemblyは、クライアント側で実行出来て、C/C++やRustからコンパイルして作ることが出来ます。
もともとのC++で作られたプログラムは標準入出力でやり取りをするだけであったので、それを一つの関数としてまとめました。
C++のSTL等にも普通に対応しており、既存のコードのIOの部分だけちょっといじればコードをそのまま流用できます。

感想

Elmは素晴らしい言語、アーキテクチャでした。JavaScriptの世界は闇の世界(偏見)だと思っているのですが、Elmは楽園(偏見)です。(ちなみに自分はElm以外のフレームワークに触ったことが無いので比較などは全く出来ません)

  • 純粋関数型のおかげか、コンパイルが通ればほとんど実行時エラーを見ることはない
  • Elm Architectureのおかげか、全く苦労することなく並行性を持ったプログラムが書ける
  • 出来ることが適切に制限されているので、全体の構造を意識せず適当に継ぎ足しながら書いても全然崩壊しない
  • UIに特化していることでHaskellのような理解にひと手間かかる概念がないのでスタートダッシュも楽々(例えばモナドとして抽象化出来る機能がいくつかあるが別に抽象化はしていない)
  • Haskellなどでメモリ効率を意識したり色々なデータ構造を導入しようとすると初心者には厳しい領域に入るが(Haskellで例えばセグメント木をどう書けば良いのか自分にはよくわかりません)、WebのUIというのは通信とDOMの操作が重たいパートなのでそこまで効率を意識したコードを書く必要もないー>そういった苦労もあまりない
  • JavaScriptの世界とElmの世界はPortで繋げることが出来るが、Elm側の世界が安全でしっかりしているのでJavascript側の世界がゆるゆるでもどうにかなる

Elmのおかげで非常に気楽にWebアプリを作ることが出来ました。

WebAssemblyも初めて使ってみましたが、期待通りの働きをしてくれました。感想としては

  • C++で書いたコードをそのまま流用できるのが非常に嬉しい。
  • 重たい処理をC++/Rustで書いてクライアント側で実行出来るのは便利。最適化やらソルバーやら画像処理やら複雑なアルゴリズムやらをクライアントサイドで実行したいならば一番良い選択肢なのでは無いか?
  • 謎の実行時エラーを吐かれたときにデバッグがしにくい(良いツールを知らないだけかも?)ので既存のライブラリを流用するのではなく自分で書く時はRustのようなメモリ安全性が担保しやすい言語で書いたほうが良いのかも。
  • Emscriptenはwasmとそれを実行するJavaScriptファイルを出力して、それを用いると非常に気軽にwasmを実行できる。パフォーマンスを重視するならば自分でfetchしたりするコードを書いたほうが良いのかも。

ちなみに、WebAssemblyはバイナリファイルにする前の中間表現としてS式(Lispに使われてるアレです)を用いているので、非常に読みやすいです。まともにアセンブリを読んだことがない人の戯言ですが、よく見るx86のアセンブリ等よりも断然読みやすいんじゃないんでしょうか

また、mallocのコストは重いのか軽いのか等細かいことはよく分かっていないので必要になったらまた勉強しようと思います。

まとめ

Elmは非常に便利でストレスなくWebアプリを作ることが出来ます。もしも重たい処理を実装したくなったり既存の資産を流用したくなったらメモリをちゃんとハンドリングする言語の中で自分が得意なもので書いてWebAssemblyにコンパイルすれば万事解決です
UIの部分をElmで書いて、重たい処理をWebAssemblyに任せるという分業はもしかしたら素晴らしいかもしれません。WebAssemblyの今後に期待です。

ちなみに今回始めてCSSを書いたんですが、CSSはよくわからなかったです。

なにはともあれ色々迷路を作って遊んでみてください!
ここまで読んでくださってありがとうございました!

ちょっとした注釈

ElmとJavascriptの間で情報をやり取りするにはFlagかPortを使うのが一般的だが、画面に描画していればJavascript側からDomをGetして中身を見ればElmの世界からJavaScriptの世界に情報を伝えられる。
今回のCanvasからグラフを生成するところではそれを用いた。

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

51歳からのプログラミング 備忘 吹き出し的なもの

吹き出し的なもの(以下、吹き出しという)を作ってみた。

入力欄を空欄で入力した時に、
吹き出しで「未入力です」ってアナウンスが
フェードインして入る、って感じで。

・ 吹き出しを、cssで用意
・ 吹き出しのcssは、<input の後の <span に設定
・ 吹き出しのcssは、最初は非表示にしとく(display:none)
・ 吹き出したいときに、表示する(ここでは、submit時)

そんな感じで作れそうだったので作った。

表示位置の設定と、フェードインアウト,
入力時には非表示状態、をコードしてるので、
ちょっと長いのが残念。

完成コードはこれ
でも、吹き出しをフェードアウトさせるところが気に入らない。いつか直そう。

//------- jquery ----------
<script>
  $(function(){
    var position,width;
    var $form=$('form');
    var $inp =$('input[name="inp"]');
    var $sp  =$('span[name="sp"]');

    $form.submit(function(){
      if(!$inp.val()){
        position=$inp.offset();
        width   =$inp.width();
        $sp.text('未入力です');
        $sp.css('display','inline');
        $sp.offset({top :(position.top-1),
                    left:(position.left+width+10),
                  });
        $sp.hide().fadeIn(100);
        return false;
      }
    });

    $inp.on('keypress',function(){
      $sp.fadeOut(100);
    });
  });
</script>
//----------------------------


//---------- css -------------
<style>
  .tip    {display:none;
           color:white;
           background:blue;
           border-radius:5px;
           }
</style>
//----------------------------


//--------- html -------------
<form action="test" method="post">
  {{csrf_field()}} // これはlaravel仕様です
  <input name="inp">
 <span name="sp" class="tip"></span>
</form>

コードを打つだけで楽しい。
心が躍るような楽しさ。
こんな楽しいことがあったんだ。

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

スクロール位置取得方法をいい加減忘れないようにメモ

スクロールの位置を取得する方法に毎度混乱していたため、記事としてまとめる。
スクロール位置の取得には3つのメソッドを利用する。

  • getBoundingClientRect
  • window.innerHeight
  • window.pageYOffset

getBoundingClientRect

getBoundingClientRectはターゲット要素をの位置を
ブラウザの表示領域の左上を(0, 0)として、そこからの相対位置で示される。

js
const target = document.querySelector('el').getBoundingClientRect().top; 

上記の場合、要素の上端がブラウザの表示領域の上端にきたときに値が0となる。

window.innerHeight

window.innerHeightは現在のブラウザの高さを取得する
getBoundingClientRectはブラウザの上端の位置を境界とするため、ブラウザの下端で判断したい場合にこれを利用する

window.pageYOffset

ブラウザの上端を基準とした縦方向のページのスクロール量を取得する。
getBoundingClientRectで取得した値にこの値を足した値がターゲット要素の絶対位置になる。

example

//現在のブラウザ表示領域の上端からの相対距離(要素の上部がブラウザの一番上にきたときに0になる)
const relativePositionTop = document.querySelector('el').getBoundingClientRect().top; 


//現在のブラウザ表示領域の下部からの相対距離(要素の下部がブラウザの一番下にきたときに0になる) window.innerHeightはブラウザの画面サイズを取得
const relativePositionBottom = document.querySelector('el').getBoundingClientRect().bottom - window.innerHeight;


//document要素の絶対位置(値は固定) window.pageYOffsetはdocumentの上端からのスクロール量
const absolutePositionTop = document.querySelector('el').getBoundingClientRect().top + window.pageYOffset;

無題のプレゼンテーション.png

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

Web Componentsの作成から公開まで

Web Componentsでスライダーを作って公開までやってみる。

Web Componentsとは

WebページおよびWebアプリケーションで使用する、カスタムでき、
再利用可能なカプセル化されたHTMLタグを作成できるようにするWebプラットフォームAPI
Web開発者はカプセル化されたスタイルとカスタム動作を使用して新しい要素でHTMLを簡単に拡張できる。
ReactやVueでいうコンポーネントにあたる。

仕様

主に4つの機能がある。

Custom Elements

独自の機能を備えたDOM要素を構築するための方法を提供する。
JavaScriptなどで機能を追加できる。
https://w3c.github.io/webcomponents/spec/custom/

Shadow DOM

カプセル化されたスタイルとマークアップを使用する方法を定義。
カプセル化することで外からCSS, JavaScriptで操作できなくなる。
https://w3c.github.io/webcomponents/spec/shadow/

ES Modules

JavaScriptファイルから別のJavaScriptファイルを読み込む仕組みを提供。
https://html.spec.whatwg.org/multipage/webappapis.html#integration-with-the-javascript-module-system

HTML Template

ページの読み込み時に描画されず、後で JavaScript を使用してインスタンスを生成。
https://html.spec.whatwg.org/multipage/scripting.html#the-template-element/

基本部分を作ってみる

開発サーバーのインストール

webpack-dev-serverを動かせる環境を整える

$ npm i webpack webpack-cli webpack-dev-server path

webpack.config.jsの作成

$ touch webpack.config.js
webpack.config.js
const path = require('path');

 module.exports = {
  watch: true,
  entry: './my-app.js',
  devServer: {
    inline: true,
    contentBase: path.join(__dirname, './'),
    compress: true,
    port: 8080
  }
 };

package.jsonにwebpack-dev-serverの起動コマンドを記述

package.json
 "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack-dev-server"
  },

Componentの作成

ファイル構成
app-name/
├ index.html
└ my-app.js

index.html
<!DOCTYPE html>
 <html lang="en">
 <head>
   <meta charset="UTF-8">
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <meta http-equiv="X-UA-Compatible" content="ie=edge">
   <title>App name</title>
   <script type="module" src="./my-app.js"></script>
 </head>
 <body>
   <my-app></my-app>
 </body>
 </html>
my-app.js
class MyAppElement extends HTMLElement {
   constructor() {
     super();
     const shadowRoot = this.attachShadow({ mode: 'open' });
     shadowRoot.innerHTML = this.template();
   }

   connectedCallback() {
     // 読み込み時に実行される
     console.log('connected');
   }

   template() {
     // 表示したいコンポーネントの構造
     return `
       <p>Create component</p>
     `;
   }
 }

 customElements.define('my-app', MyAppElement);

表示確認

# サーバーの起動 localhost:8080
$ npm run start

「Create component」と表示されていれば成功

スライダーを作ってみる

簡単に使用できるスライダーを作ってみる。
その名も「slider-helper-components」
画像を複数指定し、自動でスライドを動かしたい場合は秒数も指定するようにした。

index.html
<slider-helper-components
  images="./images/slide01.jpg, ./images/slide02.jpg, ./images/slide03.jpg, ./images/slide04.jpg"
  auto="3000"
>
</slider-helper-components>

全体のコードはこれ
https://github.com/raihara3/slider-helper-components

できた!
でもWeb Componentsを公開するにはまずnpmを公開する必要があった。

npmパッケージの公開

公開するファイルの内容を確認

以下を確認

index.js
// example 1
import Example from './src/example.js';
exports.Example = Example;

// example 2
import Example from './src/example.js';
export default Example;

npm packを実行
圧縮されたフォルダを解答し、内容を確認する。
これが配布されるデータになるので、不要なものが含まれている場合は.npmignoreに記述する。

npmアカウントの作成

https://www.npmjs.com/
ローカルでnpmアカウントにログインします。
登録したメールアドレスにメールが来ているので事前にメールアドレスを承認しておく。

$ npm login
# username, password, emailを聞かれるので登録したものを入力

package.jsonのバージョンを適切なものに変更し、いざ公開

$ npm publish

すぐに公開完了。メールで通知もきます。
これが今回作ったスライダーになります。
https://www.npmjs.com/package/slider-helper-components
もうnpm install ***が可能になった。

webcomponents.orgでの公開

https://www.webcomponents.org/publish
注意事項を確認してからパッケージ名を入力し、publish
こちらもすぐ公開された
https://www.webcomponents.org/element/slider-helper-components

以上でWeb Componentsの公開が完了。


参考

公式サイト
https://www.webcomponents.org/introduction
Web Componentの作り方
https://qiita.com/ka-miyata/items/63e41e4105d7aeb44d6c#ディレクトリ作成

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

vueつかってみた1

<!-- CDNを使用しました -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>


<h1>お菓子を選ぼう!</h1>
<div id="snack">
    <p>カゴの中身:{{ amount }}個</p>
    <ul>
        <li v-for="(snack,i) in snackList">
            {{snack}}
            <button @click="removeCart(i)">削除</button>
        </li>
    </ul>
    <button @click="addCart">カゴに入れる</button>
</div>


<script>

(function(){

new Vue({
    el: '#snack', // #cnack に紐付け
    data: {
        cart: 0,
        snackList: ['じゃがりこ','えびせん']
    },
    // 値が変わるとその値に依存しているすべてのバインディングが更新される
    computed: {
        // 合計を返すメソッド
        amount: function(){
            return this.snackList.length;
        }
    },
    // ほかのメソッド
    methods: {
        // カートへポテトを追加するメソッド
        addCart: function(){
            this.snackList.push('ポテト')
        },
        // カートの中身を削除するメソッド
        removeCart: function(i){
            console.log(i);
            this.snackList.splice(i,1);
        }
    }
});


})();

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

�【web3.js】web3.utils.BN�で大きな数字を扱う

概要

JavaScriptで大きな数字を扱うときのライブラリBN.jsを使用出来る.
一般的にJavaScriptの数値型Numberは倍精度浮動小数点で表現されるため,最大の整数はNumber.MAX_SAFE_INTEGER=9007199254740991である.
今回のBN.jsライブラリを用いることでこの数値以上の数値を扱う計算ができる.

仕様

web3.utils.BN(mixed);
  • 引数
    Number/String : 数値,数値の文字列

  • 返り値
    Object : BN.jsのインスタンス

const BN = web3.utils.BN;

new BN(1234).toString();
> '1234'

new BN('1234').add(new BN('1')).toString();
> "1235"

new BN('0xea').toString();
> "234"

参考

web3.js ドキュメント

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

jsフレームワークstimulusでyahoo地図を操作してみた(その二)

前回の続き。
今回参照したサイトはこちら……というか今回は、このサイトの記述を
【stimulus使用+「googlemap→yahoo地図」】
に書き換えただけです。

やったこと、やる予定のこと

0. railsアプリへのstimulus適用(以前書いた記事)

1. stimulusでyahoo地図を表示(前回)

2. 表示した地図上で、指定座標に移動(←今ここ)

……今回は「指定座標」はコードに直接書いています。
 座標取得の方法は「3」以降でやる予定。

3. 住所から座標を取得し、それをDBに登録(こんど書く)

4. 「3」で登録した情報を選択し、yahoo地図上でその場所に移動(そのうち書く)

ということで、前回作成した地図上を移動できるようにしてみます。

まずは、移動用のボタンを用意する。

app/views/users/test.html.erb
<div data-controller="moving">

<!-- 中略 -->
  <input type="button" id="tokyo" value="東京" data-action="click->moving#tokyo">

  <input type="button" id="shinbashi" value="新橋" data-action="click->moving#sinbasi">

  <input type="button" id="shinagawa" value="品川" data-action="click->moving#sinagawa">


</div>

ボタンを三個用意して、それぞれにデータアクション名を設定……「data-action="click->moving#tokyo"」(又は「sinbasi」「sinagawa」)
なおこの記述は、【「data-controller="moving"」であるdiv要素内で行う。

このアクション名設定により、ボタンクリックするとstimulusのアクションが発生するので……

アクション発生時の処理をstimulus用jsファイルに記述

……前回作成したjsファイルに、

app/javascript/controllers/moving_controller.js
  initialize() {

       this.map = new Y.Map(this.mapTarget.id);
       this.map.drawMap(new Y.LatLng(35.66572, 139.73100), 17, Y.LayerSetId.NORMAL);

       var center = new Y.CenterMarkControl
       var control = new Y.LayerSetControl();
       this.map.addControl(center);
       this.map.addControl(control);

  }

//↓追記
  tokyo() {
    this.map.panTo(new Y.LatLng(35.680865,139.767036), true);
  }

  sinbasi() {
    this.map.panTo(new Y.LatLng(35.666397,139.758153), true);
  }

  sinagawa() {
    this.map.panTo(new Y.LatLng(35.629867,139.74015), true);
  }
//↑追記

「35.680865,139.767036」などの座標は、参照サイトから写してきた実際の位置情報です。

これで実行してみると……
スクリーンショット 2019-05-24 14.04.00.png

「東京」「新橋」「品川」のボタンが追加され、クリックするとそれぞれの場所に移動できるようになりました。

とりあえずは今回はここまで、続きは次回以降(の、予定)。

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

Atomic Designを採用したnuxtで親から子コンポーネントのスタイルを使い分ける

対象

  • nuxtないしVueをつかっている
  • Atomic Designを使ってコンポーネントを分けたアプリをつくっている(Atominc Designに限らなくてもコンポーネントの共通化をしている)
  • styleはscopedにしていて、コンポーネントを超えてcssを使い回していない
  • Scssをつかっている(使っていない場合はcssをネストさせなければよいだけですが)

やること

背景

  • アプリの中で使うbuttonやlabelを共通化したい
  • アプリ内ではbuttonやlabelの見た目はいくつかのパターンしかない
  • しかし、横幅とかは使う場所によってまちまちである

やりたいこと

  • 見た目のパターンはそのコンポーネントの中に定義して、親コンポーネントからどのパターンかを指定したい
    • 同じスタイルを何度も書きたくはないため
  • 横幅などは親コンポーネントからその都度値を指定したい
    • 微妙に変わる部分は子コンポーネントにいちいち定義していると限りがないため

この記事でやってみること(つまり具体例)

  • MyButtonというコンポーネントをつくる
  • MyButtonは通常の見た目と、強調したい見た目を親から選んで指定する
  • 通常、強調のどちらでも影付きボタンにできる
  • MyButtonのwidthは親から直接指定する

やり方

  • Vueが用意しているクラスとスタイルのバインディングをつかう
  • htmlのセレクタに対して、:class=:style=と属性を付与することで、クラスや個別のスタイルをバインディングできる

通常と強調を使い分ける

こんな感じのコンポーネントを作る

MyButton.vue
<template>
  <!-- propsで受け取ったtypeをクラスにバインディングする -->
  <button class="MyButton" :class="type">
    <slot />
  </button>
</template>

<script>
export default {
  props: {
    // 適用したいスタイルを文字列のporpとして親から受け取る
    type: {
      type: String,
      default: ''
    }
  }
}
</script>

<style lang="scss" scoped>
.MyButton {
  <!-- 見た目のパターンをネスとしたクラスで定義する -->
  &._normal {
    background-color: white;
    border: solid 1px black;
  }

  &._strong {
    background-color: red;
    border: solid 1px black;
  }

  &._shadow {
    box-shadow: 0px 3px 6px 0px black;
  }
}
</style>

親からはこのように使用する

Parent.vue
<template>
  <div>
    <!-- 適用したいクラスを文字列で指定する -->
    <my-button class="_nomal">
      通常ボタン
    </my-button>
    <my-button class="_strong">
      強調ボタン
    </my-button>
    <!-- クラスを二つ指定する時はスペースで区切る -->
    <my-button class="_nomal _shadow">
      影付き通常ボタン
    </my-button>
  </div>
</template>

横幅を指定する

先ほどのコンポーネントに:style属性を書き加える

MyButton.vue
<template>
  <!-- propsで受け取ったwidthの値をwidthスタイルにバインディングする -->
  <button class="MyButton" :class="type" :style="{ width: width }">
    <slot />
  </button>
</template>

<script>
export default {
  props: {
    type: {
      type: String,
      default: ''
    },
    // 適用したいスタイルの値を文字列のporpとして親から受け取る
    width: {
      type: String,
      default: ''
    }
  }
}
</script>

<style lang="scss" scoped>
.MyButton {
  <!-- こちらはそのまま -->
  &._normal {
    background-color: white;
    border: solid 1px black;
  }

  &._strong {
    background-color: red;
    border: solid 1px black;
  }

  &._shadow {
    box-shadow: 0px 3px 6px 0px black;
  }
}
</style>

親からはこのように使用する

Parent.vue
<template>
  <div>
    <!-- 適用したい横幅の値を単位付きの文字列で指定する -->
    <my-button class="_nomal" width="100px">
      通常ボタン
    </my-button>
    <!-- 単位を自由に指定できる -->
    <my-button class="_strong" width="10%">
      強調ボタン
    </my-button>
  </div>
</template>
  • :style="{ width: width }"は、{ width: width }の左側にスタイルを書いて、右側に値を書く (要するに:style="{ width: 10px }"ということ)
  • 複数のスタイルを指定したいときには:style="{ width: hoge, height: fuga }"のようにカンマで区切ればOK

まとめ

クラスとスタイルのバインディングをうまく使って抽象的なコンポーネントを作れれば、その後が楽!

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

React.unstable_ConcurrentModeは廃止について

はじめに

去年の11月にReact 16.x Roadmapが公開されて、次はConcurrent Modeの登場を待っている状態です。
少し前になりますが、これに関連して変更があったようです。

React.unstable_ConcurrentMode

Concurrent Modeを試すには二通りの方法がありました。上記の記事から抜粋します。

// Two ways to opt in:

// 1. Part of an app (not final API)
<React.unstable_ConcurrentMode>
  <Something />
</React.unstable_ConcurrentMode>

// 2. Whole app (not final API)
ReactDOM.unstable_createRoot(domNode).render(<App />);

この前者の方法が廃止されたとのことです。
詳しくは、 https://github.com/facebook/react/pull/15532 を参照してください。

部分的にConcurrent Modeを適用すると難しいケースがある上に、利用シーンも分からないということのようです。

今後は後者のcreateRootを使うことになります。

おわりに

自分のテストコードで複数の場所で使っているので、近いうちに修正しておかなければと思いました。

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

React.unstable_ConcurrentModeは廃止されたようです

はじめに

去年の11月にReact 16.x Roadmapが公開されて、次はConcurrent Modeの登場を待っている状態です。
少し前になりますが、これに関連して変更があったようです。

React.unstable_ConcurrentMode

Concurrent Modeを試すには二通りの方法がありました。上記の記事から抜粋します。

// Two ways to opt in:

// 1. Part of an app (not final API)
<React.unstable_ConcurrentMode>
  <Something />
</React.unstable_ConcurrentMode>

// 2. Whole app (not final API)
ReactDOM.unstable_createRoot(domNode).render(<App />);

この前者の方法が廃止されたとのことです。
詳しくは、 https://github.com/facebook/react/pull/15532 を参照してください。

部分的にConcurrent Modeを適用すると難しいケースがある上に、利用シーンも分からないということのようです。

今後は後者のcreateRootを使うことになります。

おわりに

自分のテストコードで複数の場所で使っているので、近いうちに修正しておかなければと思いました。

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

JavaScriptのDOM操作で行うアラート表示をまとめてみた

hrefでJavaScriptを動かしてアラートを出す方法

1.htmlで下記のコードを記述

index.html
    <a href="Javascript:window.alert('OK')">アラート</a>

2.ブラウザで「アラート」をクリックすると、下記のようにアラート表示されます。
スクリーンショット 2019-05-24 13.58.36.png

DOM操作でfunctionを動かしてアラートを表示させる方法

1.任意のidを指定する

index.html
  <button id="alertbtn">ボタン1</button>

2.jsファイルでfunctionのコードを記述

app.js
window.onload = function(){
  document.getElementById('alertbtn').onclick = function(){
    window.alert('ボタン1が押されました');
  }
}

window.onload
↑ページがロードされたら function(){} の中の処理を実行します。

getElementById()
↑idプロパティが指定された文字列にアクセスします。この場合は、 index.htmlに記述されたidの値である"alertbtn" にアクセスしています。

.onclick
↑index.htmlの「ボタン1」がクリックされたときの動作を定義します。

「ボタン1」押すと、結果は下図のようになります。
スクリーンショット 2019-05-24 14.25.43.png

イベントハンドラを使用したアラート表示

上記の操作を踏まえた上でイベントハンドラを用いて記述すると、
簡潔なコードになります。

1.htmlファイルでDOMイベントを記述

index.html
  <button onclick="btn_click()">ボタン2</button>

2.jsファイルでアラートメッセージを記述

app.js
function btn_click(){
  window.alert('ボタン2がクリックされました');
}

結果
スクリーンショット 2019-05-24 14.28.56.png

参考サイト

イベントハンドラ解説
https://wa3.i-3-i.info/word11493.html

DOMイベント一覧
https://so-zou.jp/web-app/tech/programming/javascript/event/handler/

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

iOS12.2よりdevicemotion, deviceorientationがデフォルトで取得できなくなったことと、その検知方法

iOS12.2より、safariに「モーションと画面の向きのアクセス」項目が追加され、デフォルトでOFFの状態となったため、devicemotionイベント等を取得することができなくなりました。

設定はどうやって変えるの?

設定>Safariから「モーションと画面の向きのアクセス」項目が追加され、デフォルトでOFF
をONにすることで

どうやって検知したらいい?

ググると

if('ondevicemotion' in window) {}

的なコードが出てくるんですが、実際にiOS12.2の設定をOFFにしていても検知してくれません。

const id = setTimeout(() => {
                 //ここに動かない時にさせたい挙動を書く
             }, 500);
             window.addEventListener('devicemotion', (e) => clearTimeout(id));

このようなコードをかくことで、
つまり、デバイスオリエンテーションイベントが発生したときにタイマー処理をしているものをクリアしてキャンセル=>動かない時だけsetTimeout()の中身が実行される、ということができるようです。

参考

https://www.macrumors.com/2019/02/04/ios-12-2-safari-motion-orientation-access-toggle/
https://github.com/w3c/deviceorientation/issues/57#issuecomment-481039307

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

kintoneのラベルフィールドの要素を取得

はじめに

先日こんな記事を書きました。
kintoneの内部パラメータ「フィールドID」を使いこなす

入力するフィールドはこれで全部行けるんですが、
「ラベルフィールド」のDOMを取得したいときは、これでも無理。
どうやらラベルには、フィールドIDがそもそも付与されないっぽいです。

解決方法

100% DOM操作で行います。
例のごとく、今後動かなくなる可能性は大なので自己責任で。

divタグが多重になってますが、.control-label-field-gaiaクラスがラベル1個単位の大元のようです。
jQueryを使わずに書くなら、以下の2ステップで。

  • 全ラベルを取得
  • その中から特定の文字列のラベルを探す

こんな風に用途別に関数を作ってやると、取り回しやすいと思います。

// 全ラベルの取得
const allLabelElements = () => Object.values(document.querySelectorAll('div.control-label-field-gaia'))

// 特定の文字列と一致するラベルを1つ抽出
const findLabelElement = label => allLabelElements().find(_ => _.textContent === label)

// 特定の文字列を含むラベルを複数抽出
const filterLabelElements = label => allLabelElements().filter(_ => _.textContent.includes(label))

使用例

アプリストアの「旅費精算申請」の場合

「旅費」と一致するラベル

マッチしたElementオブジェクトが返る

スクリーンショット 2019-05-24 11.53.41.png

「旅費」を含むラベル

マッチした3つのElementが配列で返る

  1. 「旅費精算申請」にマッチ
    スクリーンショット 2019-05-24 11.55.18.png

  2. 「旅費」にマッチ
    スクリーンショット 2019-05-24 11.55.26.png

  3. 「旅費合計(円)」にマッチ
    スクリーンショット 2019-05-24 11.55.34.png

あとは好きなようにhackしてくださいませ!
ではまた〜。

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

「jQueryでやっていたことを、サーバーサイド側を何も変えずにフロントエンド側をVuejsにしておくれよ」

どうしてもレンダリングはサーバーサイドがやっているものを変えたくない。でもフロントは変えろ。そんな要望ありますよね?
え?ありませんって?あったんですよ。

コンテンツの中身は可変なのでSSレンダリングしたいけど、APIを作ってFE側から取ってくるほどの内容でもない。その上で、レンダリングするHTMLにはVueのためのバインディングや条件式が書けない。そんな場合を想定します。

例えばアコーディオン

スクリーンショット 2019-05-24 11.43.04.png

スクリーンショット 2019-05-24 11.42.40.png

こんなイメージのものを作って行こうと思います。
まずはサーバーサイドにレンダリングしてもらうHTMLです。

コード

html

<div id="accordion--wrapper">
  <div id="accordion--component" class="accordion">
    <h2 class="accordion--title">アコーディオンメニュー<i class="icon"></i></h2>
    <ul class="accordion--body">
      <li>メニュー1</li>
      <li>メニュー2</li>
      <li>メニュー3</li>
      <li>メニュー4</li>
    </ul>
  </div>
</div>

それではVueコンポーネントを作っていきましょう。

Vueコンポーネント

<script>
export default {
  data() {
    return {
      isOpen: false,
    };
  },
  mounted() {
    this.$el.querySelector('.accordion--title').addEventListener('click', this.clicked);
  },
  methods: {
    clicked() {
      this.isOpen = !this.isOpen;
    },
  },
  watch: {
    isOpen(isOpen) {
      const accordion = this.$el;
      if (isOpen) {
        accordion.classList.add('open');
      } else {
        accordion.classList.remove('open');
      }
    },
  },
};
</script>

<style lang="scss" scoped>
.accordion {
  & .accordion--title {
    cursor: pointer;
    display: block;
    padding: 15px 15px 15px 42px;
    margin-bottom: 0;
    border: 1px solid #CCC;

    & i.icon {
      margin-left: 20px;
      &:before {
        content: "+";
      }
    }

  }
  & .accordion--body {
    padding: 0;
    margin: 0;
    visibility: hidden;
    opacity: 0;
    overflow: auto;
    transition: all .2s ease-in;
    list-style: none;
    & li {
      border-bottom: 1px solid #CCC;
      border-left: 1px solid #CCC;
      border-right: 1px solid #CCC;
    }
  }
  &.open {
    & .accordion--title {
      & i.icon {
        &:before{
          content: "-";
        }
      }
    }
    & .accordion--body {
      visibility: visible;
      opacity: 1;
      transition: all .8s ease-in;
    }
  }
}
</style>

今回制約上テンプレートを持てないので、<template> のない単一コンポーネントを作ります。css/scssは見習い中なのでごちゃごちゃしてしまっています。

このコンポーネントでhtmlにVueで命を吹き込んでいきます。

エントリポイントJavaScript

import Accordion from './components/Accordion.vue';

const ready = (fn) => {
  if (
    document.attachEvent ? document.readyState === 'complete' : document.readyState !== 'loading'
  ) {
    fn();
  } else {
    document.addEventListener('DOMContentLoaded', fn);
  }
};

ready(() => {
  Accordion.template = '#accordion--wrapper';
  new Vue({
    el: '#accordion--component',
    render: h => h(Accordion),
  });
});

Accordion.template = '#accordion--wrapper' こちらで id="accordion-wrapper" の要素の 内側 をAccordionコンポーネントのテンプレートとして注入します。
new Vue する際の el: '#accordion--component' は、指定した要素をコンポーネントで置き換えるため、templateとelで指定するidは親子(template: 親、 el: 子)になる点に注意が必要になります。

よい点、わるい点

またサーバーサイドがレンダリングしたHTMLを前提とした実装になってしまうため、ロジックと見た目の分離ができていないのも少し残念なところで、普段Vueの単一コンポーネントでは利用することのないであろう querySelector$el が入ってしまいます。

ただ、状態管理や watch 等のVueの機能が利用できて、単純にES6だけで書いていくよりは可読性が高い書き方になっていくのではないでしょうか。

また、プロジェクトの一部ではVueを導入できているのにSSレンダリングの都合上Vue化しにくいところにも適用できるので、フレームワークが統一されるメリットも大きいと思います。

まとめ

ちょっとタイトルは雑でしたが、いろいろ制約の多い中でVueが使えないためにフロントエンドコードの一貫性がない状況を改善しようと検討した内容を書いてみました。

これで、今までjQueryで実装していたプロジェクトもこっそりとサーバーサイドに知られずにVueに置き換えることもできるかもしれませんね。

懸念

一度レンダリングしたものを置き換えることになるので、SEO評価に影響がある可能性があります。この辺はおいおい調べていきたいなと思っています。

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

jsフレームワークstimulusでyahoo地図を操作してみた(その一)

「railsアプリで、stimulusを使ってyahoo地図を操作する方法」についての纏めーー
当初はgooglemapで試してみるつもりだったが、料金請求が怖そうなのでyahoo地図に切り替えた(操作自体は似ているので、ちょっと書き換えればgooglemapにも簡単に切り替えられるはず。実際、【googlemap操作について書かれたサイト】も複数参照して書いています)。

やったこと、やる予定のこと

0. railsアプリへのstimulus適用(以前書いた記事)

1. stimulusでyahoo地図を表示(←今ここ)

2. 表示した地図上で、指定座標に移動

……この段階では「指定座標」はコードに直接書いています。
 座標取得の方法は「3」以降でやる予定。

3. 住所から座標を取得し、それをDBに登録(そのうち書く)

4. 「3」で登録した情報を選択し、yahoo地図上でその場所に移動(そのうち書く)

【事前準備】railsにstimulusを適用し、yahooのアプリケーションIDも取得しておく

stimulus適用はこちら(自記事)やこちら、アプリケーションID取得はこちらを参照。

とりあえず、地図を表示させてみる。

公式サイトを参照つつ……

まずはアプリケーションビューで、

app/views/layouts/application.html.erb
    <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
    <%= javascript_pack_tag 'application' %>

    <%# ↓これを追記↓ %>
    <script type="text/javascript" charset="utf-8" src="https://map.yahooapis.jp/js/V1/jsapi?appid=【取得した自分のID】"></script>
    <%# ↑これを追記↑ %>

と【yahooのAPIを使用する】旨を記述
……APIを使用するビューだけに個別に書いても問題ない(というか、アクセス回数制限を考え得るならそちらのほうがいい)のだろうけど、今回は面倒なので全部に適用させることにした。

で、地図を表示したい場所(ビュー、今回はusers/testを作ってそこでやりました)に

app/views/users/test.html.erb
<!-- ↓①↓ -->
<div data-controller="moving">

  <p>Google Maps APIを使ったサンプルです。</p>

 <!-- ↓②↓ -->
  <div id="idmeihatekitoudemoiiyo" style="width:500px; height:300px"  data-target="moving.map" ></div>


</div>

と記述。
①stimulus用に【div要素の枠、「data-controller="moving"」】を作成。
②「①」の枠の中に配置した【ヤフー地図を表示するためのdiv要素……「div id="map"」】に【「map」というターゲット名……「data-target="moving.map"」】を設定する。
(stimulusだと要素はデータターゲット名で指定可能なので、「②」のdiv要素のid名は適当でも問題なくなります(いやでも、これは適当すぎだろう)。

最後に、対応するstimulusファイルを作成。

↑で「data-controller="moving"」としているので、ファイル名は「moving_controller.js」となります。

app/javascript/controllers/moving_controller.js
import { Controller } from "stimulus"

export default class extends Controller {
  static targets = [ "map" ]

  initialize() {//←①

       this.map = new Y.Map(this.mapTarget.id);//←②
       this.map.drawMap(new Y.LatLng(35.66572, 139.73100), 17, Y.LayerSetId.NORMAL);//←③

    //↓④
       var center = new Y.CenterMarkControl
       var control = new Y.LayerSetControl();
       this.map.addControl(center);
       this.map.addControl(control);

  }
}

①イニシャライズ時に、
②【ビュー側で「data-target="moving.map"」としている要素】のidを使って、「this.map」を設定
 ……ここはid名を直接記入しても、問題はないです。ただ要素の指定はターゲット名で統一したほうが、なんかカッコイイ気がする(いや、id名が異なる違う要素にも使い回せますし)。
③「this.map」にヤフー地図を書き込む(「drawMap」。「35.66572, 139.73100」が初期表示する座標、お好みで変更してください。
④お好みでレイヤーを設定( 公式サイトにある「レイヤーセットID」をご参照ください)。

これで、地図が画面に表示されるように……

スクリーンショット 2019-05-24 11.46.42.png

  なりました。

とりあえずは今回はここまで。続きはこちらです。

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

[Vue.js] input type="file"で画像のuploaderを作る

やりたいこと

画像をアップロードしたら、それが表示されるものを作りたい。(以下のようなもの)

スクリーンショット 2019-05-24 11.01.23.png

component

<template>
  <div>
    <img v-show="uploadedImage" :src="uploadedImage" />
    <div class="c-btn--file">
      ファイルを選択
      <input type="file" @change="onFileChange" />
    </div>
  </div>
</template>

<script>
export default {
  name: 'BaseFileUpload',
  data() {
    return {
      uploadedImage: '',
    }
  },
  methods: {
    onFileChange(e) {
      let files = e.target.files || e.dataTransfer.files
      this.createImage(files[0])
    },
    // アップロードした画像を表示
    createImage(file) {
      let reader = new FileReader()
      reader.onload = e => {
        this.uploadedImage = e.target.result
      }
      reader.readAsDataURL(file)
    },
  },
}
</script>


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

Vue + Fontawesome - v-if でアイコンの切り替えが出来ない

環境

npmで以下を入れている。

package.json
    "@fortawesome/fontawesome-free": "^5.8.2",
    "vue": "^2.6.10",

はじめに

 Vueのv-ifでFontawesomeのアイコンを切り替えようとした。

<i v-if="flag" class="fa fa-icon"></i>
<i v-else class="fa fa-icon"></i>

 しかし、一つ目のアイコンは表示されるが二つ目はフラグを切り替えても表示されない。

原因

 断言は出来ないが、インストールしている@fortawesome/fontawesome-freeは自前のjsでアイコンの置き換えをしている。
 これがVueと相性が悪いのではないかという推測が立つ。

main.js
// アイコンを頑張って置き換えている皆さん
import '@fortawesome/fontawesome-free/js/fontawesome.js';
import '@fortawesome/fontawesome-free/js/solid.js';
import '@fortawesome/fontawesome-free/js/regular.js';

解決

 jQueryによる表示の切り替えだとうまくいった。

if (flag) {
  $('.icon-1').show();
  $('.icon-2').hide();
} else {
  $('.icon-1').hide();
  $('.icon-2').show();
}

解決2

 vue-fontawesomeを使う。vue-fontawesomeのコンポーネントはv-ifでも切り替えができた。
 ただ、インターフェースが少し特殊なので人を選ぶかもしれない。

<font-awesome-icon v-if="flag" icon="icon-1" />
<font-awesome-icon v-else icon="icon-2" />

<!-- 少し特殊なインターフェース -->
<font-awesome-icon icon="['far', 'regular-icon-1']" />

おわりに

jQueryによる解決はVueの流儀的には強引な気がするので、vue-fontawesomeの導入の検討がいいかもしれない。

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

モダン JavaScript プログラミングを始めるために知っておきたい技術セット

元々は社内向けとして書いたものですが、転載許可が出たので Qiita 向けに加筆・修正した文章です。

僕は普段ペチパーバックエンド領域を担当することが多いのですが、フロントエンド領域を含めて社内の技術アップデートを一緒に頑張っていきたい、という思いから書きました。

なんせペチパーバックエンドエンジニアの拙い知識なので一部情報が古かったり間違っているかもしれません。
フロントエンドガチ勢の皆様、間違い等ございましたら温かく指摘して頂ければと思います。

前書き

JavaScript は一応書けるけど、将来心配。なんかすごく進化してるって聞くけど、調べてみても訳が分からない。
僕も勉強し始めは同じことを何度も思いました。
そこで、僕の仕事環境を想定し、導入すべきツールを役割ごとに選定してみました。
上から順に読んで、インストールして、使えるようになるとそれなりにモダンなフロントエンドの知識を手に入れられます。

※この情報は 2019/05/20 付近の情報です。ただでさえ技術の流行り廃りが激しいフロントエンドの世界なので、概要が掴めたらなるべく最新の情報をググってみて下さい。

実行環境: node.js

モダンな JavaScript を使用するためには「変換」がつきものです。
複数の .js ファイルを結合する、圧縮する、古いブラウザに対応させる………。
で、この変換をいちいち人間がやっていては日が暮れるので、PHP がデータベースの内容を一瞬で納品書 PDF に組み替えるように、JavaScript も自動でこの「変換」を行うプログラムと環境が必要です。

経緯はさておき、この JavaScript を変換するプログラムはなんと、大抵 JavaScript 自身で書かれています。
ただしこの JavaScript が動くのは Chrome や FireFox といった「ブラウザ」ではなく、「node.js」という環境の上です。
この先説明する webpack だとか Babel だとかはこの node.js という環境で動く JavaScript なので、まずは何といってもこれが無いと始められません。

node.js はあなたが使っている OS が Windows であろうが、 Mac であろうが、Linux であろうがインストールできます。まずはこれをインストールしましょう。
なお、そのまま公式ページから直接インストーラを持ってきてもいいですが、今後 node.js のバージョン管理を行いたくなるケースが発生する可能性があるので nodist を用いてインストールした方が後々楽かもしれません。

初心者向け!3分で理解するNode.jsとは何か?
[Node.js] Node.js の導入(Windows編)

パッケージマネージャー: npm, Yarn, Bower

node.js がインストールできたら今度は欲しい JavaScript プログラムを自分の PC、またはプロジェクトにインストールするパッケージマネージャーというものを理解しましょう。
荒っぽく言えば、iPhone における AppStore みたいなものでしょうか。

パケージマネージャーの主な役割はプロジェクトに使用するプログラムの定義と、プログラム間の依存解決です。
パッケージマネージャーにプログラムのインストールを任せれば定義ファイルを作ってくれる(npm の場合は package.json)ので、新しいフォルダの中でも同じ package.json を読み込ませれば勝手にインターネットから同じプログラムを取ってきてくれます。
また、あるプログラム A を動かす時には B が必要で、B を動かすときは C と D と Z が必要……みたいな依存関係も、パッケージマネージャーなら自動で芋づる式にインストールしてくれます。

  • 定番、というか node.js をインストールするとき一緒に付いてくるのが npm です。
  • 対抗馬は Yarn でしょう。一時期は npm より速いと言われていましたが、最近はそんなに差が無いようです。
  • Bower はかつてフロントエンド系のプログラムをインストールする際に使われていましたが、現在では全ての出番を npm に奪われたと考えて間違いないでしょう。

npm入門
npmから乗り換えてわかった Yarnの4つのメリット

タスクランナー・モジュールバンドラー: gulp, Grunt, webpack, Parcel

環境とパッケージマネージャーが揃ったら、ようやくですが、ファイルの圧縮・結合等の実益に繋がるツールを用意しましょう。

こういった作業を比較的楽に行ってくれるのがタスクランナーと呼ばれるものです。

  • タスクランナーの代表格は gulp でしょう。
  • Grunt も有用なタスクランナーでしたが、 gulp にとって代わられた感があります。

しかしこれらのタスクランナーnpm に類似の機能である npm run が実装されたのに加え、最近はモジュールバンドラーに役目を奪われつつあります。

モジュールバンドラーとは、複数のファイルに分割した .js や .css, 各画像ファイルなどを一つの圧縮された .js に結合して出力するというツールです。
モジュールバンドラーのメイン機能はまさしくそれなのですが、付随して後述するコンパイラトランスパイラを挟んだりできるので、事実上のタスクランナーとしても機能しています。

  • モジュールバンドラーの代表格かつ、現代のフロントエンドで実質的ディファクトスタンダードと言えるのが webpack でしょう。
  • ただし、この webpack は設定ファイルを作る学習コストがやや高めなので、設定ファイル要らずのモジュールバンドラーである Parcel も注目を集めているようです。

最新版で学ぶwebpack 4入門 JavaScriptのモジュールバンドラ
Parcel 入門 ~Parcelはwebpackの代わりになるのか~

トランスパイラ・コンパイラ: Babel

JavaScript には es6 等のバージョンが存在するのですが、新しい es9 などの書き方で書いた .js ファイルを古いバージョンしか解釈しない IE 11 等で動かすための変換を行うのがこの トランスパイラ・コンパイラ です。

一応、Prepross 等の GUI アプリケーションを使えば上記三段階をすっ飛ばしてこの工程に入れるので試してみるのもいいでしょう。
ただ一方で、CLI で動作する環境の方が複雑な設定・自動化が容易だったり、複数人での開発環境を合わせるのに便利だったりするので、実際のプロジェクトではモジュールバンドラーなどに組み込む形が有利かと思われます。

ここに該当するのは Babel という代物で、これを使用する前提の環境が組みあがれば、レガシーな varfunction(){} ではなく letconst, () => {} が使用できるようになるので、是非ともチャレンジしてみて下さい。
classasync/await が使えるようになると楽しいですよ!

【5分でなんとなく理解!】Babel入門
webpack と Babel – React入門

Alt JS: CoffeeScript, TypeScript, PureScript

Babel のアプローチが未来の JavaScript を現代のブラウザが解釈できる形へ変換するものだったのに対し、これら Alt JS は別の JavaScript 生成用言語から JavaScript を生み出すというアプローチを取ります。
これはちょうど Sass から CSS を生成する関係と同じです。
これら言語を使えば、他の言語よりもバグへと繋がる罠の多いとされる JavaScript を効率的、または堅牢に開発することができます。

  • CoffeeScript はかつてこの分野の先駆者でしたが、現在これを新たに採用するプロジェクトはほとんどないでしょう。
    • ただし、そのコンセプトは現在の TypeScriptPureScript へ繋がるものがあります。
  • TypeScriptMicrosoft が開発した静的言語で、動的言語である JavaScript に型の恩恵を与えてくれます。
    • Java 等のように、変数や関数の引数・返り値の型を保証してくれるので潜在的なバグに開発段階で気付くことができるでしょう。
    • さらに JavaScript へのコンパイル時に古いバージョンへと変換する機能も備えているので、実質 Babel の機能を内包しているという利点もあります。
    • 素の JavaScript に比べ、単純な行数は増える傾向にあるため無条件で採用すべきではありませんが、規模の大きなプロジェクトになるほど TypeScipt の価値は大きくなっていくことでしょう。
  • PureScript は関数型パラダイムで JavaScript 開発を行う言語です。
    • JavaScript は元々プロトタイプベースのオブジェクト指向型プログラミング言語ですが、最近の人気を受けて JavaScript の世界にも関数型プログラミングがやってきました。
    • 変数がそもそも最代入不可能(=イミュータブル)になることでかなり堅牢な開発が可能になるので、関数型言語に興味があるなら試してみてもいいかもしれません。

CoffeeScriptは本当に駄目なのか?
TypeScriptで始めるNode.js入門
純粋関数型スクリプト言語PureScriptのはじめかた

おまけ(筆者が過去に書いた TypeScript の記事です)

TypeScript でタワシしか当たらないルーレットを作る
ライブラリ無しのTypeScriptでテーブル or リストのドラッグ&ドロップ並び替えをしてみる

フレームワーク・UIライブラリ: React, Vue.js, Angular, Riot.js

ここまでは JavaScript をいかに効率的に書くかという問題を解決するツール群を主に紹介してきましたが、結局 JavaScript でやりたいことはブラウザでのフロントエンド開発でしょう。特に、情報やユーザーの挙動に合わせてDOMを変更する、つまりお馴染み jQuery でやってきたことですよね?

世間は沢山の誤解で溢れていますが、現代の Web サイトであっても jQuery は有効です。ただしそれはJavaScript の仕事が少ないうちは。 という条件付きでです。
例えば Web サイトではなく、最早 Web アプリケーションと呼んだ方が良いサービス、例えば Facebook や Evernote でしょうか。これらサービスはよく観察すると、画面遷移するときにページ全体が更新されるのではなく、一部の DOM のみが書き換えれられています。これがいわゆる SPA(シングルページアプリケーション) です。

SPA(Single Page Application)ってなに?
シングルページ参考サイト

これらページは jQuery でも作成できますが、それには多くの時間と苦労が必要で、出来上がったコードは大方目をそむけたくなるようなスパゲッティコードでしょう。
餅は餅屋、ということで、あなたが作るものが Web サイトではなく Web アプリケーションだと判明したら SPA を設計することに長けたフレームワーク・ライブラリを導入するべきです。

  • React は間違いなくこの領域の代表格です。Facebook が開発したこのUIライブラリをうまく使えば美しい設計の下、アプリケーションを作成することができるでしょう。
    • ただし、慣れないうちは JavaScript の中に html を記述したような JSX 記法が気持ち悪いかもしれません。さらに、事実上 Babel を必須としています。
  • Vue.jsReact より親しみやすく、コンパクトで LIKE HTML です。
    • JSX 記法Babel といった新しい概念を勉強しなくても試せます。スケーラビリティも文句なしで、後からアプリケーションの規模が大きくなっても破綻無く再設計可能です。
  • Angular はさらに大規模向けのフレームワークです。
    • 上記二つが足りない機能は他ライブラリで補うのに対し、Angular は最初からほとんどの機能をカバーしています。もっともフレームワークらしいフレームワークでしょう。
    • ただし、ほんの少し試すだけでも TypeScript が必須です。Angular を採用するということはほとんどの場合大規模案件でしょうから、これはメリットでもあります。
  • Riot.js はより素の HTML, CSS, JavaScript に近い構文を持ったフレームワークで、未来の Web 標準と言われるカスタムタグWeb Components を先取りしています。
    • この中で学習コスト・ファイルサイズ共に最小なので、一番とっつきやすく、ミニマムな開発に最適でしょう。
    • ただし現在はそうでなくても、後々規模が大きくなっていくことが予想されるアプリケーションとは相性が悪いかもしれません。

Reactを使うとなぜjQueryが要らなくなるのか
正真正銘のReactだけの不純物なしでReact入門
はじめに — Vue.js
Angular入門
改めてRiot.jsをその他のFW・ライブラリと比較して客観的に評価した

まとめ

無茶言うな、こんなに覚えられるか、と思うのは当然ですが、世の中のフロントエンジニアは空気を吸うようにこれらを駆使して開発を行っています。

でも、よく思い出してみて下さい。現代文の教科書が一冊あったとして、先生はそれを一朝一夕で全て暗記しろとは言わなかったはずです。
皆さんきっと、一冊を一年かけてじっくり勉強したことでしょう。
Web フロントエンドも一緒です。あなたが必要としている技術をじっくり確実に学んでいきましょう。

もしかしたら学習を終える前にその技術が廃れるかもしれませんが、恐れることはありません。だいたいの新技術の80%は既存技術を弄ったなにかです。学んだことはきっと、次に学ぶことの助けになることでしょう。

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

【JavaScript】入力値を監視して別のnodeに値を反映させる

index.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Page Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script src="main.js" type="text/javascript"></script>
    <script src="sha512.js" type="text/javascript"></script>
</head>
<body>
    <div>
        <h2>Input data for replace hadh</h2>
        <input id="data" type="text" value=""/>
    </div>
    <div>
        <h2>result</h2>
        <textarea id="result" type="text" value="" style="width: 390px; height: 40px"></textarea>
    </div>
</body>
</html>

id="data"に入力された値を常に監視し、hash関数に入れた返り値をid="result"にリアルタイムで入れる。

setHash.js
window.onload = () => {
    const result = document.getElementById("result");
    const sethash = (it) => {
        const shaObj = new jsSHA("SHA-512", "TEXT");
        shaObj.update(it);
        result.innerText = shaObj.getHash("HEX");
    };
    document.getElementById("data").addEventListener("input" , (it) => {
        sethash(it.target.value);
    });
}

shaObjは以下のライブラリのsrc/sha512.jsをコピペして、HTML上で呼び出して使用する。

https://github.com/Caligatio/jsSHA

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

JavaScript で配列の shallow copy をする better な方法

動機

タイトルのとおり、JavaScript で配列のコピーをする必要がありました。
Qiita などで幾つかの方法が紹介されていましたが、
「じゃぁ、どれが一番適切なの?」という疑問が湧き、
その疑問を解決するべく調べた結果を報告します。

結論

結論を先に述べると、 執筆時点では Array.slice() を用いる方法が一番高速でした。

5種類の shallow copy のアルゴリズムと、処理速度比較結果を表示するサイト
https://jsperf.com/duplicate-array-slice-vs-concat/31 
での実行結果を以下に示します。

実行結果

動作環境
- CPU: Intel Core i7-2770K 3.50GHz
- Mem: 32GB
- OS: Windows 10 (64bit)

評価方法
- Browser は Google Chrome, Firefox, MS Edge の3種類を使用
- 5回の試行で評価(実行タイミングを公平にするため、各ブラウザ1回ずつの試行を1セットとして5セットを実施)

(1) Google Chrome 74.0.3729

アルゴリズム 実行速度[Ops/sec] 標準偏差 備考
while [] preallocate 1337.8 89.5
Array.slice 1889.4 124.2 最速!
while push 197.4 20.5 worst
while [] 418.2 19.5
Array.concat 1348.8 64.9

(2) Firefox 66.0.0

アルゴリズム 実行速度[Ops/sec] 標準偏差 備考
while [] preallocate 603.6 21.7
Array.slice 1344.4 110.0 最速!
while push 650.4 39.7
while [] 608.8 40.8
Array.concat 500.6 27.5 worst

(3) Microsoft Edge 17.17134.0

アルゴリズム 実行速度[Ops/sec] 標準偏差 備考
while [] preallocate 979.8 24.1
Array.slice 1442.4 12.4 最速!
while push 182.8 13.6 worst
while [] 425.0 35.4
Array.concat 651.4 59.4

感想

  • 各ブラウザで、処理系の特徴があって面白いなぁと感じました。
  • Edge の Array.slice の処理速度が安定しているのが、ちょっと不思議に感じました(単なる偶然?)。 これはこれで良い特徴だと思うのですが、今後 Chrome ベースになってしまうのが少し残念?です(競争相手が減るという意味で)。
  • 今回の疑問解決において、上記サイトを見つけることができて、とても幸運でした。先人の労力に感謝!!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む