うしろのこの本ください

なんでもかきます

あけおめ

12月に一回もブログ更新しなかったんですがQiitaに2本アドベントカレンダーかいてました。

Nuxt.jsで権限管理 - Qiita

Composition APIってなんだ - Qiita

去年はミリシタへの課金額振り返ったりロードマップのやつ書いたりしてましたが、どちらもそれほどモチベはないので今年はやりません。個人的に1月2日は1年で一番自由で後ろを考えなくてよくて好きです。今は実家でポケモンで遊んでます。

今年の抱負とかはあんまり思い浮かばないんですけど、年末〜年始にかけて萎びた心の状態を元に戻して元気に働けたらなと思います。

そういえば地元の友達たちと久しぶりに会って気づかされたんですが、「年の割に落ち着いているね」みたいなのは数年後は「年相応だね」になってとくに目立ったものではなりますね。

いつまでも大学生みたいなやんちゃな奴が長い目で見れば魅力的に映るんじゃないだろうか。僕みたいな騒いだり今日くらいハメを外してみたいなノリを楽しめない人は上に立つのに向いてなさそうだなって思わされた年末でした、しらんけど。

今年も書きたいことがあれば書いていきます。

lerna link convertを理解する

最近案件でモノレポ化が盛んになっていて、自分はフロントエンド周りを少しずつ進めています。

構成はlerna + yarn workspaceの基本的なものですが社内ライブラリをパブリッシュしない方向で進めることになり、モノレポ内ですべて完結させる方針です。

で、今までバラバラだったライブラリが一括管理できるので依存関係とかも全部合わせようとなりました。いわゆるFixed modeです。

イメージとしてはこんな感じ

frontend/*
packages
        /a
        /b
        /c
node_modules/*
lerna.json
package.json
yarn.lock

packagesの配下に社内ライブラリが複数入る感じ。workspaceのスコープですがfrontendの直下すべてとpackagesの直下すべてになっていて、lerna run lintとかやると全てのディレクトリでlintが実行されます。

yarn workspaceを使うと各ディレクトリにはnode_modulesが作られず、ルートに巻き上げられます。yarn.lockを見てみるとこのようになっています。(例です)

"a-lib@file:packages/a":
  version "1.0.0"
  dependencies:
    cross-env "^5.2.0"
    vue "^2.6.0"

dependenciesはこんな感じでルートのlockファイルにまとめて記述されます。ではdevDependenciesはどうかというと、普通に記述されます。要するにdevDependenciesに関してはモジュールの実体がルートのnode_modulesに入るだけで個別のスコープに紐づくわけではないということです。共通化されます。

devDependencies自体が共通化されることは良いんですが、共通化するならそれぞれのpackage.jsonに書かれているやつもルートにまとめたいですよね。ということで使うのが lerna link convert です。実行するとワークスペース内のすべてのdevDependenciesがルートへ集約されます。

これでdependencies(パッケージ固有の依存)もdevDependencies(モノレポで使う開発用依存)も良い感じになりました。

lerna link convert、どう動いてるの?

ドキュメントが薄くて裏で何が起こっているか全くわからなかったのでコード読んだ。コマンド自体は @lerna/link のもので、convertはオプションになります。

github.com

READMEには特に書かれていませんね。

コードを読むとなんとなく何をやっているかは分かります。lernaはCommandというクラスを継承してオーバーライドしています。

  execute() {
    if (this.options._.pop() === "convert") {
      return this.convertLinksToFileSpecs();
    }

    return symlinkDependencies(this.allPackages, this.targetGraph, this.logger.newItem("link dependencies"));
  }

オプションが convert なら convertLinksToFileSpecs を呼び出すようになってますね。そうでなければシムリンクを貼るような挙動になっています。つまり lerna link です。

convertLinksToFileSpecs() {
    const rootPkg = this.project.manifest;
    const rootDependencies = {};
    const hoisted = {};
    const changed = new Set();
    const savePrefix = "file:";

    for (const targetNode of this.targetGraph.values()) {
      const resolved = { name: targetNode.name, type: "directory" };

      // install root file: specifiers to avoid bootstrap
      rootDependencies[targetNode.name] = targetNode.pkg.resolved.saveSpec;

      for (const depNode of targetNode.localDependents.values()) {
        const depVersion = slash(path.relative(depNode.pkg.location, targetNode.pkg.location));
        // console.log("\n%s\n  %j: %j", depNode.name, name, `${savePrefix}${depVersion}`);

        depNode.pkg.updateLocalDependency(resolved, depVersion, savePrefix);
        changed.add(depNode);
      }

      if (targetNode.pkg.devDependencies) {
        // hoist _all_ devDependencies to the root
        Object.assign(hoisted, targetNode.pkg.devDependencies);
        targetNode.pkg.set("devDependencies", {});
        changed.add(targetNode);
      }
    }

    // mutate project manifest, completely overwriting existing dependencies
    rootPkg.set("dependencies", rootDependencies);
    rootPkg.set("devDependencies", Object.assign(rootPkg.get("devDependencies") || {}, hoisted));

    return pMap(changed, node => node.pkg.serialize()).then(() => rootPkg.serialize());
  }

まあざっくりとした理解ですが単にdevDependenciesをルートにマージして上書きしているようです。元からは消す。"巻き上げ"の正体は本当に記述を移動しているだけっぽいですね。あとは各パッケージのdependenciesへの参照を file:path/パッケージ名の形で追記しています。

おわり

lernaは基本複数のパッケージに対して同じコマンドを実行するためのツールとして使うのが良いと思いますが、依存の巻き上げなどでも使えて楽で良いです。 ただしルートにすでに同じパッケージが存在する場合バージョンが低い方に上書きされるため注意です。(直近babel周りで死にまくっている)

Nuxtのpwa-moduleでプッシュ通知令和版

直近プッシュ通知の実装が必要になったためハマったところ中心に結果をメモる。

受け取り側

pwa-moduleはOneSignalをサポートしているため、Nuxtでプッシュ通知をやりたい場合これを使うのが一番簡単。

pwa.nuxtjs.org

とりあえずpwa moduleとone signal moduleを入れてnuxt.config.jsに設定を書けば受け取り側の設定は終わる。

yarn add @nuxtjs/onesignal @nuxtjs/pwa

nuxt.config.js

// 省略
  modules:
      [
          '@nuxtjs/onesignal',
          '@nuxtjs/pwa'
      ],
  oneSignal: {
    init: {
      appId: 'One Signalコンソールで発行したID',
      allowLocalhostAsSecureOrigin: true, // localhostで動作確認する場合true
      welcomeNotification: {
        disable: true
      },
    },
    importScripts: ['sw.js'], // 後述、必須
  },
  pwa: {
    workbox: {
      dev: true, // devモードで起動した時でもServiceWorkerを有効にする
    },
    manifest: {
      name: 'test',
      short_name: 'test',
      title: 'test',
      'og:title': 'test',
      description: 'test',
      'og:description': 'test',
      lang: 'ja',
      theme_color: '#ffffff',
      background_color: '#ffffff'
    },
  },
// 省略

ワーカーが複数ある場合、メインのワーカーに他のファイルをマージする必要がある。OneSignalのSDKが取り込めるように自動生成してくれるが、デフォルトのパス指定が/sw.js?xxxxxxxxのような形で生成されうまく読み込めないので、importScriptsで明示的に指定する。

うまくいっていればdeveloper consoleのApplicationタブで確認することができる。

f:id:apple19940820:20191101110929p:plain

送信側

OneSignalにサインアップする。GitHubアカウントやGoogleアカウントで登録できる。

Add a New Appでアプリケーションを作成する。適当に名前いれてADD APP。

その後どのプラットフォーム向けに作るかの選択肢がでるためWeb Pushを選ぶ。Choose IntegrationはTypical Siteで。

Site Setupは以下のように

f:id:apple19940820:20191101112158p:plain

ローカルで確認したいので、localhosthttps扱いにする。

Permission Prompt SetupはとりあえずNative Propmtで。カスタマイズもできるがブラウザデフォルトであるメリットの方が大きい。

これ

f:id:apple19940820:20191101114516p:plain

Welcome Notificationは受け取り側で無効にしたためスルーでOK。

Advancedは少し気をつける必要がある。ServiceWorkerでキャッシュしたいページがネストしてる場合ここで設定しないとうまく動かない。今回/mediaでやりたいので以下のように設定する。

f:id:apple19940820:20191101113407p:plain

前と後ろに/をつけないと壊れる。OneSignalSDKWorker.jsとかは自動生成されるCDNからSDKをとってくるためのワーカーファイルで、importScriptsのパスがあーだこーだはこのファイルの話。

ここまでできたらSAVEを押す。するとWeb Push設定用のページに行くのでここでappIdをコピペして、クライアント側のnuxt.config.jsのOneSignalの設定に追記する。

FINISHで設定完了。

プッシュ

OneSignalのヘッダーからMessagesに行ってNewPushを押すと設定画面にいける。

f:id:apple19940820:20191101144812p:plain

適当にtitleとか埋めたらSchedleでいつプッシュするかを決められる。今回はすぐ見たいのでデフォルトのままで。

f:id:apple19940820:20191101114202p:plain

誰に対して投げるか、購読しているユーザー数、内容とブラウザーなどを確認してSEND MESSAGEでプッシュ通知が飛ぶ。

f:id:apple19940820:20191101144824p:plain

いざプッシュ

f:id:apple19940820:20191101144439p:plain

できた

おわり

パス周りでわりとハマったものの受け取り側はモジュール入れて設定ちょっと書くだけで良いしついでにPWA化もできてコスパ良い。(プッシュ通知の是非は置いておいて)

OneSignalも技術的な知見がなくても操作できるコンソールが用意されているのでマーケティング担当者が自由に通知できる。

NuxtとComposition APIとtsxで素振り

した

github.com

setupとtsxを紐づけるため別途プラグインが必要だが、普通にかける。

以下の流れで環境を作れる。

プロジェクト生成

npx create-nuxt-app

必要なモジュールのインストール

yarn add @nuxt/typescript-runtime @vue/composition-api
yarn add -D @nuxt/typescript-build babel-preset-vca-jsx

package.jsonを修正

  "scripts": {
    "dev": "nuxt-ts",
    "build": "nuxt-ts build",
    "start": "nuxt-ts start",
    "generate": "nuxt-ts generate"
  },

composition apiプラグインに登録

滅多にないだろうけど.tsにしとけばtypoが減る。

import Vue from 'vue'
import VueCompositionApi from '@vue/composition-api'

Vue.use(VueCompositionApi)

nuxt.config.jsを修正(ついでにts化)

拡張子を.tsに変更

import { Configuration } from '@nuxt/types'

const config: Configuration = {
  buildModules: ['@nuxt/typescript-build'],

  ~省略~

  /*
  ** Plugins to load before mounting the App
  */
  plugins: ['@/plugins/composition-api'],

  ~省略~

  build: {
    babel: {
      presets({ isServer }) {
        return [
          [require.resolve('babel-preset-vca-jsx')],
          [
            require.resolve('@nuxt/babel-preset-app'),
            {
              targets: isServer ? { node: 'current' } : { ie: '9' },
            },
          ],
        ]
      },
    },
  },
}

export default config

tsconfig.jsonをルートに作成

{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "moduleResolution": "node",
    "jsx": "preserve", // これないと動きません
    "lib": [
      "esnext",
      "esnext.asynciterable",
      "dom"
    ],
    "esModuleInterop": true,
    "allowJs": true,
    "sourceMap": true,
    "strict": true,
    "noEmit": true,
    "baseUrl": ".",
    "paths": {
      "~/*": [
        "./*"
      ],
      "@/*": [
        "./*"
      ]
    },
    "types": [
      "@types/node",
      "@nuxt/types"
    ]
  },
  "exclude": [
    "node_modules"
  ]
}

ルートにshimsを置いてtsx用の型を拡張

shims-tsx.d.ts

import Vue, { VNode } from 'vue'
import { ComponentRenderProxy } from '@vue/composition-api'

declare global {
  namespace JSX {
    interface Element extends VNode {}
    interface ElementClass extends ComponentRenderProxy {}
    interface ElementAttributesProperty {
      $props: any;
    }
    interface IntrinsicElements {
      [elem: string]: any;
    }
  }
}

あとはお好みでtypescript-eslintを入れたりする。今回は省略。

書き方

SFCではなくtsxファイルで書く。exportするオブジェクトは createComponent でラップすると型推論が効くようになる。

propsは PropType に型を渡してアサーションすることで型付けする。setup()の第一引数内でも型推論してくれるようになるためpropsが増えまくってもtypoとかはなさそう。第二引数にコンテキストが入っていてemitとか使える。tsxはまあtsxって感じ。

import { createComponent, PropType, reactive } from '@vue/composition-api'

interface IncrementalObjProps {
  buttonText: string
  value: number
}

export default createComponent({
  props: {
    incrementalObj: {
      type: Object as PropType<IncrementalObjProps>,
      required: true
    },
  setup({ incrementalObj },{ emit }) {
    const r = reactive({count: incrementalObj.value})
    return () => (
      <div>
        <div>{r.count}</div>
        <button onClick={() => {
            r.count++
            emit('countUp', { count:r.count } )
          } } >{incrementalObj.buttonText}</button>
      </div>
    )
  }
})

読み込み側では普通にimportしてコンポーネントとして利用できる。SFCから読み込むことも可能。

Composition APIの出来が良くてReactじゃなくVueで書きたいけどテンプレートは嫌いなんだぜって人は良さそう。

余談

サンプルのGanahaBirthday.tsxは微妙に動かないんだけどいまいち原因がわからない。setupが1度しか評価されないのでpropsで渡ってきても変更を追えてない気がする。compositionはスマートにかけてとても良いんだけど今までのVueの直感とは微妙に違う点でハマりそうな気がしてる。

それからComposition APIでやるならTSは必須だと思っていて、Option APIでは必ず値の出所がわかる(methods,computedなど)ようになっていたけどそれがsetup()に埋もれてしまい、どこから出てきた値なのかがコードで追い辛い。型情報があればマシになるだろうという考え。

ミリオンのワンナイトクルーズはガシャと比べてコスパが良いのか確認する

クルーズのステートプランがミリシタのガシャ天井より安いと話題なので、ガシャと比べてどれくらいお得なのかをざっくり調べてみる。

ガシャは狙いのSSRが出るまで引くものとすると、"好きなSSRが出る期待値"とクルーズの値段を比べてクルーズの方が下回っている場合ガシャよりコスパがいいとする。

前提条件

クルーズ側

  • ステートJ3名利用(61,600)

ガシャ側

  • SSRの排出率3%
  • ピックアップ排出率0.495%
  • 天井あり
  • 有償ジュエルのみ(消費税増税後)

計算に利用するサイト

ガチャの期待値の計算(天井付きガチャ用) - 高精度計算サイト

結果

f:id:apple19940820:20190924134439p:plain

クルーズの値段61,600円に対してSSRの期待値の値段は46,455円となった。期待値計算ではSSR取得の方がコスパが良いように見える。

しかしSSRは出た時点で実質無料となるためクルーズはガシャに比べ61,600円お得である。

おわりに

100万払っても買えないものが船にはあるはず(ドヤァ

NuxtMeetUpに登壇してきました

これに株式会社ROXX枠でLTしてきました。Composition APIについて、Nuxtと合わせて素振りした感想みたいな感じ。

nuxt-meetup.connpass.com

スライド

slides.com

初LTの割にはうまくやれたかなと思います。poaroファンとしてアナ尻遵守を心がけましたが15秒漏れました。(そもそも押してたけど)

懇親会でも色々な方と喋る機会があり色々と収穫があって良かったです。半分以上ラジオ/アイマスの話が流れるTLですがTwitterフォローしてくださった方はありがとうございます。

内容ですが意外とfunction api、つまり現composition apiについて知らない方が多かったなという印象です。まあまだRFCですし、プロダクトでのみVueやNuxtを使っている人には無用の長物というのはそれはそうなのでこんなもんかなーと。

このスライドは2週間前には完成していて、直後(ちょうど自分の誕生日)にNuxtが2.9にあがってしまい、TS周りのことも書いていたためかなり修正に時間をとられてしまいました。

さらに言うとこれは登壇後に気が付いたんですがvue-function-apiがcomposition-apiに変わっており、apiもいくつか変更されていたりして帰宅してから慌てて使用済みスライドを直すなどなかなかな体験でした。

composition apiのドキュメントまであります。(まだ薄めだけど)

vue-composition-api-rfc.netlify.com

こんな感じで簡単に破壊的変更が起こるのがこの界隈の今って思ってます。今の主役はVue3.0で、それに向けて周りが慌ただしく動いている最中です。 それでも現時点でcomposition apiの出来に関してはとても良いなとは思っていて、よく関数でやるならReactでよくないかと言われますが、言うのもまだ時期尚早な段階でcomposition apiがどういった変化をVue界隈にもたらすのかちゃんと観測してからそういうのやろうね、というのが自分の意見です。(何を言うのかは個人の自由です) 面白いおもちゃが手に入るんだからやってみようぜっていう。

Reactには似たAPIにReact Hooksがありますが、お試しで出てからの期間を考えるとユーザーに根付くまで十分な時間が経っています。Vueユーザーにはこの「HooksのようなAPI」について勘所や知見などまだ何もないのが現状で、ここが成長し成熟していくのがコミュニティであり技術だと考えています。何が言いたいかと言うと俺と一緒に人柱やらないか?ってことです。

何はともあれ数百人規模のイベントが無事に終わり、しっかり撤収まで出来たのは会場を提供いただいたメルペイの皆様のおかげですし、静かに見守っていただいた来場者の皆様のおかげですし、弊社開発チームとCTOメンバーのサポートあってのことですね。自分一人だけでは何もかも経験できないことと感じました。

次があればまた登壇したいです。あまりにも会社の話をしてなくてアレだったので今度はそっちよりの話ができればいいかも。

イベントレポート

techblog.scouter.co.jp

おわりだよ〜

Svelte3のストアを触ってみる

自分が書いたタイミングがv3リリース後すぐだったので今のSvelteと差異があるかも。

svelte.dev

つくったやつリポジトリ

github.com

つくったやつ

ushironoko-svelte-sample.netlify.com

つくったってほどでもないけど一応netlifyに投げた。

消えるフレームワークことSvelteが少し前にv3になったということで、少し前に触っていた。中でもstore周りが面白かったのでメモがてらに書き起こしてみる。

Svelteとは

Write less code、No VDOM、Truly reactiveを掲げる新し目のWebアプリケーションフレームワークで、ビルドするとランタイムが消える。つまりプレーンなjsアプリケーションとして動かすことができる。いつの間にかランタイム実装周りがTS化してた。

面白いのはSvelte本体がstoreを内包しているところで、実装自体も200行弱ととてもシンプル。

github.com

Svelteでは.svelteファイルの中にscriptとhtmlの双方を書くことができる。例えばカウントの値をstoreに管理させるコードはこうなる。

<script>
    import { count } from '../store/stores.js'
    export let incrementalButtonText
    export let decrementalButtonText
    export let resetButtonText
</script>

<p>count: {$count}</p>

<button on:click={count.increment}>{incrementalButtonText}</button>
<button on:click={count.decrement}>{decrementalButtonText}</button>
<button on:click={count.reset}>{resetButtonText}</button>

こんな風にscript内にロジックを記述して、外にJSXライクなマークアップを書く。これはボタンコンポーネントとしての実装だとして、ルートコンポーネントで使うときはこう。

<script>
    import Buttons from './components/Button.svelte'
    let incrementalButtonText = ' + '
    let decrementalButtonText = ' - '
    let resetButtonText = ' 0 '
</script>

<Buttons {incrementalButtonText} {decrementalButtonText} {resetButtonText} />

子でexportした変数を親からpropsで渡す。結構独特な記述。

肝心のstoreはこうなっている。

import { writable } from 'svelte/store';

function createCount() {
  const { subscribe, set, update } = writable(0);

  return {
    subscribe,
    increment: () => update(s => s + 1),
    decrement: () => update(s => s - 1),
    reset: () => set(0)
  };
}

export const count = createCount();

svelte/storeから必要なAPIをimportする。例えばwritableは初期値を渡すとsubscribesetupdateの3つを吐き、それぞれリアクティブに動作する関数を定義することができる。

ユニークな点としてreadableという読み取り専用ストアとderivedというストアを派生させるAPIが用意されている。派生させたストアは依存関係が更新されると引数にとったコールバックが実行される。

https://svelte.dev/docs#derived

TSで再実装されているのでTSで書けばストア周りの型が効くはず。

所感

シンプルかつ高速で型も効いて良さげ。store周りはコンポーネントごとに状態管理を持つ感じで、最近流行ってるやつだとは思う。

バケツリレー時のemitの記述量が少なくなるようなアプローチをとっている点もユニークで面白い。またアニメーション、モーション、トランジション等のサポートもコアに含まれているので、Vue飽きたなって人はこっちでそういうのやってみても面白いかもしれない。

すぐに試してみたい時は公式チュートリアルがあってブラウザ上で色々学べる。a tour of go的な。

svelte.dev

あとは公式REPLがあってこれもブラウザで試せる。

svelte.dev

ちなみに読み方はスヴェルテっぽい。フランス語でシュッとした的な意味らしい。

ミリシタAPI Princess の型定義かいた

書いた。

github.com

別にDefinitelyTypedとかにはあげてないしパッケージ化もしてないので使うときはクローンするか、index.d.tsをコピペでよろしく。

ドキュメントの型を型定義ファイルに落としただけ。

api.matsurihi.me

でれぽとかはやってなくて、あくまでミリシタのAPI部分のみ。やりたい人はPRで。 でれぽだけじゃなくて普通にissueとかもあればどんどん投げてください。周年イベラン中に書いたから自信ない。

使い方

types/princessとか切ってそこにindex.d.tsを配置、使うところで必要な型をimport

以下の設定をtsconfig.jsに書くといちいちディレクトリ構造を書かなくてもよくなる

"baseUrl": "./",
"paths": {"princess": ["types/princess"]},
"typeRoots": ["types", "node_modules/@types"],
import { Cards } from 'princess'
import axios from 'axios'

const card: Cards = axios.get('https://api.matsurihi.me/mltd/v1/cards/250')

みたいな。

レスポンスの型定義のみなのでメソッドとかはない。気が向いたらクエリメソッドの型も書くかも。

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

笑い

和藤渚さんの三角音源

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

終わり

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

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%コンボナでもそっちを使った方が良いみたいな状況になればフェス限だとしてもリストラする羽目になる。まだ先だとは思うけど気にしておくと良いかもしれない。