以下の内容はhttps://cream-worker.blog.jp/tag/Vueより取得しました。


VueのスロットとLint

VueでLint入れてると下記のコードで怒られる。

    <template v-slot:item.actions="{ item }">
      <v-icon
        small
        class="mr-2"
        @click="editItem(item)"
      >
        mdi-pencil
      </v-icon>
      <v-icon
        small
        @click="deleteItem(item)"
      >
        mdi-delete
      </v-icon>
    </template>
v-slot:item.actions="{ item }"

これがダメと。

'v-slot' directive doesn't support any modifier

っていうLintのルールで怒られるのですが、これVuetifyの公式に書かれてるサンプルなのですが。。。

うーん。って感じ。

そして、対応用のコードって下記になると思うのですが、

#[`item.actions`]="{ item }"

微妙すぎんだろ。modiferの方が全然いいじゃんとしか思えないのですが。。。

ルールのページ読んでも何でダメなのか意図が分からなかった。

誰か教えてくらはい。。。

参考:https://stackoverflow.com/questions/61344980/v-slot-directive-doesnt-support-any-modifier

Vue Mutating Props

Vueで前から気になってたのですが、やっぱりどうかなと思うので。

公式で見解ってちゃんとあるんだろうか?

Propsは変更するなっていうのはだいたい書いてあるように思うのでその延長かな?


Vueのコンポーネントで下みたいな事をしてるやつをたまに見る。

<input v-model="item.hogehoge" />

props: {
  item: {
    type: Object,
  }
}

Vueのフレームワークとしての考え方的に上記はNGなはず。

Object渡しすると参照でそのまま渡されるのでたまたま上手くいってるだけですよね。

下はダメって言われますしね。

<input v-model="hogehoge" />

props: {
  hogehoge: {
    type: String,
  }
}

Objectで渡した場合はVue側で検出できないから通ちゃってるってだけっぽ。

検出できるようになちゃったら丸ごと潰されたりするのかしら?

影響でかすぎると思うからそれはないかな?


小ネタというか小技的に扱ってる人もいるように思うのですが、

フレームワークの考え方とはあってないよなーと思ってたら

やっぱりそういう議論はあるみたいですね。

やっぱり上記でも

ugly but will work: めっちゃダサいけど動きはする

みたいな感じ。


どうなんでしょうね。個人的にはこれ系の実装は止めた方が良いとは思う。

データフローを明確にするためのルールでしょうし。

とは言ってもやりたくなるのも良く分かるところ。

ままならぬ。。。


どうでも良いのですが、「ugly but will work」ってフレーズが凄く気に入った。

結構そういう時ってありますよね。。。

Nuxtの開発環境をDockerで

DockerでNuxtのローカルの開発環境作りたくなった。

やってみたのですが、なかなか思い通りにいかなかったので記載。

やりたい事としては、nodeとかnode_modulesとかはdocker側において、ソースはローカル。

で、ローカルのソースを修正したら、nuxtというかVueのホットリロードを動かしたい。

ひとまずソースの構成。

.
├── app
│   ├── Dockerfile
│   └── webapp # nuxtのアプリ
└── docker-compose.yml

nuxtのアプリを作る

cd app
npx create-nuxt-app webapp # 質問は適当に動けばとりあえず。。。

で、ここでは「npm install」はしない。やった場合は後述。

Dockerfile

FROM node:14.1.0-alpine

WORKDIR /app

# 多分、nuxt動かすだけなら、@vue/cliとか@vue/cli-initはいらないと思う。
RUN apk update && \
    npm install -g npm && \
    npm install -g @vue/cli && \
    npm install -g @vue/cli-init && \
    npm install -g create-nuxt-app

ENV HOST 0.0.0.0

CMD ["npm", "run", "dev"]

Docker-compse

version: '3'
services:
  webapp:
    build: ./app
    ports:
      - 13000:3000
    volumes:
      - ./app/webapp:/app
    tty: true

ホスト側のPortは適当に。tty:trueはあっても無くてもどっちでも。docker内のコンソール出力を全部見たいのでひとまず付ける。

そしたら「docker-compose build」

build終わったら下記でコンテナ内で「npm install」する。

docker-compose run webapp ash
npm install
exit

そしたら「docker-compose up」

これで、上記のポートの割り当てだったら「http://localhost:13000」でnuxtのデモ画面が出る。

そのまま、ローカルのファイルを修正すればDocker側にも反映されてホットリロードされると。

全ソースはここに上げてます


で、ここから出来なかった事。

本当はbuildのところもdockerfile側に置きたい。↓の感じ。

COPY ./webapp/package.json /app/package.json
COPY ./webapp/package-lock.json /app/package-lock.json
RUN npm install

↑は上手くいくのですが、nuxtの実行をすると、「.nuxt」ってディレクトリ作られるじゃないですか?

こいつがかなりやっかいで、実行時に動的に作ってるらしく、ローカルのディレクトリと共有してると権限がおかしくなって動かなくなるのです。

dockerignoreとかマウントしてからvolumeで部分的に上書きして~とかローカルのuidと合わせて~とか色々やってみたのですが、どうにも上手くいかない。

今のところ上手くいった方法が上述のnpm installはbuild後にupする前にコンテナ内で実行しておくという手順。

んーカッコ悪いんだけど、開発環境としてローカル側にソース置きたい場合はこれしかやりようがなさそう。


途中でnpm installとかnpm run devしちゃって「node_modules」「.nuxt」出来ちゃってる場合はコンテナ内に入る前に全部消す。

sudo rm -rf .nuxt
sudo rm -rf node_modules

v-calendarのdate-pickerで外から表示する月を変える

Vueのコンポーネントで、v-calendarっていうやつがある。

相当色んな事が出来て、今まで見てきたカレンダー的モジュールの中では一番じゃないだろうかと個人的に思ってる。

中のソースもめっちゃ勉強になる。

で、これのDatePickerを使う場合に、外から日付を渡して表示する場所を変えたかったりする。

一筋縄でいかなかったのでちょっと記載。


多分普通に使う分には↓の感じ。

<vc-date-picker v-model="dateval" />

適当なdataをv-modelで指定してあげれば、それに応じた日付にはしてくれると思う。

ただ、このdatevalを外で変えた場合に、カレンダーのページがその日付の月になってない。

これをやるのに、shoPageRangeっていうの呼ぶらしい。公式のドキュメントにもそんな感じで書いてある。

これのissue

まー上手くいかないと。popoverの場合は上手くいかない。is-inlineだとうまくいくっぽい。

どうもpopoverの場合、$refs.calendarの$refsがcalendarじゃなくてpopoverになってるみたいですね。

で、ソース色々見てたら、少なくとも最新のバージョンではどうも違うっぽい。正解はこっちだと思う。

<vc-date-picker v-model="dateval" ref="calendar"/>

methods:{
  changePage(){
    this.dateval = "何かの日付"
    this.$refs.calendar.adjustPageRange()//コッチ
  }
}

datepicker使う場合は少なくとも「adjustPageRange」っぽいですね。

内部的なカレンダーの日付のページにこれで合わせてくれる模様。

コード的にはここ

あと、カレンダーが非表示の状態だと動かないっぽい?

なので、最終的にはこんな感じにしておく必要がありそう。

<vc-date-picker v-model="dateval" ref="calendar" @popoverDidShow="handleShow"/>

methods:{
  changePage(){
    this.dateval = "何かの日付"
  },
  handleShow(){
    カレンダーが表示されたときに呼ぶ
    this.$refs.calendar.adjustPageRange()
  }
}

StoryBookのVisualTest(StoryShotsとStorycap)

以前書いたやつの続き。

StoryBookでビジュアルリグレッションテストしたいときの構成。

前のはURL単位にテストファイルを作っていたのですが、一括で全URL対象にした方が楽。

storyshotsを使うかscreencapを使うか。

両方やってみてscreencapを使うようにしたのでその辺の話。


StoryShots

GitHubのReadmeを見れば導入はそのままでOK。

で、実際に使う時に設定したこと。

除外したいstoryの指定

実際のコンポーネントを考えると、ずっとアニメーションしてるものの場合、同じスクリーンショットとるとか不可能なので、テストを外したい。Readmeにも書いてあるけどその場合は↓。

import initStoryshots from '@storybook/addon-storyshots';

initStoryshots({
  storyKindRegex:/^((?!.*?DontTest).)*$/
});
スナップショット取る前にちょっと待機させたい場合

開いた瞬間アニメーションしてるやつが落ち着くまで待機させたい場合。

import initStoryshots from '@storybook/addon-storyshots'
import { imageSnapshot } from '@storybook/addon-storyshots-puppeteer'


const beforeScreenshot = (page, { context: { kind, story }, url }) => {
  return new Promise(
    (resolve) =>
      setTimeout(() => {
        resolve()
      }, 1000) // waitのミリ秒
  )
}

initStoryshots({
  suite: 'Image',
  storyKindRegex: /^(?!.*?nvis).*$/,
  test: imageSnapshot({
    storybookUrl: 'http://localhost:9009',
    beforeScreenshot,
  }),
})

で、見逃してるだけのような気もするのですが、story毎に待機時間を決められないように思うので、待機時間つけちゃうと全部遅くなっちゃう。。。

あと、除外する名前の正規表現ってURLのところになってると思うのですが、どうにも上手くいかない。名称の見方がなんか違うっぽい。どう書けばいいんだろうか。正規表現的に色々試してみたのですが、どうもやりたい事が出来ない。


StoryCap

GitHubのリポジトリ。

で、StoryShotsの代わりにこれを使うようにした。スターの数だと圧倒的にStoryShotsなんだけど、

こっちの方が望む結果にすぐたどり着いたので。導入もすごく楽。

動き的にHTMLの変化を監視して、画面のアニメーションが落ち着くのを待ってからスクリーンショットを撮ってるぽい。賢い。

もちろんずっとアニメーションしてるコンポーネントは↑のWaitのタイムアウトでエラーになるけど。

で、そういうコンポーネントを除外したい場合は↓の感じで実行コマンドに渡す。

storycap http://localhost:9009 -e "**/DontTest/**"

ルールとしてstorybookに表示されるサブディレクトリの階層で、DontTestとか階層を作って、テスト除外したい奴はそこに配置する感じ。ちょっとカッコ悪いけど、urlをminimatchしてるのかな?

ディレクトリ階層で切り分けないとうまくいかなかった。

ただし、StoryCapはスクリーンショットを撮るだけでテストまでは入ってないので、

そこは自分で書いておかないとダメ。ロードマップ見ると将来的には対応してくれるようす。

テストは↓の感じで書けばよいのではないかと。

const fs = require('fs')

function getFiles(dir, files_) {
  files_ = files_ || []
  const files = fs.readdirSync(dir)
  for (const i in files) {
    const name = dir + '/' + files[i]
    if (fs.statSync(name).isDirectory()) {
      getFiles(name, files_)
    } else {
      files_.push(name)
    }
  }
  return files_
}

const files = getFiles('storycapで画像が出来るディレクトリ')

describe.each(files)('ビジュアルテスト', (filepath) => {
  test(`file: ${filepath}`, () => {
    const data = fs.readFileSync(filepath)
    expect(data).toMatchImageSnapshot({
      customSnapshotsDir: 'テスト用のスナップショット周りが出来るディレクトリ',
      failureThreshold: 0.10,
      failureThresholdType: 'percent',
    })
  })
})

Vue Render使う時に引数有のClickイベントを定義したい

VueでClickイベントのハンドラを書く時は大体↓の感じでかく。

<button v-on:click="handleClick">click me</button>

methods:{
  handleClick: function(arg){
    this.count++
    this.args = arg
  },
}

で、引数に何か渡したい場合は↓

<button v-on:click="handleClick(1, $event)">click me</button>

methods:{
  handleClick: function(arg, event){
    this.count++
    this.args = arg
  },
}

通常のeventオブジェクトも↑の感じで渡せる。

で、これをRender使ったときにどう書くかちょっとハマったので記載。

調べても出てこないのよね。みんなこの程度の事にはハマってすらいないんだろうなーと

ちょっと寂しい気持ちになりました。頭悪くてごめんなさい。

で、renderで書く場合、onのハンドラは下の感じでcreateElementに渡す。

createElement('button', {on: {click: handleClick}}, 'ボタン')

普通に引数付きで書くとこう書いちゃう。

createElement('button', {on: {click: handleClick(1)}}, 'ボタン')

コレだとダメ。

オブジェクトはあくまで関数を渡さないといけないので、↑だと関数を呼んじゃってる。

なので、下の感じにする。

createElement('button', {on: {click: (x) => this.handleClick(1, x)}}, '引数とイベント有')

ラムダ式の引数がイベントオブジェクトになってるのでそれをそのまま渡すと。

ハマったハマった。。。

サンプルは↓。

Vue + Jestが動かない

Vueコンポーネントの単体テストをJest使ってやってたんですが、

別の端末でCloneして動かしたらなんか動かない。

エラーは下の感じ。

Configuration contains string/RegExp pattern, but no filename was passed to Babel

うーん。デバッグしてみたりもしたけど意味不明。

何じゃらほい。と思って、とりあえず「node_modules」ディレクトリを丸ごと消して、

「package-lock.json」ファイルを消して再度「npm install」したら治った。

なんだろね。。。

Vueのrender(描画関数)でJSXでv-modelを使いたい

Vueは素晴らしいと思うのですが、公式のこの辺に書いてある通り、大量なv-ifとか使わないとつらい場合等、純粋なJavaScriptのロジックで書きたくなる事はやっぱある。

その時の対応としてrender()をVueで用意してくれているのですが、いかんせん、なかなか書きにくい感じになっている。それにも対応してくれていて、JSXで書けるようにbabelのプラグインがある。

で、JSXで書けるんだけど、v-modelがまるで効かない。公式的にはv-modelは対応してないから自分でロジック作れって書いてはある。でもやっぱVueで入力系のコンポーネント作るときはv-model使いたい。

探してみると、babel-plugin-jsx-v-modelっていうプラグインで出きるっぽい、入れてみようかなーで公式のVue-Cliのソースとかみてたら↓の状態になってた。

sample

おや?つーか、そもそもプラグイン入ってるの?つか、コメントアウトされておる。。。

そしたら、Readmeに↓で書いてあった。

sample2

あらん。Babel7への対応が出来てないから一時的に無効にするとさ。残念。

復活を期待しつつ、v-modelを別の形で実装しておくしかあるまい。

Vue CLI 3 のWebPackの設定

ちょっと独り言。

公式のココに書いてあるけど、vue cli3のinspectが凄く便利だった。

vue inspect > output.js

で、vue cliで作ったプロジェクトのwebpackの設定が全部見れる。

「@」ってaliasでそういう設定してたのねとか。

設定の上書きするのにもこれ見ないとどうすればいいかわかんないですね。

babel thisがundefinedになるやつ

GitHubのリポジトリにあるソースを参考にして、

別のプロジェクトに組み込もうとかした時に、

ちょっと昔のソースだと即時関数的に↓の感じのコードになってる場合。

(function(root,factory){
  //code
}(this,function(){
  //code
}));

bable7以上だと、引数のthisがundefinedになって動かなくなる。

で、色々対応方法調べてみたら、下のがとりあえずの回避方法。

.babelrcの中に下の感じで対象のjsファイルを書く。

module.exports = {
  overrides: [
    {
      test: "./src/utils/hoge.js",
      sourceType: "script"
    },
    {
      test: "./src/utils/fuga.js",
      sourceType: "script"
    }
  ]
};

デフォだとmoduleとして読み込んでるみたいで、export defaultで定義しておかないとダメになるって事なんかな?で、それをmoduleじゃなくてscriptファイルとして読み込めっていう設定なのかしら。いまいち分かってない。

本来はソースをexport default的なやつに直した方がいいんじゃないかと思う。今時の作り方で言えば、即時関数自体やめた方がよいのかしらね。

Vueで「babel.config.js」使ってる場合も同様に下の感じで書く。

module.exports = {
  presets: ["@vue/app"],
  overrides: [
    {
      test: "./src/utils/hoge.js",
      sourceType: "script"
    },
    {
      test: "./src/utils/fuga.js",
      sourceType: "script"
    }
  ]
};



以上の内容はhttps://cream-worker.blog.jp/tag/Vueより取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14