クロコめも2。

ただのめもー

もやもやポイント書き出し

typescriptで開発する際、vueのdataプロパティをどう扱っていいのかわからない。
そのまま使うと動作はするがtypescript上で認識できないものにアクセスすることになるのでコンパイルでエラーが出る。
動いてるからエラーでは無く警告なのか?

なんとかしないと規模の大きい開発には耐えられない。
警告だらけで本当の問題が埋もれるから

TypeScriptでVue.jsを使うサンプル(前半)

【計画】前半と後半に分ける

前半

プロジェクトの作成と簡単なアプリ作成

後半

vue-router組み込み

主要な使用技術

  • TypeScript 1.6.2
  • vue 1.0.8
  • vue-router 0.7.5
  • vue-class-component 2.0.0
  • TypescriptのコンパイルオプションはES6

大まかな手順

  1. プロジェクト作成と環境の準備
  2. TypeScriptまわりの準備
  3. vueの簡単なサンプル作り
  4. vue-router挑戦

プロジェクト作成と環境の準備

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

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

# tsd で必要なものを集めてくる
tsd init
tsd install vue --save
tsd install require --save

# tslint用の定義ファイルをテンプレートからコピってくる。しかしこれ厳しすぎてつらいので適宜ゆるく改修する必要あり
cp ./node_modules/tslint-loader/node_modules/tslint/docs/sample.tslint.json ./tslint.json

webpack.config.jsの作成

module.exports = {
    entry: {
        app: "./src/assets/js/entry.ts"
    },
    output: {
        path: './build/js',
        publicPath: '/js/',
        filename: "[name].js"
    },
    resolve: {
        alias: {
            vue: "vue/dist/vue"
        },
        modulesDirectories: ['node_modules', 'src/', 'src/assets/js/', 'src/views/', 'src/components/'],
        // require()するときに拡張子を省略可能にします
        extensions: ['', '.webpack.js', '.web.js', '.ts', '.tsx', '.js']
    },
    devServer: {
        contentBase: "./build",
    },
    module: {
        preloaders: [
            {
                test: /\.ts$/,
                loader: "tslint"
            }
        ],
        loaders: [
            {
                test: /\.scss$/,
                loader: "style!css!sass"
            },
            {
                test: /\.html$/,
                loader: "html"
            },
            {
                test: /\.ts(x?)$/,
                loader: 'babel-loader?sourceMaps=true!ts-loader'
            }
        ]
    }
};

tsconfig.jsonを作成する

{
  "compilerOptions": {
    "module": "commonjs",
    "removeComments": true,
    "noImplicitAny": false,
    "target": "es6",
    "sourceMap": true,
    "experimentalDecorators": true,
    "preserveConstEnums": true
  },
  "exclude": [
    "node_modules",
    "build"
  ]
}

src/assets/js/にentry.tsを作成

console.log("hello");

/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>

ここで一旦、webpack-dev-serverを起動してプレビュー

起動コマンド : webpack-dev-server -d --inline
http://localhost:8080

コンソールにhelloと表示されたらここまでOK

横道:IntelliJ使ってる場合の設定

Preferences->Languages & Frameworks->JavaScript

Preferences->Languages & Frameworks->TypeScript

  • Enable TypeScript Compilerをチェックする(重かったらチェック外してもいいかも)
  • コマンドラインオプション --noImplicitAny --target ES6 --experimentalDecorators
  • generate sourceMapのチェックをはずす。

Preferences->Languages & Frameworks->TypeScript->Tslint

  • プロジェクトのtslint.jsonを設定する

横道から復帰

TypeScriptまわりの準備

typingsの中にあるvue.d.tsの中でexport =とある部分をexport defaultと書き換える
※es6を使うときに怒られちゃうからこれでごまかす。。。

独自定義ファイルディレクトリを作る

mkdir -p custom-typings/vue-class-component

custom-typings/vue-class-component/vue-class-component.d.tsを作る(これでいいのか不明)

declare module "vue-class-component" {
    let VueComponent:ClassDecorator;
    export default VueComponent;
}

custom-typings/custom.d.tsを作る

/// <reference path="vue-class-component/vue-class-component.d.ts" />

vueの簡単なサンプル作り

src/assets/js/entry.tsを書き換え

/// <reference path="../../../typings/tsd.d.ts" />
import Vue from "vue";
require("../sass/main.scss");
new Vue(require("../../views/app"));

src/assets/sass/main.scss作成(てきとー)

body {
  margin: 0px;
  padding: 0px;
}
.main-header {
  margin: 0px;
  padding: 3px;
  background-color: #fafafa;
  border-bottom: 1px solid #CCCCCC;
  text-align: center;
}

.main-footer {
  position: fixed;
  width: 100%;
  margin: 0px;
  padding: 3px;
  background-color: #fafafa;
  border-top: 1px solid #CCCCCC;
  bottom: 0px;
}

src/views/app/index.tsを作成

/// <reference path="../../../typings/tsd.d.ts" />
import Vue from "vue";
import Header from "../../components/header/index";
const app = new Vue({
    el: "#app",
    template: require('./template.html'),
    components: {
        'app-header': Header
    }
});

src/views/app/template.htmlを作成

<app-header></app-header>

src/components/header/index.tsを作成

/// <reference path="../../../typings/tsd.d.ts" />
/// <reference path="../../../custom-typings/custom.d.ts" />
import VueComponent from "vue-class-component";

@VueComponent
export default class Header {
    public static template:string = require("./template.html");

    public ready():void {
        console.log("hogehoge");
    }
}

src/component/header/template.html

<div class="main-header">
    <h3>TypeScript & Vue.js sample</h3>
</div>

サーバを起動して動作確認する。ヘッダーが表示されていればここまでOK

横道2 ChromeのVue Devtoolsについて

能力の無さは積極的にツールでカバーしたい。ということで便利ツールChromeプラグイン「Vue Devtools」
webpackの起動オプションに--inlineを設定してあったり、babel-loaderかますときにsourceMapつけてるのはこのツールを使えるようにするためです。
このプラグインのおかげで表示されているコンポーネントの情報をブラウザで実行時に見ることができます。
chrome上でブレイクポイント置いてステップ実行できるようになるものも相当嬉しい(それはVue Devtoolsの機能じゃないけど)

次の課題

  • TypeScriptだけどimport とかES6の書き方をしたいのでES6コンパイルする
  • けどBabelをかまして最終的にはES5でバンドルする
  • IDEでの自動コンパイルに頼らない
  • TypeScriptの利点を活かせるようにする(型とかIDEの入力補助とか)
  • ブレイクポイントしかけてステップ実行できるようにする
  • ChromeのVue用プラグインでちゃんと見れるようにする

TypeScriptでvue-class-componentを使ってみる

追記:書き直した->

http://kurokokoruru.hatenablog.com/entry/2015/11/14/131304

以下のこと書くメモ

  • Vue.jsでコンポーネントベースで開発する
  • TypeScriptでvue-class-componentを使ってみる
  • sass使う
  • gulpなしwebpackのみ

環境構築手順(mac) 2015/11/4

  1. ディレクトリを用意する
  2. npmの初期化&必要なものをインストール
  3. ビルドの設定
  4. ヘッダーコンポーネント定義&設置
  5. todoAppをクラスベースに書き換えると

vue-class-componentについて

ES7/TypeScript向けにクラスベースでコンポーネントを定義するためのもの
https://github.com/vuejs/vue-class-component

TypeScriptの1.5.0-alphaから利用可能になったDecoratorを利用している。

環境構築手順詳細(mac)2015/11/4

ディレクトリを用意する

mkdir プロジェクトのフォルダ
cd プロジェクトのフォルダ
mkdir build
mkdir -p  src/{components,views,assets,directives,filters}
mkdir src/assets/js

npmの初期化&必要なものをインストール

npm init
npm i -g webpack webpack-dev-server mocha typescript tsd tslint
npm i -S vue vue-class-component
npm i -D webpack style-loader css-loader sass-loader ts-loader html-loader typescript tsd tslint-loader mocha mocha-loader chai
cp ./node_modules/tslint-loader/node_modules/tslint/docs/sample.tslint.json ./tslint.json
// ターミナルで定義ファイルのインストール
tsd init
tsd install vue --save
tsd query require -so -a install

ビルドの設定

webpack.config.js

module.exports = {
    entry: {
        app: "./src/assets/js/entry.js"
    },
    output: {
        path: './build/js',
        publicPath: '/js/',
        filename: "[name].js"
    },
    devtool: "source-map", // source-mapを生成する
    module: {
        preloaders: [
            {
                test: /\.ts$/,
                loader: "tslint"
            }
        ],
        loaders: [
            {
                test: /\.scss$/,
                loader: "style!css!sass"
            },
            {
                test: /\.html$/,
                loader: "html"
            },
            {
                test: /\.ts$/,
                loader: "ts-loader"
            }
        ]
    },
    resolve: {
        // require()するときに拡張子を省略可能にします
        extensions: ['', '.js', '.scss']
    }
};

src/assets/js/にentry.tsを作成

console.log("hello");

/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>

webpack-dev-serverを起動してプレビュー
http://localhost:8080/webpack-dev-server/

// コンソールで"hello"の文字が出力されてればここまでOK

ヘッダーコンポーネント定義&設置

src/assets/js/entry.tsを書き換え

/// <reference path="../../../typings/tsd.d.ts" />    
import Vue = require('vue');
require('../sass/main');
new Vue(require('../../views/app'));

src/assets/sass/main.scss作成(てきとー)

body {
  margin: 0px;
  padding: 0px;
}
.main-header {
  margin: 0px;
  padding: 3px;
  background-color: #fafafa;
  border-bottom: 1px solid #CCCCCC;
  text-align: center;
}

.main-footer {
  position: fixed;
  width: 100%;
  margin: 0px;
  padding: 3px;
  background-color: #fafafa;
  border-top: 1px solid #CCCCCC;
  bottom: 0px;
}

src/views/app/index.tsを作成

/// <reference path="../../../typings/tsd.d.ts" />
import Vue = require('vue');
var app = new Vue({
    el: "#app",
    template: require('./template.html'),
    components: {
        'app-header': require('../../components/header').Header
    }
});

src/views/app/template.htmlを作成

<app-header></app-header>

src/components/header/index.tsを作成

/// <reference path="../../../typings/tsd.d.ts" />
import VueComponent = require('vue-class-component');

@VueComponent
export class Header {
    static template = require("./template.html");

    ready() {
        console.log('hogehoge');
    }
}

src/component/header/template.html

<div class="main-header">
    <h3>TypeScript & Vue.js sample</h3>
</div>

ここでプレビュー画面にヘッダーが表示されてればOK.

あとのコンポーネント追加は同じことの繰り返しなので割愛。

todoAppをクラスベースで書き換えると

先日エントリのTodoアプリのコンポーネントかつクラスベース版

/// <reference path="../../../typings/tsd.d.ts" />
import VueComponent = require('vue-class-component');


interface ITask {
    body: string;
    done: boolean;
}

@VueComponent
export class TodoApp {
    tasks:ITask[];

    newTaskBody = '';

    static template = require('./template.html');

    data() {
        return {
            tasks: this.tasks,
            newTaskBody: this.newTaskBody
        }
    }

    get computedDoneCount() {
        return this.getDoneCount();
    }

    created () {
        this.tasks = [
            {body: 'do this 1', done: false},
            {body: 'do this 2', done: false},
            {body: 'do this 3', done: false},
            {body: 'do this 4', done: false}
        ]
    }

    addNew():void {
        console.log("addNew");
        var task:string = this.newTaskBody && this.newTaskBody.trim();
        if (!task) {
            return;
        }
        this.tasks.push({body: task, done: false});
        this.newTaskBody = '';
    }

    deleteTask(delIndex:number):void {
        console.log("deleteTask");
        this.tasks.splice(delIndex, 1);
    }

    deleteDone():void {
        console.log("deleteDone");
        var oldTasks = this.tasks;
        this.tasks = [];

        oldTasks.forEach(task => {
            if (!task.done) this.tasks.push(task);
        });
    }

    getDoneCount():number {
        var count = 0;
        this.tasks.forEach(task => {
            count += task.done ? 1 : 0;
        });
        return count;
    }
}

うーん。良い。

定義ファイル(d.ts)について

d.tsファイルの作り方がよくわからない。

declare module "vue-class-component" {
    var VueComponent:ClassDecorator;
    export default VueComponent;
}

このようなものを用意したが、export defaultはES6の書き方だ。。
他にもTypeScriptがらみでintellij上で怒られるのを回避しないと行けないので課題多し。

あと、実際にやってから一週間ほど経っているのでもうすでに情報が古いかも。。

追記

  • そのままやるとbabelの6が入ってしまって動かない
  • tsのコンパイルIntelliJがやっててそれで動いてたみたい。ほかのエディタじゃ動かないはず
  • tsconfig.jsonとか用意しないとね!
  • 一応拡張子はtsだけど、とくにTypeScriptの利点が生かされてない

ってことで近いうちに書き直す

もやもやポイント書き出し

今週分のもやもや

  • export, import, requireまわりが混乱してきた。整理したい。ES6, commonjs, amd, TypeScript(内部,外部)
  • TypeScriptを使ったVue.jsのコンポーネント化が思ってたよりうまくいかない。整理したい
  • Vue.jsはES6で書いたほうがすっきりしてて良いのかも・・・でも型ほしい。ES7かな・・・いやいやいや
  • lodashとかいうjsの便利屋さんが学習でソース読むのによさそーだなー
  • vue-class-componentなどというVue.js作者さんが書いたらしきものがヒントにならないかなぁー。

vuejs/vue-class-component · GitHub

はー。。。ハロウィンなのに外出するのヤダなぁ。
仮装して気が大きくなっちゃってる変な人に絡まれませんように

TypescriptとVue.js 1.0系とwebpackと

TypeScriptプロジェクト作成メモ

このメモについて

まだtypescriptについての予備知識はないので基本トライアンドエラー

最終目標目標設定

以下の条件の最小限の開発環境の準備

  • typescript
  • vue.js 1.0系
  • webpack
  • sass

プロジェクトのフォルダ作成

mkdir プロジェクトのフォルダ
cd プロジェクトのフォルダ
mkdir build
mkdir -p  src/{components,views,assets,directives,filters}
mkdir src/assets/js

npmの初期化と準備

npm init
npm i -g webpack webpack-dev-server mocha typescript tsd
npm i -D webpack style-loader css-loader sass-loader ts-loader tsd tslint-loader mocha mocha-loader chai vue html-loader
cp ./node_modules/tslint-loader/node_modules/tslint/docs/sample.tslint.json ./tslint.json
// ターミナルで定義ファイルのインストール
tsd init
tsd install vue --save
tsd query require -so -a install

簡単なビルドができるようになるために

webpack.config.jsを作成

module.exports = {
    entry: {
        app: "./src/assets/js/entry.js"
    },
    output: {
        path: './build/js',
        publicPath: '/js/',
        filename: "[name].js"
    },
    devtool: "source-map", // source-mapを生成する
    module: {
        preloaders: [
            {
                test: /\.ts$/,
                loader: "tslint"
            }
        ],
        loaders: [
            {
                test: /\.scss$/,
                loader: "style!css!sass"
            },
            {
                test: /\.html$/,
                loader: "html"
            },
            {
                test: /\.ts$/,
                loader: "ts"
            }
        ]
    },
    resolve: {
        // require()するときに拡張子を省略可能にします
        extensions: ['', '.js', '.scss', '.ts']
    }
};

src/assets/js/にentry.tsを作成

console.log("hello");

ターミナルでwebpackを実行 -> build/jsにapp.jsができていることを確認する
/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>

webpack-dev-serverを起動してプレビュー url: http://localhost:8080/webpack-dev-server/

webpack-dev-server --content-base build/ -d
// コンソールで"hello"の文字が出力されてればここまでOK

vue.jsを入れてってみよう

src/assets/js/entry.tsを書き換えてwebpack-dev-serverでプレビューしてみる

/// <reference path="../../../typings/tsd.d.ts" />
console.log("hello");

var Vue = require('vue');

new Vue({
    el: "#app",
    data: {
        msg: "message"
    },
    template: "<p>hoge{{msg}}</p>"
});

TODOアプリがちゃんと動くようにする

IntelliJ->Preferences->Languages & Frameworks->Typescript->command line options

ここに「--sourcemap --noImplicitAny --target es5 --module commonjs」と入れる

Error:Error has occurred in the compile process TypeError: host.fileExists is not a function
このエラーがどうやっても取れない。どうすればいいんだろう???????
IntelliJ->Preferences->Languages & Frameworks->Typescript->Compiler version をカスタマイズすると下の問題に遭遇することがあるようです。
当方はtypescript をbundleされている1.4から1.6.2にあげたら起きました。

github.com

TODOアプリの実装をする(コピペするだけ)

build/index.html

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>sample</title>
    <style>
        .done-true{
            color: gray;
            text-decoration: line-through;
        }
    </style>
</head>
<body>
    <div class="todoapp">
        <h1>vue & typescript</h1>
        <p>Finish Task: {{getDoneCount}} / {{ tasks.length}}</p>
        <button @click="deleteDone">Delete Finished</button>
        <ul>
            <li v-for="task in tasks">
                <input type="checkbox" v-model="task.done" />
                <span class="done-{{task.done}}">{{$index}}:{{task.body}}</span>
                <a href="#" @click="deleteTask($index)">[x]</a>
            </li>
        </ul>
        <form onsubmit="return false;" @submit="addNew">
            <input type="text" v-model="newTaskBody" />
            <input type="submit" value="add">
        </form>
    </div>
    <script type="text/javascript" src="js/app.js" charset="utf-8"></script>
</body>
</html>

src/assets/js/entry.ts

/// <reference path="../../../typings/tsd.d.ts" />

import Vue = require('vue');

interface ITask {
    body: string;
    done: boolean;
}

class TodoApp extends Vue {
    tasks:ITask[];

    newTaskBody = '';

    constructor() {
        super(false);

        this._init({
            el: '.todoapp',
            data: {
                tasks: this.tasks,
                newTaskBody: this.newTaskBody
            },
            computed: {
                getDoneCount: this.getDoneCount
            },
            created: () => {
                this.tasks = [
                    {body: 'do this 1', done: false},
                    {body: 'do this 2', done: false},
                    {body: 'do this 3', done: false},
                    {body: 'do this 4', done: false}
                ]
            },
            methods: {
                addNew: this.addNew,
                deleteTask: this.deleteTask,
                deleteDone: this.deleteDone
            }
        });
    }

    addNew():void {
        console.log("addNew");
        var task:string = this.newTaskBody && this.newTaskBody.trim();
        if (!task) {
            return;
        }
        this.tasks.push({body: task, done: false});
        this.newTaskBody = '';
    }

    deleteTask(delIndex:number):void {
        console.log("deleteTask");
        this.tasks.splice(delIndex, 1);
    }

    deleteDone():void {
        console.log("deleteDone");
        var oldTasks = this.tasks;
        this.tasks = [];

        oldTasks.forEach(task => {
            if (!task.done) this.tasks.push(task);
        });
    }

    getDoneCount():number {
        var count = 0;
        this.tasks.forEach(task => {
            count += task.done ? 1 : 0;
        });
        return count;
    }
}

export var todoApp = new TodoApp();

webpack-dev-serverを起動して動作を確認する -> 動いていればOK

これからのこのメモに関するTODO

  • モジュールをちゃんと分割してみる
  • sassの設定
  • ハシゴはずされるかもしれないけどvue-routerでroutingしてみたい