npm publishできないと思ったら、masterブランチを修正していなかった

問題

以下のコマンドでnpmに公開する予定がダメ。
500のエラーが返ってきた。

npm publish ./


npm ERR! publish Failed PUT 500
npm ERR! code E500
npm ERR! Registry returned 500 for PUT on https://registry.npmjs.org/vue-gutter-resize

解決

npm publishするときのブランチがmasterでは無かった。
開発用のブランチだったのが問題。

1.masterにマージ
2.最新のgit tagを削除

git tag -d v0.10.0
git push origin :v0.10.0

3.ローカルをmasterブランチに切り替え
4.git tagの付け直し
5.npm publish ./

これで解決できました。

Vue.jsのコンポーネント間で値の共有をする5つの方法

やりたいこと

Vue.jsでコンポーネント間での値の共有をしたい。
親子関係問わず行えるとOK。

方法

主に以下の5つがありそう。

  • V-bind/Props/Emit
  • State Management Library (etc: Vuex, Redux...
  • EventHub/EventBus
  • BrowserStorage (etc: localStorage, IndexedDB...
  • Server (etc: firebase...

V-bind/Props/Emit

親子関係のコンポーネントだとすごい楽に値を渡せる。

parent.vueのhogeをchild.vueにv-bindで渡し、
child.vueでは、propsで受け取り、
child.vueのemitでparent.vueに値と渡し、 parent.vueのメソッドイベントハンドラhogeを更新。

ディレクト

  • Project
    • parent,vue
    • child.vue

parent.vue

<template>
  <div>
    <child
      :hogehoge="hoge"
      @hogehogeHandler='fromChild'
    >
    </child>
  </div>
</template>

<script>
import child from './child.vue'

export default {
  data () {
    return {
      hoge: 'fuga'
    }
  },
  components: {
    child
  },
  methods: {
    fromChild (msg) {
      this.hoge = msg
    }
  }
}
</script>

child.vue

<template>
  <div>
    <p>{{ hogehoge }}</p>
    <button @click="hogehogeHandler">hogehoge -> fugafuga</button>
  </div>
</template>

<script>
export default {
  props: ['hogehoge'],
  methods: {
    handler () {
      this.$emit('hogehogeHandler', 'fugafuga')
    }
  }
}
</script>

parent.vueの:hogehoge="hoge"のイコールの左側はchild.vueのprops名になり、右側はparent.vueのdataのプロパティ名が入る。

parent.vueの@hogehogeHandler='fromChild'のイコールの左側はchild.vueのemitの第一引数名になり、右側はparent.vueのmethods名または何かしらの処理が入る。

child.vueのthis.$emit('hogehogeHandler', 'fugafuga')は、第一引数にはparent.vueの@hogehogeHandler='fromChild'のイコールの左側が入り、第二引数以降には渡したい値が入る。
今回の場合はfugafugaという文字列をparent.vueの@hogehogeHandler='fromChild'を介しparent.vueのfromChild (msg)の引数で受け取っている。
なお、$emitでたくさん値を渡したい場合は以下のようにobjectにして渡している。

this.$emit('foo', {
  bar: 'yahho',
  baz: 10000,
  qux: {
    quux: 'apikey'
  }
})

State Management Library (etc: Vuex, Redux...

Vueでのコンンポーネント間で値を共有したいならとりあえずVuexを使うことがおすすめ。
筆者が他のライブラリについて詳しくないからなんとも言えないけど、VueでReduxを使うとパッとしないと思ってる(そもそもReduxをよく知らない)。
なのでここでは、Vuexを紹介。

VuexにはStateの管理方法(値の共有の仕方?)には2つの方法が存在する。
- クラシックモード - モジュールモード

今回は、モジュールモードのみ書く。(モジュールモードの方が好きだから)
ビルドは、parcelでします。
スプレッド演算子(...)を利用できるようにbabelやtypescriptでトランスパイルできるように設定しないといけない。

だいぶ複雑なディレクトリになる。
foo.vueとbar.vueで設定した値をindex.vueで見る仕組みを以下に記入。

ディレクト

  • src
    • index.html
    • index.js
    • index.vue
    • components
      • foo.vue
      • bar.vue
    • store
      • index.js
      • modules
        • baz.js

index.html

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Vuex</title>
</head>

<body>
  <div id="app"></div>
  <script src="./index.js"></script>
</body>

</html>

index.js

import Vue from 'vue'
import Vuex from 'vuex'
import App from './index.vue'
import { store } from './store'

Vue.use(Vuex)

new Vue({
    el: '#app',
    store,
    render: h => h(App)
})

index.vue

<template>
  <div>
    {{ count }}
    <foo></foo>
    <bar></bar>
  </div>
</template>

<script>
import { mapGetters } from 'vuex'
import foo from './components/foo.vue'
import bar from './components/bar.vue'

export default {
  name: "App",
  components: {
    foo,
    bar
  },
  computed: {
    ...mapGetters({
      count: 'baz/count'
    })
  }
}
</script>

components/foo.vue

<template>
  <div>
    <button @click="set({ count: 1000 })">set 1000</button>
  </div>
</template>

<script>
import { mapActions } from 'vuex'

export default {
  methods: {
    ...mapActions({
      set: 'baz/set'
    })
  }
}
</script>

components/bar.vue

<template>
  <div>
    <button @click="set({ count: 5 })">set 5</button>
  </div>
</template>

<script>
import { mapActions } from 'vuex'

export default {
  methods: {
    ...mapActions({
      set: 'baz/set'
    })
  }
}
</script>

store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
import baz from './modules/baz'

Vue.use(Vuex)

export const store = new Vuex.Store({
  modules: {
    baz
  }
})

store/modules/baz.js

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

const state = {
  count: 0
}

const actions = {
  set ({ commit }, { count }) {
    commit("SET", { count })
  }
}

const mutations = {
  SET (state, { count }) {
    state.count = count
  }
}

const getters = {
  count: state => state.count
}

export default {
  namespaced: true,
  state,
  getters,
  actions,
  mutations
}

流れとしては、index.vueのcount(mapGettesのcount)でstore/modules/baz.jsのstateのcountを参照(購読?)しておく。
components/foo.vueのbutton押された場合(発行?)、
components/foo.vueのbuttonのset(mapActionsのset)で値(1000)を
store/modules/baz.jsのactionsのsetの第二引数で受け取り、
commitでstore/modules/baz.jsのmutationsのSETの第二引数で受け取り、
store/modules/baz.jsのstateに保存する。
保存するとindex.vueのcount(mapGettesのcount)は自動的に1000という値が入る。

index.vueのmapGettersでstore/modules/baz.jsのgettersの準備。
index.vueのmapGettersのcount: 'baz/count'ののkey(count)はdataのプロパティのように利用できる。
例えば、index.vueならthis.countのように利用できるようになっている。
value('baz/count')のスラッシュの左側bazはstore/index.jsのmodules: { baz }で,
スラッシュの右側countはstore/modules/baz.jsのgettersのcount。
ちなみにgetterに引数を持たせることが可能。
他に、mapGettersのstoreのmodule名を省略する書き方もある。

store/modules/baz.jsのcount: state => state.countのkey(count)がindex.vueのcount: 'baz/count'value('baz/count')のスラッシュの右側にあたる。
count: state => state.countvalue(state => state.count)は、store/modules/baz.jsのconst stateのcountを読むようになっている。

Vuexのactionsを利用しなくても構わないが、非同期処理を行いたい場合はactionsのfunctionにasync/awaitを付けて非同期処理を行うことができる
firebaseとかと連携したい場合は利用するかもしれない。

構造は違うけどサンプルリポジトリ

EventHub/EventBus

wip

BrowserStorage (etc: localStorage, IndexedDB...

wip

Server (etc: firebase...

wip

firefoxのdrag, dragendイベントはclientX, clientYの値が0になる

環境

firefox: 61.0.1 (64-bit)

問題

ここのサイトの下の方のdemo
以下のdragイベントの'event'の中身を見ると
document.addEventListener("drag", function( event ) {
clientX, clientYの値が0になっている。

他のブラウザ

chromeだとちゃんとclientX, clientYが取得できる。

対応状況

ここを見ると9年前から話題になっている。

解決案

jQuery UI draggable eventを使う
ここのjsfiddleでdraggableのdragイベントを見る
drag event で取得した中身のoriginalEventにはclientX, clientYがfirefoxでも入ってる!!!
だけどdrag eventを、mousemove eventで補っているっぽい

vueのメソッドイベントハンドリングで引数にeventと任意の引数を渡す

やりたいこと

vueのtemplateでイベントハンドリングするときに、引数でeventとその他の引数もメソッドへ渡したい。

方法

自分は以下の二通りを見つけた。

<button @click="handler($event, foo)"></button>
<button @click="e => { handler(e, foo) }"></button>

サンプル index.vue

<template>
  <button @click="handler($event, foo)"></button>
</template>

<script>
export default {
  data () {
    return {
      foo: 'bar'
    }
  }
  methods: {
    handler (event, foo) {
       console.log(event, foo)
    }
  }
}
</script>
<template>
  <div>
    <ul>
      <li v-for="(word, index) of words" :key="index"
        @click="e => { handler(e, word) }">
        {{ word }}
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  data() {
    return {
      words: ['a', 'd', 'c', 'd', 'e']
    }
  },
  methods: {
    handler (e, word) {
      console.log(e, word)
    }
  }
}
</script>

chromeの開発者ツールでconsole was clearedが表示される

問題

chromeの開発者ツール(developer tool)のコンソールにconsole was clearedが表示される。

解決法

chrome://settings/reset にアクセスしてリセットする。

vueでisomorphic-gitを利用する

やりたいこと

vueでisomorphic-gitを利用する。
DBはブラウザのIndexedDBを利用。

必要なもの

  • vue
  • node
  • npm
  • isomorphic-git
  • browserfs
  • yarn (これはお好み)

install 環境構築

$ yarn global add vue-cli        # or $ npm i -g vue-cli
$ vue init webpack nantokakantoka-project
$ cd nantokakantoka-project
$ yarn     # or $ npm i

使いたいモジュールをinstall

$ yarn add isomorphic-git
$ yarn add browserfs

src/App.vue を以下に変更

<template>
  <div id="app">
    <img src="./assets/logo.png">
    <HelloWorld/>
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld'

export default {
  name: 'App',
  async mounted () {
    const git = require('isomorphic-git');
    const BrowserFS = require('browserfs');
    BrowserFS.configure({ fs: "IndexedDB", options: {} }, function (err) {
      if (err) return console.log(err)
      const fs = BrowserFS.BFSRequire("fs")
      const files = git.listFiles({fs, dir: '/'})
      console.log(files)
    })
  },
  components: {
    HelloWorld
  }
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

実行

$ yarn dev

config/index.jsのmodule.exportsのdevのportを確認(自分は8080)
http://localhost:8080にブラウザでアクセス
ブラウザのコンソールを確認

注意

importを使うと、何故かだめだったので、requireで利用

import git from 'isomorphic-git'
import BrowserFS from 'browserfs'

↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

const git = require('isomorphic-git');
const BrowserFS = require('browserfs')

作成したリポジトリ

github.com

vueでdomを参照したい

必要なこと

  • 言語ブロックのtemplateの参照したいdomにrefを設定
  • 言語ブロックのscriptでthis.$refsを使う

$elについて

this.$refs.hoge.$elにすることで、Elementを参照していることになる。
this.$refs.hoge.$el.firstElementChildみたいな書き方ができる。

注意

this.$refs.hoge.$elでElementを参照できる場合と、
this.$refs.hogeElementを参照できる場合がある

this.$refs.hogeの場合

tempalte言語ブロックのrefをインラインで書いたdomがカスタムエレメントでない場合

<div ref="foo"></div>
mounted () {
  console.log(this.$refs.foo)
}

this.$refs.hoge.$elの場合

template言語ブロックのrefをインラインで書いたdomがカスタムエレメント(importしたcomponent)?の場合

<my-element ref="foo"></my-element>
mounted () {
  console.log(this.$refs.foo.$el)
},
comopnents: {
  'my-element': myElement
}