カテゴリーアーカイブ Vue

著者:杉浦

【Laravel】【Vue】propsを使わずVue上で組んだページ部にLaravelのBladeから値を表示する

 LaravelからVueに値を渡す時によくやる手法は次です。

        <upload-image
            v-bind:dist-url="{{json_encode(route('hoge.update'))}}"
            v-bind:hoge="{{json_encode($hoge)}}"
            v-bind:fuga="{{json_encode($fuga)}}"
            ...
            ...
        ></upload-image>
<template>
  <div>
    {{ hoge }}
    {{ fuga }}
    ...
    ...
  </div>
</template>
export default {
  props: {
    distUrl: {type: String, default: ''},
    hoge: {type: String, required: true},
    fuga: {type: String, required: true},
    ...
    ...
  },

 引数をjson化してpropsとして渡し、Vue内のテンプレートに埋め込みます。やり方はシンプルなのですが情報が増えるにつれpropsの肥大化がひどいことになります。なんらかのオブジェクトにまとめるのも手ですが、それはそれでバリデーションが辛くなります。この問題の回答の一つがVueのslotの仕組みを使うことです。
スロット — Vue.js
 slotは指定された位置のHTMLコードをVueコンポーネントを呼び出す階層から決定するための仕組みです。そのため先ほどの例ならば次の様に書けます。

        <upload-image
            v-bind:dist-url="{{json_encode(route('hoge.update'))}}"
        >
          <div slot="slot-no-namae">
              {{ $hoge }}
              {{ $fuga }}
              ...
              ...
          </div>
        </upload-image>
<template>
  <div>
    <slot name="slot-no-namae"/>
  </div>
</template>
export default {
  props: {
    distUrl: {type: String, default: ''},
  },

 Blade上のHTMLエスケープエコーの短縮構文{{}}を実行してHTMLコードを生成。それのHTMLコードをslotに割り当てます。これにより、propsを介することなくPHP内部で扱っていた変数の値を持つHTMLコードをVue上で組んでwebページのテンプレート内に組み込めました。propした値を後から変更しない、Vue内の関数で参照する予定が無い、といった値は大体この方法に組み込むとスッキリしつつ問題のないコードを作るのに貢献してくれます。

  • この記事いいね! (1)
著者:杉浦

Chromeのtextareaでスクロール位置の変更が上手くいかないことがある

 次のデモはVueを使ったtextarea中のクリック、キーアップ、キーダウンに反応してscrollTopを取得、表示するデモです。
 
 これを動かすと少々直感的でない動作とそのscrollTopの値が現れます。例えば次です。デモはclick, key up, key downそれぞれについていますが動画はclick, key downをまとめた一つのみです。

 screenTop:339になってからEnterを押して改行を実行すると、textareaのカーソルが画面内のてっぺんになり、screenTopが更新されません。どういうわけか度々この現象が起きます。マウスでスクロールぐりぐり、上下に余裕のある場所へクリックでカーソル配置、改行で度々再現します。
 これに近い正常な現象は次の様にすると見えます。カーソルをセット、画面外に追いやってからEnterで改行をすると、textareaのカーソルがtextarea内のてっぺんになります。こちらの場合はscrollTopが更新されます。

 この二つから推測されるのは、「何らかの原因で期待するscrollTopがとれず、仮にscrollTop=0と扱って改行を実行。正常な動作同様にtextareaのカーソルが画面内のてっぺんになるようにscrollTop変更。」という動作です。Chrome自体のコードは非公開ですし、詳しい内容はわかりませんがなかなか面白い現象です。
 余談ですがChromeのscrollTopは少数以下も取っていますがFireFox, Microsoft Edgeは少数以下を切り捨ててInt型の様に振る舞っていました。また、Operaは少数ありでした。加えてOperaもカーソルの挙動が不穏でした。少数絡みで何かしらあるのかもしれませんね。

  • この記事いいね! (0)
著者:杉浦

【Vue.js】Vue.jsに関する色々が置かれているリポジトリawesome-vue

vuejs/awesome-vue: 🎉 A curated list of awesome things related to Vue.js
 awesome-vueはVue.jsに関する様々なモノのリストが置かれたリポジトリです。ライブラリはもちろん、教材Vue.jsが使用されたプロジェクトもあります。
 awesome-vueのオーナーは開発の元締めであるvuejsそのものです。そのためか現在もとても活発にリストが更新されており、その多くが有用です。(草の根ライブラリやスニペットを探すと無視できないバグを持っていることがままあります)

 リンクと概要のみを大雑把な分け方で1ページにずらっと並べているの探すのは気持ち手間ですがリンク先は2000を優に超えており、とりあえず何か探したい時に見てみる場所として満足に使えます。

  • この記事いいね! (0)
著者:杉浦

【JavaScript】フレームワーク環境付きオンラインエディタCodeSandbox

CodeSandbox: Online Code Editor Tailored for Web Application Development
 CodeSandboxはJsFiddleなどと同じJavaScript用のオンラインエディタです。CodeSandboxの特徴は次の画像の様にフレームワークを使うことを前提とした部分です。

 上画像にある様にJavaScriptのフレームワークを広くカバーしており、サーバサイド用のフィドルにもなれます。

 自分はよくVue.jsの単一ファイルコンポーネントを使うのでJSFiddleでVue.jsを操るのは少々苦手でしたが、CodeSandboxならば簡単です。一度に複数のファイルを扱えるため、単一ファイルコンポーネントを複数集めて一つのページを作る手法がそのまま使えます。

 青色のShareボタンからコードを見せるためのリンクとHTMLコードを発行することもできます。

  • この記事いいね! (0)
著者:杉浦

【Vue.js】連なった単一ファイルコンポーネント間のイベント発火

 Vue.jsはwebページを部品単位で構築することによって開発を支えるJavaScriptのフレームワークです。
Vue.js
 Vue.jsには単一ファイルコンポーネントという仕組みがあります。これは次のコードの様にあるwebページの部品のHTML、JavaScript、CSSを一ファイルにまとめる仕組みです。

<template>
  <p>{{ greeting }} World!</p>
</template>

<script>
module.exports = {
  data: function () {
    return {
      greeting: 'Hello'
    }
  }
}
</script>

<style scoped>
p {
  font-size: 2em;
  text-align: center;
}
</style>

単一ファイルコンポーネント — Vue.js
 単一ファイルコンポーネントを用いるとDOMツリーならぬコンポーネントツリーともいうべき構造のコードを記述することになります。下図はあるページのコンポーネントの階層構造です。まとめ役のmain-template、その下に検索ボックスのsearch-box-templateと各モーダル、それぞれに入力欄のform-group-row-inputが連なっています。

 これはまだシンプルに保ててますが、モノによってはもっと深い構造なることもあります。
 離れた場所にある異なるメニュー内に共通アクションのボタンをつけるなど、異なるコンポーネント内で定義されているイベントを発火したい時があります。そういった時は$emitで親のイベントを発火することができます。
API — Vue.js#vm-emit
 サンプルコードなどでもよく現れ、よく使う形はmethod内部での$emitです。次の様に子ファイルの内部で$emit(‘イベント名’,引数)とすると、親ファイル中でhoge-eventが動き、$emitで渡された引数が格納された$event付きのhogeEventAction()が動作します。

// 親ファイル
<first-template
  @hoge-event="hogeEventAction($event)"
/>


// 子ファイル
method{
  fuga(){
    // 何か色々処理
    this.$emit('hoge-event', 'hogehoge');
  }
}

 これがよくある書き方ですが孫の様な直下よりも下にあるコンポーネントから親のイベントを発火する際には冗長です。

// 親ファイル
<first-template
  @hoge-event="hogeEventAction($event)"
/>


// 子ファイル
<second-template
  @hoge-event="hogeEventAction($event)"
/>
// ---省略---
method{
  hogeEventAction(argv){
    this.$emit('hoge-event', argv);
  }
}


// 孫ファイル
method{
  fuga(){
    this.$emit('hoge-event', 'hogehoge');
  }
}

 イベントの定義時にはイベントに対応する関数を記述するのがよくあるパターンですが、この部分はワンライナーならば何でも入れられます。このため中間になるコンポーネントには次の子ファイルの様に、親のイベントを発火する親と同名のイベントを定義すると簡潔なコードになります。

// 親ファイル
<first-template
  @hoge-event="hogeEventAction($event)"
/>


// 子ファイル
<second-template
  @hoge-event="$emit('hoge-event', $event)"
/>


// 孫ファイル
method{
  fuga(){
    this.$emit('hoge-event', 'hogehoge');
  }
}

 子ファイルの内部を汚さずに孫から親のイベントを発火できました。

  • この記事いいね! (0)
著者:杉浦

【Vue.js】デフォルト値による簡易な引数で呼び出せる柔軟なコンポーネント

 Vue.jsではしばしばカスタムHTMLタグとも言える程、小さな部品を作りたくなります。例えばform中の入力欄です。簡易に作るのであれば次の様にlabelとinputに必須属性とユーザ向けの多少の文言をつけるのみです。

<label for="login_username">ユーザ名</label>
<input
  id="login_username"
  type="text"
  name="username">

 実際にはこれにデザインが加わります。

<div class="row">
  <div class="col-md-12">
    <div class="form-group row">
      <label
        class="col-md-3 col-form-label text-md-right"
        for="login_username">ユーザ名</label>
      <input
        id="login_username"
        type="text"
        class="col-md-9 custom-control-input"
        name="username">
    </div>
  </div>
</div>

 Vue.jsを用いるならば、inputタグのvalueと何かしらの変数を同期させたくなるでしょう。動的なバリデーション、警告を追加もするでしょう。

<div class="row">
  <div class="col-md-12">
    <div class="form-group row">
      <label
        class="col-md-3 col-form-label text-md-right"
        for="login_username">ユーザ名</label>
      <div class="col-md-9">
        <input
          id="login_username"
          v-model="vModelValue"
          type="text"
          class="custom-control-input"
          name="username"
          @input="validation()">
       <div
          v-show="isValidationErr"
          class="text-black-50">{{ localValidationErrMsg }}</div>
      </div>
    </div>
  </div>
</div>

 シンプルなtype=”text”でこれです。inputタグの属性に凝ると更にvalue、placeholder、required、typeがnumberならばstep、max、minなどが増えます。これはたかだか一つの入力欄でありフォーム全体を考えた場合、inputタグセットが十個近く並んだりもします。素のHTMLコードのままその様なものを記述した場合、可読性が落ちてしまい開発も保守もやり難くなってしまいます。Vue.jsでコンポーネントとしてまとめることでとりあえずの解決を図ります。

 上のJSFiddleでコンポーネントを用いているHTMLコードは18行程です。実際はコンポーネント定義をHTMLコードと別の1ファイルにまとめるため、コンポーネント定義部はカウントしていません。上述した20行程のinputタグ周りを重ねて書くよりよっぽど楽になりました。
 コンポーネントを用いることで記述は楽になりましたが、全てtype=”text”で様々な属性が全くないinputタグセットでしかなく、柔軟性が犠牲になっています。属性を指定するための引数を増やすことによってもっと柔軟なコンポーネントにします。柔軟にするうえで気を付けることに、呼び出し側で不要なコードが増えない様することがあります。コンポーネントを呼び出すための引数にデフォルト値を与えることによってこれを実現します。

 inputタグの効果が多彩になりました。メールアドレスは\や日本語の様なメールアドレスに含まれることのない文字を打つと、その文字を消して警告を表示します。コードのHTMLを見て頂けばわかる通り引数がinputタグの属性の大部分をカバーする様になっています。それだけ大量の引数が用意されているにもかかわらず、コンポーネント呼び出しが短くまとまっているのがデフォルト値の効果です。

<input
 :value="value"
>
/** 省略 */
props:{
  value: {type: String, default: null},
}

の様に記述するとコンポーネント呼び出し時に引数valueを指定しない場合、value=nullとなりHTMLコード上でもvalue属性は指定されなかったことになります。これと同じ様ことを他の属性にも適用することで大量の引数を用意しつつも、呼び出しを簡単にしています。
 実際のコーディングではンポーネントの定義を1ファイルにまとめる単一ファイルコンポーネントの仕組みを用います。これにより、JSFiddleで表示しているなかなかリッチな画面を作るHTMLコードは次の様にまとまります。

<div id="app">
  <form-input
    label="ユーザ名"
    id="edit_username"
    name="username"
    placeholder="例:浜松 太郎"></form-input>
  <form-input
    label="メールアドレス"
    id="edit_mail"
    name="mail"
    type="mail"
    validation-template="mail"></form-input>
  <form-input
    label="番号"
    id="edit_number"
    name="number"
    type="number"
    value="0.53"
    max="1" min="0" step="0.01"></form-input>
  <form-input
    label="色"
    id="edit_color"
    name="color"
    type="color"
    value="#F0115F"></form-input>
</div>
  • この記事いいね! (0)
著者:杉浦

Vue.jsの配列監視

 Vue.jsはJavaScriptのフレームワークで、目玉の機能として変数の値の変化に同期して画面上の描画を変更する機能があります。変数の変化は変数の監視機能で実現されていますが、オブジェクトや配列のプロパティをどこまで監視するかという問題があります。JavaScriptである以上prototypeと付き合っていく必要があり、再帰的にプロパティを見ることはprototypeを大量に見ることにつながり計算量的な問題が発生します。
 Vue.jsではいくつかの配列用のメソッドを上書きすることによって監視を実現しています。

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()

リストレンダリング — Vue.js#配列の変化を検出より引用

 このため

a[a.length + 1] = 1;

 の様にするよりも

a.push(1);

の様に配列操作メソッドを使うことが推奨されます。
 上記のリストには配列中のある値を変更するためのメソッドが存在しません。加えてVue.jsはプロパティの変更を検知しません。

a[0] = 1;// Vue.jsは変数aが変化したことに気づかない

 このような時には

vm.$set(/** 変更対象元の配列やオブジェクト */vm.items, /** インデックスやプロパティ名 */indexOfItem, /** 変更後の値 */newValue)

 の様にvm.$set()なるVue.jsにこの変数は変更したと通知するメソッドで値を変更することになります。
 原因はわかりませんでしたが、これでもなお画面が変化しないことがありました。このような時には

vm.$forceUpdate();

なる強制再描画メソッドを呼び出します。このメソッドの再描画は特徴的で、呼び出したコンポーネントとその子コンポーネントでのみ再描画をします。とにかく更新したい時には、大本の親コンポーネントにvm.$forceUpdate()を実行するイベントを設定して子コンポーネントから発火させる方法が望ましいでしょう。

  • この記事いいね! (0)
著者:杉浦

HSL表記によって色を自然に変化させる

 色の表記の定義である色空間には様々なものがありますよく聞くものに赤青緑の三原色を用いたRGB、それに加えて透明度を足したRGBAがあります。他にも印刷で使われるCMYK(Cyan, Magenta, Yellow, Key plate)やLab色空間、XYZ表色系など目的に合わせて多くのものが作られています。HSLはその中の一つでHLSとも呼ばれ色相(Hue)、輝度(Lightness)、彩度(Saturation)の3値から定義される色空間です。HLSの値の大雑把な意味は次です。

  • 色相:色味を0~360度の範囲の角度で表す。0度は赤で、その反対側に位置する180度は赤の反対色にあたる青緑。すなわち、反対色を見つけるのも容易。色相についてはHSVと同じ。
  • 彩度:HSVとは違い、純色から彩度が落ちるということは、すなわち灰色になっていくという考え方に基づいている。
  • 輝度:明度100%を純色とし、そこからどれだけ明るさが失われるかを示すHSVとは違い、輝度0%を黒、100%を白とし、その中間(50%)を純色とする。50%以下はHSVの明度を示し、50%以上はHSVの彩度を示すと考えると分かりやすいだろう。

HLS色空間 – Wikipediaより引用

 紹介するHSLの主な利点は二つです。まず第一にCSSのオプションとして直接指定できます。次の様にhsl(hue, ssaturation, lightness)を指定するのみで大変楽です。

style="background-color: hsl(90deg, 100%, 50%);

CSS を使った HTML の要素への色の適用 – HTML: HyperText Markup Language | MDN #HSL_functional_notation
– CSS: カスケーディングスタイルシート | MDN #HSL_colors
 わざわざ変換式を実装してRGBに変える必要も、RGB上でややこしい計算をする必要もありません。
 第二は自然で多彩な色の変化を簡単に表せることです。次のJSFiddleはHSL方式とRGB方式それぞれで箱の中の色を変えるコードの実装です。

 色相の下に彩度、輝度が引っ付いているイメージで簡単に色を決定することができると思います。RGBでは赤緑青から離れる程どうやって思いついた色を作るのかてこずるでしょう。
 HSLがRGBに比べて特に便利なのがある色からある色への変化です。同時刻にある色になる様に三値をそれぞれ一次関数的に変化させるのみできれいな色の変化を実現できます。

  • この記事いいね! (0)
著者:杉浦

Vue.jsでVue.js向けに開発されていないライブラリを用いる

 Vue.jsはwebページを部品化してコーディングすることを目的としたJavaScriptのフレームワークです。単一コンポーネントの仕組みを用いたVue.jsのコード例の多くは(少なくとも私の読んだコードでは)次の様になっています。

// コンポーネント定義用Vue.jsコード
<template>
  // Vue.jsと連携するHTMLコード
</template>

<script>
export default {
  name: 'hoge',
  // prop:{}とかdeta(){return {hogehoge}}とかmethods:{}とかVue.jsと連携するためのプロパティ
};
</script>

<style scoped>
  // このコンポーネント内でのみ適用されるcss
</style>
// コンポーネント読み込み用JavaScriptコード
window.$ = window.jQuery = require('jquery');
// 色々グローバルで使うライブラリ

window.Vue = require('vue');

Vue.component('hoge', require('./hoge.vue').default);

const app = new Vue({
  el: '#app',
});

 自分はVue.jsで使うことを前提にしていないライブラリはJavaScript側2、3行目の様に色々読み込むものだと思っていました。一方でこのようなやり方はグローバル汚染がひどく、重要なごく一部のライブラリ以外を読み込んだ場合あっという間にバグが生まれるとも思っていました。実際はそのようなことをせずともよく、次の様に記述することで管理しやすくライブラリを用いることができました。

<template>
  // Vue.jsと連携するHTMLコード
</template>

<script>
import 'fuga';
import foo from 'foo';

// Vue.jsに依らない静的処理
const bar = foo.bar();

export default {
  name: 'hoge',
  // importしたライブラリや変数barを用いたVue.jsと連携するコード
};
</script>

<style scoped>
  // このコンポーネント内でのみ適用されるcss
</style>

 <script>タグの先頭に記述するだけです。<script>タグの内部ならばVue.jsがいい感じにスコープを作ってくれるのでグローバル汚染を気にせず好き勝手できます。
 <script>export default {で始めて、}</script>で終わらなければならないという風に思い込んでいましたがVue.jsにその様な縛りはなく、実際はかなり自由に記述できました。

  • この記事いいね! (0)