Babelの最小構成を試す

Babelとは?

ブラウザがまだ対応していない最新のJavaScriptソースコードを、ブラウザが対応しているソースコードへ変換してくれるツールです。 トランスコンパイラとも呼ばれます。

例えば、以下のようなアロー関数を用いたJSはIE11で動作しません。

const sayHello = name => {
  console.log(`hello ${name}`);
}

Babelを用いると、次のようなIE11で動作するコードに変換(トランスパイル)してくれます。

var sayHello = function sayHello(name) {
  console.log("hello ".concat(name));
};

Babelを使ってソースコードを変換(トランスパイル)してみる

公式サイトを参考にしています。

コマンドラインからBabelを使用して、IE11では動作しないソースコードを、IE11でも動作するソースコードへ変換してみます。 以下がインストールされていることを前提とします。

  • node.js
  • npm

以下が手順概要です。

  1. Babelをインストールする
  2. 手動で必要なファイルを作成する
  3. コマンドラインからBabelを実行する
  4. 確認する

1. Babelをインストールする

任意のディレクトリで以下のコマンドを実行します。

mkdir babel-demo
cd babel-demo
npm init -y
npm install --save-dev @babel/core @babel/cli @babel/preset-env
npm install --save @babel/polyfill

babel-demoというディレクトリを作り、そのディレクトリをnpmのプロジェクトとして初期化し、Babelをインストールするというコマンド群になります。

2. 手動で必要なファイルを作成する

Babelをインストールしたディレクトリでに以下のファイルとディレクトリを追加します。

  • babel.config.js
  • /src/main.js
  • /lib/

この時点で、以下のようなディレクトリ構造になっているはずです。

│  package.json
│  babel.config.js
│
├─lib
│
├─node_modules
│  └─etc...
└─src
    main.js

main.jsの内容

const sayHello = name => {
  console.log(`hello ${name}`);
}

babel.config.jsの内容

const presets = [
  [
    "@babel/env",
    {
      targets: {
        ie: "11"
      },
      useBuiltIns: "usage",
    }
  ]
];

module.exports = { presets };

このファイルはBabelの動作を決定するコンフィグを定義しています。 IE11に対応するソースコードに変換するための設定を記述します。

コマンドラインからBabelを実行する

以下のコマンドを実行します。

npx babel src --out-dir lib

srcディレクトリのファイルを全て変換(トランスパイル)し、libディレクトリに変換後のファイルを配置するというコマンドになります。

確認する

libディレクトリにmain.jsが配置されていることを確認し、ファイルを開いてみます。

以下のようなソースコードが記述されていれば成功です。

"use strict";

var sayHello = function sayHello(name) {
  console.log("hello ".concat(name));
};

デザインを説明するってどうすることなんやろうか?

f:id:sho_ya:20190320130245j:plain

少し立ち読みしてから買ってみた。デザイナーに限らず、製品に関る人に幅広く読まれているデザインの基礎本だそうだ。

僕が主に興味があるデザインは、普段使いするアプリケーションのデザインだ。機能性やコンテンツの中身がより重視される場面だと思っている。例えば、恋人には映画的な尖った要素やエピソードを求めがちだけど、結婚相手には互いに生活をサポートし合えることが重視される点に似ている気がする。

アプリケーションも使い始めは便利そうに見えたり使いやすそうに見えることが重視されるけど、普段使いされるアプリケーションに選ばれるのは、サポート力が高いものだと思っている。

だから、今僕はシングルページアプリケーション(SPA)をテーマにしている。何でSPAだとサポート力が高いアプリケーションが作れると思っているのかというと、前の仕事で作っていたとか、SPAのWEBアプリ(Airbnbとか)で凄く良い体験したとかいろいろある。既存のものより使いやすかったり、もっと人が何かを考えるためのツールにできたり、濃く使いぬくことができるものを作れる気がしている。そんな所が面白そうで開発している。

では、使いやすいってどういうことなのか?
そして、そんなアプリケーションを作るには何を考えることが大事なのか?

という疑問に対して、僕は今とりあえずSPAを作ってみればいいじゃんというアプローチをとっているけれど、作ったものを言語化して説明することが思ったより高い壁だったことに気づいた。
この「誰のためのデザイン?」を立ち読みしてみると、そのための語彙力が早くも少しインストールされてきて面白かったので買ってみた。

しかし、デザイナーの人達はデザインを何を根拠にして説明したり、テーマを積み上げているんだろう。

webpackの最小構成を試す

webpackとは?

複数ファイルに分けて管理しているjs(モジュール)を一つのファイルにまとめてくれるツール(モジュールバンドラー)です。
(正確には1つのファイルとは限りませんが、役割に応じてファイルをまとめられます)

以下のように、lodash.jsとindex.jsの2つのファイルをロードしているHTMLがあるとします。

<script src="https://unpkg.com/lodash@4.16.6"></script>
<script src="index.js"></script>

webpackを使用すると、上記2つのjsを1つにまとめたmain.js(名前は任意)を作成してくれます。

<script src="https://unpkg.com/lodash@4.16.6"></script>
<script src="index.js"></script>

↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

<script src="main.js"></script>

2ファイル程度ではあまり恩恵を感じませんが、ソースコードが非常に長くなってきた時にファイル単位で処理を分割するとどんどんファイルが増えます。そういった時にパフォーマンス面等でファイル数が問題にならなくなるので、管理することに集中しやすくなって便利です。

また、新しく作成されたファイルのことをバンドルファイル(bundle:束の意)と言ったりします。こういった言葉と実際の意味をちゃんと繋げておくことも他人のソースを読んだりAPIリファレンスを読む際に必要になります。

以下では実際に手を動かし、上記と同じことを実施します。

webpackを使ってファイルをまとめてみる(バンドルしてみる)

公式サイトを参考にしています。

また、以下がインストールされていることを前提に記述しています。
・node.js
・npm

以下は手順の概要です。

  1. 必要なモジュールをインストールする
    • webpack
    • webpack-cli
    • lodash
  2. package.jsonを書き換える
  3. 必要なファイルを手動で作成する
    • ./webpack.config.js
    • ./dist/index.html
    • ./src/index.js
  4. webpackのコマンドでlodashとindex.jsをまとめた(バンドルした)main.jsを作成する
  5. 確認する

1. 必要なモジュールをインストールする

任意のディレクトリで、以下のコマンドを続けて実行します。

mkdir webpack-demo && cd webpack-demo
npm init -y
npm install webpack webpack-cli --save-dev
npm install --save lodash

上記を実行すると、以下のファイルやディレクトリ等が作成されます。

│  package.json
│
└─node_modules
  ├─lodash
  ├─webpack
  ├─webpack-cli
  └─etc...

package.jsonの内容です。

{
  "name": "webpack-demo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
      "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
  "webpack": "^4.29.6",
  "webpack-cli": "^3.3.0"
  },
  "dependencies": {
  "lodash": "^4.17.11"
  }
}

2. package.jsonを書き換える

以下のように書き換えます。

  • - "main": "index.js",
  • + "private": true,
  • + "build": "webpack",


{
  "name": "webpack-demo",
  "version": "1.0.0",
  "description": "",
  "private": true,
  "scripts": {
  "build": "webpack",
  "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
  "webpack": "^4.29.6",
  "webpack-cli": "^3.3.0"
  },
  "dependencies": {
  "lodash": "^4.17.11"
  }
}

3. 必要なファイルを手動で作成する

続いて、手動で以下のファイルを作成します * ./webpack.config.js * ./dist/index.html * ./src/index.js

./webpack.config.jsの内容

const path = require('path');

module.exports = {
  entry: './src/index.js',
  output: {
  filename: 'main.js',
  path: path.resolve(__dirname, 'dist')
  }
};

./dist/index.htmlの内容
読み込むjsファイルはindex.jsではなく、バンドルファイルとなるmain.jsを指定します

<!doctype html>
<html>
  <head>
    <title>Getting Started</title>
  </head>
  <body>
    <script src="main.js"></script>
  </body>
</html>

./src/index.jsの内容
importでlodashを指定します。

import _ from 'lodash';

function component() {
  let element = document.createElement('div');

  element.innerHTML = _.join(['Hello', 'webpack'], ' ');

  return element;
}
    
document.body.appendChild(component());

以下のディレクトリ構造になります。

│  package.json
│  webpack.config.js
│
├─dist
│   index.html
│
├─node_modules
│  ├─lodash
│  ├─webpack
│  ├─webpack-cli
│  └─etc...
└─src
    index.js

4. webpackのコマンドでlodashとindex.jsをまとめた(バンドルした)main.jsを作成する

以下のコマンドを実行します。

npm run build

上記コマンドは、package.jsonにてbuildコマンドに対してwebpackを指定しているため、以下のコマンドと同等になります。

npx webpack

ここまで実行すると、./dist/main.jsが自動で作成されます。 以下のディレクトリ構造になっています。

│  package.json
│  webpack.config.js
│
├─dist
│   index.html
│   main.js
│
├─node_modules
│  ├─lodash
│  ├─webpack
│  ├─webpack-cli
│  └─etc...
└─src
    index.js

5. 確認する

./dist/main.jsを開くと、ソースが圧縮されています。
index.jsと、index.jsからインポートしたlodashが圧縮して記述されています。 また、index.htmlを開くと、「Hello webpack」と表示されることがわかります。

参考

公式サイト

【メモ】Vue.jsでサーバサイドレンダリングを最小限に行う

公式のVue.jsサイトのデモにあるHackerNewsクローンをコードリーディングする前段階として、公式のVue SSR ガイドに掲載されている最小限のSSRを試したのでメモ。

const Vue = require('vue')
const server = require('express')()
const renderer = require('vue-server-renderer').createRenderer()

server.get('*', (req, res) => {
  const app = new Vue({
    data: {
      url: req.url
    },
    template: `<div>The visited URL is: {{ url }}</div>`
  })

  renderer.renderToString(app, (err, html) => {{
    if (err) {
      res.status(500).end('internal server error')
    }
    res.end(`
      <!DOCTYPE html>
      <html lang="ja">
      <head>
        <title>hello</title>
      </head>
      <body>
        ${html}
      </body>
    `)
  }})
})

server.listen(8080)

server.get()

第1引数に、URLのパターンマッチングを渡し、マッチするURLでHTTPリクエストを受け取ると第2引数のコールバックが実行されます。今回はアスタリスクなので、全てのURLでマッチします。
第2引数のコールバックにはRequest、Responseオブジェクトが渡されます。

RequestオブジェクトはHTTPリクエスト関連の情報を取得できますが、中にはNodeのhttp module等から引き継いでいるプロパティ等もあるそうです。 Responseオブジェクトも同様です。

APIリファレンスを見ると、Requestは読み取り系のプロパティが豊富ですが、Responseはこれからクライアントへ送信する情報を決定するため、HTTPレスポンスを編集するメソッドが豊富なイメージです。

renderer.renderToString()

第一引数に渡したVueのインスタンスを内部でレンダリングし、静的な文字列を生成しています。 このメソッドによって初期表示のHTMLが生成されているようです。
生成された文字列は第二引数のコールバックに渡されます。

このdemoではVueのインスタンスを文字列に変換していましたが、ストリームでの対応もしているようです。 HTMLをチャンク単位で破片毎に生成するということだと思いますが、個人的には今は出番は無さそうです。

res.end()

レスポンスの処理が終了した際に呼び出します。 HTTPレスポンスのボディになるデータを引数で渡します。

server.listen()

HTTPサーバーの待ち受けを開始します。 引数には使用するポートを指定します。

Raspberry Pi3 Model B(ラズパイ)と、教育向けコンピュータ

f:id:sho_ya:20181206151658p:plain

Raspberry Piを使おうとしていると、偶然木苺をもらった。なるほど。うまい。小学生の頃はよく食べた気がする。この時期は味は薄いが、旬だとジャムも作れるくらいになるらしい。


f:id:sho_ya:20181206152202j:plain 今日、Raspberry Pi3 model B(通称ラズパイ)にOSをインストールし、少し触ってみた。 ラズパイとは、イギリスのラズベリーパイ財団によって、教育用途を意図して開発されているコンピュータである。 コンピュータとは言ったが、小さな一枚の基板の上にCPUやUSB等、必要最小限の部品で構築されており、5000円程度で入手できる。

「Model B」という名は、1980年代にイギリスで教育向けコンピューターとして普及した「BBC Micro Model B」の流れを組む意味合いがあるのだろう。他にはModel Aなどもある。

教育向けコンピュータBBC Microシリーズは、英国放送協会の「BBC Computer Literacy Project」と呼ばれる、一般人向けのコンピューター啓発プロジェクトの中で仕様が提案された。

そして数ある企業の中で、主に学生で構成されたチームを持つエイコーン社によって開発されたマシンが採用され、販売された。このマシンは採用を競ったマシンにおいてほとんど全ての面で優れていたそうだ。
この後継機の開発の中でARMアーキテクチャのCPUが生まれ、それは僕達のスマートホン等で今でも動いているCPUの先祖にあたる。

僕が何故こんな話を書いているかというと、このBBC Microの「ストーリー」に少し興味を持ったからだ。

  • ARMアーキテクチャという今でも身近な技術がここで生まれたという技術的なこと。
  • 学生がメインの開発チームという、一般的に不安要素となるマシンだったとしても、素晴らしいマシンを採用する英国放送協会のスタンス。

など、面白そうな開発物語がありそうな気がしたのだ。

あと、色々妄想もした。
もしも、BBC Microに採用されるマシンが他の開発チームのマシンとなり、ARMアーキテクチャは世に出ず、ジョブズiphoneが作れなかったかもしれないな、と思うと少し面白い。
iphoneはハード、ソフト共に素晴らしいUIを持つ製品だと思うが、ハードウェアの進化がジョブズの時代に無ければiphoneも生まれなかっただろう。
まあ、ムーアの法則を思えば杞憂かもしれないけれど。

話を戻す。 今回ラズパイを使える状態にした目的は、機械学習、そしてディープラーニングを用いて人工知能の育成を行うためだ。

通常、本格的に人工知能の育成を行うには、ハイエンドのGPUカードを使う必要がある(1枚10万など)。
ラズパイは安価であるため、今回は基礎的な学習が目的となる。学習とはいえ、かなり面白そうなテーマなので今後も詳細を書きたい。

Vue.jsとAce.jsで、ブラウザで動作するマークダウンエディタを作ってみた

f:id:sho_ya:20190116131614p:plain

ブラウザ上で動くVue.jsマークダウンエディタはVueのチュートリアルにもあるが、Aceエディタ化されたものが欲しかったので、githubで似たようなものを見つけて、改造した。 エディタの方に行番号がついていて、それをAce側で制御している。

本当はキャプションの画像にGIFを入れて、動いている所を見せたかったのだが、何故かgifのアップロードできないので、今後でき次第差し替える。

module.exports = {
    render: function (h) {
        var height = this.height ? this.px(this.height) : '100%'
        var width = this.width ? this.px(this.width) : '100%'
        return h('div',{
            attrs: {
                style: "height: " + height  + '; width: ' + width,
            }
        })
    },
    props:{
        value:{
            type:String,
            required:true
        },
        lang:String,
        theme:String,
        height:true,
        width:true,
        options:Object
    },
    data: function () {
        return {
            editor:null,
            contentBackup:""
        }
    },
    methods: {
        px:function (n) {
            if( /^\d*$/.test(n) ){
                return n+"px";
            }
            return n;
        }
    },
    watch:{
        value:function (val) {
            if(this.contentBackup !== val){
                this.editor.setValue(val,1);
                this.contentBackup = val;
            }
        },
        theme:function (newTheme) {
            this.editor.setTheme('ace/theme/'+newTheme);
        },
        lang:function (newLang) {
            this.editor.getSession().setMode('ace/mode/'+newLang);
        },
        options:function(newOption){
            this.editor.setOptions(newOption);
        },
        height:function(){
            this.$nextTick(function(){
                this.editor.resize()
            })
        },
        width:function(){
            this.$nextTick(function(){
                this.editor.resize()
            })
        }
    },
    beforeDestroy: function() {
        this.editor.destroy();
        this.editor.container.remove();
    },
    mounted: function () {
        var vm = this;
        var lang = this.lang||'text';
        var theme = this.theme||'monokai';

        require('brace/ext/emmet');
        var editor = vm.editor = ace.edit(this.$el);

        this.$emit('init',editor);

        editor.$blockScrolling = Infinity;
        editor.setOption("enableEmmet", true);
        editor.getSession().setMode('ace/mode/'+lang);
        editor.setTheme('ace/theme/'+theme);
        editor.setValue(this.value,1);
        this.contentBackup = this.value;

        editor.on('change',function () {
            var content = editor.getValue();
            vm.$emit('input',content);
            vm.contentBackup = content;
            vm.$root.editorValue = content;
        });
        if(vm.options)
            editor.setOptions(vm.options);
    }
}


document.querySelector('body').append(document.createElement('div'))
window.aceVue = new Vue({
    el: '#app',
    template:'<div><editor v-model="content" lang="html" height="500" @init="initEditor"></editor><div id="preview" v-html="cmpledmkdown"></div></div>',
    data:{
        content:"",
        editorValue: ''
    },
    components:{
        editor:require('vue2-ace-editor')
    },
    computed: {
        cmpledmkdown: function () {
            // console.log(this.value);
            return this.editorValue ? marked(this.editorValue, { sanitize: false }) : '';
        }
    },
    methods:{
        initEditor:function (editor) {
            require('brace/mode/html');
            require('brace/theme/monokai');
        }
    }
})

無職(家政夫)と化したプログラマが月1恒例の勉強会(?)に行ってきたので成果報告する

f:id:sho_ya:20181203121331j:plain

身内でやっている勉強会に参加してきた。プログラマーたるもの日々勉強が必要なのだ。

そして、無職のために家政夫と化している自分にはいい刺激なのだ...!

しかし、始まるや否や飯会となり、Jazz鑑賞会となり、Queen鑑賞会となってしまった。なぜだ。

そう、写真右上で情熱的にラテンジャズを弾くベーシストの名はアンソニー・ジャクソン
彼はバディ・リッチやミシェルカミロ等ジャズ界の巨匠達のサポートメンバーとして腕を鍛え上げ、数多の名演を生み出してきた。最早スタジオミュージシャンでありながら1人の巨匠と言えるだろう。
そんな彼が上原ひろみ矢野顕子などの日本の名だたるジャズピアニストのサポートをしてくれているのだと思うと感慨深い。

上の写真は、ミシェルカミロトリオ(ラテンジャズバンド)で弾いているアンソニーの様子だ。曲名はOn Fire。タイトルからしてアツイ。そのライブだ。

www.youtube.com

曲は何を弾いているか訳が分からなくても、カミロの素敵な笑顔とアンソニーのキレのある動き(顔面)だけでも楽しめる素晴らしいライブである...! f:id:sho_ya:20181203130921p:plain

しかし、我々の趣旨はこれではない...!僕らは社会人(無職家政夫含む)である。時間は有効に使わねば。
だが、曲のリクエストはヒートアップし、僕のPCは次々とYoutubeを垂れ流すジュークボックスと化す。

「素人の演奏はしょっぱいな!」
「やっぱ本家の一体感すげーな」

これどうでも良いだろ、と思いながも盛り上がっていく。やばい。 しかし、流石は主催者。最後にはきっちり仕切って、僕はYoutubeをレイヤーの奥底にそっと仕舞い込んだ。


しかし、各々の進捗報告が終わり、取り組む課題が見えたてきた頃合いだっただろうか。

「そういえばQueenの映画見た?」と、誰ともなく言い、

Queenはあの曲が好き」
「まじで、確認しよう」

再びYoutubeが唸りをあげ、この日はついにVisual Studio Codeに一行もコードを書くことなくお開きとなった。完璧にスタンバイしていたが。すまん。

しかしだ、そんな勉強会の顛末なんてどうでもいい!Queenの素晴らしさに比べればどうでも良いと僕は断言しよう!

見てくれ!映画でもおなじみになったが、Live AidQueenである。
フレディが一人前線に立って客を煽る時の言語不可能な奇妙さと格好良さはなんなのだ...!
同じことを他の誰がやってもここまで格好よく決まらないだろう。

www.youtube.com

www.youtube.com

以上が今月の勉強会の成果である。