tacamy--log

JavaScriptを勉強中の人のブログです。

Lodashを使って2つのオブジェクトのDiffを抽出する

JavaScriptで2つのオブジェクトの差分を出したいとき、Lodashの omitBy を使うと簡単に書けた。

const before = {
  a: 1,
  b: 2,
  c: 3
}
const after = {
  a: 0,
  b: 1,
  c: 3
}
const diff = _.omitBy(after, (v, k) => before[k] === v)

この場合、 diff の結果は👇こうなる。

console.log(diff)
// { a: 0, b: 1 }

差分がない場合は空のオブジェクトが返ってくる。

ちなみに、 omitBy の第一引数に渡すオブジェクトのkeyとvalueを基にしてもうひとつのオブジェクトの値と比較してるから、👇こんな感じだとDiffは出ない。 aftera っていうkeyしか持ってないから。

const before = {
  a: 1,
  b: 2
}
const after = {
  a: 1
}
const diff = _.omitBy(after, (v, k) => before[k] === v)
console.log(diff)
// {}

なので、オブジェクトを渡す順番を逆にしたら、どのkeyの値が変わったかというDiffなら取れるけど、

const before = {
  a: 1,
  b: 2
}
const after = {
  a: 1
}
const diff = _.omitBy(before, (v, k) => after[k] === v)
console.log(diff)
// { b: 2 }

基本、keyが同じなオブジェクト同士で、値だけが変わるみたいなときに使おう。

Vueのtemplateで1つのイベントに複数のハンドラを設定する

たとえば、button要素のクリック時にonClickAonClickBという2つのイベントハンドラを実行したいというケースで。

本来は、👇みたいにちゃんとメソッドにまとめてから指定してあげるべきなんだろうけど、

<button @click="onClick"></button>
methods: {
  onClick() {
    this.onClickA()
    this.onClickB()
  }
}

👇こんな感じにしたいときもあるけど、これだと動かなかったので、

<button @click="onClickA, onClickB"></button>

👇()をつけてみたら動いた。

<button @click="onClickA(), onClickB()"></button>

でもこんな書き方していいのかどうかはわからない。

Vue.jsのカスタムディレクティブを付与した要素にDOMイベントのコールバックを指定する

Vue.jsのカスタムディレクティブのフック関数には

  • bind ディレクティブが初めて対象の要素にひも付いた時
  • inserted ひも付いている要素が親 Node に挿入された時
  • update ひも付いた要素を抱合しているコンポーネントの VNode が更新される度
  • componentUpdated 抱合しているコンポーネントの VNode と子コンポーネントの VNode が更新された後
  • unbind ディレクティブがひも付いている要素から取り除かれた時

があるけど、カスタムディレクティブを付与した要素でclickとかのDOMのイベントが発火したときにコールバック関数を指定したい場合はどうするんだろう?って思ったら、

// `v-foo`というグローバルカスタムディレクティブを登録
Vue.directive('foo', {
  bind: (el, binding) => {
    el.addEventListener('click', onClick, false)
  }
})

function onClick() {
  console.log('clicked')
}

みたいに、bindの中でel.addEventListenerを普通に指定すればよいだけだった。

addEventListenerのコールバック関数に引数を渡す

ちなみに、

<button v-foo="value"></button>

こんな感じにカスタムディレクティブを使うときに引数を渡して、その値をDOMイベントで使いたい場合は、addEventListenerのコールバック関数に引数を渡す必要があるから、

Vue.directive('foo', {
  bind: (el, binding) => {
    const {value} = binding
    el.addEventListener('click', () => onClick(value), false)
  }
})

function onClick(value) {
  console.log(value)
}

みたいに、第二引数を無名関数?にしたらできた。まぁこれは、Vue.jsは関係なくてただのJavaScriptの話だけど。

参考 :

Atomの自作packageのアップデート方法

以前作ったAtomのpackage(プラグイン)に、半年前にissueが立ってて、最近になって他の人からも+1が続いたので修正しようと思ったものの、packageの開発方法とか完全に忘れてて、どうしたらいいのか分からなくて困ったので未来の自分のために残す。

過去に自作のpackageをリリースしてる前提です。

開発環境の準備

まず、環境を準備するために、apm developコマンドを実行する。

$ apm develop <package-name> 

次のようなログが流れる。

Cloning https://github.com/<user-name>/<package-name> ✓
Installing modules ✓
/Users/<user-name>/.atom/dev/packages/<package-name> -> /Users/<user-name>/github/<package-name>
  1. apm developコマンドを実行すると、GitHubからソースコード一式をCloneしてくる
  2. ~/.atomdevディレクトリが作られ、packages内に該当のpackageのショートカットができる
  3. Cloneしてきたコード一式は、~/github以下に保存される
    (クローン先を変更したい場合はATOM_REPOS_HOMEで変更できるらしい)

リンクの確認

apm linksを実行して確認すると、次のようにpackageがローカルの開発環境にリンクされているを確認できる。

$ apm links
/Users/<user-name>/.atom/dev/packages (1)
└── <package-name> -> /Users/<user-name>/github/<package-name>
/Users/<user-name>/.atom/packages (0)
└── (no links)

開発環境に移動しておく。

$ cd /Users/<user-name>/github/<package-name>

devモードでAtomを起動

Atomのメニューから、[View] - [Developer] - [Open In Dev Mode...] でdevモードでAtomを起動する。

devモードにするとAtom~/.atom/dev/packagesを見てくれるようになるので、つまりそこからリンクされているローカルの開発環境を見てくれるようになる。

packageの中身を修正する

ソースコードをいじったら、Atomのメニューから、[View] - [Developer] - [Reload Window] でエディタ上に修正内容を反映できる。

GitHubにpushする

masterにpushすればよいはず。

publishする

$ apm publish <version-type>

version-typeの部分は修正内容に合わせて、majorminorpatchを指定する。それに合わせてpackage.jsonのバージョンが自動で書き替わるので。

もし、次のようなメッセージが表示されたら、https://atom.io/accountでログインして、API tokenをコピーしてターミナルに貼り付けてEnterすればOK。

Welcome to Atom!

Before you can publish packages, you'll need an API token.

Visit your account page on Atom.io https://atom.io/account,
copy the token and paste it below when prompted.

Press [Enter] to open your account page on Atom.io.

これで、新しいバージョンのpackageがユーザーに自動で届きます。めでたし🎉

Vue.js Tokyo v-meetup="#3" に参加してきました

vuejs-meetup.connpass.com

申し込んだ時点で213/80人と絶望的だったのですが、たまたまブログ書く枠が空いたので勢いでポチってしまいました。ただ、私はVue.js初心者(※)なのでブログを書けるほど内容が理解できるのかかなり不安でしたが、結果的にどのトークも楽しめました(理解したとは言ってない)。なので、同じような初心者の方でも、興味があれば参加してみるとよいのではと思います。

(※)人がつくったアプリ(Vue 1.x)の改修をしたり、開発合宿でミニマムな評価アプリ(Vue 2.x)をギリギリつくった程度のレベルです。

以下、それぞれのトークの内容のメモです。

Vue.js の中身 - 算出プロパティはどうやって動いているか @kitak

https://kitak.github.io/slides/170316-vue-meetup/

Vue.jsのようなライブラリは生産性を高めてくれるけど、内部実装を理解しないで誤った使い方をすると、バグの原因やパフォーマンスの低下に繋がるからちゃんと理解しようねという話でした。私のことか。

算出プロパティ(computed)は普通のメソッドと違って、依存プロパティの値が変わらない間はキャッシュしてくれるそう(知らなかった)。依存関係の追跡や状態変更の通知と再計算を実現するために、内部ではリアクティブプロパティやWatcherインスタンスが使われていること、またそれらはJavaScriptでどのように実装されているかを、温かみのある絵とともに解説してくれました。

Vuexを使ってみなかった話 @atsushiss15

Vuexを使わずに自前で簡易Fluxをつくる方法とかの話でした。

自前簡易Fluxをつくることになった経緯は、当初、状態管理やロジックを各コンポーネント内で書いていたけど、だんだんコンポーネント間で状態を共有する必要が出てきて、propsなどでがんばったけど、密結合や複雑化していってしまい、何によって状態が変更したか分からない状況になってしまった。そこから、storeパターンを取り入れようということになったそう。

Vuexには「State」「Mutation」「Actions」があって、このうちの「State」「Actions」を自前実装して、Fluxアーキテクチャの「複数コンポーネントで状態共有」「Viewとデータロジックを分離」を実現。新しくつくるならVuexをつかう方がいいけど、学習コストを割かずにミニマムに実装するならこんな感じで自前実装もできる。ただし、自前の場合はVue.js devtoolsが使えないのがつらみ。

E2D3 の Vue.js 活用 @chimerast

E2D3はExcelのアドインで、Excel上でインタラクティブなチャートを生成したり、SVGに保存したりWebにシェアしたりできるそう。MacOffice 2016でもブラウザ上のOffice 365でも使えるらしい。

当初は、D3.jsとjQueryでがんばっていたのでしんどみがあったけど、Vue.jsで実装するようになってHTML上に構造ができるようになったので、拡張性が担保された。

Vue.jsはカジュアルにもガチにもつかえて便利なのでもっと使っていこうという話でした。

型付きテンプレートがほしい @ktsn

テンプレート内の変数名やコンポーネント名が間違っていたら、コンパイル時にターミナルにエラーが出るツールをつくったよという話でした。

実装は、VDOMにコンパイルしたテンプレートをTypeScriptにしてコンパイルして実現しているけれど、細々した調整が必要でその解説が主な内容でした。

まだ完成形ではなくて、現状の問題点としては、ソースコードが書き換わるのでエラーの箇所と行数がズレてしまうということと、vue-class-componentしか対応できないので、TypeScript 2.3を待ってるとのことでした。

VueでComponentをはじめました @kawakami_kazuyoshi

メトロノームのデモの解説と、使用したライブラリなどの紹介でした。

コンポーネントの単位は細かく分ける派だそう。たとえばボタンコンポーネントをひとつつくれば、色とアイコンを渡せばどこでも使いまわしができるから。

Vue.js with Go @k2wanko

ToDoのJavaScript部分をGoで書くデモとそのためのライブラリなどの話でした。Goへの愛を感じました。

  • GopherJS
    • GoをJSに変換するトランスパイラ
    • GUIアプリケーションをブラウザにもってこれる
  • go-vue
  • go-loader
    • WebpackのGoファイルローダー

結論として、VueをGoで書くのはつらい。

Vue.jsとFirebaseでSPA @buddy7

会社で超短期間で機能てんこ盛りのサービスをリリースすることになったけど、人員も足りないのでサーバーサイドはFirebaseにしてVue.jsで実装したという話でした。

でもサーバーからのメールが英語になってしまったり、Vuexを使わなかったのでデータフローがぐちゃぐちゃになってしまったなどの問題もまだ残っているそう。

その話と平行して、vue-cliでのプロジェクト作成から、Firebaseのデータを画面上に表示するところまでを、その場で5分くらいで実演していました。私も合宿で同じ構成でやったのですが、簡単で便利ですよね。

Nuxt.js @inouetakuya

SSRが必要なときに、Nuxt.jsのレールに乗るだけで、簡単にユニバーサルなVue.jsアプリを構築できるらしい。具体的には、Nuxt.jsには以下の機能が備わっているそう。

  • サーバーサイドレンダリング
  • ルーティング(vue-router)
  • Vuex store
  • 非同期データの取り扱い
  • head要素の管理(vue-meta)
  • Webpackと組み合わせてやることアレコレ

使い方も、vue init nuxt/starterするだけ。あとは、pagesディレクトリに*.vueを置くだけでそれがそのままルーティングされる。すごい。

nuxt generateというのは*.vueを使った静的ファイルジェネレータで、静的ファイルができるのでGitHub Pagesなども利用できるようになる。

Nuxt.jsすごそう!

まとめ

全体を通しての感想は、「vue-cli便利」「Vuex使おう」でした。

ちなみに、参加者はほぼ男性で、女性は5人くらいだった気がしました。 それにヒヨって懇親会には参加できなかったのだけが心残りですが、Vue.jsへの愛も深まったし参加してよかったです。

スピーカーや運営のみなさまありがとうございました。

Python 3ではSimpleHTTPServerではなくhttp.serverを使う

以前、Pythonを使ってMacでローカルサーバーを簡単に立てる方法を書いたけど、

tacamy.hatenablog.com

新しいMacBookではanyenvにして、(無駄に)Pythonもバージョン管理するようにしたら、SimpleHTTPServerが使えなくてちょっとハマった。

原因は、Python 2にはあったSimpleHTTPServerという標準ライブラリのモジュールが、Python 3ではhttp.serverというモジュールに統合されたからだった。

要するに、Python 2では

$ python -m SimpleHTTPServer [ポート番号]

としていたのを、Python 3では

$ python -m http.server [ポート番号]

にすればいいだけでした。

ありがとうStack Overflow。

stackoverflow.com

タブを折り返して多段表示するAtomのプラグインをつくった

花金なので、multiline-tabっていうAtomプラグインをつくりました🍻

atom.io

Atomって開いてるタブが多くなると、タブ幅が小さくなりすぎてファイル名が読めない上に、エディタの幅に収まらないタブは横スクロールで画面外に消えてしまって使いづらすぎるので、タブ幅の最小値を大きくして、エディタの幅を超える場合はタブを折り返して表示するためのプラグインです。

https://i.github-camo.com/602640c3d96c274180b08dd6db3d7c02bbc1e1b1/68747470733a2f2f7261772e67697468756275736572636f6e74656e742e636f6d2f746163616d792f61746f6d2d6d756c74696c696e652d7461622f6d61737465722f73637265656e73686f742e706e67

もともと、開いているタブを縦に並べるプラグイン(👇)を使わせてもらっていたんだけど、

GitHub - 1000ch/atom-vtab: Verticalize tabs on Atom.

横並びのままで折り返したいなって思って、上記のコードをパク参考にさせてもらいました。ありがとうございます :bow:

やってることはCSSしか触ってないくらい簡単なんだけど、いろんなテーマで崩れないように調整するのが結構つらかったです。結局、あまりいじらないのがベストだってことになりました。

タブの幅はとりあえず固定になってるけど、本当は設定で自由に変更できるようにしたかったので、時間とれたら実装するかも。でもプルリクも待ってます。

最初はwrap-tabっていう名前だったんだけど、apm publishが最初うまくできなくて、あれこれしてるうちにAPI tokenのキーをうっかり作り直してしまって、権限がなくなって削除もできなくなってしまって残骸になってしまったので、multiline-tabという別の名前に変えました。どうやって消そうかな。