SPAJAM 2019 東京A予選 - ハッカソン戦記

モバイルアプリ・ハッカソンの SPAJAM 2019 東京A予選に参戦してきました。
2日間で開催され 24時間でのアプリ開発、本選をかけての熱いハッカソンの戦記です。

いろいろなアウトプットがある中で、ハッカソンはとても好きです。
アイデア、企画設計、開発、テスト・デバッグ、プレゼンテーション、文章、チームワーク、判断に決断などと、とても多くのスキルを限られた時間の中で全力投入して形を作り上げ、それを強力なライバルたちと競い、そして打ち解けあう。

自分が発想をできないようなアイデアを見せてもらったり、新しい技術を教えてもらったり、同じ技術でも使い方使いこなしを目の当たりにしてと、多くのことを学ばせてもらえます。

また、自分自身のスキル点検というかテストを受けるよう気持ちもあります。アイデアは衰えてないか、柔軟さを失ってないか、技術力はキープできてるか、新しいものを取り込めているかなど。

そんな楽しい時間を、今回は SPAJAM という全国の猛者が競い合うハッカソンで過ごさせてもらいました。どんなイベントなのか、どんな戦いだったのかを綴ります。

とても素晴らしいイベントなので興味を持っていただければと、そしてぜひ参加して熱い戦いを楽しみましょう。

SJAPAM 概要

SPAJAM とは “「温泉でハッカソン」を合言葉に、スキルを向上するための競技と交流の場を提供する国内最高峰のハッカソン - What’s SPAJAM“ です。

“国内最高峰” を謳うとおり、日本全国で予選を行い、予選を勝ち抜いたチームで本戦を行うという大規模なイベントです。2019年は全国6地域7会場で予選が行われています。

本戦入賞者には豪華な賞品が出ます!
2019年の最優秀賞は「深セン・上海スペシャルツアー」!!
副賞多数も多数で去年の実績は、こちら 結果発表 | SPAJAM2018公式サイト – 温泉でハッカソン

ハッカソンのテーマは当日発表され、そこから24時間かけてのハッカソン勝負となります。主なスケジュールは下記。
1日目

  • 開場: 開会(挨拶・趣旨説明・テーマ発表)
  • 午前: アイデアソン
  • 午後: ハッカソン

2日目

  • 午前: ハッカソン
  • 午後: プレゼン、審査&表彰式

メンバーは1チーム5人までで、自分たちでチームを作って参戦します。
あらかじめチームを作って参加できるので安心です。

なお、本選出場とならないと「温泉」は楽しめません。
予選は協賛企業さんのオフィスを使わせていただいての開催となります。今回の東京A予選は 株式会社gumi さん、新宿中央公園のお隣でした。

参戦経緯

もちろん、ハッカソンが好きで楽しみたいから!
というものありますが 2018年 SPAJAM 東京D予選で優秀賞を手にするも、本選出場ならずのリベンジ戦でもあります!!

その際の発表資料と作品は、こちら。

発表資料: リアルタイムの競演と参加型観戦で音楽を最高に楽しむ「ラップ、タップ、アップ🎶」

作品: ラップ、タップ、アップ 🎶

そしてなんと、2018 東京D予選で最優秀賞を取った方が、メンバー構成は違えど今年も同じ会場という偶然。
さらに、現在私が受けているブログメンタリングの同期さん(他のハッカソンで優勝経験あり)も同じ会場!
強敵すぎるライバルたちとの、熱いハッカソンとなりました。

そして我らがチーム。

2人。。。そう、2人、通称「ねこ」と「うさぎ」で参戦です。
ふざけてたり、あまくみてるわけでなく。年明けからチーム編成とか、とくにデザイナーさんを探そうとか、たくさん考えていました。ところが「イベントでの発表の連続」「仕事のピーク」「ブログメンティー開始」とか、いろいろしてたら〆切前日💦

でも「あきらめたらそこで試合終了だよ」ですよね。たとえ2人でも本戦、全国制覇の夢はあきらめられません!参戦です!!

テーマ


テーマ「NEWS」。毎度ざっくりではありますが、今回は硬い。硬いなぁ。
聞いた瞬間に一瞬眩暈がしました。難しい。

なお、テーマについての説明はありません。
どうとらえ、どのような作品にするのかは参加者にゆだねられています。審査項目の1つ “テーマ性(テーマに沿って利用者と共感できる価値を提供できているか)- 予選概要 | SPAJAM2019“ がありますが、説明できればオーケーです。

今回、アイドルグループも連想されますが審査基準を満たせればオーケーだと思われます。実際に2018年本戦では “モビリティというテーマから、調味料が動くに発想を転換した点 - 結果発表 | SPAJAM2018“ 他が評価され審査員特別賞を受賞されたチームがありました。

某アイドルグループの連想、やってみたかったですが詳しくないのでやめました。今考えても、いいアイデアが浮かばないので、ムリは良くないです。ハッカソン中の中間インタビューでは「某アイドルグループ」のアイデアを考えているチームもありましたが、最終発表では発表がなかったので断念してしまったのかな。見たかったので残念です。

アイデアソン

まずは全体でアイデアソンです。みんなで、いろいろなアイデアを出して思考の枠を広げます。こちらのフレームワーク、すごくいいですよね。

アイデアソンは各チームのテーブルで行い、時に他チームのアイデアを見て歩くようなスタイルで行いました。

最初に、この1日で心が動いたことについてのワーク。
あったことを書き、その時の感情を顔の絵文字キャラで表現し、気持ちを書く。何人か発表して共有。

私は「今朝、熊野神社でお参りをした。」「ハッカソンではやる気持ちが静まり落ち着いた。」でした。顔の絵文字キャラは、絵心がなさ過ぎてヤバいものを書いてしまいました。ヤバすぎるので貼るのはやめておきます。

続いてテーマ「ニュース」について、イメージや思いついたことなどを付箋で書き出し。その後、各チームのテーブルを回って共有。ニュース番組名や話題の人などいろいろ出ていました。

もう一度最初のワークのフォーマットで、テーマについて喜怒哀楽の表現。それをもって、4人の参加者とアイデア交換&インタビュー。皆さん色々な発想をされていてすごかったです。「協賛の 株式会社エーアイ さん、提供の 高品質音声合成エンジンAITalk® + Vtuber」といったアイデアなども出ていました、

そこから上記スライドのフォーマットで、より具体化。発表して共有という流れになります。

まずは「共創」の考えで、たくさんのアイデアが出ますのでアイデアが浮かばずに終了ということはないので、安心して参加できます。

去年はアイデアソンの時間はチームとばらけて作業し、できあがった資料や喜怒哀楽シートなどが張り出されていましたが、今年はチームでやり張り出しはなかったです。張り出したほうがいいのに、どうしたんだろう。聞けばよかった。(実際に去年の予選で優秀賞とったチームが、最後まで張り出しをみて悩んでギリギリでアイデアが作れたと発表してました)

ハッカソン!

アイデアソンが終わったらハッカソン開始。ここからは「競争」です。
ちょうど、お昼なので各チーム食事へ向かいながらアイデアを検討。私たちもファミレスに入ってアイデア練り。幸いにも昼食時点で方向性も決まり、会場に戻ってすぐにコーディングへ。2人だから、どんどん進めないと間に合いません。

午後はひたすらコーディングを進めましたが、何も準備してないし、人手も足りないのでベース部分の作成で時間をとってしまいました。基本的には慣れている Nuxt.js PWA と AWS Lambda で Web API の構成です。

開場は20時まで、それ以降はメンバーの家なり宿泊施設なりで作業です。複数日での開催は宿泊をどうするのか考えておく必要があります。また SPAJAM 本戦は会場が宿泊施設で主催者提供です。(♨ 温泉いいなぁ!)

予想されていたことではありましたが、結局徹夜。ちゃんと寝ないと体にこたえるので良くないのだけど。

意識高いポッドキャストで、意識(こっちの場合は睡眠不足による意識不明のほうの意識)を失わないよう高める感じで。とても面白かったです。夜間の開発とかはポッドキャスト(ラジオがやってるならラジオも)が良さそうです。

そして夜明け頃に問題発生!肝心の機能が AWS Lambda で処理しきれない😱
各種パラメーターを最大にしても時間内に処理が終わらない。急遽 DynamoDB Streams を併用する方向に転換するも、こちらも処理しきれず。

やりたかったことは形態素解析です。処理能力不足なのか、情報量が多いのか、設定が悪いのか、とにかく処理時間がかかって機能させられませんでした。タイミング的にも、人手的にも厳しい状況でした。悔しいな。(今後、ちゃんと調査して再実装します。お金の力でクラウドとかに逃げる手もありますが、それだとサービスの継続が厳しいので難しいところです。)

この時点でプレゼン資料は未着手なので、泣く泣く私はプレゼン側へ作業転換となりました。まだ他にも必要な機能が残っており「うさぎ」1人での実装では間に合わない部分が出てしまうことに。

発表!「📰NEWʑ Link」!!

できあがった作品は「パーソナルニュースの配信と交換によって爆速で仲良くなるアプリ「📰NEWʑ Link」」です!!

https://riotz.works/slides/2019-spajam-qualification

テーマ「NEWS」に対して、私たちは「個人がニュースを発信し、嬉しい楽しいの思いを広げてつなげていきたい」というコンセプトに設定しました。「1億総メディア時代」個人がニュースを発信、その中でもパーソナルで嬉しくなるニュースを扱いたいと考えました。

スライド2ページ目ですが、ここ1週間のことなのですが、見ててほっこり、見てるこっちに嬉しくなるような TL が流れてました。世の中には楽しいパーソナルなニュースがあふれています。

そのニュースをつなげ、嬉しい楽しいの輪が広がり、そしてまた新しいニュースが生み出され発信されていく。そんな世界観を作り出すアプリです。

そしてお互いの興味が重なる部分、それが共感であり、人は同じようなことに共感している人と仲良くなりやすいというのがあります。そこに着目し、お互いのニュースを交換し共通点を見つけ出して表示することで親密度を上げらられ、仲良くなるきっかけを作り出せるのではないかと考えました。

こちらは、まだ打ち解けきれてないところで使ったり、グループに初対面の人が入った時や、会議や研修のアイスブレイクなどを想定しています。このニュースの交換は当初 Web NFC を使う予定でしたが、時間切れとなってしまい QR コードとなりました。
(ちょうど別件で Web NFC が欲しくて勉強する予定でしたが、ぶつけ本番となってしまった)

アーキテクチャは以下ですが、詳細記事を書きます。

  • Nuxt.js の PWA。JAMStack になります。
  • サーバーサイドは AWS で、サーバーレス(API Gateway, AWS Lambda, DynamoDB)。

審査&結果発表

残念ながら、受賞には至りませんでした。

「NEWS」というテーマにアイデアが追い付かなかったのと、技術の引き出し不足でした。

何とかアイデアを絞り出しアプリを作ったものの、こうして振り返ると「個人がニュースを発信したくなる」としているのに、その仕掛けが入っていないと、すぐに思いつくレベルで練り切れてないのがわかります。いろいろなテーマに対しても柔軟に対応できる発想力をもっと鍛えねば。。。

最優秀賞は、去年の同じ予選の覇者さん。もう3回連続で予選最優秀賞なのだとか。すごい!!

そしてブログメンティーの同期さんは、優秀賞。すばらしい!!さすがハッカソンでの優勝経験者。すごいです。

また、他の参加者さんもステキな作品ばかりで、アイデアも実装力も感心感嘆するばかりでした。またどこかのハッカソンでお手合わせしましょう。2日間、ありがとうございました。

東京A予選 -結果発表 | SPAJAM2019公式サイト – 温泉でハッカソン

写真


左上、新宿中央公園と三連ビル。
中央、勝利のビル!?見た時に勝利を確信したけど、みんな見てるって。
右上、会場案内。
下段、協賛のモンテールさん提供のお菓子。ごちそうさまでした!


最高に楽しい2日間!!。レベルの高い人たちと戦うことができ楽しかったし、賞は取れなかったけど、現在持てる力はすべて投入できました。

反省点は多々あるものの、総じて満足のいく結果でした。
また次のハッカソンで反省点を活かし、さらなる技術力をつけて来年の SPAJAM にも挑戦します!!

ぜひハッカソンの場で一緒に楽しみましょう♪

また、アプリは早めに改修を終えて公開できるようにします。
技術パートについては、ブログにします。


ところで、そもそも、このアプリ、ハッカソンへ参加する前に必要だったのではないだろうか 🤔
爆速で仲良くなって、SPAJAM のメンバーになってもらうために。。。 😇

TypeScript プロジェクト用サンドボックスで簡単コード検証

TypeScript を使うと JavaScript のコードに型を導入することができ、コードの安全性や生産性を高めることができます。いっぽうで TypeScript は JavaScript にコンパイルしてから実行する必要があり、ちょっとしたコードの確認をするにしても一手間かかります。
そこで検証を簡単にするため、プロジェクト内のファイル変更を監視し、ファイルの変更があったら自動的に再コンパイル&実行してくれる環境を作ります。

TypeScript では、ちょっとしたコードを試したいときでも tsc でコンパイルして node で実行してと、コンソールでの作業が必要となります。コードの動きを試したかったり、ネットで見つけたおもしろいコードを動かしたいといっただけでも手間がかかります。
今回は TypeScript 用のサンドボックス・プロジェクトを用意して、手軽にコードを試せるようにします。(サンドボックスというとセキュリティモデル的な感じもしますが、今回はお砂場遊び的な感覚。最近はプレイグラウンドと言う?)

環境
本記事の開発環境は以下となります。

  • Windows 10 64bit + WSL Ubuntu 18.04.1 LTS
  • Visual Studio Code
  • Node.js 12.2.0
  • Yarn 1.15.2
  • TypeScript 3.4.5

TypeScript プロジェクトの作成

いろいろなモジュールが組み合わさるので、順を追って作っていきます。
まずは基本となる TypeScript プロジェクトを作成します。

プロジェクトのディレクトリを作成し /package.json ファイルを作ります。(今回は sandbox という名前のディレクトリ)

  • devDependencies は、常に最新版のモジュールを使いたいためにバージョンは "*" としています。
  • scripts: clean は、最新のモジュールを使いやすいように node_modules などを削除します。OS に依存しないよう npx rimraf で削除しています。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    {
    "name": "sandbox",
    "private": true,
    "dependencies": {},
    "devDependencies": {
    "@types/node": "*",
    "typescript": "*"
    },
    "scripts": {
    "clean": "npx rimraf yarn.lock node_modules dist",
    "setup": "yarn clean && yarn install --ignore-optional"
    }
    }

同じくプロジェクト直下に tsconfig.json を作ります。
こちらは普段使う TypeScript の設定に合わせておくとよいでしょう。今回は esnext で最新仕様を使い、"strict": true でチェックを厳しくしています。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
{
"compilerOptions": {

/* Basic Options */
"target": "esnext", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
"module": "esnext", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
"outDir": "./dist", /* Redirect output structure to the directory. */
"removeComments": true, /* Do not emit comments to output. */

/* Strict Type-Checking Options */
"strict": true, /* Enable all strict type-checking options. */
"noUnusedLocals": true, /* Report errors on unused locals. */
"noUnusedParameters": true, /* Report errors on unused parameters. */
"noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
"noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */

/* Module Resolution Options */
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
"baseUrl": "./", /* Base directory to resolve non-absolute module names. */
"paths": { "~/*": [ "./src/*" ] }, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */

/* Experimental Options */
"experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
"emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */

/* Additional Options */
"forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */
"newLine": "LF", /* Use the specified end of line sequence to be used when emitting files: "crlf" (windows) or "lf" (unix). */
"resolveJsonModule": true, /* Allows for importing, extracting types from and generating .json files. */

/* Specify library files to be included in the compilation. */
"lib": [
"esnext"
]
},
"include": [ "./src/**/*.ts", "./test/**/*.ts" ]
}

これで /src ディレクトリ以下の TypeScript ファイルをコンパイルして実行する環境ができました。
/src/index.ts

1
console.debug('Hello World !');

コンパイルして実行。

1
2
3
$ yarn tsc -p .
$ node dist/index.js
Hello World !

ts-node で TypeScript を直接実行

コンパイルの手間をなくして、TypeScript をコンソールから直接実行できるようにします。

1
$ yarn add -D ts-node@* tsconfig-paths@*

/package.json に以下の start スクリプトを追加します。(scripts のみ抜粋)

  • ts-nodesrc/index.ts を実行。(以降、プログラム実行の起点は index.ts になります)
  • TypeScript の alias path を ts-node で使えるように -r tsconfig-paths/register を登録します。
    1
    2
    3
    4
    5
    6
    7
    {
    "scripts": {
    "clean": "npx rimraf yarn.lock node_modules dist",
    "setup": "yarn clean && yarn install --ignore-optional",
    "start": "ts-node -r tsconfig-paths/register src/index.ts"
    }
    }

実行!新たに1行追加しましたが tsc なしで動作します。また先ほど作られた dist ディレクトリは削除して大丈夫です。
/src/index.ts

1
2
console.debug('Hello World !');
console.debug('Hello TypeScript !!');
1
2
3
$ yarn start
Hello World !
Hello TypeScript !!

nodemon でファイルの変更を検知して TypeScript を直接実行

さらにファイルの変更を検出して、自動的に ts-node を実行できるようにします。

1
$ yarn add -D nodemon@*

/package.jsonstart スクリプトを修正します。(scripts のみ抜粋)

  • nodemon -w src で、nodemon から起動し src ディレクトリを監視します
  • -x ts-node で、nodemon から実行するプログラム ts-node を指定しています
    1
    2
    3
    4
    5
    6
    7
    {
    "scripts": {
    "clean": "npx rimraf yarn.lock node_modules dist",
    "setup": "yarn clean && yarn install --ignore-optional",
    "start": "nodemon -w src -x ts-node -r tsconfig-paths/register src/index.ts"
    }
    }

実行!今回は yarn start から先に始めます。
先ほどまでの Hello World, TypeScript がコンソールに表示され、その後に nodemon が変更待ちをしている状態になります。

1
2
3
4
5
$ yarn start
...(省略)
Hello World !
Hello TypeScript !!
[nodemon] clean exit - waiting for changes before restart

ここで /src/index.ts を変更し、保存します。

1
2
3
console.debug('Hello World !');
console.debug('Hello TypeScript !!');
console.debug('Hello Sandbox Project !!!');

するとファイルの変更を検知して、コンソールに新しい結果が自動的に出力されます。

1
2
3
4
5
6
...(省略)
[nodemon] restarting due to changes...
[nodemon] starting `ts-node -r tsconfig-paths/register -r dotenv/config src/index.ts`
Hello World !
Hello TypeScript !!
Hello Sandbox Project !!!

コードの検証で利用

実際にコードの検証で使ってみます。

最近気になったのが、こちら ソート可能なUUID互換のulidが便利そう - Qiita の記事。
よく UUID v4 でランダムな ID 発行を使っています。それに対してソート可能なランダムな ID 発行できるというのは興味深いです。記事は Python ですが Node.js/TypeScript にも ulid/javascript モジュールがあります。

モジュールを導入し、サンドボックス・プロジェクトを起動しておきます。

1
2
3
$ yarn add uuid ulid
$ yarn add -D @types/uuid
$ yarn start

単なるコンソール出力ですがコードを書いて保存するだけで結果が見れます。

1
2
3
4
5
import { ulid } from 'ulid';
import * as uuid from 'uuid';

console.debug(`ULID: ${ ulid() }`);
console.debug(`UUID: ${ uuid.v4() }`);

ランダムな値を生成するので保存するたびに変わるのがわかります。
“また UUID との 128 ビット互換性” ですが、表現方法は異なるようです。ソートの必要があるかはシステム次第ですが、文字数が詰まるのは良さそうです。また TypeScript 対応されているのも Good!

1
2
ULID: 01DB2S8RCY1EM7XBJG6P6A2KFE
UUID: 67d6e72b-97d9-4a65-ac92-35c476e02f7b

もうひとつ、こちら 日付時刻操作ライブラリをmomentからdayjsへ乗り換えた - Qiita の記事。

普段、日時を扱う場合には Moment.js を使っていますが、もっと軽量の iamkun/dayjs があるとのこと。
サーバサイドの開発では軽量に越したことはありませんがシビアに考えることは少ないですが、フロントでは大事なポイントです。

さっそくモジュールを導入し、サンドボックス・プロジェクトを起動しておきます。

1
2
$ yarn add dayjs moment
$ yarn start

ここではコードをまとめて書いていますが、保存しながら書き足している感じで作業しています。コーディングしながら Ctrl + S して、コンソールが流れている間に次のコードといった感じです。
デフォルトのタイムゾーンとフォーマットが異なるものの、通常は ISO 文字列で使ったり、フォーマットのテンプレートを指定したりするので問題はないでしょう。2KB の軽さは魅力的です。

1
2
3
4
5
6
7
8
9
10
11
import dayjs from 'dayjs';
import moment from 'moment';

console.debug(`Day.js: ${ dayjs() }`);
console.debug(`Moment.js: ${ moment() }`);

console.debug(`Day.js: ${ dayjs().unix() }`);
console.debug(`Moment.js: ${ moment().unix() }`);

console.debug(`Day.js: ${ dayjs().toISOString() }`);
console.debug(`Moment.js: ${ moment().toISOString() }`);

徐々にコンソール出力が増えていくのを確認しながら、動きの差異などを確認しています。

1
2
3
4
5
6
Day.js:    Fri, 17 May 2019 13:52:02 GMT
Moment.js: Fri May 17 2019 22:52:02 GMT+0900
Day.js: 1558101122
Moment.js: 1558101122
Day.js: 2019-05-17T13:52:02.637Z
Moment.js: 2019-05-17T13:52:02.637Z

以上、ちょっとしたコードの検証に使えるサンドボックス、お砂場プロジェクトでした。

このようなプロジェクトを1つ作っておくと、TypeScript で型を調べたり、新しいライブラリの使い方を確認したりといった際に便利です。

nodemon まで入ったところで Git にコミットしておくと、散らかってもクリーンな環境へ簡単に戻せます。
私は AWS Lambda で環境変数を使うことが多いので、さらに motdotla/dotenv も追加しています。また、TSLint もかけています。また Strict なコーディングの確認や練習のために TSLint も設定しています。(ESLint へ引っ越さないと💦)
ソースはこちら lulzneko/sandbox

Nuxt.js PWA のベースアプリを GitHub Pages へデプロイする

Nuxt.js を使うことで手軽に PWA なアプリを作ることができます。そして開発した PWA を世に出すためには、サーバー環境へデプロイする必要があります。Nuxt.js を SPA の JAMStack な作りにしている場合は、ウェブサイトをホスティングできる環境であれば、どこでも大丈夫です。今回は GitHub Pages でホスティングをします。

前回までの実装で Nuxt.jsPWA with TypeScript ができました。アプリとしての機能はまったくないですが、やはりデプロイして実際にスマートフォンからアクセスしてみたいところです。今回は簡単にデプロイできる GitHub Pages でホスティングします。また、一手間かかりますが CircleCI とも連携して、アプリのソース管理とホスティングを同じリポジトリで行い、ソースコードを変更したら自動的にビルド&デプロイする方法も紹介します。

シリーズの記事

環境
本記事の開発環境は以下となります。

GitHub Pages へデプロイ

GitHub Pages は、ソースコード管理 Git のホスティングサービス GitHub が提供する、ウェブサイト・ホスティングの機能です。
ユーザーや組織のウェブサイト、またはアプリなどのプロジェクト・ウェブサイトを公開できます。GitHub Pages はプログラムなどを実行できませんが、静的なサイトを手軽に公開できます。

まず GitHub にホスティング用のリポジトリを作成します。
[リポジトリ名] は、任意の名前です。 (今回は pwa-base-app としました)
※ プロジェクトサイトを想定しています。ユーザーまたは組織サイト(リポジトリ名が username.github.io のパターン)の場合は設定が異なるので各項目で注釈を入れます。ご注意ください。

リポジトリをクローンしてローカルの開発ディレクトリに、アプリのソースをコピーしておきます。

GitHub リポジトリの [Settings] から GitHub Pages を有効にします。

  • [develop] は master branch を選択します

nuxt.config.ts に以下の設定を追加します。(必要箇所のみ抜粋)

  • build: publicPath は、CSS や JS などが置かれるディレクトリでデフォルトの _nuxt_ 始まりが GitHub Pages で使えないため変更します。(_ で始まらなければ任意の文字列で大丈夫です)
  • router: base は、GitHub Pages のプロジェクトサイトがサブパスのリポジトリ名に配置されるので [リポジトリ名] を設定します。
    ※ ユーザーまたは組織サイトの場合はドメイン直下に配置されるため router: base の設定は不要です
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    const config: NuxtConfiguration = {
    // ...(省略)

    build: {
    publicPath: '/static/',
    extend(config, ctx) {
    },
    },

    router: {
    base: '/pwa-base-app/'
    },
    }

GitHub Pages デプロイ用のモジュールを導入します。

1
$ yarn add -D gh-pages

※ npm の場合 npm i -D gh-pages

デプロイの実行!
以下のコマンドでビルドとデプロイをします。

1
2
$ yarn build
$ yarn gh-pages -d dist -b master -m 'Update Site'

コンソールに Published が表示されたら GitHub Pages の設定で表示されていた URL にアクセスします。(ちょっとタイムラグがあるかもしれません。表示されなかったら少し待って再アクセスしてください。)

以上、簡単に GitHub Pages へデプロイでした。
毎回コマンドを打つ必要がありますが、GitHub Pages のリポジトリを用意するだけで簡単にホスティングできます。

CircleCI と連携した自動デプロイ

GitHub Pages へデプロイするのはコマンド1つで簡単ですが、開発中にちょくちょく実行するのは手間です。ソースがプッシュされたら自動でビルドしてデプロイしておいて欲しいです。これは CircleCI と GitHub を連携させることで実現できます。

ちょっと手間がかかりますが、設定を1回するだけで後はリポジトリにプッシュすると自動的にデプロイしておいてくれるので便利です。なにより「常にアクセス可能なプロトタイプ環境」が実現できるのでありがたいです。

CircleCI とは

CircleCI は Continuous Integration and Delivery と、サイトのタイトルにつけている通り CI(Continuous Integration)/CD(Continuous Delivery) の機能を提供するサービスです。
ソースコードの変更を受け、あらかじめ定義されたビルドやテスト、デプロイといったことを自動的に行ってくれます。無料で始められるので、CI/CD の入門や小規模なプロジェクトでも使いやすいですし、大規模なプロジェクトにもしかりと対応してくれます。

GitHub リポジトリにデプロイキーを追加

GitHub に CircleCI からデプロイできるようにデプロイキーを追加します。
まず ssh-keygen で、パスフレーズなしの ECDSA キーペア(~/deploy_key~/deploy_key.pub)を作ります。
※ すべての作業が終わったらローカルのキーペアファイルは削除します

1
$ ssh-keygen -t ecdsa-sha2-nistp521 -C "" -f ~/deploy_key

※ ECDSA が使えない場合は ssh-keygen -t rsa -b 4096 -C "" -f ~/deploy_key

そして ~/deploy_key.pub の内容を、GitHub リポジトリの [Settings] - [Deploy keys] に貼り付けます。

  • [Title] は、あとでわかるようにします。(今回は CircleCI 用なので CircleCI としました)
  • [Key] は、~/deploy_key.pub の内容です。
  • [Allow write access]: チェックを付けます。これを忘れると CircleCI からプッシュできません。

追加されたキーの Fingerprint をひかえておきます。

プロジェクトの npm スクリプトを設定

プロジェクト直下 /package.jsondeploy スクリプトを追加します。generate の行末にカンマを忘れないようにします。(scripts のみ抜粋)
これにより先ほどのデプロイコマンドを yarn deploy だけで実行できるようになります。[ci skip] は CircleCI を動作させないためのコミットメッセージになります。master ブランチは GitHub Pages の公開用 HTML なので CI は使わないためです。

1
2
3
4
5
6
7
"scripts": {
"dev": "nuxt",
"build": "nuxt build",
"start": "nuxt start",
"generate": "nuxt generate",
"deploy": "gh-pages -d dist -b master -m 'Update Site [ci skip]'"
},

CircleCI の設定ファイルを作成

プロジェクト直下に .circleci ディレクトリ、そのディレクトリに config.yml ファイルを追加します。
/.circleci/config.yml に以下の部分を環境に合わせて変更したものを書きます。

  • 58行目 [fingerprints]: 上記 GitHub に追加したデプロイキーの Fingerprint
  • 64行目 [user.name]: GitHub の master ブランチへプッシュするユーザーの名前 (コミットログ用、私は CircleCI を使うことが多いです)
  • 65行目 [user.email]: GitHub の master ブランチへプッシュするユーザーメールアドレス (コミットログ用、自分または開発チームのメーリングリストなど)
  • 21行目 [branches: only]: 後述の開発ブランチ名、今回は develop としました
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    version: 2.1

    executors:
    default:
    working_directory: ~/workspace
    docker:
    - image: circleci/node:12
    environment:
    TZ: /usr/share/zoneinfo/Asia/Tokyo

    commands:
    system_info:
    steps:
    - run:
    name: System information
    command: |
    echo "Node $(node -v)"
    echo "Yarn v$(yarn -v)"
    setup:
    steps:
    - restore_cache:
    name: Restore Yarn Package Cache
    key: yarn-packages-{{ checksum "yarn.lock" }}
    - run:
    name: Setup
    command: yarn install
    - save_cache:
    name: Save Yarn Package Cache
    key: yarn-packages-{{ checksum "yarn.lock" }}
    paths:
    - ~/.cache/yarn

    jobs:
    build:
    executor:
    name: default
    steps:
    - system_info
    - checkout
    - setup
    - run:
    name: Build
    command: yarn build
    - store_artifacts:
    path: dist
    - persist_to_workspace:
    root: ~/workspace
    paths:
    - ./*
    deploy:
    executor:
    name: default
    steps:
    - attach_workspace:
    at: ~/workspace
    - add_ssh_keys:
    fingerprints:
    - "[GitHub に追加したキーの Fingerprint]"
    - run:
    name: Deploy
    command: |
    mkdir -p ~/.ssh
    ssh-keyscan github.com >> ~/.ssh/known_hosts
    git config --global user.name "[your name]"
    git config --global user.email "[email@example.com]"
    yarn deploy

    workflows:
    build-deploy-flow:
    jobs:
    - build:
    filters:
    branches:
    ignore:
    - master
    - deploy:
    requires:
    - build
    filters:
    branches:
    only:
    - [開発ブランチ名]

ちょっと長いですがざっくり以下です。(個々の詳細は、こちら CircleCI を設定する - CircleCI をご確認ください)

  • executors で、実行環境を定義しています
  • commands で、再利用可能な実行ステップ system_infosetup を定義しています
  • jobs で、ワークフローから実行するジョブ builddeploy を定義しています
  • workflows で、GitHub のブランチに応じて実行するジョブの定義をしています

[開発ブランチ名] にプッシュすると build ジョブに次いで deploy ジョブが実行されます。
build ジョブの中心は、yarn installyarn build の実行です。
deploy ジョブの中心は、yarn deploy です。

空ブランチへプッシュ

プロジェクトのディレクトリで以下のコマンドを実行します。今回は [ブランチ名] を develop としました。ブランチ名は、先に作成した /.circleci/config.yml と合わせる必要があります。

1
2
3
4
$ git checkout --orphan [ブランチ名]
$ git add .
$ git commit -m "initial commit"
$ git push -u origin [ブランチ名]

git checkout --orphan は、これまでのブランチとは関係のない空のブランチを作るオプションです。これにより master ブランチとはつながっていない develop の空ブランチが作られます。
ここに開発用のソースをコミットして GitHub へプッシュします。
GitHub でブランチが追加されていることが確認できます。

また [Insights] - [Network] では、master ブランチと途切れて develop ができていることが確認できます。(master ブランチは GitHub Pages 用で基本的にみることがないので [Settings] - [Branches] からデフォルトを develop にしておくと便利です)

CircleCI にプロジェクトを追加

CircleCI https://circleci.com/dashboard へ GitHub のアカウントでログインします。
左のメニューから [➕ ADD PROJECTS] を選択します。

リポジトリが列挙されるので、今回のリポジトリ名の横 [Set Up Project] をクリックします。
設定と手順が書かれていますが、設定済みなので [Start building] をクリックします。

ワークフロー画面が表示され、しばらく待つとビルド失敗が表示されます。(デプロイキーを CircleCI に登録していないため)

ワークフロー画面の左メニュー、プロジェクト名横の [⚙] 歯車アイコンをクリックします。
左メニューから [SSH Permissions] を選び、画面右の [Add SSH Key] ボタンをクリックします。
SSH Key 追加ダイアログが表示されるので情報を入力し、[Add SSH Key] ボタンをクリックします。

  • [Hostname]: github.com
  • [Private Key]: ~/deploy_key ファイルの内容

再ビルドするので、先ほどのワークフローのビルド失敗画面から [Rerun] - [Rerun from beginning] をクリックします。
(左メニューアイコン2段目の WORKFLOWS です)

以上で自動ビルド&デプロイ、「CI/CD」の完成です!
develop ブランチへプッシュするたびに CircleCI が自動的にビルドし、GitHub Pages へデプロイしてくれます。

ローカルのキーペアファイルのクリーンアップ

ビルドが通り、GitHub Pages でアプリが見れるようになったらローカルのキーペアファイル ~/deploy_key~/deploy_key.pub を削除します。
とくに秘密鍵側 ~/deploy_key は、リポジトリに書き込みができてしまうので注意してください。

ソースコード

今回作成した部分までのソースを GitHub へアップしました。
(プロジェクトのソース管理の都合上、リポジトリ名 samples-pwa-base-appsource ブランチになります。)
https://github.com/riotz-works/samples-pwa-base-app/tree/0.0.3

GitHub Pages にホスティングもしています。
(公開サイトは1つのため記事公開に合わせて変わり、本記事の内容とは異なります)
https://riotz.works/samples-pwa-base-app/


Nuxt.js アプリを、GitHub Pages へデプロイできるようになりました。
ホスティング先はいろいろ選べますが、小規模で手軽に扱いたい場合は GitHub Pages を使うと簡単です。

今回 CircleCI 連携をしました。今回のようなサンプルアプリでも CI/CD を使うことで簡単にホスティングできるようになるので便利ですし、本格的に使う場合はテストも CI で実行して早期にビルドエラーやテストの失敗を気づけるようにします。

私は CircleCI を好んで使っていますが、他にもさまざまなサービスがあるので環境にあったものを使うとよいでしょう。

Nuxt.js PWA(Progressive Web Apps) のベースアプリをTypeScript対応する

Nuxt.js を使うことで簡単に PWA なアプリを作ることができます。そのアプリを作る際に TypeScript を使うとコードの品質を高めることができ、また複数人やチームでの開発生産性を向上させることができます。今回は Nuxt.js で作ったアプリに TypeScript を導入する方法を紹介します。

前回 Nuxt.js で PWA(Progressive Web Apps) のベースアプリを作る で、Nuxt.js を使った PWA のベースアプリを作りました。そのまま JavaScript で開発を進めることができますが、TypeScript を導入することで変数や関数に型定義が行えるようになり開発生産性や保守性を向上できます。今回のベースアプリはゼロから作っているので最初から入れてしまうのがよいでしょう。また TypeScript の関連モジュールを導入したとしても、必ずしも TypeScript の利用が必須ではなく、JavaScript で書いていって、TypeScript にできるところから適用していく段階的な利用も可能です。そのような観点からも入れてしまうのがよいでしょう。

シリーズの記事

環境
本記事の開発環境は以下となります。

  • Windows 10 64bit + WSL Ubuntu 18.04.1 LTS
  • Visual Studio Code
  • Node.js 12.1.0
  • Yarn 1.15.2
  • Nuxt.js 2.6.3

TypeScript とは

TypeScript は JavaScript に型とクラスを導入する AltJS のプログラミング言語で、JavaScript のスーパーセットです。クラスは ES6 で導入されたので TypeScript 固有ではなくなりましたが、やはり「型」の導入が TypeScript のメリットといえるでしょう。

型の例で、たとえば JavaScript で以下のようなコードがあったとします。
単純な足し算を想定したコードですが、型がないので引数に任意の値を投入できます。その結果、戻り値が想定外になることもあります。

1
2
3
4
5
6
7
8
const add = (a, b) => {
return a + b;
}

console.log(add(1, 2)); // 3
console.log(add(1, "2")); // 12
console.log(add(1, [ 2 ])); // 12
console.log(add(1, { value: "2" })); // 1[object Object]

TypeScript では以下のようになります。
引数と戻り値に :number で、数値型を明示します。それにより数値でない引数を渡している部分はコンパイルエラーとなり実行コード(JavaScript)が生成できません。

1
2
3
4
5
6
7
8
const add = (a: number, b: number): number => {
return a + b;
}

console.log(add(1, 2));
console.log(add(1, "2")); // error TS2345: Argument of type '"2"' is not assignable to parameter of type 'number'.
console.log(add(1, [ 2 ])); // error TS2345: Argument of type 'number[]' is not assignable to parameter of type 'number'.
console.log(add(1, { value: "2" })); // error TS2345: Argument of type '{ value: string; }' is not assignable to parameter of type 'number'.

基本的には型定義が入っただけで書いていることに変わりはありません。またコンパイラや Lint の設定で、制約を強くしたり、甘くしたりできます。最初から使う場合はある程度強くできるでしょうし、途中から導入したり慣れていない場合などは徐々に強くしていくこともでき、状況に合わせて使えます。

このぐらいの関数でしたら型がなくてもとくに問題ないかもしれませんが、もっと大きなものや他の人が書いたコードなどになってくると話が異なります。関数の引数が何を期待しているのか、戻り値は何が返ってくるのか、型情報があるだけでだいぶ変わってきます。

そんな TypeScript ですが、最近はとくに注目されており Meetup イベントが東西で開催されます!抽選枠で、まだ応募可能です。
TypeScript Meetup #1 にエントリーしましたが、案内メールが来て一瞬で定数枠の 60 を超え、本記事執筆時点で 500 人を超えてます。スゴイ勢い!

Riotz.works は、TypeScript で開発するチームなのですが、実はずっと Java で開発をしてきたチームでした。あるトラブルがあって TypeScript に舵を切り、以来 TypeScript で開発するチームとなったのですが、その際の発表資料がこちらになります。もしよかったらご参照ください。(いつかの Meetup の機会でお話しできるといいな)

Nuxt.js のプロジェクトに TypeScript を導入

前回作成 Nuxt.js で PWA(Progressive Web Apps) のベースアプリ に TypeScript を導入します。

TypeScript サポートのモジュールを追加

まずは TypeScript サポートのモジュールを追加します。プロジェクトのディレクトリで下記コマンドを実行します。

1
$ yarn add -D @nuxt/typescript ts-node

※ npm の場合、npm i -D @nuxt/typescript ts-node

TypeScript の設定ファイルを生成

TypeScript の設定ファイル tsconfig.json を作成します。
※ この feat(ts): auto generate tsconfig.json by kevinmarrec · Pull Request #4776 · nuxt/nuxt.js プルリクがリリースされると touch しなくても済みそうですが、2019年5月現在、まだ事前に作成が必要です。

1
$ touch tsconfig.json

Nuxt.js 開発サーバーを起動し、tsconfig.json の定義を生成します。起動情報に TypeScript support is enabled が出ていることがわかります。

1
2
3
4
5
6
7
8
9
10
$ yarn dev
╭──────────────────────────────────────────╮
│ │
│ Nuxt.js v2.6.3 │
│ Running in development mode (spa) │
│ TypeScript support is enabled │
│ │
│ Listening on: http://localhost:3000/ │
│ │
╰──────────────────────────────────────────╯

tsconfig.json の内容が更新されます。JSON ファイルをモジュールとして扱える定義 resolveJsonModule を追加します。これは追加したほうが便利なのと、後述の nuxt.config.tspackage.json を扱えるようにするためです。

1
2
3
4
5
6
{
"compilerOptions": {
// ...(省略)
"resolveJsonModule": true,
}
}

この定義は追加するプルリクと削除するプルリクが流れているようで、日付的には消すほうが最後なので自分で追加が必要そうです。
“Remove resolveJsonModule as , after little thoughts, we should not force it to users - #4842“ のだそうで、フレームワークのポリシーや考え方ですね。(なぜなのかは知りたかった)

Nuxt.js の設定ファイルを TypeScript 化

続いて設定を TypeScript で書くため nuxt.config.js ファイルを nuxt.config.ts にリネームします。
ファイルの内容、先頭と最後を以下のように変更します。とくに './package.json'.json をつけ忘れないように注意します。この package.json を読み込むのに先ほどの resolveJsonModule が必要でした。

変更前

1
2
3
4
5
import pkg from './package'

export default {
// ...(省略)
}

変更後

1
2
3
4
5
6
7
8
import NuxtConfiguration from '@nuxt/config'
import pkg from './package.json';

const config: NuxtConfiguration = {
// ...(省略)
}

export default config

これにより設定ファイル内で Ctrl + Space などの入力補完が使えるようになります。

ページやコンポーネントを TypeScript 化

プロジェクトが TypeScript 対応したので、ページやコンポーネントを TypeScript 化します。

今回はスクリプトベースで実装します。基本的な構文は下記です。

  • <script> タグに lang="ts" を追加して TypeScript として認識させます
  • 全体に型定義を効かせるために Vue.extend を追加します
  • </script> タグ前の括弧閉じに ‘)’ を追加するのを忘れないよう注意します
    1
    2
    3
    4
    5
    6
    <script lang="ts">
    import Vue from 'vue'

    export default Vue.extend({
    })
    </script>

ベースアプリの /pages/index.vue は以下のようになります。

1
2
3
4
5
6
7
8
9
10
<script lang="ts">
import Vue from 'vue'
import Logo from '~/components/Logo.vue'

export default Vue.extend({
components: {
Logo
}
})
</script>

また、Visual Studio Code の Vue 拡張 Vetur を使っている場合 /components/Logo.vue に以下の空実装コードを追加します。
(nuxt コマンドはエラーではないので、Vetur を使わない場合は追加しなくても大丈夫です)

1
2
3
4
<script lang="ts">
import Vue from 'vue'
export default Vue.extend({})
</script>

Note
公式ドキュメント TypeScript サポート - Nuxt.js では、今回のようなスクリプトベースではなくクラスベースの “vue-property-decorator を利用することを強くお薦め - 公式サイト“ しています。
しかしながらクラス構文にすると、実装方法が Vue.js/Nuxt.js とまったく異なってしまいます。今回は、これまでの実装経験やサイト、書籍を活かすためにスクリプトベースとしました。まったくゼロから勉強するとのことでしたらクラス構文を使うのも手かもしれません。

カウンターを設置して TypeScript の実装を確認

TypeScript を導入したので実装を確認します。
トップページにボタンを設置し、クリックした数をカウントする簡単なアクションを追加します。

絵文字ボタンとカウンターの導入

/pages/index.vue の [Documentation] と [GitHub] の下、<div class="links"></div> ブロックの直後に以下を追加します。

1
2
3
4
5
6
7
<div class="actions">
<a @click="addTada" class="button--action">🎉 {{ tada }}</a>
<a @click="addSparkles" class="button--action">✨ {{ sparkles }}</a>
<a @click="addThumbsup" class="button--action">👍 {{ thumbsup }}</a>
<a @click="addHeart" class="button--action">🧡 {{ heart }}</a>
<a @click="clear" class="button--grey">Clear</a>
</div>

カウンター表示用のデータプロパティを追加します。(data のみ抜粋)

1
2
3
4
5
6
7
8
9
10
11
export default Vue.extend({
// ...(省略)
data() {
return {
tada: 0,
sparkles: 0,
thumbsup: 0,
heart: 0
}
},
})

@click で呼び出されるメソッドを追加します。(methods のみ抜粋)
各絵文字ボタンから呼び出されるメソッドは対応する変数をインクリメント、[Clear] は全変数を 0 に初期化します。各メソッドは TypeScript の型定義を導入し、戻り値 :void と明示しています。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
export default Vue.extend({
// ...(省略)
methods: {
addTada: function(): void {
this.tada++
},
addSparkles: function(): void {
this.sparkles++
},
addThumbsup: function(): void {
this.thumbsup++
},
addHeart: function(): void {
this.heart++
},
clear: function(): void {
this.tada = 0
this.sparkles = 0
this.thumbsup = 0
this.heart = 0
}
}
})

以上で、絵文字ボタンを押すと各ボタン横のカウンターが増える動作となります。
カウンターぐらいだと型のメリットが感じられませんが、たとえば clear の初期化で "0" のような文字列を入れるとコンパイルエラーとなります。しっかり TypeScript の型チェックが効いています。

カウンターのトータル回数、ロゴを回すアニメーションの導入

カウンターの値を使ったアクションを作りたいので、各カウンターのトータルを計算し、その値の回数ロゴを回してみます。

/pages/index.vue に、すべての絵文字カウンターの総計を持つ算術プロパティを追加します。(computed のみ抜粋)
counter() メソッドを作り、戻り値は number 型、すべての絵文字カウンターの合計です。

1
2
3
4
5
6
7
8
export default Vue.extend({
// ...(省略)
computed: {
counter(): number {
return this.tada + this.sparkles + this.thumbsup + this.heart
}
},
})

ロゴを回転させるために、ロゴのコンポーネントへカウンターの値を渡します。
HTML テンプレートの <logo> タグに v-bind:counter="counter" 属性を追加し counter の値をロゴのコンポーネントで使えるようにします。

1
<logo v-bind:counter="counter" />

/components/Logo.vue で、カウンターの値を受け取りロゴを回転する CSS を追加します。
ロゴ全体を回転させるため、現在の HTML テンプレートの実装部分を<div :style="rotation"> でラップします。
回転の CSS は style 属性直接に、rotation プロパティ経由で設定します。

1
2
3
4
5
6
7
8
9
10
<template>
<div :style="rotation">
<div class="VueToNuxtLogo">
<div class="Triangle Triangle--two" />
<div class="Triangle Triangle--one" />
<div class="Triangle Triangle--three" />
<div class="Triangle Triangle--four" />
</div>
</div>
</template>

後は counter の値を使って CSS を作るだけですが、CSS アニメーションを再実行できるようにするため、一手間かけています。(スクリプト全文)

  • 親コンポーネント /pages/index.vue で絵文字ボタンを押されると、props: counter に新しい値がセットされます。それを watch で監視していて、変化があると watch: counter を呼び出します。
  • watch: counter は、data プロパティにアニメーションの CSS をオブジェクトとして設定します。いったんアニメーション無し { animation: 'none' } をセットし、10ミリ秒後に { animation:rotate 1s linear 0s ${ value } forwards} をセットします。(アニメーション再実行のトリック)
  • 最後に computed: rotation 経由で作られた CSS オブジェクトを HTML テンプレートへ渡します。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    export default Vue.extend({
    props: {
    counter: Number
    },
    data() {
    return {
    animation: {}
    }
    },
    computed: {
    rotation: function(): object {
    return this.animation
    }
    },
    watch: {
    counter: function(value: number): void {
    this.animation = { animation: 'none' }
    setTimeout(() => {
    this.animation = { animation: `rotate 1s linear 0s ${ value } forwards` }
    }, 10)
    }
    }
    })

だいぶ TypeScript の型が登場しました。
computed: rotation では、戻り値が object で CSS の文字列ではないことがわかります。
watch: counter では、引数が number 型です。(フレームワークからの呼出し&テンプレートリテラルなので型明示のメリットはあまりないですが)

なお、CSS のアニメーション定義は下記です。

1
2
3
4
5
6
7
8
@keyframes rotate {
0% {
transform: rotateY(0);
}
100% {
transform: rotateY(360deg);
}
}

このくらいの規模だと型定義によるメリットはまだまだありませんが、アニメーションのためのトリックとは言え色々と取り回ししているので、他人の書いたこんなコードが急に出てきたと思うと型情報ぐらいあると嬉しいのではないでしょうか。

動作確認!

yarn dev で開発サーバーを起動し、ブラウザで http://localhost:3000/ へアクセスします。

※ 開発サーバーは常に立ち上げておいて大丈夫です。実際の開発に当たってはホットリロードを活用し、コードを保存するとブラウザが自動でリロードしてくれます。

ソースコード

今回作成した部分までのソースを GitHub へアップしました。
https://github.com/riotz-works/samples-pwa-base-app/tree/0.0.2

GitHub Pages にホスティングもしました。
(公開サイトは1つのため記事公開に合わせて変わり、本記事の内容とは異なります)
https://riotz.works/samples-pwa-base-app/


ベースアプリを TypeScript 化できました。
今回のカウンターボタンぐらいだとコーディング量が増えるだけでメリットが感じられませんが、他の人が書いた共通ライブラリを使うときなどに型があることで助かることが増えてきます。

型を導入できたところで Lint も入れたいところですが、こちらの話はまた重いので別途書きます。
もし使たい場合は、公式ドキュメント ESLint を使った Linting - TypeScript サポート - Nuxt.js をご参照ください。

Nuxt.js で PWA(Progressive Web Apps) のベースアプリを作る

ウェブの技術を使い、またウェブサイトとしてホスティングしながらも、モバイルやデスクトップのアプリとして振る舞うことができる PWA。最近では Google Play Store でも配信できるようになったり、iOS での対応強化など話題に事欠きません。
そんな PWA を簡単に高速に開発することができる Nuxt.js を紹介するとともに、今後さまざまな機能を試すためのベースアプリを作ります。

シリーズの記事

環境
本記事の開発環境は以下となります。

  • Windows 10 64bit + WSL Ubuntu 18.04.1 LTS
  • Visual Studio Code
  • Node.js 12.2.0
  • Yarn 1.15.2
  • Nuxt.js 2.6.3

PWA とは

PWA は Progressive Web Apps の略称で、ウェブサイトをモバイルアプリとして使えるようにする仕組みです。さらに Google Chrome 67 からデスクトップアプリとしても動作させることができるようになりました。

アプリを PWA とすることで、以下のような機能が提供できます。

  • 端末にインストールしホームやデスクトップにアイコンを追加できる
  • ブラウザとは異なるネイティブアプリのような操作感
  • オフラインでも使えるように作ることができる
  • プッシュ通知を受け取ることができる
  • リンクやサイトへの埋め込みができる
  • 自身の運営サイト、および Google Play Store から配信できる

一方でデメリットもあります。

  • OS(ブラウザ) により使える機能のサポートレベルが異なる
  • ネイティブ実装に対してブラウザでサポートしている機能が足りない (e.g. NFC, 2019年5月現在)

主な PWA (アプリがない状態でブラウザを使いアクセスすると [ホームに追加] が表示される)

また私たち Riotz.works は、モバイルアプリのハッカソン SPAJAM 2018(東京D予選) にて Nuxt.js
で PWA を作り優秀賞を獲得しました。「モバイルアプリ」の大会なので当然ですがネイティブアプリ開発のチームに対して、「唯一の PWA」として健闘し受賞に至りました。PWA スゴイ!

発表資料: リアルタイムの競演と参加型観戦で音楽を最高に楽しむ「ラップ、タップ、アップ🎶」

作品: ラップ、タップ、アップ 🎶
[バトルを募集する] ボタンから [対戦者] の [QRコード] または [URL] をスマホに渡しエントリー & カメラ付き PC は [対戦者] ラベル右の [入場] アイコンからエントリーで、ラップバトルの中継が行えます。PWA のため <iframe> で埋め込みもでき実際に動作させられます。スマホアプリなのにこういったことができるのも PWA のメリットになります。

Nuxt.js とは

Nuxt.js は、Vue.js を使ったアプリの開発を支援するフレームワークです。”Vue.js アプリケーションの開発を楽しくするために必要なすべての設定が揃っています - Nuxt.js“ と謳う通り Vue.js で開発する際に必要となる、さまざまなライブラリや設定があらかじめ構築済みで、簡単にアプリ開発が始められるフレームワークです。

開発できるアプリは3パターンあります。

  • サーバーレンダリング - Universal SSR(Server Side Rendering)
  • シングルページアプリ - SPA(Single Page Applications)
  • 静的サイト(プレレンダリング)

SSR と JAMStack の両方を1つのソースから生成できるため、アプリ特性に合わせてモードを切り替えられます。

Vue.js は、ウェブフロントを開発するためのフレームワークです。”The Progressive JavaScript Framework” と題しており、ウェブ UI に対して少しずつ適用でき、コンポーネントへの分離と再利用がしやすくなっています。

Vue.js は強力なフレームワークですが、あらゆるウェブサイトに対して柔軟に利用できるようコア機能に絞っています。そのため開発を支援するための周辺ライブラリが多数あります。
たとえば、ページのルーティング(URL のマッピング) はコア機能には含まれていません。独自に実装するか、公式ライブラリの Vue Router を別途導入します。

より踏み込んで高度なことの実装に耐えうる Vue.js ですが、アプリを高速に作り上げるには、ある程度のレールを敷いておいてほしい(たとえば Vue Router は設定済みにしてほしい)、そんなときに Nuxt.js を使うと手軽に開発できます。

Nuxt.js で、アプリ開発

さっそく Nuxt.js で PWA のベースアプリを作っていきます。
今回のアプリはベースなので以下の仕様としました。今後、このアプリをもとにさまざまな機能の検証などをします。

  • SPA の JAMStack として作成
  • TypeScript 化

今回は PWA アプリで、SPA の JAMStack で作成します。SSR(Universal) にするとサーバーサイドの処理を同時に実装できるので便利ですが、SEO 重視で CGM(Consumer Generated Media) や変化の激しいコンテンツでもない限り JAMStack にして、フロントとサーバーサイドは分離し Web API 経由でデータアクセスするほうが良いでしょう。
詳しくは JAWS DAYS 2019 で AWS x JAMStack な、サーバーレス Web Front について発表をしました をご参照ください。

TypeScript は JavaScript に型定義とクラスを導入する AltJS のプログラミング言語(正確にはスーパーセット)です。型定義を導入することで開発効率や不具合回避ができるため注目されており、最近で導入されるケースが多いようです。今回はゼロから作るので導入しておきます。
※ Nuxt.js は、バージョン 2.6 からオフィシャルに TypeScript 対応したのですが、2019年5月現在 CLI から簡単に導入できないため詳細は次回の記事で書きます。

2019年5月10日追記
書きました!
Nuxt.js PWA(Progressive Web Apps) のベースアプリをTypeScript対応する

プロジェクトの作成

公式のウィザード CLI (scaffolding tool) があるので、そちらを使います。
今回は最後の引数 <my-project>pwa-base-app としました。

1
$ npx create-nuxt-app <project-name>

※ yarn の場合 yarn create nuxt-app <my-project> がありますがグローバル汚染回避のために npx を使いました

プロジェクト名と説明を聞かれます。必要に応じて入力します。

1
2
? Project name (pwa-base-app)
? Project description (My majestic Nuxt.js project)

サーバーのフレームワークを聞かれますが JAMStack でいくので none を選択します。

1
2
3
4
5
6
7
8
9
? Use a custom server framework (Use arrow keys)
❯ none
express
koa
adonis
hapi
feathers
micro
(Move up and down to reveal more choices)

インストールする機能を聞かれます。

  • PWA Support は、今回の目的なのでチェックします。
  • Axios は、HTTP Client で Web API などにアクセスするのに使います。使うケースが多いので入れておきます。
  • Linter / FormatterPrettier は TypeScript との兼ね合いがあり CLI からは入れません。
    1
    2
    3
    4
    5
    ? Choose features to install (Press <space> to select, <a> to toggle all, <i> to invert selection)
    ◉ Progressive Web App (PWA) Support
    ◯ Linter / Formatter
    ◯ Prettier
    ❯◉ Axios

UI のフレームワークを選択します。ここはお好みで。
今回は none を選択し、本アプリを開発する際に別途導入します。(普段 Vuetify を使ってますがウィザードで入れると生成されるプロジェクトのサンプルコードが大きくなりすぎるので自分で入れてるのもあります)

1
2
3
4
5
6
7
8
? Use a custom UI framework (Use arrow keys)
❯ none
bootstrap
vuetify
bulma
tailwind
element-ui
buefy

テスティングのフレームワークを選択しまが、TypeScript にするので後から追加します。(このタイミングで追加すると babel-jest になるため)

1
2
3
4
? Use a custom test framework (Use arrow keys)
❯ none
jest
ava

Nuxt.js のモードを選択します。JAMStack にするので Single Page App を選択します。

1
2
3
? Choose rendering mode (Use arrow keys)
Universal
❯ Single Page App

Author を入力し、パッケージマネージャーを選択します。

1
2
3
4
? Author name ()
? Choose a package manager (Use arrow keys)
npm
❯ yarn

以上でウィザードが終了し、プロジェクトの生成とパッケージのインストールが行われます。
ウィザード終了時の案内に従いプロジェクトのディレクトリへ移動し、開発サーバーを起動します。
※ Yarn を選んだ場合に yarn run dev を案内されますが run は省略可能です

1
2
$ cd pwa-base-app
$ yarn dev

ウィザード終了時の案内に出ていた通りブラウザで http://localhost:3000/ へアクセスすると、PWA な Nuxt.js アプリが表示されます。

Note
2019年5月現在、開発サーバーを起動すると ERROR (node:515) DeprecationWarning: Tapable.plugin is deprecated. Use new API on .hooks instead の警告が表示されますが @nuxtjs/pwa のバージョンが古いためです。ERROR と出ていますが問題なく動作します。気になる場合は下記コマンドでモジュールをアップデートします。

1
$ yarn upgrade --latest

※ npm の場合は、tjunnone/npm-check-updates を使うとよいでしょう

デプロイ用のビルド

デプロイ用のビルドを実行すると dist ディレクトリが作られます。
この dist ディレクトリの内容物をウェブサーバーに配置するだけでデプロイ完了となります。

1
$ yarn build

※ 同じようなコマンドで generate がありますが、こちらは完全に静的化され HTTP Request が使えなくなるので build を使います。各種サンプルなどで generate が出てくる場合は build に読み替えてください。たとえば下記デプロイの FAQ は generate で書かれていますが build を使います。

デプロイについては環境によって設定などが異なってくるので別途書きます。
試してみたい方は、公式 FAQ をご参照ください。

また、現時点でデプロイしても PWA として機能します。

キャプチャ左側がスマートフォンでブラウザ表示時 [ホームに追加] が案内されます。中央がホームに追加後アプリとして起動した状態です。まだ何もないアプリですがブラウザのアドレスバーが消えてアプリっぽくなっています。アプリ内のボタンを押すとブラウザに戻るのは、アプリのドメイン外へ出たためです。

キャプチャ右側は Windows PC に、デスクトップ PWA としてインストールして起動したものになります。こちらもアドレスバーがなくアプリのような感じになります。

ソースコード

今回作成した部分までのソースを GitHub へアップしました。
https://github.com/riotz-works/samples-pwa-base-app/tree/0.0.1

GitHub Pages にホスティングもしました。
(公開サイトは1つのため記事公開に合わせて変わり、本記事の内容とは異なります)
https://riotz.works/samples-pwa-base-app/


Nuxt.js で PWA のベースアプリができました!

後は pages ディレクトリに表示するページの vue ファイルを追加していくことでアプリが作れますが、その前に TypeScript は入れておいたほうが良く次回の記事で、その辺のところを書きます。
TypeScript 対応しても、ページ単位で TypeScript/JavaScript を切り替えられますし、TypeScript の中に JavaScript が書けるので、TypeScript をやったことがなくても導入してしまい少しずつ使うことができます。

2019年5月10日追記
書きました!
Nuxt.js PWA(Progressive Web Apps) のベースアプリをTypeScript対応する

本文中で紹介しました SPAJAM は「2019 予選」が 募集中 です。本記事で PWA のアプリはできたも同然、ぜひハッカソンで試してみましょう!
Riotz.works は「東京A予選」でエントリー中です。チーム名「Riotz.works、進撃のPWA」と、今回こそ PWA で最優秀賞を目指します!!(エントリー結果が出てないので参戦できるかは決まってません)
予選一覧 | SPAJAM2019公式サイト – 温泉でハッカソン

2019年5月13日追記
SPAJAM 東京A予選 に参戦が決まりました!
Gmail の迷惑メールに仕分けされてしまったようで気づいてませんでした😭 最近メール使わないからトラブったら迷惑メールフォルダーの確認を忘れてしまい。。。

SPAJAM 東京A予選の方、ご一緒いたします。ハッカソンを楽しみましょう!!
応募されてない方、まだ募集中です。本戦、箱根で会いましょう!!

Asana を Nativefier でアプリ化する

タスク管理の Asana、スマートフォンアプリはあるのですがデスクトップアプリが提供されておらずブラウザで使います。Google Chrome でアプリウィンドウ化ができますが、常駐化をするなどの高度な使い方をしたい場合は Node.js の Nativefier を使ってアプリ化をする方法があります。Node.js の実行環境とコマンドラインでの操作が必要となりますが、手軽にアプリ化ができます。

前回 Google Chrome を使って Asana をアプリウィンドウ化 をしましたが、ウィンドウを閉じてしまうとウェブアクセスから始まり起動に時間がかかります。常時使うようなサービスなので常駐化し、いつでも使えるようにしたいです。

公式フォーラムでもデスクトップアプリのニーズは What’s the status of a native Asana Mac app? - Product Feedback - Asana Community Forum のように 2017年と古くから上がっているようですが進展はなさそうです。

今回は、フォーラムでも紹介されている Nativefier を使いアプリ化し常駐、必要な時にすぐに利用できるようにします。

環境
本記事の開発環境は以下となります。

  • Windows 10 64bit
  • Node.js 12.1.0
  • Nativefier 7.6.12

Nativefier とは

Nativefier は、ウェブページをデスクトップアプリ化する Node.js のツールで、ウェブサイトを Electron でラップした Windows, macOS, Linux 用のアプリを作成します。

Electron は、ウェブの技術である HTML/CSS/JavaScript を使ってクロスプラットフォームのデスクトップアプリを作るツールで Atom や Visual Studio Code などのエディター、Slack や Discord などのチャットクライアント等、さまざまなアプリで使われています。

ただし Electron アプリはメモリ消費が大きく、1アプリで数百メガ (私の Asana のケースでは 250MB) 使うのでアプリの作りすぎには注意が必要です。

Nativefier は Node.js 6 以上で動作します。インストールされていない場合は、Node.js 公式サイト にしたがってインストールしてください。今回は Node.js 12.1.0 を使いました。

Nativefier で Asana をアプリ化

以下のコマンドで Asana をアプリ化します。

1
C:\Temp> npx nativefier -n "Asana" --internal-urls "accounts\.google\.com.*" --single-instance --tray "https://app.asana.com"

指定したオプションは下記となります。

  • -n は、アプリ名(=ディレクトリと実行ファイル名)
  • --internal-urls は、Google 認証をウィンドウ内で行えるようするため
  • --single-instance は、アプリの起動を1つだけにし、ウィンドウを複数開かないため
  • --tray は、システムトレイに常駐させます

Google 認証については、GitHub に Issues が上がっています。いくつかパターンがありますが、今回は一番狭いスコープで設定する #282 @tianhuil さん のを使わせてもらいました。情報ありがとうございます!

オプション最後の https://app.asana.com は、アプリ化する URL で、アプリ起動時に表示したい URL たとえば Asana マイタスクなども指定できます。システムトレイに常駐させるならば起動後は最後に見た状態を覚えているのでトップページの URL がよいでしょう。

また Node.js の npm モジュールのグローバル汚染回避と、使う頻度が少く常に最新版を使いたいため npx で実行しました。npx はローカルに存在しないモジュールを実行する場合はダウンロードして実行し、実行後はダウンロードしたファイルを削除します。そのためグローバルにモジュールを追加するよりもクリーンですが、毎回モジュールをダウンロードすることになるので試行錯誤したりする場合は、グローバルに追加して実行した方が効率的です。
グローバルに追加して実行するには下記のコマンドとなります。

1
2
C:\Temp> yarn add --global nativefier
C:\Temp> nativefier -n "Asana" --internal-urls "accounts\.google\.com.*" --single-instance --tray "https://app.asana.com"

※ npm を使う場合は npm -g nativefier

Asana アプリ

Nativefier を実行すると、実行したディレクトリにアプリが作られます。
ディレクトリ名が Asana-win32-x64 になっていますが変更したり、別の場所に移動しても大丈夫です。

ディレクトリ内の Asana.exe をダブルクリックするとアプリが起動します。
ログイン画面から始まるので、使っているアカウントでログインします。

システムトレイにアイコンが配置され常駐していることがわかります。ウィンドウを閉じても常駐されているので、システムトレイのアイコンから最後の状態で表示されます。
今回はシングルインスタンスにしているので Asana.exe を繰り返しダブルクリックしたり、ショートカットから起動させてもウィンドウは1つで、最初に起動したウィンドウが手前に表示されます。

終了するにはシステムトレイのアイコンを右クリックして [Quit] を選びます。
常駐型のアプリと同じように扱うことができます。

Nativefier の、その他のオプション

Nativefier には、たくさんのオプションがあり柔軟に設定を行うことができます。
主なオプションを紹介します。(詳しくは --help を参照ください)

プラットフォームを指定してクロスビルドできます。

  • -p, --platform <value> は、プラットフォームで ‘osx’, ‘mas’, ‘linux’, ‘windows’ から指定できます
  • -a, --arch <value> は、CPU アーキテクチャで ‘ia32’, ‘x64’, ‘armv7l’ から指定できます

macOS の場合はバッジを付けることができるようです。(macOS がないので試せない & Asana は何がバッジされるんだろう🤔)

  • --counter は、ドックでカウンターのバッジを付けます
  • --bounce は、カウンターが増えた時にバウンドします

アプリの挙動を柔軟に設定できます。

  • --tray start-in-tray は、システムトレイ常駐で初回起動時にウィンドウを表示させません
  • --always-on-top は、ウィンドウを常にトップに表示します

Nativefier を使うことで簡単にウェブサイトをアプリ化できます。Asana に限らず、よく使うサービスでデスクトップアプリがない場合に Nativefier を使うと手軽にアプリ化できるのでとても便利です。

一方であくまでもウェブサイトをラップしているだけなので、アプリらしい機能はありません。やはり公式でアプリをリリースされることが望ましです。Asana アプリ、リリースされないかな。。。

Asana を Google Chrome でアプリウィンドウ化する

タスク管理の Asana、スマートフォンアプリはあるのですがデスクトップアプリが提供されておらずブラウザで使います。ブラウザで使っているとタブの中に埋もれてしまい欲しいときにすぐ使えなかったり、ブラウザごとまとめて閉じられてしまうことなどがあります。もっと快適に利用できるよう Google Chrome でアプリのように扱う方法を紹介します。

今回は Google Chrome の機能を使ってアプリウィンドウ化します。この機能について一時期使えなくなっていたこともあり、環境によってはうまく動作しないかもしれません。今回の動作確認環境は下記となります。(Google Chrome にメニューがなかったりなど今回の方法でできなかったり、Google Chrome を使っていない場合は、次回投稿予定のアプリ化の方法をご検討ください)

環境
本記事の開発環境は以下となります。

  • Windows 10 64bit
  • Google Chrome 74.0.3729.108

Google Chrome でショートカットの作成

まずは Google Chrome で Asana https://app.asana.com/ へログインします。
現在ブラウザで表示しているページが、アプリのトップになるので [マイタスク] やプロジェクトなど、最初に出てほしいページを表示しておきます。今回は [マイタスク] としました。

右上の [メニューアイコン] - [その他のツール] - [ショートカットを作成] を選びます。

ショートカットの作成ダイアログが表示されます。
[ショートカットの名前] を入力し、[ウィンドウとして開く] にチェックを付け [作成] をクリックします。

ブラウザが終了し、アプリウィンドウとして Asana が表示されます。
あとはアプリとして独立したウィンドウのため、ブラウザを閉じても巻き込まれることはありません。

スタートメニューとタスクバーへの登録

Google Chrome のアプリとして登録されているので、起動は chrome://apps/ またはブックマークバーの [アプリ] からとなります。
※ ブックマークバーは [Ctrl + Shift + B] で表示できます

起動をもう少し簡単にするため、スタートメニューへ登録します。
Google Chrome の [アプリ] にあるアイコンを右クリックし [ショートカットを作成] を選びます。

ショートカット作成のダイアログが表示されるので、必要なショートカットを選び [作成] をクリックします。
今回は両方作成しました。

デスクトップまたはスタートメニューのアイコンの右クリックからタスクバーにも追加できます。
これによりタスクバーからもいつでも起動することができるようになります。


タスク管理の Asana をデスクトップアプリ風にしました。これにより簡単に起動できますし、ブラウザのウィンドウやタブに紛れて探す必要もなくなります。なによりブラウザの閉じるでまとめて消えてしまわないのが助かります。

この方法は Asana だけではなく、ほとんどのウェブサイトで同様のことができます。よく使うサイトをアプリウィンドウ化することで便利に使うことができるようになります。

次回は Google Chrome の機能ではなく、アプリ化する方法を紹介します。

ブログメンティふりかえり4週目

カック@ブロガー / k9u(@kakakakakku)さん にブログメンターについていただき4週間。ありがたいことに日々多くの学びと気付きをいただいています。
この記事は私自身の学びの整理ではありますが、私がカックさんに教わっていること、学んだことなどはお役に立つのではないかと思い共有します。

シリーズの記事

ブログ メンティー としての目標設定

技術系記事を以下のペースで公開する(※ このシリーズの記事は記載するがカウント外)

  • 週1回を必達とする
  • 週2回を目標として設定する

以下の記事を書き目標を達成することができました。

2週目

3週目

4週目

Hexo のブログ環境機能に関する記事が中心となりました。これまで、なんとなくで構築してあまり手を入れていなかったので本格的に書くにあたって手を入れていったとこから、Chrome DevTools の使い方などに話を広げられたともいます。

また、プルリクを出す記事を書けたのが良かったです。利用させてもらっている OSS に貢献できたこと、また貢献のしかたを発信でき、カックさんの教え「OSS に感謝し貢献する」を小さいながらも実践できました。プルリクを出すのは引き続き実践できるよう狙っていき、記事を増やしていきたいです。そして、このシリーズの記事からプルリクをする人が出てくることにつながったら嬉しいです。

そして Asana でタスク管理。気に入っているサービスなので色々なサービス連携で活用法や API を使う方法などを研究して発信していきたいです。

各種メトリクスの推移と考察

毎週日曜日に以下のメトリクスを取得しています。こちらは達成数値のノルマとかではなく、推移を見ていただいています。

記事数は安定して出せていて、またテーマもたまっているので安心です。記事の質を上げて良い情報発信をできるようにしていきたいです。

PV は1週目のところで Shiftup! JP_Getshifter Vol3! で JAMStack なサーバーレスウェブフロントについて発表をしました ので、PV が上がってイベント効果がなくなり下がったところから少し戻った感じです。良い情報発信を心がけ伸ばしていけるようかんばります。

記事数

せっかくなので追加しました。(投稿数は目標設定カウント数のみ)

開始時1週目2週目3週目4週目
トータル2125283133
投稿数-3232

週間 PV

Google Analitycs で取得しています。

開始時1週目2週目3週目4週目
全体232478355276305
/articles82241242199205
@lulzneko78203225174191

はてなブックマーク数

サイト内の合計はてブ数を表示するツール|はてブチェッカー で取得しています。

開始時1週目2週目3週目4週目
全体2929303030
/articles1010111111
@lulzneko1010111111

Twitter フォロワー数

lulzneko (ラルズネコ)(@lulzneko) | Twitter を見ています。

開始時1週目2週目3週目4週目
@lulzneko158169170171172

気づき、学び

アウトプッターの使命と目的意識

ブログを書いたりイベントで発表したり、アウトプットするからにはやはり目的があります。そこをもっと明確にしたいなと思いました。カックさんとお話(DM)をしているなかで “ツール紹介などは「XXX はできないよね」という誤解を解く意味もある” というのがありました。そのものずばりで「プラグインや機能を紹介するのはアウトプッターの使命」と。

自分が調べたことや、見つけた良い方法などを発信し、多くの人の役に立ちたいというのは大前提としてあります。その上でもっと目的意識を明確化することで、伝えたいことがはっきりできるし、その伝えたいことの周りにも目が行って、これも合わせてお伝えしたいが生まれるのかなと。

これは Riotz.works というチーム全体のテーマでもあり日々考えていることでした。きっと Riotz.works 全体、そしてブログ、イベント発表、ハッカソンと活動の個々にも持つとよいのだろうなと感じ、まずはメンティー期間を通じてブログのアウトプッターの使命と目的意識の明確化に取り組んでいきます。

慌て記事への反省

今週は「Markdow + グラフ」の調査をしていて行けそうな雰囲気だったので置いておき、後半に着手したら見当違いが発覚し慌てての Asana 紹介記事になりました。調査が甘かったことから慌て記事なってしまい、記事で書きたい方向性やターゲットがブレてしまったことが反省です。
記事を書く目的は何か、読んでくださる方に何を得てほしいか、書いて発信するからには意識をしっかり持たなければと。

このように急ぎになってやってしまうことのほかに、「これいいな」「これ話したいな」「よし!書こう!!」みたいな感じの勢いで書き始めてしまうケースも同じで、気を付けたいところです。

やりたかったことは本記事のグラフ表示で、今回は Chart.js を使うことで実現しました。(これも Chart.js スゴイ!書くぞ!!の勢いになりそうでした。。。)

ブログへの愛情と厳しいチェック

恥ずかしいことですが、せっかく書いているのに typo してしまう。カックさんにレビュー依頼すると読むのがとても速いのに typo のチェックが入る。どうやって見てるの?と不思議です。

その厳しいチェックもブログへの愛情から。お話している中でとても感じますし、書くことだけでなく読んでくださる方へ伝わるか、読みやすいかということを考えているからなんですね。
書いたからには読んでもらいたいし、読んでもらうからには内容や読みやすさはもとより、誤字脱字チェックの基本的なことは然るべく。そのためには厳しくセルフチェックをしなければならない。typo がないように、しっかりチェックします。

コミュニティへの貢献

こちらは前回も書きましたが、ブログを書くだけではなく使っている OSS やコミュニティへ貢献すること。これは、もちろん感謝があり、そしてアウトプットの活動を楽しむことなのだと感じました。プルリクを出しマージされると、とても嬉しいです。嬉しさを積み重ね、楽しく感謝と貢献をしていきたいです。

こちらはカックさんのプルリクまとめの記事です。まとめができるぐらい私も貢献していきたい。


なかなか書ききれなかったり、うまくお伝えできてない部分がありますが、少しでも共有できればと。

最低限、週1本書くことでやっていますが、まだ習慣化できてなかったり、書くのに時間がかかったりとなかなか成長できてないのを感じます。しかしながらカックさんとのやり取りというのがとても楽しく、まさに「メンター」が付いているという安心感と楽しさでこなせています。

4週目=1ヶ月、あっという間に過ぎてしまいました。もっともっとレベルアップし、お役に立てるような良い記事を書けるようになっていきたいです。

どうぞ、よろしくお願いいたします。

Asana で個人のタスク管理をする

週1本~2本のペースでブログを書く中でアイデアの広がりや、やってみたいことなどがいろいろ出てくるようになりました。これまでは簡単なメモなどで済ませていたのが、いよいよ本格的に管理の必要を感じてくる中で、いくつか試したところ Asana が良かったので紹介します。

タスク管理導入の背景

カック@ブロガー / k9u(@kakakakakku)さん にブログのメンターについていただいて早1ヶ月。最初に 20 のブログテーマを考え、またお話(DM)していく中で新しいブログのテーマをいただき、そして自分でも書いてみたいネタを考えるようになり、アイテムの整理が大変になってきました。

タスクの管理はもともと苦手、にもかかわらず色々思いついては書き散らかす困ったタイプでした。ところがブログを定期的に書いていくには優先順位や難易度(と、その準備)といったところから、ある程度整理が必要となります。

また超大型連休前で仕事も詰め込み状態、Slack で次々にやることが積まれてはスレッドが押し流されていく状況も重なったのもありました。

仕事のプロジェクト管理は別なので、個人のタスク管理として手軽に使えるもを探している中で良さそうなものから、同僚に紹介された Asana を使い始めたのが経緯です。

Asana とは

Asana https://asana.com/ja は “一番大事な仕事に、もっと時間を” をトップに題するプロジェクト管理のサービスです。

ToDo リストや、カンバン方式のタスク管理機能があるほか、ガントチャートのようなタイムライン、カレンダーなど豊富な機能を備えています。また無料プランもあるので個人や小規模なチームに手軽に導入できるもありがたいです。

導入企業もそうそうたるもので、2019年中には東京オフィスも開設されるとのことです。そのため4月1日には 東京でイベント も開催されたとのことで、いろいろと楽しみなサービスです。
また、とても使いやすさ楽しさを感じたのですが、その辺についても記事があり、こういったのも楽しいです。

参考情報

Asana の良いところ

日本語化されている

メニューもアプリも日本語化されています。また簡単なテンプレートプロジェクトも日本語化され説明も読めるので助かります。ノウハウ的なことやケーススタディも日本語で多く公開されているので、使い始めに困るこもなく、研究もいろいろとでいそうです。また最近話題の 1 on 1 プロジェクトのテンプレートもありおもしろいです。

参考情報

スマートフォンアプリが爆速!

私が持っているスマホ Xperia XZ1 での操作感になりますが、このアプリ、起動の速さが最高です!
まずスプラッシュがありません。一瞬だけ Asana のロゴカラーで画面が埋まりますが1秒もないぐらいの時間でマイタスクのリストが表示されます。その後リストがネット同期化されますがタスクの入力は行うことができます。この速さは個人タスク管理において最高の体験といえるでしょう。思いついたことを即座に入力できます。

これまではちょっとした ToDo や、思い付きをメモしておきたいときに待たされるのは苦痛ですし、待ってるうちに他のことをしたり、考えたりして入力することを忘れてしまうといったことがありました。しかしながら Asana のアプリを入れてから即入力できるようになりメモ忘れがなくなりました。

できればウィジェットも欲しかったのですが、表示が大きすぎで1画面使ってしまう勢いだったのでやめました。こちらは ToDo リストで上から表示できる分だけ「1 x 1」や「1 x 2」ぐらいだと嬉しかったです。(スケジュール管理は ジョルテ を使っていて、その ToDo ウィジェットが良いので Google Calendar 経由で連携に挑戦してみたいです)

アプリはこちらから入手できます。

ネットにつながっていなくても利用できる

スマホアプリなら当然でないと困りますが、ネットにつながってない状態でも入力できます。もちろんネットへつながった時に同期してくれるので安心です。いつでも使えるのが助かります。
これはブラウザでも同様でリストはあらかじめアクセスしていないといけませんが、リストが表示できていればタスクは追加できます。残念ながらローカルストレージへは保存してくれないのでブラウザを閉じてしまったら消えてしまうのはありますが。それでもリストさえ取得しておけばある程度操作できるのはよいです。

アプリやサービス連携が豊富

公式ページには 100以上と書かれていますが、とにかくたくさんあります。Gmail などのメール連携から、Slack などのチャット、ストレージなどなど。
また API も公開されているようなので、自分なりのアクションを作りこむこともできそうです。こちらは存在の確認だけで詳細を読んでないのですが、いずれ挑戦してみます。

参考情報

エフェクトでテンションが上がる

タスクを完了するとキラキラで流れて行ったり、サプライズ的な特別効果が表示されたりと楽しさがあります。
こういったエフェクトが作業する楽しさにもつながり、とても良いテンションで作業ができますし、また登録したくなります。

Asana のサインアップ

まず https://asana.com/ja へアクセスし利用するメールアドレスを入力し [無料で試す] をクリックしサインアップします。(Gmail を使うと会社のメールアドレスを求められますが [個人用メール] で続けられますし、Gmail の受信トレイを開くボタンも用意してくれるユーザーフレンドリー仕様です)

入力したメールアドレスに来たメールから [メールアドレスを確認] をクリックし、ようこそ画面が表示されるので [始める] をクリックします。
プロフィールのセットアップ画面が表示されるので、写真、氏名、パスワードを入力し [続行] をクリックします。

チームの情報を聞かれるので [チームタイプ] と [チーム名] を入力し [チームを作成] をクリックします。チームは ID 管理されているようでユニーク性は求められないため安心です。

続いてチームに参加するユーザーのメールアドレスを求められます。必要に応じて追加します。
最後に [無料トライアルを始める] 画面が表示されクレジットカードの入力を求められますが 無料の Basic 版を使うには入力する必要はありません
画面右の [無料トライアルを始める] の下にある [制限付きバージョンで続ける] をクリックします。

無事 Asana を使い始めることができるようになりました!

プロジェクトを作るように求められてますが [マイタスク] だけで使っていくことができます。

そして、ぜひサンプルタスクを作って完了してみてください!
タスク完了のエフェクトとサプライズが待ってます。
試していただきたいのでキャプチャは割愛 (取るのが難しかったのは内緒の方向で)
さらに [プロフィール] - [プロフィール設定] - [ハック] から [さらに楽しく] できるみたいです。


今回タスク管理のためにいろいろと調べていたら、最近はプロジェクト管理系のツールにモダンなチャットが統合されるようになってきているようで持ちました。もしかしたら新しいツールの時代が来ているのかもしれませんね。

普段 Slack を使っていますが、タスクが大量に発生してくる状況になってくるとタスク(イシュー、チケット etc…)を作成するのが手間になったり、埋もれたりしてしまいます。そうなるとチャットから簡単にタスクが作れたらいいなぁと思っていて、チャットからタスク管理よりも、使いやすいタスク管理に Slack などのモダンチャットがついたら最高なのかなと。

当面は個人タスク管理として使う感じですが、少しずつ本格的に使っていく方法を考えてみます。
Asana は ToDo リストタイプと、カンバン形式の両方が選べます。個人タスク管理であることと、使ってみた感覚から、私は ToDo リストが好みのようでした。(たぶん管理がちゃんとできないから雑に上下するぐらいがちょうどいい。それでいてセクション分けできるのがいいです。)

最初はメンターさんに倣って #22 「Trello があるので眠れない」 | #omoiyarifm な、眠れない Trello を検討していたw のですが、感覚的に Asana がマッチした感じで使っています。リストでざっくりと、エフェクト好きな感じが合ったのかな。

「テキスト校正くん」を使って読みやすい文章を書けるようにする

文章を書くときに「この単語は、ひらがな、カタカナ、漢字どれがよいか」や「気づいたら “の” が連なっていた」とったことがあるのではないでしょうか。
気を付けて書いたり、読み返したりしてチェックしますが人力だけではどうしてもすり抜けてしまいます。定型でできるチェックは自動で行い、人でないと判断できない部分に注力しましょう。

環境
本記事の開発環境は以下となります。

  • Windows 10 64bit + WSL Ubuntu 18.04.1 LTS
  • Visual Studio Code
  • テキスト校正くん

「テキスト校正くん」とは

テキスト校正くん は Visual Studio Code で、文章を自動でチェックしてくる拡張機能です。

テキストや Markdown を入力中にリアルタイムで校正してくれます。公式サイト 文章作成・メール作成に役立つ! VS Codeの拡張機能「テキスト校正くん」を公開 - ICS MEDIA によると以下のようなチェックを行ってくれます。

設定も簡単に行うことができるので、不要なチェックを外すこともできます。Visual Studio Code の拡張機能として容易にインストールでき、設定も手軽なのでテキストチェックの入門として、とてもよいです。

また内部的には textlint を使っているとのことです。さらに「テキスト校正くん」自体も ics-creative/project-japanese-proofreading で MIT ラインセンスのもと公開されています。

インストール

Visual Studio Code を起動して拡張機能(Ctrl + Shift + X) を表示します。
[テキスト校正くん] で検索、[インストール] をクリックします。(再読み込みの必要な場合があります)

さっそくテキスト校正くんで文章をチェックしてみます。
新しいファイルを .txt または .md で作成します。テキスト校正くんはテキストファイルをチェックするため .txt または .md のファイルである必要があります。

青空文庫から「夏目漱石 吾輩は猫である」を拝借してエラーが出るようにしてみました。

エディター部分にリアルタイムで赤線が表示され、カーソルをのせるとエラーの詳細が表示されます。また「問題」のビューに一覧も出るので便利です。

まずは指摘を修正し、それから目視によるチェックをすることで文章の質を上げることができます。

設定

デフォルトの設定でいい感じに校正してくれますが、自分用にカスタマイズしたいこともあります。たとえば「半角括弧を使いたい」や「英単語の後に半角スペースをいれたい」など。
テキスト校正くんは簡単な設定でチェックルールをオフにできます。

Visual Studio Code の設定を開きますが、GUI で設定したほうがわかりやすいので GUI エディターを表示します。
コマンドパレット(Ctrl + Shift + P) から prefui で絞り込み「基本設定: 設定(UI)を開く」を選択します。

設定の GUI エディターから テキスト校正くん で絞り込み、不要なルールのチェックを外して設定をします。

設定もリアルタイムで反映されるので、エディターを並べて探りながら設定することもできます。下図例では「句点(。)と読点(、)」が「生まれたかとんと見当がつかぬ」のピリオドのエラー有無に効いています。


私の文章の書き方が独特ということもあり、当初はかなりチェックが入ってしまっていました。ですが「テキスト校正くん」を入れてからはリアルタイムで指摘してくれるので都度直せますし、漢字の開きなど文章の勉強にもなり助かっています。
(過去記事は、そこまで多くはないのですが指摘が多すぎるので追々直していきます。。。)

とはいえ、漢字の間違えや誤入力などはチェックできないので最終的な確認は目視が大事なことに変わりはありません。単純なミスを防ぎ本質的なところに注力させてくれる拡張機能になります。

そしてブログメンター Eyes に耐えられる文章力とセルフレビュー力を鍛えたいところです。

ICS MEDIA さんには、こんなに素晴らしい拡張機能を作り、無料の OSS で公開してくださり感謝しかありません。ありがとうございます!