うしろのこの本ください

なんでもかきます

Role-Based Access Control (RBAC) をVue.jsで表現する

元ネタ

auth0.com

最近仕事で権限ごとに表示できるコンポーネントを制御する必要がでてきて、さてどうするかというタイミングでチームメンバーがRBACのことを教えてくれた。

Roleは1つ以上の権限を持ち、権限はコンポーネントの表示を制御する。まあ難しい考えかたは必要なくて、単にロールと権限の定義と、ログインユーザーのロール、コンポーネントを表示できる権限があれば簡単に実装できる。

以下は動的な制御が不要な場合の例

rbac.js

import rules from "./rbac-rules"

const check = (role, action) => {
  const permissions = rules[role]
  if (!permissions) {
    // ロールが存在しないためfalse
    return false
  }

  if (permissions && permissions.includes(action)) {
    // ロールが渡された権限を持つためtrue
    return true
  }

  return false
}

export default check

rbac-rules.js

export default {
  visitor: [
    'contents_read'
  ],
  writer: [
    'contents_read',
    'contents_update'
  ],
  admin: [
    'contents_read',
    'contents_update',
    'user_update'
  ]
}

Vueではslotを使ってコンポーネントを渡し、中でcheckで権限を見て出し分けると良い感じになる。そのためのラッパーコンポーネントを作る。

Can.vue

<template>
  <div>
    <slot v-if="checkRule" name="yes" />
    <slot v-else name="no" />
  </div>
</template>

<script>
import check from '~/auth/rbac'

export default {
  props: {
    role: {
      type: String
    },
    action: {
      type: String
    }
  },
  computed: {
    checkRule() {
      return check(this.role, this.action)
    }
  }
}
</script>

これで一応コンポーネント2つ渡せば権限NGだった時に任意のコンポーネントにフォールバックさせることができる。不要なら2つ目は渡さなくても良い。

Can.vueを使った例

Hoge.vue

<template> 
 <div>
    <Can role="visitor" action="contents_read">
      <template #yes>
        <Text text="権限あるよ" />
      </template>
      <template #no>
        <Text text="権限ないよ" />
      </template>
    </Can>
  </div>
</template>

<script>
import Can from '~/components/Can'
import Text from '~/components/Text'
export default {
  components: {
    Can,
    Text
  }
}

今回の場合ロールが複数あると判定できないので、ロールを配列で渡してキーとマッチングして取れた配列を単一の配列にマージする、もしくは同じことをするreducerを書くとかする必要がある。ロールはpropsじゃなくてCanからstoreを見るなりしても良い。

また、動的な権限制御が必要な時は定義にstaticとdynamicの概念を追加する必要がある。元ネタの方に載っているので興味ある人は実装してみると良いと思う。

おわり

Flutter始めたので導入周りメモ

Flutterがfor webを発表し名実ともにマルチプラットフォーム対応になったのでいっちょやるかと思い立ち、環境構築した。環境はmacOS64bit。

flutter.dev

FlutterはDartという言語を用いてマルチプラットフォームGUIアプリケーションを作ることができるGoogle製モバイルアプリケーションフレームワーク。モバイルとはいえ昨今はWebもモバイルファーストばかりなので問題なし。

環境は基本公式のドキュメントを見ながらやれば特につまづくことはないはず。

flutter.dev

今回はユーザーの下に落としたzipをunzipした。Goも同じ場所にあるしここでいいかみたいな感じで。ほんとはDeveloper直下とかのが良いはず。。。

パスを通すとflutterコマンドが使えるようになる。手順にはexportでやるとあるけど揮発するので.bash_profileに追記する方が良い。

その後はflutter doctorで足りない依存を検査してくれる。これがかなり親切で、○○が足りないから××をやれと教えてくれる。

自分の場合はAndroid Studioが入っていなかったので、以下のようになった。

f:id:apple19940820:20190512194328p:plain

VSCodeは入ってる人多いと思うけどなかったら最新版入れる。入れるだけでOK。

Visual Studio Code - Code Editing. Redefined

Android SDKのためにAndroid Studioを入れる。

Download Android Studio and SDK tools

Android Studio入れるだけではダメで、FlutterプラグインDartプラグインが必要らしい。セットアップ後pluginsからマーケットプレイス開けるのでそこでFlutterを入れるとついでにDartプラグインも入る。

f:id:apple19940820:20190512200037p:plain

あとxcodeのインストールが不完全とかbrewで色々いれろコマンドはこれだとかとにかく親切。よっぽど酒が回ってない限りは大丈夫なはず。

自分はターミナル分割して上で逐次doctorしながら下でbrewで必要なものを入れてインストーラあるやつは別でみたいな感じで作業を進めた。昨今はインストール作業自体軽くなってて同時に進行してもクソスペックPCじゃなければ固まることもない。(ブログも並行して書いてる)

f:id:apple19940820:20190512200400p:plain

特にcocoa podsのセットアップは時間かかるので先にやった方が良い。XCodeはクソ重くて後回し。

connected deviseは適当なスマホをPCに繋げればいい(はず)。色々やってもだめならflutter doctor -vで認識する(はず)。

自分のflutter doctor -vの結果

f:id:apple19940820:20190512203621p:plain

もろもろセットアップが終わったらflutter createでプロジェクトを生成でき、flutter runで起動するが、xcodeで署名を作った少し設定をいじる必要がある。詳細は以下が詳しい。実機で動かしたい場合も参考になる。

qiita.com

f:id:apple19940820:20190512203740p:plain

使うエディタは公式的にはAndroidStudio or Intelli J or VSCodeVSCodeがお手軽だしWebから来た人はflutterプラグイン入れるだけで良くておすすめ。プロジェクトを生成すると以下のような構成になる。

f:id:apple19940820:20190512204632p:plain

今気づいたけど、VSCodeは刺しているデバイスを検知して表示してくれるっぽい。

f:id:apple19940820:20190512204759p:plain

VSCodeのコマンドパレットでflutter Lunch EmulatorとするとAndroidか、iOSエミュレータが勝手に起動する。

Webを始める場合はflutter New Web Projectで作れる。すごい。

f:id:apple19940820:20190512210224p:plain

デプロイは面倒でまだやってない。けどドキュメントに各プラットフォーム向けに書かれている。

あとはDartゴリゴリ書いていくだけ。頑張ろう。

おわり

auto chessやろう

面白いので皆やりましょう

Dota2のおまけみたいな感じなのでDota2入れればできます。Dota2は無料なのでauto chessも無料です。

store.steampowered.com

mobile版も出るらしいのでそれまでに触っておくと流行にいい感じに乗れます。

確実に流行ります。ゲーム性が日本人に向きすぎていて、しかも無料。無料の麻雀。正直グウェント初めて触った時より感動してる。

絶対流行ります。海外の有名配信者はこぞってauto chessやってます。(AmazとかAsmodaiとかSavjzとか)

日本でもハースプレイヤーやシャドバプレイヤーが徐々に始めてます。今後auto chessの経験が長くて環境を良く知っている、または知識が深い配信者のauto chess入門動画が死ぬほど再生されるでしょう。

日本で競技化されるかは知りませんが、既にそういうのをやっているところはあります。詳しくはWikiで。

まあ、色々書きましたけど超面白いからやった方がいいぜ

おわり

令和最初の○○

令和最初の

聴いたラジオ

鷲崎健のヨルナイト×ヨルナイト

www.joqr.co.jp

書いたコード

github.com

born-the-reiwa.netlify.com

開いたウェブサイト

Twitter

twitter.com

投稿したツイート

いいねしたツイート

開いたゲーム

シャニマス

shinycolors.enza.fun

観た動画

www.nicovideo.jp

読んだ本

蒼き鋼のアルペジオ 17巻(Kindle

聴いた曲

I did + I will

youtu.be

(危く三角音源になるところだった…)

見た画像

https://cdn.discordapp.com/attachments/514369806351335431/572799479900536837/suga.jpg

笑い

和藤渚さんの三角音源

久しぶりに聴いたけどまだマイク吹いてて安心感あった

終わり

旧時代の人間として生きていきましょう

SI→スタートアップで成長感じる系男子

男子です。3月末で2年お世話になった会社を辞めてニートになろうと意気込んでいたらおかんに殴られそうになったので転職活動して今は東京のスタートアップでインターンとして働いてます。

元々はJavaとかC#でバックエンド書きつつたまにマークアップが仕事だったりだったんですが、今はほぼフロントエンド専門でじょいんしてます。3週間くらいたったのでお気持ち表明です。

なんでインターンかっていうと前職と働き方が大きく違うので様子見という感じです。僕が採用でも同じ事します。理にかなっとる。

成長を感じる

やんわりと感じる事もあればレベルアップの音がすることもあります。前職はゴリゴリのSI企業だったし全力ウォーターフォール(たまに逆流)みたいな感じでそれも愉快でしたが、今はスクラムやってます。

何もかも違う、正直初♡体♡験な事しかなくて覚えるの大変(特に業務面のドメイン知識)だけど、滅茶苦茶楽しい。良い刺激になってます。

開発メンバーやスクラムマスターに助けてもらいつつ、フロントエンドに関する部分は自分から提言してチームも真剣にそれを受け止めてくれてます。

スクラムではインクリメントという、そのスプリントで何を達成したかを明確にする目標みたいなものがあるんですが、「成長を感じた」をインクリメントとするなら自分の場合はこんな感じです。

  • GitHub使ったチーム開発ちゃんと覚えた

  • スクラムの基本的な流れを覚えた

  • Vue.js/Nuxt.jsの良い設計悪い設計が分かるようになってきた

ここら辺は個人の価値を直接あげるタイプの成長なので自然と身についていったのがありがたかった。設計の良し悪しは中規模以上の開発現場に入ってコード見て初めて合点がいったって感じでした。

知識として知っていたものが実際目の前に現れて辛い気持ちになった延長で直し方まで察せるようになってきた。分かりやすく成長したなと思えました。

結構早めに気が付いたことがあってスクラムとも関連するんですけど、出社してすぐその日のうちにやる事を明確にしてチーム全員が知っている状態にしておくと良いです。これやっとくと何が身に付きそうとかこいつどこまで出来るようになったんだとか全部透過的になるのでひじょーーーに健全です。そして金曜は感謝と共に終わる。素晴らしいね。

分報はいいぞ

自分はかなりお喋りな方だと思ってますが、それ以上に書く事が好きです。自由に書きなぐっていいし困ってそうだったらヘルプ貰えるチャットとして分報を書きまくってます。

まだ数週間しか働いてなくて、既存のコードとかに手を入れる場合結構深掘りしなきゃいけないんですが、脳内で出てきたワードは全部分報に出します。書きすぎて流れて行ったらコピペして戻します。つまり分報とは遊戯王でいう墓地みたいなもので、第二の手札なのです。つまり強い。墓地利用しないデュエリストおるか?おらんやろ。

人の分報をのぞくのも結構面白く、自分みたいにあーとかうーとか分報書いてからコードに落とす人もいれば〇〇終わった、プルリク出す。みたいな感じの人もいます。

流れていっちゃうのが勿体ないのでピックしてGistあたりに置いておけるといいなと思いました。勿論privateで。じゃないと脳直で書けなくなりそう。

余談:Vue/Nuxtの話

というよりVuexの話。実際に中規模以上のNuxtやVueアプリケーションを見て感じる事は、Fluxが守られてないことがままあるということ。僕自身はインターンやるまでフロントエンドを完全に独学でやってたので、どこまで通用するかという面で不安は感じてました。実際現場に入ってみると苦労はするものの読める。書かれた意図も何となく掴めて、どうすれば受け入れ条件を達成できるのかも大体浮かぶ。

みたいな感じなんですが、Vuexが本来の思想から大きくずれていると難易度が跳ね上がります。単一方向フローが破壊され、変更による影響範囲が分からなくなり、状態そのものが信頼できないものになります。Vuexは使う事ではなく守る事が大事なんだと。

ぶっちゃけmapStateはAPIごと消して欲しい。Flux守ってたらGettersで良いはずなので。

中規模開発で沢山関数書いて分離とかしてるとVuexに似た何かみたいなのが出来がちです。apiエンドポイントをまとめて外出しした場合、呼び出しは必ずコンポーネントかVuexのactionsでやりましょう。VueにはローカルステートとVuexの2種類しか状態が存在しないことを魂に刻んでおくと平和だと思います。

余談:TOKYOは高い

まず飯が高い。んで家賃が高い。友人宅に転がり込んでるから関係ないけど、家賃聞いてびっくりした。びっくりしたのでSEKIRO買ってあげた。

パンの安さに気付いた。米は高い。ローソンの500円で買える大盛パスタのコスパに感謝。

東京在住の方々、よく生きていけますね。こんなに違うのかと正直引いてます。友人はインターン始まる前に東京は住むとこじゃねえって何度も言ってましたが身に沁みました。食費追いつかない。。。

でもUber Eatsは神です。外国人の配達に部屋番教えたらThanks!と返ってきましたが日本人は今のところ返答なしです。人情だね。

余談:退職当月の給与は死ぬほど低い

社会保険2回分と住民税引かれて6000円くらいでした。GWでクレカの引き落としが5月まで伸びてなかったら即死だったぜ。神に感謝。

おわりに

経歴弱いしアウトプットも大したことない僕を拾ってくれたことに感謝してます。見てるか知らんけど前職の人、僕は元気ですのでご心配なさらず。

Vue.jsで全然使ってない機能とかが割とあるって思って

吾輩はなんか使ってない/使う機会がないみたいなVue.jsの機能がある。理由はまだない。

最初に言っちゃうとグローバルで使うようなのは殆ど書いてない。Vue.useくらい?

filter

<!-- mustaches -->
{{ message | capitalize }}

<!-- v-bind -->
<div v-bind:id="rawId | formatId"></div>
filters: {
  capitalize: function (value) {
    if (!value) return ''
    value = value.toString()
    return value.charAt(0).toUpperCase() + value.slice(1)
  }
}

マスタッシュ{{}}かv-bindでパイプ演算子をフィルタにかけたいdataに繋げていくことで使える。パイプは複数連結できて、前から順番に通る。

便利じゃん。なんで使ってないんだろう?って思ったけどdataの加工は全部computedでやってるからかな。よくなさそう。使おう。Vue.filterで使えばグローバルに定義もできる。こういうのはグローバルに生えてても良いのかもしれない。目的が明確だしね。

mixin

// `myOption` カスタムオプションにハンドラを注入する
Vue.mixin({
  created: function () {
    var myOption = this.$options.myOption
    if (myOption) {
      console.log(myOption)
    }
  }
})

new Vue({
  myOption: 'hello!'
})
// => "hello!"

嘘です使ってない理由あります。グローバルmixinは主語がデカくて嫌。スコープを持てない奴は万死。みたいな感じ。なんかそういう事するときはprovideinjectがあるのでそっち使う。

jp.vuejs.org

一応、Vue 3.0からmixinを経由してもクラスAPIでの型が消えないようにはなってるらしいけど。。。使う気起きないな。

observable

const state = Vue.observable({ count: 0 })

const Demo = {
  render(h) {
    return h('button', {
      on: { click: () => { state.count++ }}
    }, `count is: ${state.count}`)
  }
}

2.6から追加された新参者、今後超重要なポジションに付く予定の逸材。しかし書いたことはない。新しいから仕方ないね。

簡単に言えば渡したオブジェクトをリアクティブにできるという機能。オブジェクトは再帰的に探索され、Object.definePropertyを通ってgetterとsetterが生える。2.x系では渡されたオブジェクトを直接弄るが3.0以降は別オブジェクトが返るようになる。

何故この機能の存在が重要かというのはVue.jsのrfcを眺めて見ると何となくわかる。

github.com

ワクワクするrfcだがあまり反響がないように感じる。。。何故。。。

render

render: function (createElement) {
  return createElement('h1', this.blogTitle)
}

所謂描画関数。ReactみたいにJSXがさらっとかける訳でもなく、そもそもtemplate捨ててる時点でVueの良さが死んでる気がして中々手を出し辛い。

templateにfunctionalって書く奴は稀に使う。あれはいいよね、なんか軽そうだし(適当)

Vue 3.0でfunctional componentは遂にただの関数になります!って言われてたけどよく分からない。そうですか。。。

updated

updated: function () {
  this.$nextTick(function () {
    // ビュー全体が再レンダリングされた後にのみ実行されるコード
  })
}

ReactのcomponentDidUpdateに相当するって考えると出番多そうだけど、何故か一回も書いたことがない。

コード例のように$nextTickで子のコンポーネントの再描画を待つことが出来る。便利。しかし書いたことはない。createdmountedは頻繁に書くのにね。

思うに値の変更監視はcomputedでできるVueでは更新時の処理を更新時のライフサイクルメソッドで書かないことが多いんじゃないかと。自分だけなのかな?

ref

<!-- vm.$refs.p は DOM ノード -->
<p ref="p">hello</p>

<!-- vm.$refs.child は child-component のインスタンス -->
<child-component ref="child"></child-component>

全く使った事がないわけではないけど、めったに書かない。要するに子への直接的な参照が欲しい時にrefへ公開させアクセスする。

konva系のライブラリを触った時にcanvasへの参照が必要で書いた。なんかやんちゃ方面なAPIって気がしてる。少なくとも連打して良いもんじゃない。参照を持つってそいつに束縛されるって事だもんね。

cloak

<div v-cloak>
  {{ message }}
</div>
[v-cloak] {
  display: none;
}

上記はv-cloakが付いたDOMはVueインスタンスコンパイル終了時まで見えなくなる。v-cloakCSSと組み合わせて、インスタンスコンパイル終了まで残り続けるcloakを作ることが出来る。(cloakってマントって意味らしいよ)

なにこれ便利じゃん知らんかったわ。使います。

おわり

細かい所言うとVue.versionとか全く使わないけど流石にスルーで。APIのページは結構読んでるつもりだけど知らないのもあって面白かった。

Vue 3.0になった時にまた大きく更新されるだろうし、それも楽しみですね。早くこないかな~~~~~~~~~~~~~~~

おわり

暫くTOKYO

です。インターン行ってきます。

フロントエンドのおしごとします。楽しみです。

東京おうち高いです。暮らすだけでせいいっぱいです。友達がいてよかったと思います。

自宅のPC達ともしばらくはお別れです。それが一番寂しいです。こんなに愛しているのに。

ブログは普通に更新するしMacもってくからコードも書けるのでリクエストあれば対応します。

おわり

最近のミリシタカード事情、スコアタとか

別に研究してるわけでもないので自分が感じていることだけ。間違ってたらTwitterのリプかなんかで言ってくれれば直すし、GitHubでプルリクもOK。

github.com

それから旧フェス限(紬、歌織)と特化限定を比べた時の差の情報も欲しい。わかる人教えて。

スキルについて

先日フェアリー組のフェス限が追加され、これでALL楽曲のサポートメンバーがすべてフェス限で埋まるようになった(と思う)。

センター効果は通常のタイプバフ90%、1色限定編成時タイプバフ95%の2種。

スキルは信号機とPFが持つダブルブースト(以下DB)と、通常のコンボボーナスより少し強い28%バフのフェス限コンボナ(以下Co)がある。

VoDaVi別にまとめると以下のようになる。(Exは省略)

Vo Da Vi 合計枚数
Fa 90%/Co,95%/DB (95%/Co)*2 95%/DB 5
An (95%/Co)*2 95%/DB 90%/Co,95%/DB 5
Pr 95%/DB 90%Co,95%/DB 95%/Co 4

全種95%編成ができるようになり、各タイプ2か所ずつDB持ちを組み込むことができる。

また、Prを除き強化Co持ちを2枚使う事ができる編成もある。

スコアについて

色々な計算式はググれば全部出てくるためここでは省略。計算結果から見て取れる傾向のみに触れる。

当然DB持ちを編成すると理論値は大幅に上がる。PF組が追加されるまでは総アピール値の差もあり「DBを編成に組み込むと理想が伸びて1%~周りが落ちる」という状況だったが現状はアピール値の差が詰まり、DB持ち編成が上回るケースもある。これは志保+伊織のFaDa編成と、貴音+紬のFaVo編成、静香編成等を比べると分かりやすい。

ミリシタにおいてスキル発動率の上限が40%である限り(発動しない確率の方が高い限り)「発動したら強い」カードよりも「発動しなかった時のロスが少ない」カードの方が重要視される。7秒間隔で4秒間発動するスキルが強いという理屈はこれ。

しかし、「7秒は強い」は真だが「7秒より13秒は弱い」は偽。また「偶数秒は弱い」も違う。楽曲の長さによって7秒スキルとの相性は変わるため、ある曲では探偵紬の方がスコアが伸びるけどある曲では限定ロコの方が伸びるという現象は平気で起きる。

基本7秒/4秒や9秒/5秒のようなコスパの良いカードは強いため即採用される。スコア2:コンボナ2~3(orコンボナ2: DB1)のバランスがもっとも良いとされている現在では相方のカードを変更する事が多いが、これは楽曲の長さに合わせなければロスするので注意。ここら辺は書き始めると面倒だから今はスルーで。

コンボナ3>スコア3の理屈はWikiで(誤差)

スコア計算

今後引かなければならないガシャ

特化ステータスカードを所持しているか否かが大きく影響する状態だが、今後はすべてのパターンでこれが発生しかねないためスコアタに影響のないカードだから引かない戦略がし辛くなった。全パターンで95%センター編成できるようになった影響。

今のところ優先順位は以下のようになる。

引いた方が良い

  • 特化ステカード

スルーして良い

  • フェス限と被る発動率のコンボナ

特化ステ律子の例を見ると7秒/4秒と9秒/5秒の特化ステータスカードは一度確保すれば半年以上使っていける。

それ以外は楽曲ごとに対応できるよう1枚は持っておきたい。

旧フェス限カードについて

冒頭でも触れたが、旧フェス限カードの性能はいつか通常限定SSRに追い抜かれる。

対象はフェス限の紬、歌織、星梨花、エミリーで、全員28%コンボボーナス持ち。特化ステータスのインフレが進んで26%コンボナでもそっちを使った方が良いみたいな状況になればフェス限だとしてもリストラする羽目になる。まだ先だとは思うけど気にしておくと良いかもしれない。

退職した

した!!!!!!!!!!!!!!

俺は二―ト!!!!!!!!!!!!!!!!!!!!!!!!!

f:id:apple19940820:20190329223625p:plain

先輩に貰った宝物、大切にします。

おまけ

転職活動中ですがお金がないのでプログラミングのバイトやろうかなと悩んでいます。

Vue/Nuxtできます。依頼はDMかメールで。

人は5分間だけだったら教師になれるかも

って先日鷲崎さんがラジオで喋っててなるほどなって思った。自分は何種類くらい教師になれるだろうと思って考えて見た。

何となくだけどパブリックイメージとして教師=間違ったことを教えてはいけないみたいなところがあるから「これはできるぞ」って思ったものは相応に自身があるものって事になる。

例えば自分なら

  • ポケモンに関する知識
  • (昔の)遊戯王に関する知識
  • 簡単な野球に関する知識
  • 推理SFに関する知識

あたりは5分くらいなら教師できると思っている。ポケモン遊戯王はもはや今の自分を作り上げたといっても良いくらいどっぷりだったし、昔は野球少年だったし、中高、高専時代はよく本を読んだ。

これらは自分の中で枯れた知識になっているため、バックボーンが厚く自信を持つことが出来るわけだ。

逆にこういうのは自身がない。

  • プログラミングに関する知識
  • ラジオに関する知識
  • 歌、音楽に関する知識
  • アイマスの知識

これが何かと考えると、今の自分を構成している要素だった。つまり現在進行形でハマっている or やっているものについて自分はまだ教師が出来るなんて思えるほど自信を持ててないということか。

それから共通点を探すと、これらは割と大人になってから途中参戦したものだった。まだ思い出がないから思い出をバックボーンにして語る事ができない。

人が人に教える時って多分自分の体験を元にすることが多い。それができるのは自分の中で知識が枯れていて、まー喋ってもいいかな間違ってへんやろうしという納得感と、話の材料になる思い出がないと出来ないんだと思った。それでそれが自信という奴になる。

それで5分という短い時間で話したい事、つまりプレゼン資料の中で最優先に伝えたいことを喋ってくれることが期待できる。5分教師というしくみとても面白いですね。

鷲崎さんは知らないお姉さんとかに5分だけ先生やってよ何でもいいからって話を振るのが面白いって言ってた。確かに、得意&好き&伝えたいのコンボプレゼンが面白くないわけないな。

ついでに今の自分のステータスが何となく把握できてそれも面白かった。

おわり

Vue 3.0で入る(予定の)Class APIについてのRFCを読んだ

github.com

Evanが先日VueのRFCに投げたやつ。VueのコンポーネントがES6 Class Syntaxで記述できるよう拡張するというもの。

ブラウザ(CDN版)

class App extends Vue {
  // options declared via static properties (stage 3)
  // more details below
  static template = `
    <div @click="increment">
      {{ count }} {{ plusOne }}
    </div>
  `

  // reactive data declared via class fields (stage 3)
  // more details below
  count = 0

  // lifecycle
  created() {
    console.log(this.count)
  }

  // getters are converted to computed properties
  get plusOne() {
    return this.count + 1
  }

  // a method
  increment() {
    this.count++
  }
}

SFC

<template>
  <div @click="increment">
    {{ count }} {{ plusOne }}
    <Foo />
  </div>
</template>

<script>
import Vue from 'vue'
import Foo from './Foo.vue'

export default class App extends Vue {
  static components = {
    Foo
  }

  count = 0

  created() {
    console.log(this.count)
  }

  get plusOne() {
    return this.count + 1
  }

  increment() {
    this.count++
  }
}
</script>

これに伴い今までのオブジェクトベースで記述する際に行っていた new Vueによるマウントは行わず、別に専用のグローバルなAPIを生やしてそっちでやるようになる。ReactDOM的な?

なんでクラスAPIを入れるか、というのはとても丁寧にEvanが書いてくれているので本文を読んで欲しい。

一応要約すると既存のオブジェクトベースの構文では型推論が効き辛い部分があるため、TypeScriptとの相性改善という面が強い。vue-class-componentの利用も考えられるが、vueのcoreとの互換性維持のオーバヘッドがあって、ならcoreに取り込んだれという感じ。

オブジェクトベースな書き方が出来なくなるわけじゃなく、公式のドキュメントも双方確認できるようにするっぽい。描画関数がどんどん希薄な存在になってる気がするけど。例にあるように、基本はVueのサブクラスを作って使う。

data

dataはクラスフィールドとして宣言できるけど、Field declarationsはまだstage 3なのでTSかBabelが必要。

class MyComponent extends Vue {
  count = 0
}

TypeScriptでは型注釈付けられる。

class MyComponent extends Vue {
  count: number = 1

  created() {
    this.count // number
  }
}

それ以外では普通にconstructorthis.count = 0みたいな奴が考えられる。こっちはコンストラクタの中でsuper()が必須になる。

ただこれは非推奨とされていて、何故かというとコンストラクタの中にいるインスタンスをライフサイクルメソッドからthisで指すことが出来ないため。ライフサイクルメソッドから見たthisは実際には本体のインスタンスを指している。

let instance

class MyComponent extends Vue {
  constructor() {
    super()
    instance = this // actual instance
  }

  created() {
    console.log(this === instance) // false, `this` here is the Proxy
  }
}

というわけで、トランスパイルする手段のない環境では以下のようにオブジェクトベースで書いていたものそのままでも良いようだ。

class MyComponent extends Vue {
  data() {
    return {
      count: 0
    }
  }
}

ライフサイクルフック

ライフサイクルフックはクラスメソッドとして記述する。普通。

class MyComponent extends Vue {
  created() {
    console.log('created')
  }
}

props

propsが結構特殊でなんやかんやあってこれだけデコレータ使うんだけど、ちゃんとその理由も順を追ってサンプルコードと一緒に説明されている。Evanのドキュメントはかなり読みやすい。英語も簡単で紛らわしい表現がなくて助かる。

まず、propsを使うだけならば静的プロパティとして書けばおk。

class MyComponent extends Vue {
  // props declarations are fully compatible with v2 options
  static props = {
    msg: String
  }

  created() {
    // available on `this`
    console.log(this.msg)

    // also available on `this.$props`
    console.log(this.$props.msg)
  }
}

全部$props経由でもアクセスできる。ここはv2と変わりない。v3では宣言自体省略できる。

class MyComponent extends Vue {
  created() {
    console.log(this.$props.msg)
  }
}

TypeScriptによる静的な型のチェックのために@propデコレータが提供されるみたいだ。以下は静的解析時のみに動き、実行時ではstatic props = ["count"]と等価になる。

import { prop } from 'vue'

class MyComponent extends Vue {
  @prop count: number

  created() {
    this.count // number
  }
}

またより具体的な検証のためのオプションオブジェクトも渡せるようになる。

import { prop } from 'vue'

class MyComponent extends Vue {
  @prop({
    validator: val => {
      // custom runtime validation logic
    }
  })
  msg: string = 'hello'

  created() {
    this.count // number
  }
}

propsのデフォルト値を設定したい場合TypeScript側の制限で@props count: number = 1みたいには書けない。オプションオブジェクトのプロパティとして渡す方法が提供されている。

class MyComponent extends Vue {
  @prop({ default: 1 }) foo: number
  bar = this.foo + 1
}

TypeScriptではインターフェイスを定義して渡すことが出来る。

interface MyProps {
  msg: string
}

interface MyData {
  count: number
}

class MyComponent extends Vue<MyProps, MyData> {
  count: number = 1

  created() {
    this.$props.msg
    this.$data.count
  }
}

いいっすね~~~

computed

本家と前後したけどcomputedプロパティはクラスのgetterとして記述する。内部ではVueの算出プロパティに変換され、計算結果やキャッシュを返す。

class MyComponent extends Vue {
  count = 0

  get doubleCount() {
    return this.count * 2
  }
}

methods

普通にクラスメソッドとして書く。ライフサイクルフックもこれ扱い。

class MyComponent extends Vue {
  count = 0

  created() {
    this.logCount()
  }

  logCount() {
    console.log(this.count)
  }
}

このthisは自動的にインスタンスにバインドされ、this.hoge.bind(this)をしなくても良くなっている。

その他

クラスベースAPIにないものは静的フィールドとして宣言する必要がある。たとえばtemplate

class MyComponent extends Vue {
  static template = `
    <div>hello</div>
  `
}

静的クラスフィールドはstage 3なのでトランスパイルが必要。だめならテンプレートリテラルObject.assignなどで取り付ける。

class MyComponent extends Vue {}

MyComponent.template = `
  <div>hello</div>
`

または

class MyComponent extends Vue {}

Object.assign(MyComponent, {
  template: `
    <div>hello</div>
  `
})

継承

普通に extends で継承していける。

class A extends Vue {}
class B extends A {}
class C extends B

Vue.extendでこうするのと同じ意味。

const A = Vue.extend({})
const B = A.extend({})
const C = B.extend({})

UIコンポーネントの直接継承は有用ではないとしている。そのロジックだけを継承し、レンダリングに関してカバーできないため。代わりにmixinかslotによるコンポジション集約をする方が良い。まあ継承の継承は変更に弱くなるのでそもそもアレ。GUIは特に変更が多い領域だし、Javaとかのそれより継承とは相性が悪い。基底クラスからの派生以外ではやらない方がいいと思う。

mixins

型推論が飛ばないようにするにはmixin自体をVueのサブクラスとして宣言する必要があるらしい。実際に使う時はサブクラス化したmixinからさらに拡張して使う。

ただし、型推論が必要ないならオブジェクトベースの書き方でも良い。

import Vue, { mixins } from 'vue'

class MixinA extends Vue {
  // class-style mixin
}

const MixinB = {
  // object-style mixin
}

class MyComponent extends mixins(MixinA, MixinB) {
  // ...
}

その他質問とか

本文読んで。1つだけ紹介すると、Reactはhooksでてクラスから離れていってるよ?っていうのに対してEvanは「Reactのコンポーネントモデルの概念はクラスと相性が良くないけど、本質的にクラスが悪いってわけじゃなくてVueではReactのそれよりも適している。hooksみたいなクラス&オブジェクトベースと同等の機能を持つものも提供するかもしれないけど、まだ先のことだね」とのこと。

その他にも色々考えたけど結局こうなったみたいなのが書いてあって読んでて面白かった。nuxt-tsもリリースされたことだしVue3に向けて素振りしておくと良いかもしれない。Vue.extend or vue-class-component で迷うのはあるけど。

個人的な感想

twitterを眺める限り反応よさげ。自分も良いと思ってます。1つ気になったのがVue.extendについて言及がなかった点だけど、既出だったみたい。

t.co

やりとりを見てるとクラスAPIはこれの置き換えな存在っぽい?Vue.extendの型推論を強化するか、クラスAPIへの機械的(又は内部的)な変換が出るか、まだ結論出てない様子。

はい

@rickyruiz

The RFC does not mention anything about Vue.extend explicitly. As a TypeScript user, I've never needed vue-class-component.

Using classes will be the recommended option for TypeScript users? Are there going to be any disadvantages if I keep using Vue.extend instead of classes? Vue.extend will continue to work as part of the existing object-based API support. However, if you are indeed using TypeScript, it's recommended to use classes in v3 as that's where we will be focusing on in the future in terms of >improving type inference.

ようするに、残すけど型のサポート強化されるしクラスで書いた方が良いと思うよってことらしい

おわり

ミリシタ担当アイドルシートをウェブでつくれるやつつくった

million-tantou-gen.netlify.com

はい

スコア計算機に比べて超省エネだけどまあね。

スマホで一々画像読み込んで配置してってやるよりマシだと思う。Canvasで色々やってるからスマホ対応が面倒でやってない。

ただPWAモジュール入れてるからオフラインでも動くし、PCならChromeのDesktop for PWAでデスクトップアプリ化もできる。メリット分からんけど。最初画像沢山読むけどキャッシュされるから2回目以降は軽い。計算機と一緒。

使ったのはvue-konvaっていうkonvaをvueでラップしたやつ。

github.com

Canvas使うの初めてだったから最初は分けわかんなかったけど何とかなった。ゲーム作ってる人たちはこういうのでゴリゴリやってるんだと思うと正気じゃないね。

こういう軽めのをポンポンあげていきたいとは思いつつ、身が入り切らなくてやべーコード量産しかねないというジレンマがある。これも相当ヤバいからリポジトリ貼りたくないし。見るなら探してご勝手にって感じ。書き捨てだから許して欲しい…

そのうち使いやすくするかもしれん。来週の自分にまかせた。

おわり

ミリシタのスコア計算をする簡易ツールつくった

つくった

millionlive-simple-score-calc.me

みりしたつーるさんのAPIと姫のAPIで成り立ってる。リポジトリはこれ。

github.com

使い方は楽曲選んで、5枚カード選んで、任意の総アピール値を入れて、計算を押す。そうすると1計算で1万回分のライブシミュレーションを行う。編成とアピ値は端末ごとに保存できて呼び出すことが出来る。

シミュ結果はこういう情報。

f:id:apple19940820:20190217040323p:plain

そのチーム編成での楽曲ベーススコアと理論値~50%の確率で達成できるスコア。あとアイコンはFantasiaへの個別カードリンクになってる。同僚は自分のリーダーと同じになるようにしてある。(手抜いた)

詳細はそのカードのスキルがその楽曲プレイ時に何回まで発動するかと、シミュ1万回で合計何回発動したかが書いてある。今思えばリザルトに総アピール値書いてないな。。。まあいいや

f:id:apple19940820:20190217034717p:plain

ほぼみりしたつーるのAPIで返って来る値まま。

情報量が少なくてさくっと動いて、自分の使ってる編成が使い物になるかどうか分かるだけで良かったから余計だと思った機能は全部削った。

スマホならホーム画面に追加をするとアプリ化できる。PWAってやつ。PCもChromeでメニューからインストールみたいなのするとデスクトップアプリ化できる。

f:id:apple19940820:20190217044615p:plain

f:id:apple19940820:20190217044632p:plain

f:id:apple19940820:20190217044728p:plain

オフラインでも動くと行きたいけどシミュ1万回をブラウザでやるのは結構色々準備がいるのでまだ。実装は試すかも、そしたらネットに繋がってなくてもシミュ回せるようになる。

技術的な方の話

Nuxt.js + Element UIで作ってデータは全部API経由で取得する。一部は自分で建てたFirebaseのcloud functionsを経由している。(モバイル対応)

ホストはNetlifyで、GitHubのmasterが更新されたら自動的にNuxt generateが走って静的なサイトがデプロイされる。

PWAモジュールを雑に突っ込んであって、1発目の読み込み以降はキャッシュが効いて早くなる。楽曲データに関してはlocalStorageに保存して、最後尾楽曲のidをリクエストで投げて更新があれば差分が戻って来るようにした。(API追加のissueを出して対応して貰った、ありがとうございました)

チーム編成もlocalStorageに保存してある。というか、vuex-persistedstateでストアと同期している。これめっちゃ便利ね。

github.com

これ作り始めたのが去年の年末で、ラウンジのメンバーにフィードバック貰いつつこの形に落ち着いた。今後はTSで置き換えられるところは置き換えていきたいのと、スクショからチームをセットできる機能を付けたい。やっぱポチポチ面倒だし総アピ値いちいち入れるのもだるい。

あとこのアプリに実装するかは分からないけど、手持ち一覧のスクショから強そうなチームを作って提案するとか。こっちは色々事情があって難しそうだけど。あれば便利。

要望あればリプでもDMでもissueでもなんでもいいので投げて貰えれば出来るときにやります。

おわり

Element UIのel-tableでslot-scopeを使っていた人はVue2.6.xにあげるのは待った方がいいかも

先日Vue2.6.0がリリースされてv-slotが追加されたので、ついでにNuxtと一緒にバージョンアップしようとしたら既存コンポーネントが動かなくなった。

Element UIのel-tableはel-table-column内にslotを差し込むことで任意の要素を表示できる。この部分で今まではslot-scopを使っていたがVue2.6.0での変更によって壊れてしまったみたいだ。

issue

github.com

自分が壊れたのを確認したのは以下のコードだ

<el-table :data="selectedCardList" target-order="push" max-height="960">
  <el-table-column label="カード">
      <template slot-scope="scope">
        <a :href="createImgUrl(scope.row.id)" target="_blunk"><img :src="scope.row.resourceId" style="max-width: 100px; width: 100%;"/></a>
      </template>
  </el-table-column>
  <el-table-column label="ボーカル値">
      <template slot-scope="scope">
        <span>{{ scope.row.vocalMaxAwakened }}<br>{{ "(" + `${scope.row.vocalMaxAwakened + scope.row.vocalMasterBonus * 4}` + ")" }}</span>
      </template>
  </el-table-column>
  <el-table-column label="ビジュアル値">
      <template slot-scope="scope">
        <span>{{ scope.row.visualMaxAwakened }}<br>{{ "(" + `${scope.row.visualMaxAwakened + scope.row.visualMasterBonus * 4}` + ")" }}</span>
      </template>
  </el-table-column>
  <el-table-column label="ダンス値">
      <template slot-scope="scope">
        <span>{{ scope.row.danceMaxAwakened }}<br>{{ "(" + `${scope.row.danceMaxAwakened + scope.row.danceMasterBonus * 4}` + ")" }}</span>
      </template>
  </el-table-column>
</el-table>

ミリシタのカードデータ表示部分で使っていた。特にURLを整形してPrincessへのリンクを貼るところとか他にどうしようもないし、一旦Nuxtごとバージョンを戻した。

対処法

scope.row ? scope.row.title : '' のようにv-ifでscope-rowの値を見ると通る。恐らくv-slotが親のレンダリングサイクルから分離し、子としてインライン関数に展開される最適化がされているため起こったもの。

v-slotと親テーブルのレンダリング処理が分離したので、v-slotより先に親テーブルがコンパイルされて、未定義の子を参照しようとして落ちるという話。

v-ifでv-slotがレンダリングされるまでは空を返してやれば良いということだ。

v-slotに合わせてslot-scopeも同様の動作になるよう変更されている。つまり現状v-ifを付けない限り解決はできない。(代替手段がない)

ただし、テーブルコンポーネントのような要素の多いものにv-ifを付けて回るのもアレなので余程急ぎでもない限りElementUI自体のアプデを待った方が賢明だろう。

Nuxtだけバージョン上げてNuxtが使うVueのバージョンだけ下げるみたいなこと出来ないのかな。いやできるだろうけど、出来るだけそういうので苦労はしたくない。

おわり

ミリシタライブシミュレーションAPI 「API for TD Score Calculation 」をNuxt.jsで使う

多分みんな使ってる編成とか出してくれるツールの計算部分だけAPIに切り出して公開してくれたもの。まだ試験運用らしい。

api.megmeg.work

ツールの方

megmeg.work

んで作った奴

github.com

使い方というか、GETで投げる時のパラメータが必要でそれの情報源としてmatsurihi.me使う前提な感じあるので前作った奴を拡張する形で進めた。

その時の記事

ushirock.hateblo.jp

megmeg.work が提供しているのは以下の3つ

  • 楽曲情報
  • 単体チーム編成で指定楽曲のライブシミュレーション
  • 複数チーム編成で指定楽曲のライブシミュレーション

つまり、計算に用いる楽曲の情報も提供している。(計算式の関係もあるだろうしそりゃそう)

楽曲情報にはそれぞれIDが振られていて指定すれば1つずつ取れるが、今回はページロード時に楽曲選択用コンポーネントのcreatedで全曲分取ってVuexストアに入れている。

components/songs/SongSelectionView.vue

  async created() {
    await this.$store
      .dispatch('fetchMusicData')
      .then(() => {
        this.$notify.success({
          title: '成功',
          message: '楽曲情報を更新しました',
          position: 'top-right',
          duration: '3000'
        })
      })
      .catch(err => {
        this.$notify.error({
          title: '失敗',
          message: `${err}`,
          position: 'top-right',
          duration: '3000'
        })
      })
  },

取得した楽曲から1つ選んで指定、チームをポチポチして計算開始ボタンを押すと単体チーム編成で指定楽曲のライブシミュレーションをするAPIに計算に必要なデータを整形して飛ばす。

これもストア経由だが返却値が3種あって扱いやすいように配列に詰めなおしている。fetchLiveSimulationData → setLiveSimulationData → liveSimulationData → getterから値取得の流れ。

store/index.js

export const state = () => ({
  //...省略
  liveSimulationData: []
})

export const getters = {
  //...省略
  liveSimulationData: state => state.liveSimulationData
}

export const mutations = {
  //...省略
  setLiveSimulationData(state, data) {
    data.forEach(x => {
      state.liveSimulationData.push(x)
    })
  }
}

export const actions = {
  //...省略
  async fetchLiveSimulationData({ commit }, payload) {
    const resultData = await this.$axios.$get(
      `https://api.megmeg.work/mltd/v1/score/singleunit/`,
      {
        params: payload
      }
    )
    commit('setLiveSimulationData', resultData)
  }
}

actions で引数として受け取るpayloadには単体チーム編成でのシミュに必要な情報が入っている。以下がmatsurihi.meとmegmeg.workから得たカード、楽曲情報を詰め替えてpayloadにする部分。

components/cards/CardTransfer.vue

const music = this.selectedMusic
const team = this.selectedCardList
const SongId = music[0].SongId
const Course = 4
const AppealValue = this.appealValue
const Unitlds = team.map(x => {
    return x.id
})
const GuestId = team[0].id
const SkillLvs = [10, 10, 10, 10, 10]
const TryNumber = 10000
const Prob = [0.1, 1, 50]
const Minimize = false

const requestParams = {
    SongId: SongId,
    Course: Course,
    AppealValue: AppealValue,
    UnitIds: Unitlds,
    GuestId: GuestId,
    SkillLvs: SkillLvs,
    TryNumber: TryNumber,
    p: Prob,
    Minimize: Minimize
}

そのままactionsにdispatchする。ここら辺の値についてはAPIドキュメントを読む方が早い。

サンプルコードがjQueryなのでaxiosとオプションの名前が違う点は注意。axiosではGETリクエストパラメータはdataではなくparams。それからチームのアピール値が必須なんだけど、ここを計算で出す場合matsurihi.meから得られる情報を使って求めることになる。面倒だし人によって変動が大きいため手動が無難。本家ミリシタツールも手入力。

動作画面はこんな感じ。

f:id:apple19940820:20190121002711g:plain

複数チームでのシミュレーションはまだ試してない。

matsurihi.meと同じく使う場合注意。また、サービスとして公開するならmegmeg.workの表記も必要。

追記

楽曲を毎回全件取得するのもあれなので、1回取ったらlocalStorageに突っ込んで楽曲更新があれば差分取得するようにした。みりしたつーるさんにincrementalUpdateの実装をお願いしたら1日で実装してくれた。ありがとうございました。

おわり