クロコめも2。

ただのめもー

vuexのソースを見てて理解できないとこ。その1

src/util.js

/**
* Deep clone an object. Faster than JSON.parse(JSON.stringify()).
*
* @param {*} obj
* @return {*}
*/

export function deepClone (obj) {
    if (Array.isArray(obj)) {
        return obj.map(deepClone)
    } else if (obj && typeof obj === 'object') {
        var cloned = {}
        var keys = Object.keys(obj)
        for (var i = 0, l = keys.length; i < l; i++) {
            var key = keys[i]
            cloned[key] = deepClone(obj[key])
        }
        return cloned
    } else {
        return obj
    }
}

for文の中にある「l」はなんの意味があるんやろうか?

    for (var i = 0; i < keys.length; i++) {

じゃあだめなんやろか?

パフォーマンスの問題かと思って50万回ぐらい実行したタイムを比較したら特に違いが見られなかったんだよねぇー

んで、書式的にもlが理解できない。

var でもない。let をつけたらコンパイルエラー。

何これ?

vuejsプロジェクト雛形を楽に作りたい

2日経つと前に何作ってたか忘れてまた最初からやりたくなる病。
そのために作り始めを早めるメモ

主に組み込むものとバージョン

  • node(v5.4.0)
  • npm(3.5.3)
  • vue(^1.0.14)
  • vuex(^0.2.0)
  • es6
  • mocha(2.3.4)
  • power-assert(1.2.0)

手順概要

  1. ディレクトリ用意
  2. いろいろインストール
  3. 環境に必要なもの用意
  4. 小さいvuexサンプル
  5. 最小テスト

手順詳細

ディレクトリ用意

# ディレクトリ作成
mkdir プロジェクトのフォルダ
cd プロジェクトのフォルダ
mkdir -p {build,test/{unit,e2e},src/{components,views/app,filters,directives,assets/{js,style,images}}}

いろいろインストール

npm init --y
npm i -g webpack webpack-dev-server mocha babel-core babel-loader power-assert
npm i -S vue vuex
npm i -D babel-core babel-loader webpack style-loader css-loader sass-loader html-loader mocha mocha-loader babel-plugin-add-module-exports babel-plugin-transform-runtime babel-polyfill babel-preset-es2015 babel-preset-stage-2 babel-runtime file-loader url-loader espower-babel power-assert

環境に必要なもの用意

.babelrc

{
  "presets": ["es2015", "stage-2"]
}

webpack.config.js

module.exports = {
    entry: {
        app: "./src/assets/js/app.js"
    },
    output: {
        path: './build/js',
        publicPath: '/js/',
        filename: "[name].js"
    },
    resolve: {
        alias: {
            vue: "vue/dist/vue"
        },
        modulesDirectories: ['node_modules', 'src/', 'src/assets/js/**', 'src/assets/style/**', 'src/views/**', 'src/directives/**', 'src/filters/**', 'src/components/**'],
 // require()するときに拡張子を省略可能にします
 extensions: ['', '.webpack.js', '.web.js', '.js']
 },
 devServer: {
            contentBase: "./build",
        },
 module: {
            preloaders: [],
            loaders: [
                {
                    test: /\.woff(2)?(\?v=[0-9]\.[0-9]\.[0-9])?$/,
                    loader: "url-loader?limit=10000&minetype=application/font-woff"
                },
                {
                    test: /\.png$/,
                    loader: "url-loader?limit=100000"
                },
                {
                    test: /\.jpg$/,
                    loader: "file-loader"
                },
                {
                    test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
                    loader: "file-loader"
                },
                {
                    test: /\.scss$/,
                    loader: "style!css!sass"
                },
                {
                    test: /\.html$/,
                    loader: "html"
                },
                {
                    test: /\.js(x?)$/,
                    loader: 'babel-loader?sourceMaps=true'
                }
            ]
        }
 };

package.jsonのscriptsの中身を書き換え

  "scripts": {
    "hot": "webpack-dev-server -d --inline",
    "test": "mocha --compilers js:espower-babel/guess test/unit",
    "e2e": "mocha --compilers js:espower-babel/guess test/e2e"
  },

上から、
npm run hotデバッグ用のサーバ起動するためのもの
npm testでtest/unit配下に格納したテストを実行するもの
npm run e2eでe2eテストに格納したテストが動くはず(今回はやらない)

小さいvuexサンプル

前段その1サーバ起動確認

build/index.html

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>sample</title>
</head>
<body>
    <div id="app">this is app</div>
    <script type="text/javascript" src="js/app.js" charset="utf-8"></script>
</body>
</html>

src/assets/js/app.js

console.log('hoge');

ここでコンソール上でnpm run hotを実行して下記URLで"this is app"、デバッグコンソールに"hoge"と表示されればここまでOK

http://localhost:8080/

前段その2小さいvueコンポーネント

build/index.html書き換え

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>sample</title>
</head>
<body>
<app></app>
<script type="text/javascript" src="js/app.js" charset="utf-8"></script>
</body>
</html>

src/assets/js/app.js書き換え

import Vue from 'vue'
import App from '../../views/app'

new Vue({
    el: 'body',
    components: { App }
});

src/views/app/index.js

export default {
    template: require('./template.html')
}

src/views/app/template.html

<div>this is app view</div>

http://localhost:8080/にthis is app viewと表示されていればOK

小さいvuexサンプル(vuexさん公式のcounterサンプルを丸パクリします)

src/views/app/index.js書き換え

import Counter from '../../components/counter'

export default {
    template: require('./template.html'),
    components: {
        "counter": Counter
    }
}

src/views/app/template.html書き換え

<counter></counter>

src/components/counter/index.js

import store from '../../assets/js/store'

export default {
    template: require('./template.html'),
    computed: {
        count () {
            return store.state.count
        }
    },
    methods: store.actions
}

src/components/counter/template.html

<div>
    Clicked: {{ count }} times
    <button @click="increment">+</button>
    <button @click="decrement">-</button>
    <button @click="incrementIfOdd">Increment if odd</button>
    <button @click="incrementAsync">Increment async</button>
</div>

src/assets/js/store/index.js

 import Vue from 'vue'
 import Vuex from 'vuex'

 Vue.use(Vuex);

 // mutation types
 // optional if you don't like constants.
 const INCREMENT = 'INCREMENT';
 const DECREMENT = 'DECREMENT';

 // root state object.
 // each Vuex instance is just a single state tree.
 const state = {
     count: 0
 };

 // actions are what components will be able to
 // call as store.actions.xxx
 // note these are not the final functions the
 // components will be calling.
 const actions = {

     // for simple actions that just dispatches a single mutation,
     // we can just provide the mutation type.
     increment: INCREMENT,
     decrement: DECREMENT,

     // for a normal action function, it always recieves the store
     // instance as the first argument, from which we can get the
     // dispatch function and the state object. Any additional
     // arguments will follow the store argument.
     incrementIfOdd: ({ dispatch, state }) => {
         if ((state.count + 1) % 2 === 0) {
             dispatch(INCREMENT)
         }
     },

     // Same thing for async actions.
     incrementAsync: ({ dispatch }) => {
         setTimeout(() => {
             dispatch(INCREMENT)
         }, 1000)
     }
 };

 // mutations are operations that actually mutates the state.
 // each mutation handler gets the entire state tree as the
 // first argument, followed by additional payload arguments.
 // mutations must be synchronous and can be recorded by middlewares
 // for debugging purposes.
 const mutations = {
     [INCREMENT] (state) {
         state.count++
     },
     [DECREMENT] (state) {
         state.count--
     }
 };

 // A Vuex instance is created by combining the state, the actions,
 // and the mutations. Because the actions and mutations are just
 // functions that do not depend on the instance itself, they can
 // be easily tested or even hot-reloaded (see counter-hot example).
 // 
 // You can also provide middlewares, which is just an array of
 // objects containing some hooks to be called at initialization
 // and after each mutation.
 export default new Vuex.Store({
     state,
     actions,
     mutations
 })

http://localhost:8080/にカウンターが表示されていればOK

最小テスト

test/unit/store-test.js

 import assert from 'power-assert';

 import store from '../../src/assets/js/store'

 describe('storeのテスト', () => {

     it('mutationsのincrement処理を実行するとstateのcountが増える', () => {
         let before = store.state.count;
         store.actions.increment();
         let after = store.state.count;
         assert(before < after);
     });

     it('mutationsのdecrement処理を実行するとstateのcountが減る', () => {
         let before = store.state.count;
         store.actions.decrement();
         let after = store.state.count;
         assert(before > after);
     });

 });

npm testで実行して結果が表示されればOK

assert(before === after)などに書き換えてエラーを出してみるのもよいかも

es6のファイル名問題。

es6でクラスを直接export defaultする場合、ファイル名はクラスと同じ名前にしておくべきか?

言い換えると「大文字で始まるファイル名にすべきか?」


今までのjsの文化ではファイル名は全て小文字始まりである。

理由はhtmlの世界は大文字小文字の区別がないからファイル名も小文字で統一しておく。
あとは「みやすさ」?


しかし昨今のjsはwebpackの利用が前提だったりで、その制約いる?みたいな状態でもある。

airbnbのes6コーディング規約では大文字始まりのキャメルケースらしい。


つか、そうしたい。

しかし、昔ながらのじゃばすくりぷたーさんが大文字始まりのファイル名は拒絶反応を起こしちゃう。


うーむ。

Nightmareさんとこをざっくり訳

英語わかんねっす。

Nightmare

Nightmareはハイレベルな自動ページ表示ライブラリーです。

達成したいことは、ネストが深く分かり難いコールバック地獄なしに下のことを実現する

  • シンプルに書ける
  • 同期処理っぽく書ける

これは、APIを持っていないサイトのE2Eテスト用に設計されています。

Electronベースでできてます。PhantomJSに似てますが、もっと早くてモダンです。

Daydream

Daydreamは@stevenmiller888さんによって作られた、Nightmareを使う際のお助けツール
chromeのエクステンションで、ブラウザ操作でNightmare用スクリプトを生成するものです。

Examples

yahooをサーチしてみよう

var Nightmare = require('nightmare');
var vo = require('vo');

vo(function* () {
    var nightmare = Nightmare({ show: true });
    var link = yield nightmare
        .goto('http://yahoo.com')
        .type('input[title="Search"]', 'github nightmare')
        .click('.searchsubmit')
        .wait('.ac-21th')
        .evaluate(function () {
            return document.getElementsByClassName('ac-21th')[0].href;
        });
    yield nightmare.end();
    return link;
})(function (err, result) {
    if (err) return console.log(err);
    console.log(result);
});

以下のコマンドで実行できます

npm install nightmare vo
node --harmony yahoo.js

もしくは mochaのテストを実行してみましょう:

var Nightmare = require('nightmare');
var expect = require('chai').expect; // jshint ignore:line

describe('test yahoo search results', function() {
    it('should find the nightmare github link first', function*() {
        var nightmare = Nightmare()
        var link = yield nightmare
            .goto('http://yahoo.com')
            .type('input[title="Search"]', 'github nightmare')
            .click('.searchsubmit')
            .wait('.ac-21th')
            .evaluate(function () {
                return document.getElementsByClassName('ac-21th')[0].href;
            });
        expect(link).to.equal('https://github.com/segmentio/nightmare');
    });
});

全機能の例はこっち
https://github.com/segmentio/nightmare/blob/master/test/index.js
※この例はmocha-generatorsを使ってるから注意してね
動かすには

  1. npm install
  2. npm test

API

Nightmare(options)

Web参照できる新しいインスタンスを生成します。
指定できるオプションはこれ
https://github.com/atom/electron/blob/master/docs/api/browser-window.md#new-browserwindowoptions

加えてnightmareのオプションとして

waitTimeout

.wait()が指定の時間までにtrueを返さないとexceptionを投げます。

paths

Electronが使用するデフォルトシステムパス。
下のようにして上書き可能

var nightmare = Nightmare({
    paths: {
        userData: '/user/data'
    }
});

switches

Chromeで使えるコマンドラインスイッチ Electronでサポートしているのは下の通り
https://github.com/atom/electron/blob/master/docs/api/chrome-command-line-switches.md

electronPath

Electronのバイナリを指定。別バージョンのElectronでテストしたいときに使う。

.useragent(useragent)

Electronで使われるuseragentを指定する

.end()

キューを完了し、Electronのプロセスを切断、終了します。

ページ操作

.goto(url

指定したURLのページをロードします。

.back()

バックします。

.forward()

進みます

.refresh()

ページをリフレッシュします。

.click(selector)

指定したセレクタでクリックイベントを発生させます。

.mousedown(selector)

指定したセレクタでマウスダウンイベントを発生させます。

.type(selector[, text])

指定したセレクタに文字入力する。空文字やfalseを設定すると対象のセレクタの値がクリアされる

.check(selector)

指定したチェックボックスセレクタのトグルを切り替える

.select(selector, option)

指定したdropdownセレクタのオプションを変える

.scrollTo(top, left)

スクロールする

.inject(type, file)

指定したフィアルをページにロードする。ロードできるのはjsかcss

.evaluate(fn[, arg1, arg2,...])

指定したfnを指定した引数で実行する。引数はオプショナル。実行終了後にfnの戻り値が戻り値となる。
情報を抜き取るときに便利

var selector = 'h1';
var text = yield nightmare
    .evaluate(function (selector) {
        // now we're executing inside the browser scope.
        return document.querySelector(selector).innerText;
    }, selector); // <-- that's how you pass parameters from Node scope to browser scope

.wait(ms)

指定したミリ秒待つ

.wait(selector)

指定したセレクタが存在するまで待つ(.wait('#pay-button'

.wait(fn[, arg1, arg2,...])

指定したfnがtrueを返すまで待つ

ページから情報を抜き取る

.exists(selector)

指定したselectorが存在するかどうか

.visible(selector)

指定したselectorが表示状態か非表示状態か

.on(event, callback)

ページのイベントを関ししてコールバックを呼ぶ。
.goto()を呼ぶ前に読んでおかないといけない
サポートしているイベントはここ
http://electron.atom.io/docs/v0.30.0/api/browser-window/#class-webcontents

追加されているページイベント

.on('page', function(type="error", message, stack))
このイベントはページで何かしらjavascriptのエラーが起きたときに発火します。
しかし、挿入されたjavascriptコードでは発火しません。(例 .evaluate())そういうときはただの例外

ページのイベント

window.addEventListener('error'), alert(...),prompt(...) とかconfirm(...)を監視します

.on('page', function(type="alert", message))

Nightmareではデフォルトでポップアップのwindow.alertが無効です。
しかし監視できます。

.on('page', function(type="prompt", message, response))

Nightmareではデフォルトでポップアップのwindow.promptが無効です。 しかし監視できます。
もし、異なるものをハンドリングしたい場合は独自のプリロードスクリプトを用意する必要があります。???なんのこっちゃ

.on('page', function(type="confirm", message, response))

Nightmareではデフォルトでポップアップのwindow.promptが無効です。 しかし監視できます。
もし、異なるものをハンドリングしたい場合は独自のプリロードスクリプトを用意する必要があります。???なんのこっちゃ

.on('console', function(type [, arguments, ...]))

typeにはlog,warnもしくはerror、そしてargumantsはコンソールから渡されてくるものです。

.on('console', function(type, errorMessage, errorStack))

このイベントはconsole.logがページ内で使われているときに発火します。しかし、injectされたscriptでは発火しません(例 console.log()の中で.evalute()

.screenshot([path][, clip])

現在ページのスクリーンショットを取ります。デバッグに便利。
出力はpngです。引数はどっちともオプショナルです。
pathが指定された場合、画像を指定のディレクトリに保存します。指定されてないと画像データのバッファを返します。
clipが指定された場合、下にもあるようにイメージが矩形にクリップされます。
https://github.com/atom/electron/blob/master/docs/api/browser-window.md#wincapturepagerect-callback

.pdf(path, options)

指定されたパスにpdfファイルを保存します。細かいオプションについての情報は下
https://github.com/atom/electron/blob/v0.35.2/docs/api/web-contents.md#webcontentsprinttopdfoptions-callback 

.title()

現在ページのタイトルを返却します。

.url()

現在ページのurlを返却します。

クッキー

.cookies.get(name)

指定した名前のクッキーを取得します。対象のurlは現在ページのurlです。

.cookies.get(query)

クエリを使って複数のクッキーを取得します。もしquery.nameが指定された場合、最初に見つかった値、もしくは配列を返します。   query.urlが指定されていない場合は現在urlになります

例)

// get all google cookies that are secure
// and have the path `/query`
var cookies = yield nightmare
    .goto('http://google.com')
    .cookies.get({
        path: '/query',
        secure: true
})

   使えるプロパティは下
https://github.com/atom/electron/blob/master/docs/api/session.md#sescookiesgetdetails-callback

.cookies.get()

現在ページのurlをすべて取得します。

.cookies.set(name, value)

現在ページのクッキーをセットします。

.cookies.set(cookie)

クッキーをセットします。もしcookie.urlが指定されてない場合、現在ページにセットします。
例)

yield nightmare
    .goto('http://google.com')
    .cookies.set({
        name: 'token',
        value: 'some token',
        path: '/query',
        secure: true
})

使えるプロパティ一覧は下
https://github.com/atom/electron/blob/master/docs/api/session.md#sescookiessetdetails-callback

.cookies.set(cookies)

一度に複数cookieをセットします。cookiescookieの配列です。   詳しくは.cookies.set(cookie)

Nightmareの拡張

Nightmare.action(name, action|namespace)

下の例のようにカスタムアクションをNightmareのプロトタイプに追加できます。

例)

Nightmare.action('size', function (done) {
    this.evaluate_now(function() {
        var w = Math.max(document.documentElement.clientWidth, window.innerWidth || 0)
        var h = Math.max(document.documentElement.clientHeight, window.innerHeight || 0)
        return {
            height: h,
            width: w
        }
    }, done)
})

var size = yield Nightmare()
    .goto('http://cnn.com')
    .size()

注意:これはstatic classに追加してます。Nightmareのインスタンスに追加してるわけじゃないです。
evaluate_nowという処理を読んでいます。これはnightmare.evaluateとは違います。
これは即時に実行されますが、nightmare.evaluateはキューにたまります。

忘れないための簡単な方法は:evaluateについて疑問におもったとき、もしカスタムアクションを作ってるときならevaluate_nowを使ってください
なぜならすでにキューイングされたものを処理している最中に、その処理をまたキューにためてはいけないからです

またカスタムのネームスペースを作ることもできます。
nightmare.cookies.get,nightmare.cookies.setなどで内部でも使っているように
アクションをまとめるのに便利です。しかしnightmareオブジェクトが乱雑になります。

例)

Nightmare.action('style', {
    background: function (done) {
        this.evaluate_now(function () {
            return window.getComputedStyle(document.body, null).backgroundColor
        }, done)
    }
})

var background = yield Nightmare()
    .goto('http://google.com')
    .style.background()

.use(plugin)

nightmare.useは一つのインスタンスでタスクを再利用するのに便利です。
nightmare-swiftlyでサンプルをチェックしてください
https://github.com/segmentio/nightmare-swiftly

Custom preload script

ウィンドウ環境起動時になにかしたいときは、プリロードスクリプトを定義できます。
どうやるかっていうと

var nightmare = Nightmare({
    webPreferences: {
        preload: custom-script.js
    }
})

このプリロードするスクリプトは下の記述を入れておく

window.__nightmare = {};
__nightmare.ipc = require('ipc');

使い方

インストール

NightmareはNodeのmoduleなのでNode.jsは入れておく。
インストールはnpm install で行う

$npm install --save nightmare

実行

Nightmareはnodeのスクリプトやmoduleから利用できる。

例)

var Nightmare = require('../nightmare');
var vo = require('vo');

vo(run)(function(err, result) {
    if (err) throw err;
});

function *run() {
    var nightmare = Nightmare();
    var title = yield nightmare
        .goto('http://cnn.com')
        .evaluate(function() {
            return document.title;
        });
    console.log(title);
    yield nightmare.end();
}

これはcnn.jsとして保存したら下のように実行する

npm install vo nightmare
node --harmony cnn.js

デバッグ

内部で何が起きてるかをしるために3つの方法があります。 1. DEBUG=*デバッグフラグ 1. Nightmareコンストラクタ{show:true}を渡すと実際にウィンドウを表示する 1. specific eventsを監視する(下URL参照)

https://github.com/segmentio/nightmare#onevent-callback

同じテストをデバッグモードで動かすときはDEBUG=nightmare node --harmony cnn.js
windowsなら(set DEBUG=nightmare & node cnn.js

そうすると追加情報が出力されます。

テスト

MochaとChaiを使ってテストします。どっちともnpmでインストールします。
nightmareのテストはmake testで実行します

テストできたら下のように見えます。

make test
    ․․․․․․․․․․․․․․․․․․
    18 passing (1m)

実践Node.jsプログラミングのマルチルームチャットアプリの構築を写経した時のメモ

世界にはまだまだ知らないことだらけ。Node.jsのサーバサイドについても把握せねば。  

環境

  • node v4.2.3
  • mime 1.3.4 (本では1.2.7)
  • socket.io 1.3.7 (本では0.9.6)

単純に動かなかった2点

1点目 chat_server.js l50~l60あたり

//var usersInRoom = io.sockets.clients(room);
// ↑だとclientsなんぞ知らん。というエラーが起きる
var usersInRoom = findClientsSocket(room);

// 以下のメソッドを別途追加
function findClientsSocket(roomId, namespace) {
    var res = []
        , ns = io.of(namespace ||"/");    // the default namespace is "/"

    if (ns) {
        for (var id in ns.connected) {
            if(roomId) {
                var index = ns.connected[id].rooms.indexOf(roomId) ;
                if(index !== -1) {
                    res.push(ns.connected[id]);
                }
            } else {
                res.push(ns.connected[id]);
            }
        }
    }
    return res;
}

2点目 chat_server.js l20~l30あたり

//socket.emit('rooms', io.sockets.manager.rooms);
// ↑だと「TypeError: Cannot read property 'rooms' of undefined」というエラーが起きる
socket.emit('rooms', io.sockets.rooms);

なぜ動かなかったか?

APIが変わったから。
どうやらsocket.ioの1.0になったときにいろいろ変わったらしいですね

ふぅ。

Server側復習

server.js => ほぼstaticなwebサーバに関する記述
chat_server => socket.ioに関するところだけ抜き出して意味を調べてみる

io = socketio.listen(server)

API docsより

 Server#listen
 Synonym of Server#attach.

 Server#attach(srv:http#Server, opts:Object):Server
 Attaches the Server to an engine.io instance on srv with the
 supplied opts (optionally).

engine.ioとはなんぞや?

https://github.com/socketio/engine.io

socket.io用のトランスポート層で双方向通信するための実装ということか?
これをhttpサーバにアタッチすることでリクエストをインターセプトする

io.set('log level', 1);

意図がわからない。log levelを1にするということはログを多くすることなのか?少なくすることなのか?ログを無くしたいのか?
そしてversion1.0以降はなにやら変わっているらしい。このログレベルの設定自体が不要なのか?

http://socket.io/docs/logging-and-debugging/
↓で何やら説明があった。
http://socket.io/docs/migrating-from-0-9/

Options like log-level are gone. ごーん

socket.emit('eventName', obj);

対象のsocketに対してイベントを発火させる。
渡したいものはobjに詰めておく

socket.join(room)

API docsより

Socket#join(name:String[, fn:Function]):Socket
Adds the socket to the room, and fires optionally a callback fn
with err signature (if any).

The socket is automatically a member of a room identified with its
session id (see Socket#id).

The mechanics of joining rooms are handled by the Adapter
that has been configured (see Server#adapter above), defaulting to
socket.io-adapter.

roomに追加してoptionのコールバックを発火。
roomとはなにものか?

デフォルトではセッションID名のroomに自動的に入れられているってことか

socket.leave

joinの反対で良い模様

socket.broadcast.to(room).emit('message', {...})

ブロードキャスト。

そして↓のように書いても動いた。
socket.to(room).emit('message', {...}
違いはなんだろう?

socket.on('イベント名', function(パラメータ){ハンドリング処理});

見ての通りのイベントハンドリング処理の模様

io.of(...)

カスタムのネームスペースを作れるらしい。
デフォルトは"/"

socket.id

API docsより

Socket#id:String
A unique identifier for the socket session, that comes from the
underlying Client.

はい。

Client側復習

io

おもむろに登場するグローバルオブジェクト。

socket.on('イベント名’, function(パラメータ){ハンドリング})

イベントのハンドリング

socket.emit('イベント名', パラメータ)

イベントの発火

クライアントはハンドリングと発火だけなのでシンプル

vuejsの作者さんが考えたvuejsにマッチするflux?

vuexを動かしてみる

動機:githubで見つけて単純にDLして動かそうとしたら動かなかったから

環境メモ

  • babelは5系
  • nodejsは4.2.2
  • npmは3.5
  • .vueファイルを使わない(ただの個人的な好み)

作業順

  1. ディレクトリを作る
  2. npm でいろいろインストール
  3. vuexをプロジェクトの中に
  4. exampleにあるcounterを移植して動かしてみる
  5. testを移植して動かしてみる

[ディレクトリを作る]

# ディレクトリ作成
mkdir プロジェクトのフォルダ
cd プロジェクトのフォルダ
mkdir -p build test src/{components,views,assets/{js,css}}

[npm でいろいろインストール]

# npm で必要なものをあつめてくる
npm init 
npm i -g webpack webpack-dev-server mocha babel-loader@^5.3
npm i -S vue 
npm i -D babel-loader@^5.3 webpack style-loader css-loader sass-loader html-loader mocha mocha-loader chai

[vuexをプロジェクトの中に]

今回は/src/assets/vuexというディレクトリを用意してその中にgithubから持ってきたvuexのsrc配下を入れる

[exampleにあるcounterを移植して動かしてみる]

build/index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>vuex counter example</title>
</head>
<body>
<counter></counter>
<script src="js/app.js"></script>
</body>
</html>

src/assets/js/main.js(vuexのexampleのままimportのパスを変えただけ)

import Vue from 'vue'
import Counter from '../../components/counter/index'

new Vue({
  el: 'body',
  components: { Counter }
})

src/assets/js/vuex.js(vuexのexampleのcounter内にあるvuex.jsをそのままもってきてimportのパスだけ変更)

import Vue from 'vue'
import Vuex from '../vuex'

~省略

src/components/counterにCounter.vueを分割して2ファイル
index.js

import vuex from '../../assets/js/vuex'

export default {
    template: require('./template.html'),
    computed: {
        count () {
            return vuex.state.count
        }
    },
    methods: vuex.actions
}

template.html

<div>
    Clicked: {{ count }} times
    <button @click="increment">+</button>
    <button @click="decrement">-</button>
    <button @click="incrementIfOdd">Increment if odd</button>
    <button @click="incrementAsync">Increment async</button>
</div>

サーバを起動して確認

webpack-dev-server -d --inline

http://localhost:8080/

[testを移植して動かしてみる]

testディレクトリにあるtest.jsをそのまま移植してimportのパスだけ直す

mocha --compilers js:babel-core/register

通りました。これでいろいろ試せるかな

見ないふりをしてたけど、eslintを入れて動かすとなんか警告が出る。けどなんの警告だかはわかってない

findConfigFileの引数が変わってます。

完全に元のエントリーの内容残って無いけど、うそんこタイトル残しとくよりマシかな!という判断で書き直し

20151124版のtypescript1.8.0-dev.20151124のfindConfigFileメソッドは第2引数にfileのexistsを確認するための関数を渡さなければいけなくなった。

※具体的にいつのリリースでそういう仕様になったかはわからないけれど、つい最近なんだろうなーと想像

 

そのために、既存のfindConfigFileを利用していたライブラリはそれに対応しないとエラーが発生するようになっている。

 

2015年11月24日時点でespower-typescript-babelを利用しようとすると当該のエラーにぶつかった。

 

findConfigFileメソッドを利用している箇所をルートパス+"tsconfig.json"をセットするように変更してしまえば動いた。けど、ちゃんと対応するなら第2引数にisExistsをわたしてあげないとか?

そもそも1.6系使えば動きそうだからわざわざ1.8の安定してないやつ使う必要がなくなったのでは?