もがき系プログラマの日常

もがき系エンジニアの勉強したこと、日常のこと、気になっている技術、備忘録などを紹介するブログです。

今から始めるWebフロントエンド開発を読んで勉強した(パート1)

こんにちは。

フロントの勉強するにあたって、最近のモダンなフロントエンド開発を知らないため、コチラの本を購入しました。

Backbone.jsから止まっている自分の時間をすすめるため、頑張って勉強しました。

コチラの本では、3分の1以上がJSの歴史と、現代の仮想DOM時代への流れが記載されていました。

その後簡単なTODOアプリを作成します。その際に、モダンなフロントエンドの技術を用いて開発を行います。

それぞれ名前はしっているけど、実際に使用したことはほぼないものばかりでした。

今回はこちらを自分なりに噛み砕きながら、覚えるためにブログ記事にしていこうと思います。

使用したツール群

BABEL

ES2015準拠で書かれたJSコードをES5のもの(各ブラウザで動かせるもの)に変換してくれるトランスパイラというものです。

開発時にES2015準拠でコードを書いていき、製品版として仕上げる際は、BABELを噛ませてES5のものに変更するという使い方だと思います。

インストール

# Babelのインストール
$ npm install --save-dev babel-cli

# ES2015からの変換を行うプリセットをインストール
$ npm install --save-dev babel-preset-es2015

セットアップ

.babelrc というファイルが、babelの設定ファイルとなります。コチラを作成し、以下になるように編集してください。

$ cat .babelrc 
{
  presets: ["es2015"]
}

使ってみる

テスト用のjsファイルを作成して、以下のコードを書いてみました。

# test.js
class A {
  constructor() {
    this.name = 'Aですよ';
  }

  getName() {
    return this.name;
  }
}

こちらをBabelで変換してみます。

変換のコマンドは以下になります。

# -oでファイルに書き出しする
node_modules/.bin/babel test.js -o compiled.js

出来上がったファイルの中身は以下になります。

# compiled.js

'use strict';
  
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var A = function () {
  function A() {
    _classCallCheck(this, A);

    this.name = 'Aですよ';
  }

  _createClass(A, [{
    key: 'getName',
    value: function getName() {
      return this.name;
    }
  }]);

  return A;
}();

正常に変換されているようでした。

Browserify / WebPack

各JSファイルの依存関係をよしなに解決してくれて、一つにまとめてくれる優れものツールということでした。

WebPackに関しては、JSに限らず、cssや画像などもまとめて1つのjsファイルにしてくれるようです。

該当のJSファイルに書かれているrequire関数を読み取り、依存関係を解決してくれます。

require関数とは?と思って調べると、CommonJS Modulesというものに含まれているということでした。

CommonJSとは?と調べると、以下引用です。

CommonJSとは、サーバーサイドなどのウェブブラウザ環境外におけるJavaScriptの各種仕様を定めることを目標としたプロジェクトである。

ツールというより仕様ということでした。

NodeJSのモジュールシステムはこれを元に動いているようです。

大本ということなのでしょうか?

どちらにしても、nodejs経由でインストールしているため、どちらもrequire関数を検知できるということなのだと理解しました。

(もう一つのモジュール管理でAMDというものがあり、そちらにRequireJSというのがあるようです。こちらもrequire関数と同等のものが使え、webpackに関してはコチラも対応しているということでした)

こちらの本では、Browserifyを使用していたので、自分も例にそって同じものを使ってみます。

インストール

$ npm install --save-dev browserify

使ってみる

早速使ってみます。 以下のような3つのJSファイルを作成します。

# test1.js
let a = require('./test2.js');
console.log(a.getName());

let b = require('./test3.js');
console.log(b.getName());
# test2.js
class A {
    constructor() {
      this.name = 'my name is A!';
    }

    getName() {
        return this.name;
    }
}
module.exports = new A;
# test3.js
class B {
    constructor() {
      this.name = 'my name is B!';
    }

    getName() {
        return this.name;
    }
}
module.exports = new B;

test1.jsはtest2.jsとtest3.jsに依存しているものがわかると思います。

こちらをbrowserifyで変換してみます。

$ node_modules/.bin/browserify test1.js -o bundle.js

後は確認のため適当なHTMLを作成します。

# test.html
<!DOCTYPE html>
<html>
<head>
    <title>test</title>
</head>
<body>
    <script type="text/javascript" src="bundle.js"></script>
</body>
</html>

test.htmlをブラウザで開きconsole.logを確認すると、以下のようになると思います。

f:id:kojirooooocks:20180115010855p:plain

実際の開発現場では、ES2015でJavaScriptコードを書いていき、Babelで各ブラウザ対応のJSへ変換し、Browserify(WebPack)を使用して依存関係を解決し1つにまとめるというのが流れのようです。

(この本は2016年8月くらいの本なのですが、今もそうなんでしょうか・・・?知ってる人いたら教えてください)

Babelify

コード修正の度に、Babelコマンド叩いて、Browserifyを叩いてとするのはとてもめんどいです。

さらに以下、引用ですが、重要なところが書かれていました。

require関数はもともとはNode.jsで用意されていた関数で、importやexportはES2015で定義された仕様です。これに対してBabelは、importをrequire関数に変換しますが、多くのブラウザではrequire関数を読み込めず、モジュール間の依存解決を解決できません。

たとえば、A.jsでimportを、B.jsでexportを使用している場合、それぞれBabelを噛ませることで各ブラウザ対応のJSへ変更してくれます。

その際、A.jsのimportはrequireに変換されますが、対象ファイル自体はBabelを実行する前のES2015で記述されているファイルになります。

Browserifyを実行する前にわざわざファイルを開いて、Babel実行後のファイルに向き先を変更して上げる必要があります。

これはあまりにつらい作業です。。。

その辺の辛さを解消してくれるのがBabelifyというツールです。

BabelifyはBrowserifyの変換時にES2015の変換も同時に行ってくれるものです。つまりBabelの仕事も一緒にやってくれるということのようです。

インストール

$ npm install --save-dev babelify

使ってみる

例となるスクリプトはありませんが、以下が実行コマンドになります。

# -tオプションで babelifyを指定する
$ node_modules/.bin/browserify A.js -t babelify -o bundle.js

ESLint

本ではその他の取り組みとして、JSコードの静的解析を行うツールとして名前だけ紹介されていましたが、今回せっかくなので、どんなものか試してみたいと思います。

コチラを参考にしました。

インストール

$ npm install --save-dev eslint

セットアップ

eslintのセットアップを行います。

色々と質問されます。

CommonJS使いますか?JSX使いますか?React使いますか?などなど。

Yesとすると追加パッケージを落とすこともあります。 (Reactとかはそう)

$ ./node_modules/.bin/eslint --init
? How would you like to configure ESLint? Answer questions about your style
? Are you using ECMAScript 6 features? Yes
? Are you using ES6 modules? Yes
? Where will your code run? Browser
? Do you use CommonJS? Yes
? Do you use JSX? No
? What style of indentation do you use? Spaces
? What quotes do you use for strings? Double
? What line endings do you use? Unix
? Do you require semicolons? Yes
? What format do you want your config file to be in? JSON

回答が終了すると以下のようなファイルが作られます。

# .eslintrc.json
{
    "env": {
        "browser": true,
        "commonjs": true,
        "es6": true
    },
    "extends": "eslint:recommended",
    "parserOptions": {
        "sourceType": "module"
    },
    "rules": {
        "no-console": "off",
        "indent": [
            "error",
            4
        ],
        "linebreak-style": [
            "error",
            "unix"
        ],
        "quotes": [
            "error",
            "double"
        ],
        "semi": [
            "error",
            "always"
        ]
    }
}

使ってみた

eslintのテストをするため以下のファイルを作成しました。

# eslint_test.js
class Cats {
    Constructor(name) {
        this.name = name;
    }

    getName() {
        return this.name;
    }
}

let cats = Cats('ドラえもん');
console.log(cats.getName())

このファイルを対象にeslintを実行してみます。

$ ./node_modules/.bin/eslint eslint_test.js

/home/vagrant/eslint_test.js
  11:17  error  Strings must use doublequote  quotes
  12:1   error  Unexpected console statement  no-console
  12:28  error  Missing semicolon             semi

✖ 3 problems (3 errors, 0 warnings)
  2 errors, 0 warnings potentially fixable with the `--fix` option.

ダブルクォート使えよっていうのと、セミコロン付け忘れてるぞ!っていうのは理解できました。

no-consoleというのはなんだろうと、調べていると、最初のeslintが設定してくれているおすすめ設定の中に、console記述禁止の設定があるようです。

console.logは使いたいので、rulesを書き変えてみます。

.eslintrc.jsonrules の部分に以下を追記します。

"no-console": "off",

セミコロンとダブルクォートの修正も行い、再度実行してみます。

$ ./node_modules/.bin/eslint eslint_test.js

何も表示されなくなりました。つまり静的解析は問題なく通っているということになります。

便利ですね!

uglify

javascriptコードのminifyを行うさいには uglify を使うようです。

こちらは本で紹介されていませんが、まぁ実際にアプリケーションを使って本番に公開する際はきっと使うことになるでしょうし、実際使ってみます。

ちなみにこちらはgulp経由で使用した経験はありますが、ググりながらコピペで使ってたので、理解はしていませんでした。。

インストール

$ npm install --save-dev uglify

使ってみた

上の方でbabelコマンドを使って変換したjsファイルで実験してみます。中身は以下。

'use strict';

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var A = function () {
  function A() {
    _classCallCheck(this, A);

    this.name = 'Aですよ';
  }

  _createClass(A, [{
    key: 'getName',
    value: function getName() {
      return this.name;
    }
  }]);

  return A;
}();

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

$ node_modules/.bin/uglifyjs compiled.js -c -m -o compiled.min.js

実行で出来たファイルの中身は以下。

"use strict";function _classCallCheck(e,n){if(!(e instanceof n))throw new TypeError("Cannot call a class as a function")}var _createClass=function(){function e(e,n){for(var t=0;t<n.length;t++){var a=n[t];a.enumerable=a.enumerable||!1,a.configurable=!0,"value"in a&&(a.writable=!0),Object.defineProperty(e,a.key,a)}}return function(n,t,a){return t&&e(n.prototype,t),a&&e(n,a),n}}(),A=function(){function e(){_classCallCheck(this,e),this.name="Aですよ"}return _createClass(e,[{key:"getName",value:function(){return this.name}}]),e}();

圧縮・難読化されていました!

終わり

なんか周辺ツールの勉強だけで結構なボリュームになってしまった。。。

TODOアプリはReactを使って作成するので、今度はそちらの勉強記事をあげようと思います。

では。

投稿数が早くも2017年を超えた件

先ほど今週1発目の記事を上げたのですが、ふと見ると2017年の記事の総数を超えていました。

まぁ振り返り記事とかもあるので、実のある記事は6記事ですが。

それでも早くも超えたことに感激しました。

人間やろうと思えば出来るもんですね。

丁寧に記事を書いておけば、勉強しておいたことを思い出すきっかけにもなるし、本当に一石二鳥と思いました。

これからも頑張って記事書いて、自分の勉強を続けていこうと思います。

さて。筋トレしてねよ。

JavaScriptの復習を行った

概要

今までNode.jsやReactNativeに触れて、自分のJSの知識がいかに乏しいかというのを、しっかり理解することが出来ました。

今年度の目標として、フロントエンドの知識を身につけるというのがあるので、これを機会にしっかり基本から勉強し直して、自分のJSの知識の底上げをしていきたいと思います。

参考にした本

色々とJS関連の本を漁ったところまさに良さそうな内容の本が何冊かありましたので、それらを見ながら、復習をしていきたいと思います。

今回は以下の本を見ながら、脱初心者を目指そうと思います。

脱初心者のJavaScript力を底上げするための本

参考になったところ

argumentsオブジェクト

関数に渡された引数全てを管理しているオブジェクト

配列に似た構造を持ってるけど別物。

function test(aaa, bbb, ccc) {
    for (var p of arguments) {
        console.log(p);
    }
}

test('ドラえもん', 'のび太くん', 'しずかちゃん', 'ジャイアン', 'スネ夫');
// ドラえもん のび太くん しずかちゃん ジャイアン スネ夫と表示される

test('ドラえもん', 'のび太くん');
// ドラえもん のび太くんと表示される

test();
// 何も表示されない

定義している引数の数に見合わない呼び出しをしても問題ないということは知っていましたが、このオブジェクトの存在は知りませんでした。 でもそんな使わなそうな気がします。 引数ちゃんと合わせろよって話ですし。。。

変数の巻き上げ

関数内のどの位置にでもvar文を使用して変数を宣言できるが、関数内のどの位置に変数の宣言をしても、 その関数の先頭で宣言された と見なされる。

以下は本から引用させていただいたコードです。

var a = 'hoge';
var f = function() {
    console.log(a); // undefined
    var a = 'fuga';
    console.log(a); // fuga
}

実際にはf ファンクション内の、2行目で a変数に fugaという文字を入れたが、結果的にはファンクション内の1行目(先頭)で a変数を定義したことになってしまうということです。

しかも、宣言だけで、初期化は行われません。

なので、 undefined になっているということになります。

つまり fファンクション内は以下みたいなります。

var f = function() {
    var a;
    console.log(a);
    a = 'fuga';
    console.log(a);
}

これはまじで知りませんでした。 まぁ引数で渡しちゃったり、基本的に宣言・初期化は先にしちゃうので気づいてなかったというのもありますが。。 勉強になりました。

即時関数

無名関数を定義し、そのまま () をつけて実行することが出来る。無名関数は、function定義から全体を()で囲む必要がある。

自分が慣れていないだけなのですが、使ったことがありませんでした。

結構使う技術なんでしょうか??

(function(test) {
  console.log(test);
})('パリピ');
// パリピ

apply, call, bind

関数内で参照するthisを束縛するためのメソッド。 thisは呼び出される場面によって自身の参照先が変化するが、これらのメソッドを使うと常に指定した通りのthisで動作させることが出来る。

なお、bindに関しては、関数に渡す引数も束縛することが出来る。

これらは、jsのthisの勉強をしていたときに、なんとなく理解したのですが、実際にきちんと理解するために、今回もう一度勉強しようと思いました。

apply

applyの特徴は、引数をまとめて配列で渡すことのようです。

function profile(age) {
    console.log(this.name + "です。" + age + "才です。");
}

var user1 = { name: "ドラえもん" };
var user2 = { name: "のび太" };

profile.apply(user1, [20]); // ドラえもんです。20才です。
profile.apply(user2, [10]); // のび太です。10才です。

call

callの特徴は、引数をそのままで渡すことが出来るようです。

function profile(age) {
    console.log(this.name + "です。" + age + "才です。");
}

var user1 = { name: "ドラえもん" };
var user2 = { name: "のび太" };

profile.call(user1, 20); // ドラえもんです。20才です。
profile.call(user2, 10); // のび太です。10才です。

bind

bindはほか2つと少し違っていて、引数も同時に束縛できることが出来ます。

userProfile3のように、引数を束縛しないことももちろん可能です。

function profile(age) {
    console.log(this.name + "です。" + age + "才です。");
}

var userProfile1 = profile.bind({ name: "ドラえもん"}, 20);
var userProfile2 = profile.bind({ name: "のび太"}, 10);
var userProfile3 = profile.bind({ name: "ジャイアン"});

userProfile1();   // ドラえもんです。20才です。
userProfile2();   // のび太です。10才です。
userProfile3(15); // ジャイアンです。15才です。

一度まとめるとスッキリわかりました。勉強になりました。

Map

ES2015からサポートされた map は従来のオブジェクトで書くよりいろいろメリットがあるとのことでした。特に以下です。

  1. キーを任意の型で指定できる
  2. 専用のメソッドが用意されている

専用のメソッドに関しては、名前でだいたい想像できますが、一応書いてみます。

メソッド名 詳細
set(key, val) mapにデータをセットする
delete(key) 指定のkeyのデータをmapから削除する
size mapの要素数を返す(こちらはメソッドではない)
has(key) 指定のkeyのデータがmap内にあるかどうかを確認する
get(key) 指定のkeyのデータをmapから取得する
keys() map内に存在するすべてのkeyを返す
values() map内に存在するすべてのvalueを返す
entries() mapの全てのkey, valueのペアを返す

それぞれ、サンプル書いてみました。

var testMap = new Map([
    ['name', 'ドラえもん'],
    ['age', 20],
    [12345, 'ppppp']
]);

console.log(testMap.set('isError', true));
// => Map(4) {"name" => "ドラえもん", "age" => 20, 12345 => "ppppp", "isError" => true}

console.log(testMap.delete('isError'));
// => true

console.log(testMap.size);
// => 3

console.log(testMap.has(12345));
// => true

console.log(testMap.get(12345));
// => ppppp

console.log(testMap.keys());
// => MapIterator {"name", "age", 12345}

console.log(testMap.values());
// => MapIterator {"ドラえもん", 20, "ppppp"}

console.log(testMap.entries());
// => MapIterator {"name" => "ドラえもん", "age" => 20, 12345 => "ppppp"}

ES2015に関しては、また別途勉強するつもりなので、簡単に書きましたが、こういうのもあるのですね。勉強になりました。

クラス

こちらもES2015でサポートされたもののようです。

class Animal
{
    constructor(name)
    {
        this.name = name;
    }

    getName()
    {
        return this.name;
    }
}

var animal = new Animal('ねこ');
console.log(animal.getName());
// => ねこ



class Cats extends Animal
{
    constructor(name, nickname)
    {
        super(name);
        this.nickname = nickname;
    }
    
    getNickName()
    {
        return this.nickname;
    }
    
    // オーバーライド
    getName()
    {
        return super.getName() + "(名前: " + this. getNickName() + ")";
    }
}

var cats = new Cats('ねこ', 'こじろう');
console.log(cats.getName());
// => ねこ(名前: こじろう)

classはphpで触り慣れているので、比較的すんなり入れました。

アクセス修飾子がないというところが少し気になりますが、無理やり作ることは出来るみたいなので、クラスは積極的に使っていきたいと思います。

勉強になりました!!

Promise

これもES2015でサポートされた機能のようです。

以下のようなコールバック地獄を回避するための デザインパターン ということです。

request1(function(response1) {
    request2(function(response2) {
        request3(function(response3) {
            request4(function(response4) {
                request5(function(response5) {
                    // まだまだ続く可能性あり・・・
                });
            });
        });
    });
});

↑のようなコードをPromiseを使って書くと以下のようにきれいに書けるようです。

request1()
    .then(function(response1) {
        return request2();
    }).then(function(response2) {
        return request3();
    }).then(function(response3) {
        return request4();
    }).then(function(response4) {
        return request5();
    }).then(function(response5) {
        // 処理はつづく・・・
    });

シンプルでわかりやすいです!勉強になりました。

Promiseはもう少し色々できそうなので、別でもう少し勉強してみようと思います。

ES2015

ES6とも呼ばれる事があるようなのですが、次バージョンのES2016はES7とは言わないようなので、ES2015で覚えておこうと思います。

classやPromise以外にも色々とわかっておいたほうが良い機能があり、こちらも勉強しようと思います。

ES2015に関してはコチラの記事がとてもわかりやすくまとめて頂いているので、コチラを見ながら特に使いそうだなと感じたものを幾つか記載していきます。

let, const

let: 再宣言が出来ない

const: 再宣言・再代入が出来ない

アロー関数式

無名関数に対して、functionという記述を省略できるようです。

let sum_func = (a, b) => {
    return a + b;
};

console.log(sum_func(1, 2));
// => 3

分割代入

一度に複数の変数に、それぞれの値を代入できるようです。

var [name, age] = ['ドラえもん', 20];
console.log(name);
// => ドラえもん

console.log(age);
// => 20

デフォルト引数

これもPHPなど他の言語でも使われているので、分かりやすかったです。

function test(a = 1)
{
    console.log(a);
}

test();
// => 1

test(2);
// => 2

テンプレート文字列

PHPのヒアドキュメント的な感じでかけるようです。

var name = 'ドラえもん';

var message = `こんにちは!
ぼく${name}です。`

console.log(message);
// => こんにちは!
// ぼくドラえもんです。

終わり

今週一発目の記事は今週に入って1時間後にpostしました。

いろいろと勉強できて良かったです。

これだけ学べることがあったことで、自分が今まで理解できていなかったということがよくわかりました。

JSは流行り廃りが早いとかいいますが、そもそもそれを気にするレベルに達してないので、とりあえず貪欲に勉強し続けていこうと思います。

多分次の記事もJS関連だと思います。

ではでは。

Node.js超入門を読んだ

自分は現在インフラよりのサーバサイドというポジションで今まで働いていたのですが、仕事でnodejsを触る機会がありませんでした。

個人的にインストールして、expressでなにかしら動かして、「なるほどこういうもんなのか」と納得して終わった記憶があります。

jsやcssのminifyをするためgulpを使いたいなーとおもい、インストールしたりしましたが、ググってとりあえず使ったりしているだけでしたので、この勉強+アウトプット年にこの際きちんと勉強しようと思い、この本を購入しました。

ちなみにこの本は買ってからずっと放置していた積み本でした。

大雑把な感想

流石に超入門とついているだけあり、全体的にかなり初心者よりな本でした。

言葉も噛み砕いて書かれていて、初心者にはとても良い内容だったと思います。

流れ的には、nodejsでサーバを立ててみようというノリだったので、自分が知りたい部分とはマッチしていなかったのですが、ただ、そのなかでも自分の無知で知らない部分があり、勉強になったところも多々有りました。

聞くは一時の恥、聞かぬは一生の恥

初心者レベルの本でも学べる部分があるという、自分の無知さへの落胆と、知れてよかったという安堵感がせめぎ合っております。

まぁそんな感じで、今回も勉強になった箇所を、書き残しておこうと思います。

勉強になったところ

偶数バージョン・奇数バージョン

知らなかったのですがググるとかなり出てきた情報だったので、常識なことなのですね。。

偶数バージョンはLTS

奇数バージョンは半年程度のサポートのみ。奇数バージョンで、新しい技術を積極的に取り組んでいって、偶数バージョンで、確実に動くものとして提供する。

という運用のようです。

勉強になりました。

npm install -g

グローバルにinstallするのは -g オプションをつけてinstall

ローカルにinstallするのは何もつけずにinstall

ここも結構どっちがどっちとかあまり気にせずやっていたな。。。 気をつけます。

package.json

npmで使用するパッケージ情報が記述されているもの。

これもちゃんと理解していなかったので、確認できてよかった。

npm init した際に作られるpackage.jsonが以下。

他にも様々な項目がある用で、コチラで詳しく書かれていたので、参考にさせてもらいながら自分でもまとめてみる。

{
  "name": "express-test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "node index.js"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.16.2"
  }
}
項目 詳細
name パッケージの名前
version パッケージのバージョン
description パッケージの説明文
main メインプログラム。モジュール内で最初に呼ばれるスクリプトファイルという感じ?
scripts 自分で指定したShell Scriptを実行できるエイリアスコマンドを定義できる。サンプルだと npm start というコマンドが打てて、 node index.js を実行してくれるようになる。
author 作者名
license ライセンス
dependencies このパッケージが依存するモジュールとバージョン

npm install パッケージ名 --saveオプション

--save オプションを指定することで、 package.jsondependencies にインストールしたパッケージが追記される。

他の人がこのpackage.jsonを使ってnpm installすると、dependenciesに記載されているパッケージ全てがインストールされるようになる。

これも普通に使いそう。勉強になりました。

npm install --save jquery

jqueryのnpm install出来るとのこと。全然知らなかったです。。

ラスト

とまあこんな感じで、最初にもいいましたが、初心者向けの本でも勉強できることは多かったです。 初心者というよりも超初心者向けという感じでしたがw

最後の章でJSの書き方についても丁寧に説明されているので、本当に真っ白の状態から、勉強するには良いかと思いました。

今年の目標でフロントエンドの知識をつけたいというのがあるので、今度もフロントエンド系の知識が身につく本を買って勉強しようと思います。

Node.js超入門

Node.js超入門

Laravel-Breadcrumbが便利だった

今とあるプロジェクトで現行サイトをLaravelに載せ替える作業をしているのですが、そのさい、べた書きで実装されていたパン屑をどうやって実装しようかとPackagistを漁っていたところ、laravel-breadcrumbsという超便利ライブラリがあったので、早速使ってみました。

作業環境

  • Laravel: 5.5
  • PHP: 7.1.5
  • laravel-breadcrumbs: 4.2.0

Laravelインストール

以下でLaravelプロジェクトを作成します。

$ composer create-project --prefer-dist laravel/laravel breadcrumbs_test

laravel-breadcrumbsインストール

以下でlaravel-breadcrumbsをインストールします。

$ cd breadcrumbs_test
$ composer require davejamesmiller/laravel-breadcrumbs

config/breadcrumbs.phpを作成

以下でbreadcrumbsの設定ファイルを作成します。

$ php artisan vendor:publish --provider='DaveJamesMiller\Breadcrumbs\BreadcrumbsServiceProvider'

routes/breadcrumbs.phpを作成

上で作ったconfig/breadcrumbs.phpを見ると分かるのですが、パン屑設定を記述するファイルはroutes/breadcrumbs.php というファイルになります。

なので、routes/ にファイルを作成します。

$ touch routes/breadcrumbs.php

パン屑の設定を書いていく

作成したroutes/breadcrumbs.phpにパン屑の設定を書いていきます。

せっかくなので、公式に書いてあったものの似たような感じで作ってみます。

<?php

// Home
Breadcrumbs::register('home', function ($breadcrumbs) {
    $breadcrumbs->push('Home', route('home'));
});

// Home > About
Breadcrumbs::register('about', function ($breadcrumbs) {
    $breadcrumbs->parent('home');
    $breadcrumbs->push('About', route('about'));
});

// Home > Blog
Breadcrumbs::register('blog', function ($breadcrumbs) {
    $breadcrumbs->parent('home');
    $breadcrumbs->push('Blog', route('blog'));
});

// Home > Blog > [Category]
Breadcrumbs::register('category', function ($breadcrumbs, $category) {
    $breadcrumbs->parent('blog');
    $breadcrumbs->push($category->title, route('category', $category->name));
});

// Home > Blog > [Category] > [Post]
Breadcrumbs::register('post', function ($breadcrumbs, $post) {
    $breadcrumbs->parent('category', $post->category);
    $breadcrumbs->push($post->title, route('post', [$post->category->name, $post->id]));
});

パン屑に対応するrouteを書いていく

せっかくパン屑の設定を書いても、laravelのプロジェクトを作ったばかりの場合は、対応するrouteが存在しません。

なので、ルーティングファイルを編集します。

今回はパン屑設定に対応するルーティングを設定しました。

<?php

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', function () {
    return view('home');
})->name('home');

Route::get('/about', function () {
    return view('about');
})->name('about');

Route::get('/blog', function () {
    return view('blog');
})->name('blog');

Route::get('/blog/{category_name}', function ($category_name) {
    $category        = new StdClass;
    $category->title = 'カテゴリ1';
    $category->name  = $category_name;
    return view('category', ['category' => $category]);
})->where('category_name', '[A-Za-z]+')->name('category');

Route::get('/blog/{category_name}/{id}', function ($category_name, $id) {
    $category        = new StdClass;
    $category->id    = 1;
    $category->title = 'カテゴリ1';
    $category->name  = $category_name;

    $post            = new StdClass;
    $post->id        = $id;
    $post->title     = "ポスト{$id}";
    $post->category  = $category;
    return view('post', ['post' => $post]);
})->where('category_name', '[A-Za-z]+')->where('id', '[0-9]+')->name('post');

カテゴリやポストは本来はDBから取得するものですが、今回はテストなのでべた書きしています。

viewファイル作成

ルーティングに対応するbladeファイルを作成してください。

$ touch resources/views/home.blade.php
$ touch resources/views/about.blade.php
$ touch resources/views/blog.blade.php
$ touch resources/views/category.blade.php
$ touch resources/views/post.blade.php

各ファイルは以下みたいな感じで書いておきます。

h1やtitle部分は脳内補完してください。

<!DOCTYPE html>
<html>
<head>
    <title>home</title>
</head>
<body>
    <h1>Home</h1>
</body>
</html>

テンプレートにパン屑を表示する

早速表示してみます。

例えばaboutページであれば、h1の下に以下を記述してください。

{{ Breadcrumbs::render('about') }}

早速確認してみます。

f:id:kojirooooocks:20180111004816p:plain

パン屑表示されてますね!!

引数も渡せます。categoryページの場合は、以下のように記述します。

{{ Breadcrumbs::render('category', $category) }}

f:id:kojirooooocks:20180111004821p:plain

表示されてます!

テンプレートの切り替え

スタイルを効かせたい場合など当然あると思います。

その場合は、設定ファイルでパン屑が表示されるテンプレートを変更することが出来ます。

config/breadcrumbs.php の以下の項目を変更します。

'view' => 'breadcrumbs::bootstrap4',
    ↓
'view' => 'elements/breadcrumbs'

上のように変更すると、laravel-breadcrumbsは resources/views/elements/breadcrumbs.blade.phpを見るようになります。

新たにファイルを作り、以下のように記述します。

@if (count($breadcrumbs))
    <ul class="breadcrumb">
        @foreach ($breadcrumbs as $breadcrumb)
            @if ($breadcrumb->url && !$loop->last)
                <li class="breadcrumb-item"><a href="{{ $breadcrumb->url }}">{{ $breadcrumb->title }}</a></li>
            @else
                <li class="breadcrumb-item active">{{ $breadcrumb->title }}</li>
            @endif
        @endforeach
    </ul>
@endif

f:id:kojirooooocks:20180111004827p:plain

ulに変更したとことで、ナンバリングがなくなっています。

新しいテンプレートが使われていることがわかると思います。

JSON-LDでのパンくずリスト対応

ここまでやったのでschema.orgパンくずリストも一緒に実装してみます。

上で作った、新たなテンプレート(elements/breadcrumbs.blade.php)に記述する感じで実装します。

@if (count($breadcrumbs))
    <ul class="breadcrumb">
        @foreach ($breadcrumbs as $breadcrumb)
            @if ($breadcrumb->url && !$loop->last)
                <li class="breadcrumb-item"><a href="{{ $breadcrumb->url }}">{{ $breadcrumb->title }}</a></li>
            @else
                <li class="breadcrumb-item active">{{ $breadcrumb->title }}</li>
            @endif
        @endforeach
    </ul>
    <script type="application/ld+json">
    {
      "@context": "http://schema.org",
      "@type": "BreadcrumbList",
      "itemListElement":
      [
        @foreach ($breadcrumbs as $k => $breadcrumb)
        {
          "@type": "ListItem",
          "position": {{ $k + 1 }},
          "item":
          {
            "@id": "{{ $breadcrumb->url }}",
            "name": "{{ $breadcrumb->title }}"
          }
        }
        @if (!$loop->last)
        ,
        @endif
        @endforeach
      ]
    }
    </script>
@endif

f:id:kojirooooocks:20180111004830p:plain

問題なく表示されてますね。

感想

laravelはいろんなライブラリがあって面白いですね。

こらからどんどん移植をしていくうちに、他にもいいライブラリをみつけたら、また紹介したいと思います。

React-Nativeでカメラ起動してみた

React-Nativeで端末にアプリを落として、カメラ起動するところまでやってみました。

今回のアプリで使用したいツールは以下の

React-Native-Camera

を使用したいと思います。

参考サイト

インストール

以前上げた記事を見ながら、プロジェクトを作成します。

$ react-native init test2

その後作成したプロジェクトのディレクトリに移動して、以下を実行します。 以下のコマンドは、今回使用するReact-Native-Cameraの README に記載されている Mostly automatic install with react-native になります。

$ cd camera-test
$ npm install react-native-camera --save
$ react-native link react-native-camera

↑のコマンドを実行すると、以下のエラーが出ました。

Command `link` unrecognized. Make sure that you have run `npm install` and that you are inside a react-native project.

npm installしなさいよと。なので、実行します。

$ npm install

再度実行すると、うまくいきました。

$ react-native link react-native-camera
Scanning folders for symlinks in /path/to/test2/node_modules (20ms)
rnpm-install info Linking react-native-camera android dependency 
rnpm-install info Android module react-native-camera has been successfully linked 
rnpm-install info Linking react-native-camera ios dependency 
rnpm-install info iOS module react-native-camera has been successfully linked 

コード修正

今回はiOSで実行してみようと思います。

plistファイル修正

まずplistを修正します。

test2/ios/test2/Info.plistを開いて以下を追加します。

    <key>NSCameraUsageDescription</key>
    <string>Your message to user when the camera is accessed for the first time</string>
    <key>NSPhotoLibraryUsageDescription</key>
    <string>Your message to user when the photo library is accessed for the first time</string>
    <key>NSMicrophoneUsageDescription</key>
    <string>Your message to user when the microphone is accessed for the first time</string>
    <key>NSPhotoLibraryAddUsageDescription</key>
    <string>Your message to user when the photo library is accessed for the first time</string>

それぞれ、意味があります。

  • NSCameraUsageDescription

    カメラへのアクセス

  • NSPhotoLibraryUsageDescription

    フォトライブラリへのアクセス

  • NSMicrophoneUsageDescription

    マイクへのアクセス

  • NSPhotoLibraryAddUsageDescription

    フォトライブラリへのアクセス(こちらはiOS11以降で必要になったようです)

string部分は、端末で表示する注意文言となります。

App.jsファイル修正

test2/App.jsの中身をREADMEのサンプルコードに移し替えます。

サンプルコード

XCodeでの編集

今回iOSで確認する予定ですので、端末で確認するためにはXCodeからビルドする必要があります。

なので、test2/ios/にある、 test2.xcodeproj を開きます。

そして、ビルド先を添付画像のように、端末に設定します。

f:id:kojirooooocks:20180103020945p:plain

ビルドしてみましょう。

f:id:kojirooooocks:20180103021006p:plain

ビルド失敗しました。。。 確認してみます。

f:id:kojirooooocks:20180103021021p:plain

プロビジョニングプロファイルの指定がなかったために出てたエラーのようです。

以下を修正して対応しました。

f:id:kojirooooocks:20180103021040p:plain

エラーも消えたので再度ビルドしてみると、端末にアプリを落とすことが出来ました。

ただ、ビルドされたアプリではエラー画面が表示されていました。

f:id:kojirooooocks:20180103021055p:plain

色々と調べていたのですが、こちらのissueの内容で、解決しました。

class BadInstagramCloneApp extends Component {

の箇所を

export default class BadInstagramCloneApp extends Component<{}>

に変更しました。

再度実行してみます。

f:id:kojirooooocks:20180103021112j:plain

問題なく写真が取れて、画像を保存できました!!

たのしー!!

ただ、実際理解していくにはReact.jsの知識が必要そう。。。

なんとなくJSを使ってきた自分にとって、勉強すべき課題が見つかったいい振り返りになりました。

React-Native触ってみた

Web+Db Press vol102で紹介されていたReact-Nativeを触ってみました。

ちょうど仕事でもアプリ開発を求められる可能性が出てきているので、先に素振りしておきたいと思ったので、ちょうど良かったです。

ではスタート。

参考サイト

https://qiita.com/ponnjinnka/items/006b632a0b56369451b9

React-Native Install

以下のコマンドでインストールします。

npm install -g react-native-cli

react-nativeのコマンドが使用できるようになります。

このブログ作成時の最新バージョンはv0.51.0でした。

サンプルアプリ(雛形)を作成

以下のコマンドで作成します。

react-native init --version='バージョン' プロジェクト名

バージョンは指定しない場合、最新のバージョンが設定されるようです。

実行すると色々と表示され、インストールを頑張ってくれます。

以下が表示されれば完了です。

To run your app on iOS:
   cd /path/to/プロジェクト名
   react-native run-ios
   - or -
   Open ios/プロジェクト名.xcodeproj in Xcode
   Hit the Run button
To run your app on Android:
   cd /path/to/プロジェクト名
   Have an Android emulator running (quickest way to get started), or a device connected
   react-native run-android

iosシミュレータ起動

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

cd /path/to/プロジェクト名 react-native run-ios

いろいろ頑張ってビルドしてくれてますが、シミュレータが起動するまでは早いです。

f:id:kojirooooocks:20171228012932p:plain

ビルドが終わりアプリが立ち上がると、シミュレータは以下のような表示になります。

f:id:kojirooooocks:20171228012905p:plain

Androidシミュレータ起動

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

cd /path/to/プロジェクト名
react-native run-android

iosのビルド同様、頑張ってビルドしてくれます。

しかし自分の環境ではエラーが出ました。

FAILURE: Build failed with an exception.

* What went wrong:
A problem occurred configuring project ':app'.
> SDK location not found. Define location with sdk.dir in the local.properties file or with an ANDROID_HOME environment variable.

ANDROID_HOMEというandroid-sdkまでのパスを格納する環境変数がセットされてないみたいです。

ということで環境変数をセットして再度実行

ANDROID_HOME=/path/to/Android/sdk react-native run-android

更に違うエラーが発生。

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:installDebug'.
> com.android.builder.testing.api.DeviceException: No connected devices!

調べたところAndroidの場合はエミュレータを先に立ち上げておく必要があるみたいです。

iosは立ち上げてくれたからandroidも勝手に立ち上げてくれると思っていた。。。

エミュレータを立ち上げた状態で、再度コマンドを叩くとビルドされたアプリがエミュレータ上に表示されました。

f:id:kojirooooocks:20171228012957p:plain

とりあえず触ってみる

App.jsがメインのプログラムのようなので、こちらを修正してみます。

Welcome to React Native となっている部分を うぇるかむ とぅ りあくと ねいてぃぶ に変えてみます。

その後シミュレータ上でCmd+Rで画面更新してみると更新されました。

f:id:kojirooooocks:20171228013021p:plain

毎回ビルドしなくていいというのは、スピード感が失われないのでうれしいですね。

さらにCmd+Rを押さずとも自動リロード出来る機能もあるようです。

シミュレータ上でCmd+Dを押して Enable Hot Reloading を選択してください。

そうすると、同じように文章を変えると、Cmd+Rを実行しなくても画面が更新されるようになりました。

React-Native面白いですね!

もうちょっと触ってみます!

ではでは。