typeof Diary

VimとかJSとか。やったことのメモ。自分のため。

Goはじめた

先日、「みんなのGo言語」を購入。

このお盆休みの9連休でやってやろうと思ってやり始めてます。
進めていくと、まずは「A Tour of Go」をやろうとのことなので、ちょっとずつ進めていきました。

一通り最後まで進めたはいいものの、まだまだ理解は追いついていません。
まだ慣れていないからっていうのもあるし、書いているうちに慣れるでしょう・・・。

それはそうと、Tour of GoのExercise難しくないですか?
僕のレベルが低いだけ?

解けたの2問しかない。

最初のforのやつと、mapのやつ。
forのやつも差がうんぬんの意味がわからなくてできてないし、
できそうだったフィボナッチもできなかった。

問題読んでも何をしたらいいのかサッパリで、自分のレベルの低さに嫌気がさしました。

こういった問題が解けないんだなと、改めて苦手を意識。
trackとかの問題も解くのが苦手で、同じ感じですね。
AtCoderなんかも同じ感じ。

これも数こなして慣れるしかないんでしょうか。

まぁ、さすがに克服したいので、少しずつやっていきたいと思います。

Tour of GoのExercise、自力で解きたいなぁ・・・。

VuexのcreateNamespacedHelpers、同名どうするよ

昨日書いた「VuexのcreateNamespacedHelpersでdispatchの記述を短くしたい」なんですが。
一つ考慮が漏れてました。

namespaceがあることで同じ名前のアクション名でも問題なかったのが、あれをやると死にますね。
(よくよく考えたら当たり前)

例えば、それぞれのモジュールでステートを初期化するアクションがあるとすると・・・

// foo/types.js
export const namespace = 'foo'
export const RESET = 'RESET'

// hoge/types.js
export const namespace = 'hoge'
export const RESET = 'RESET' 
import * as fooTypes from '@/store/modules/Foo/types'
import * as hogeTypes from '@/store/modules/Hoge/types'
import { createNamespacedHelpers } from 'vuex'

const { mapActions: mapActionsOfFoo } = createNamespacedHelpers(fooTypes.namespace)
const { mapActions: mapActionsOfHoge } = createNamespacedHelpers(hogeTypes.namespace)

// ...
methods: {
  ...mapActionsOfFoo([fooTypes.RESET]),
  ...mapActionsOfHoge([hogeTypes.RESET])
}

この場合、同じRESETマッピングされてしまうので、結果的にはHoge側がマッピングされてしまいます。

防ぐには、

  • FOO_RESETとかHOGE_RESETとか分けた名前にする(namespaceの意味とは)
  • mapActions({ FOO_RESET: fooTypes.RESET })みたいにマップするときに名前を変える

かなと思いますが、namespaceで得たものを自ら捨てている感じがして、なんとも言えない感じに。

記述量は減らせますが、一長一短ですね。

うーん・・・。

VuexのcreateNamespacedHelpersでdispatchの記述を短くしたい

初めてVuexを触ったとき、とくにnamespaceも使わずやってました。

ちなみにmapActionsは使わず、this.$store.dispatchでしたい派。
this.$store.dispatchの方がdispatchしてるわー!今dispatchしてるわー感があるので好き。
というのは嘘で、コード見たときに、「アクション叩いてる」っていう部分が、ぱっと見分かりやすかったってのがあります。

問題

namespaced: trueにしてみると、namespace + actionNameと書く必要がでてきます。

methods: {
  handleClickBefore(e) {
    this.$store.dispatch('actionName', payload)
  },

  handleClickAfter(e) {
    this.$store.dispatch('namespace/actionName', payload)
  }
}

こう見てみると、「そんな嫌か?」ってレベルに見えるんです。

typesを分ける

実際開発しだすと、直接文字列で指定することは少なくて、types.jsみたいなのをつくって、中にconstでミューテーションとかアクション名を書いていきます。

export const namespace = 'File/'

export const SET_FILE_LIST = 'SET_FILE_LIST'
export const FETCH_FILE_LIST = ' FETCH_FILE_LIST'

こうすると、dispatchの部分は

import * as fileTypes from '@/store/modules/File/types'
// ...
methods: {
  handleClick(e) {
    this.$dispatch(fileTypes.namespace + fileTypes.FETCH_FILE_LIST)
  }
}

このレベルだとまだマシですが、中にはPREFIXがついて長くなるアクション名がでてきます。

this.$dispatch(fooTypes.namespace + fooTypes.PREFIX_PREFIX_ACTION_NAME_FOR_XXX)

ここまでなると、さすがにdispatchが良いとは言い辛い状況になってきました。

解決

createNamespacedHelpersというのがVuexに用意されていることを知りました。
Vuex/modules

import { createNamespacedHelpers } from 'vuex'
import * as fileTypes from '@/store/modules/file/types'

const { mapActions: mapActionsOfFile } = createNamespacedHelpers(fileTypes.namspace)

//...
methods: {
  ...mapActionsOfFile([fileTypes.FETCH_FILE_LIST]),

  handleClick(e) {
    this[fileTypes.FETCH_FILE_LIST]()
  }
}

結局mapActionsに屈したわけですが、思っていたほど見辛いこともなく、楽に書けるので良いかなといった具合です。
(Vuex噛んでいるところはthis[types.XXXX]ってなるので、他とか区別できているので)

その他も

mapStatemapGetteresmapState(namespace, [xxxx])としていたのも同じく解決です。
また、複数のモジュールを使うときも明確になりますね。

import * as fileTypes from '@/store/modules/File/types'
import * as userTypes from '@/store/modules/User/types'

const { mapState: mapStateOfFile, mapGetters: mapGettersOfFile } = createNamespacedHelpers(fileTypes.namesace)
const { mapState: mapStateOfUser, mapGetters: mapGettersOfUser } = createNamespacedHelpers(userTypes.namesace)

//...
computed: {
  ...mapStateOfFile(['fileList']),
  ...mapStateOfUser(['userList']),
  ...mapGettersOfFile(['imageList', 'pdfList'])
}

みたいな具合で。
一つのモジュールしか使わないなら、import * as types from '@/store/modules/xxxx'でいいんですけどね。

参考記事

vscodevimでやってる設定

VSCode使い始めて1年ほど。
結局Vimから乗り換えたというか、両方使ってる感じではあります。

というのも、このissueにあるように、行数が多いとかなりパフォーマンスが落ちてしまって、まともにコード書けなくなるんですよね・・・。
VSCodeVIM is unusable (incredibly slow) on larger files, especially in INSERT MODE #2216

そんな中でこれだけは設定してる!というのピックアップしておきます。

設定

自分はVimでLeaderをスペースに設定しているので、それを踏襲。
<Leader> hで文頭、<Leader> lで文末をよく使ってます。
最低限、これを入れるだけでかなり使い勝手がよくなりました。
むしろ、手が勝手にこれを打つのでないと厳しい...。

そこの設定はこんな感じ。

{
  "vim.leader": "<space>",
  "vim.useSystemClipboard": true,
  "vim.normalModeKeyBindingNonRecursive": [
    {
      "before": [
        "leader",
        "h"
      ],
      "after": [],
      "commands": [
        {
          "command": "cursorHome",
          "args": []
        }
      ]
    },
    {
      "before": [
        "leader",
        "l"
      ],
      "after": [],
      "commands": [
        {
          "command": "cursorEnd",
          "args": []
        }
      ]
    }
  ]
}

キーボードショートカット

もうひとつ、Uniteで,fとかでファイラ開いてたのも似たような感じにしたくて、Shift + fでクイックオープン開くようにしました。

Shift + fはいろいろ問題あったのでやめました。。。
まずエクスプローラーでShilf + fすると、普通にFが入力されてしまうし、そもそもShift + f潰すとFが入力できない()

Ctrl + , fに変えました。ちょっと面倒かな。。。

こっちは基本設定→キーボードショートカットから、workbench.action.quickOpenの設定を変えてください。

MacのデフォがCommand + pなんですけど、片手で打ちやすいようにした感じです。
Ctrl + fが良いんですけど、Ctrl + fVimのスクロールとバッティングするので避けました・・・。

あとは

fコマンドを使うとき、自分がいかにclever-f.vimに依存してるかが分かりました。
自然とf連打してしまう・・・。VSCodeでも似たようなことしたいなーなんかちょいちょい思ってます。

スクロールを連動させる

Qiitaなんかである入力エリアとプレビューエリアのスクロールが連動するやつ。
要件は違ったんですが、スクロール同期という点では同じだったのでやってみたので書いておきます。

単純に連動する

まずは単純に同じサイズの要素を連動させてみます。
単純な連動デモ

const areaA = document.getElementById('area-a')
const areaB = document.getElementById('area-b')

areaA.addEventListener('scroll', e => {
  const scrollTop = e.target.scrollTop
  areaB.scrollTo(0, scrollTop)
})

同じサイズのA→Bと連動させるなら、AのスクロールイベントでscrollTopをとって、BのscrollTo()に放り込んであげるだけです。  

サイズが違う要素を連動する

次にQiitaみたいな要素の大きさが異なる場合の連動を見ていきます。
サイズ違いの連動デモ

const areaA = document.getElementById('area-a')
const areaB = document.getElementById('area-b')

areaA.addEventListener('scroll', e => {
  const scrollMaxA = e.target.scrollHeight - e.target.clientHeight
  const scrollMaxB = areaB.scrollHeight - areaB.clientHeight
  const percent = e.target.scrollTop / scrollMaxA

  areaB.scrollTo(0, scrollMaxB * percent)
})

さっきと異なるのがサイズが異なるので、何%スクロールしたのかを求めることが必要です。
まずはscrollHeight - clientHeightで、どれだけスクロールできるのかをそれぞれ求めます。
次にAのscrollTop / scrollMaxAで%を計算。
最後にBのscrollToscrollMaxB * percent)でスクロール量を放り込んであげて完成です!

相互連動させる

最後に相互に連動させる必要がある場合です。
相互連動デモ

const areaA = document.getElementById('area-a')
const areaB = document.getElementById('area-b')

function scrollA(e) {
  const scrollTop = e.target.scrollTop
  areaB.scrollTo(0, scrollTop)
}

function scrollB(e) {
  const scrollTop = e.target.scrollTop
  areaA.scrollTo(0, scrollTop)
}

areaA.addEventListener('scroll', scrollA)
areaB.addEventListener('scroll', scrollB)

areaA.addEventListener('mouseenter', e => {
  areaB.removeEventListener('scroll', scrollB)
})
areaB.addEventListener('mouseenter', e => {
  areaA.removeEventListener('scroll', scrollA)
})

areaA.addEventListener('mouseleave', e => {
  areaB.addEventListener('scroll', scrollB)
})
areaB.addEventListener('mouseleave', e => {
  areaA.addEventListener('scroll', scrollA)
})

ちょっと面倒な書き方してるかもしれないですが。。。
相互連動するとA→Bをしたときに、Bのスクロールイベントがトリガーされます。
デモぐらい単純な中身なら気にはなりませんが、たまーにズレたりします。

そこで、AをスクロールさせたときはBのイベントをトリガーしない。
BをスクロールしたときはAのイベントをトリガーしない。みたいなことが必要になります。

方法は

  • どこからスクロールされているのかを明確にする
  • そもそもイベント自体を消してしまう

どっちかだと思いますが、今回は後者です。

各要素にmouseenterしたら別要素のイベントをremoveEventListenerで削除。
mouseleaveしたら別要素にイベントをaddEventListenerで付与しています。

Electronアプリを自動アップデートする

Electronのアプリの自動アップデートについて調べる機会があったので書いておきます。

いくつか方法があって

  • Electronに元々入っているautoUpdater
  • electron-builder + electron-updater
  • update-electron-app

今回は、electron-builder + electron-updaterを試してみました。

electron-builder + electron-updater

npm install electron-updater

Githubでやる場合、package.jsonはこんな感じで。

"build": {
  "publish": {
    "provider": "github",
    "owner": "owner",
    "repo": "reponame"
  }
}

トークン求められるので、適宜Githubトークン発行できるところからトークン発行しましょう。
.envGH_TOKEN=xxxxxxの形で置いておけばOKです。
GH_TOKENがあるだけで、勝手にGitHub使われるっぽいですが・・・。

あとはビルドのオプションに--publish alwaysとかしとくと、ビルドする度にGithubのReleaseにビルドされたファイル上げてくれます。
それが嫌な場合はneverとか、他はタグつけたときだけとか指定できます。

その他、自前のサーバとかに置く場合はこんな感じに。

"build": {
  "publish": {
    "provider: "generic",
    "url": "http://xxx"
  }
}

(HTTPSじゃないとだめ、みたいな記事も見たけど、そうでもない?)

あとはurlで指定しているとこに、ビルドしてできたファイル群とlatest.yml、latest-mac.ymlを配置しておきます。
versionに-alpha、-betaあたりをつけると、alpha.yml、beta.ymlが作られます。

Vue CLI3

参考までにVue CLI3でプロジェクト作ってて、vue add electron-builderしてる場合はこんな感じです。
(この構成でやってます)

vue.config.jsに

pluginOptions: {
  electronBuilder: {
    builderOptions: {
      publish: {
        provider: 'generic',
        url: 'http://xxxxx'
      }
    }
  }
}

そんなに変わらないですね。

コードの方

メインプロセスの方に各イベントごとに処理を記述していきます。

一旦↓ここを参考にしつつ、update-downloadedのイベントだけ足してあげます。
electron-updater-example

よくあるアップデートがあったら再起動するかを聞いてくるやつにしたいので、

autoUpdater.on('update-downloaded', ({ version, files, path, sha512, releaseName, releaseNotes, releaseDate }) => {
  const detail = `${app.getName()} ${version} ${releaseDate}`
  
  dialog.showMessageBox(
    win, // new BrowserWindow
    {
      type: 'question',
      buttons: ['再起動', 'あとで'],
      defaultId: 0,
      cancelId: 999,
      message: '新しいバージョンをダウンロードしました。再起動しますか?',
      detail
    },
    res => {
      if (res  === 0) {
        autoUpdater.quitAndInstall()
      }
    }
  )
)

const min = 10
app.on('ready', () => {
  setInterval(() => autoUpdater.checkForUpdatesAndNotify(), 1000 * 60 * min)
})

10分に一回ペースで見に行って、あれば聞いてくれます。

update-downloadedにくるのはlatest.ymlに書いてあるもの。
何があるか分かりやすいようにしてます。

やってみて

思っていたよりも簡単にできました。
自前でアップデートがあるかを確認するAPIが必要?という情報で知識が止まっていたので...。

Windowsはこれでも大丈夫ですが、Macはコード署名が必要らしく、そこはまだ試せていません。

https://www.electron.build/configuration/configuration

ng-japan2019に参加した

今年は初のカンファレンスはnj-japanになりました。
お仕事の方でAngularを触る機会があり、その辺から行くことは決めてました。

その後いろいろあって、やってたPJはなくなってしまって、同時にAngular触る機会はなくなったのですが・・・

f:id:lisia:20190714094811j:plain

会場はGoogleのTokyo Office。 こういうカンファレンスに参加すると、他社の中に入れる機会があるので楽しいですよね!

六本木ヒルズなんか普段入れませんから・・・。
今日だけヒルズぞく!!!

f:id:lisia:20190714095125j:plain

印象的だったのは、メトロノームの発表。
Angularとかに関わらず、自分で好きなように機能実装してみて、機能追加していって。。。
チュートリアル終わってから何したらいいか分からない人、実際めっちゃ多いと思いますし、
そういう人たちの参考になったことでしょう。
少なくとも、そうだよこれだよ!って僕は思いました。

本当ならスライドのまとめなんかもしたかったけど、誰かがやっている・・・?

ここ数年、カンファレンスには参加するようにしていて思うのが、「英語リスニングできない」問題。
正直、聞いていても内容入ってこなくて、感覚でしか聞けてないのが辛いです。
言語の壁が高い。

とはいえ、ツイッターの#ng-japan2019には助けられました。
タグ追ってれば誰かしら反応してるのでw

英語に関しては、どうにかこうにかしたいと思うけど、実際に行動に移すこともできず。
英語セッション、なんとなくでも意味理解しながら聞けたら楽しいんだろうなーと。

というような感じなので、セッションひとつひとつ細かくレポートはできないです。。。

みんなどうやって英語できるようになったの・・・?
本当に知りたい。

なんやかんや、ng-japanは参加してよかったです。
Angular個人的に触りたい欲は終わってからもあったので、その刺激になりました。
それと、TypeScript書いていこうと強く思いました。

社内でTypeScript書いてる人間はいないので、次はここで抜けていかないと。
NestJSも触ってみようかな?

キッカケは与えてもらってると思うので、少しずつやっていきます。

f:id:lisia:20190714095222j:plain