へっぽこ社会人4年生がプログラミングを頑張る

へっぽこ社会人4年目がプログラミング系統を中心に書きたいことをつらつらと書きます

DojoCon Japan 2019 参加レポート Part 1

2019/12/21(土)に名城大学で開催された DooConJapan 2019 に参加してきました。2020年のプログラミング教育必修化にあたり、今回のDojoConでは、全国各地のCoderDojoの関係者だけでなく、先生方やプログラミング教育支援に携わる大学の方々まで、様々な分野の方々が参加され、規模の大きいイベントとなりました。今回のDojoConでは、KeynoteからDiscussionまで、私にとって非常に良い刺激になりました。

記事を編集していたら、思いの外長くなったので、複数パートに分けて書いていきたいと思います。Keynoteから素晴らしすぎる内容で、書きたい内容も盛り沢山なので、今更感もありますが、徐々に参加レポートの記事を書いていきたいと思います。もしかしたら、記事公開後に加筆、修正などするかもしれませんので、ご了承願います。

前置き: 自分とCoderDojo

私自身は、CoderDojo 梅田にてメンターとしてCoderDojoの活動に参加しています。大学院在学中は、TAとして学部生のプログラミングの実習を補助する立場を経験したことがあり、どうしたらプログラミングが面白いということを伝えられるか、プログラミングをもっと学びたい人たちにどのようなアプローチで教えていくのが良いかということに興味がありました。

CoderDojoでは、プログラミングを学びたい子供を対象に、プログラミングを学ぶ環境を提供し、分からない箇所などをメンターに相談できるということで、CoderDojoに興味がわき、2019年の9月に梅田のCoderDojoを見学させていただきました。以降はCoderDojo 梅田のメンターとして、ぼちぼちと活動しています。

Keynote

Keynoteでは、 さくらインターネット の田中 邦裕氏が「 子どもたちが活き活きと輝ける未来へむけて ~プログラミングが好きな子ども達のためにできること~ 」という題目で登壇されました。セッションの様子は こちら から閲覧できます。非常に価値の高い内容なので、是非ご覧いただくことを強くお勧めします。

さくらインターネットはCoderDojoにサーバを100台を提供しており、日本のCoderDojo活動に寄与されています。

プログラミングを楽しむ

セッション内で、田中氏は プログラミングは楽しいものだ ということを強く主張されていました。2020年からプログラミング教育が始まるということもあり、これからの子供たちにはプログラミングを通じて何かをハックすることを楽しめるようになってほしいとのことでした。

プログラマという仕事はブラックなイメージが社会的に根強いと思いますが、本来プログラミングは嫌々やるものではなく、楽しんでやるものだと述べられていました。自分自身、プログラムを組むことが好きでプログラマになったので、この意見に強く共感しました。同時に、この感覚はプログラミングだけにとどまらず、様々な分野で同様に当てはまるのではないかと感じました。機械のパーツを組み立てて物を作るのが好きでモノづくりに従事したり、物語を読んだり書いたりするのが好きで小説家になったり、 自分が本当に好きなものに従事する ことが重要だと思います。

社会人になってからまだ日も浅く、そこまで多くの人と話したわけでもありませんが、意外と 自分が本当にやりたいことを分かっていない人が多い のではないかと感じることが増えてきました。おそらく、社会的にいい大学に行って、大企業に入れば将来安泰と言われ、自分のやりたいことに関係なく右に倣えだったのが一つの要因ではないかと考えます。収入を多さを中心に考えるあまり、若い頃から自分のやりたいことを押しつぶし、やがて自分がやりたいことが何なのか見えなくなっていくのではないかと思います。

別の要因として、社会人になるまでの選択肢が限られていて、さらに回りと違うことに対して、非常に不寛容な社会であることが挙げられると思います。例えば、多くの人は高校に進学するでしょうが、果たして本当に勉強したくて進学した人々はどの程度でしょうか? 高校以降は義務教育ではないから、嫌なら行かなければ良い、という意見もあるかと思いますが、学歴が中学卒業で雇用する会社はどの程度あるでしょうか? おそらく、中学卒業で雇う会社はほとんど無いかと思います。選択肢があるように見せておいて、実際のところはレールから外れることには不寛容な社会なので、結局選択肢が限られているのではないかと感じます。

社会と余裕

人々に余裕がないと社会が回っていかないという話を、渋滞を例に用いてされていました。渋滞は車の多さが原因ではなく、車間距離が詰まってしまっていることが原因で、車間距離が短いと前の車がブレーキをかけた際に後続の車もブレーキをかけ、やがて止まってしまうとのことでした。社会も同様に、人々に余裕がないと、なんらかのブレーキがかかった時に行き詰まってくると述べられていました。私がなんとなく感じる、社会全体の息苦しい空気は、もしかしたら社会全体に余裕がないことが原因なのかなと思いました。

私自身が怠け者すぎるだけなのかもしれませんが、たとえ仕事に関係ない、自分が積極的に入れたプライベートな用事であっても、ずっと予定が詰まっている状態はしんどいと感じます。私から見ると、社会全体的にストイックすぎる人が多いと今までずっと感じていましたが、田中氏も同様のことを述べられていたので、少し安心しました(笑) (それを差し引いても、おそらく私自身は単なる怠け者であることにに変わりないのでしょうがw)

まとめ・考察

今回のKeynoteのセッションの内容は共感するものが多く、講演者の田中氏の考えに非常に強い興味をおぼえました。私自身、プログラミング教育の必修化には少し疑問を持っていて、そもそもプログラミングは、誰かからやらされるものではなく、自主的に楽しんでやるものだと考えていたので、 プログラミングは楽しい という主張に大きく共感しました。もちろん、プログラミングだけでなく、他の分野についても、興味を持ったものに対して楽しんで取り組むことが重要だと考えます。

プログラミング教育とは別に、我々社会人も、今の社会のあり方について考えるべき時期になりつつあるのではないかと思うセッションでもありました。今まで、なんとなく感じていた息苦しさが、実は人々に余裕がない状態で、社会が回りづらくなっている状態なのではないかと思います。自分のやりたいことが出来ず、あるいはやりたいことすら分からず、仕事の忙しさに追われて余裕がなくなっているというのは、社会的にどことなく感じる部分があると思います。なんとなく、生活のための仕事ではなく、仕事のための生活になっているのではないかと感じることがあり、忙しくて当たり前、仕事は辛くて当たり前で良いのかと疑問を感じます。

最近では、YouTuberがなりたい職業としてあがっており、このことに対して嫌悪感を抱いている人も少なからずいると思います。仕事は辛くて当たり前という価値観からすれば、最近急に出てきて、かつぱっと見て辛くなさそうな、楽しんでいることに対して違和感があるのでしょうか? いずれにせよ、自分のやりたいことなどを押し殺してまで自分が楽しいと思えない仕事では、子供から見たら憧れる職業とは思えないでしょう。 (正直、自分自身も仕事のための生活とか絶対したくありませんが...)

人々の心に余裕を持つためにも、余暇だけでなく、仕事の内容自体が自分の熱中出来る分野に就けることが重要だと思います。そのためにも、子供の頃にどの分野に強く憧れるかを探し、実際に体験できる機会や社会の枠組みが必要になると考えます。自分自身も、何か機会や枠組みの支援に寄与できないかを考え、実践していきたいと思います。


Part 2 へ続きます。今回Keynoteのことしか書いてないですが、講演内容が素晴らしすぎて、色々考えさせられることも多かったので、分量が多くなってしまいました(笑)社会人向けに講演してもらいたい内容で、ぜひとももう一度聞きに行きたいです。

Ubuntuでディスプレイのリフレッシュレートを変更する方法

自宅のモニタが寿命を迎え始めていたので、約5年使ってきたLGのモニタを買い替えることにしました。幸いにも、モニタを譲り受けることが出来たので、購入することなくモニタを交換出来ました。

モニタが4K対応なので、画面を広々使えるようになったと感じられるようになりました。今まで1920x1080のサイズで慣れていたので、しばらくは解像度4Kは多少文字が小さいと感じながら、徐々に慣れていくことかと思います(笑)

職場のモニタはリフレッシュレートを60Hzに設定して作業していて、目が肥えてしまい、30Hzでは少しマウスの動きがカクついているに見えました。Windowsではディスプレイのプロパティからリフレッシュレートを変更できましたが、Ubuntuのモニタ設定にはリフレッシュレートの設定項目が見当たりませんでした。Ubuntuでは、 xrandr コマンドを用いることでリフレッシュレートを変更できます。

ディスプレイ情報の取得

ディスプレイの設定情報を取得するためには、 xrandr コマンドにオプションを指定せずに利用します。HDMIなどの出力に関する情報や、解像度とリフレッシュレートの情報が取得できます。また、 xrandr の実行結果には、現在の設定のみだけではなく、設定可能な解像度とリフレッシュレートの一覧が表示されます。

以下は、私の環境で xrandr コマンドによって取得したディスプレイの情報です。ディスプレイには HDMI-1 のポートに接続されています。解像度は 3840x2160 で、リフレッシュレートが 30.00 に設定されています。

$ xrandr
Screen 0: minimum 8 x 8, current 3840 x 2160, maximum 32767 x 32767
DVI-D-0 disconnected (normal left inverted right x axis y axis)
HDMI-0 disconnected (normal left inverted right x axis y axis)
HDMI-1 connected primary 3840x2160+0+0 (normal left inverted right x axis y axis) 880mm x 510mm
   3840x2160     30.00*+  59.94    50.00    29.97    25.00    23.98  
   2560x1440     59.95  
   1920x1200     59.88  
   1920x1080     60.00    60.00    59.94    50.00    50.00    23.97    60.00    50.04  
   1680x1050     59.95  
   1600x900      60.00  
   1440x900      59.89  
   1280x1024     75.02    61.00  
   1280x800      59.81  
   1280x720      59.94    50.00  
   1152x864      60.00  
   1024x768      75.03    70.07    60.00  
   800x600       75.00    72.19    60.32    56.25  
   720x576       50.00  
   720x480       59.94  
   640x480       75.00    72.81    59.94    59.93  
DP-0 disconnected (normal left inverted right x axis y axis)
DP-1 disconnected (normal left inverted right x axis y axis)
DP-2 disconnected (normal left inverted right x axis y axis)
DP-3 disconnected (normal left inverted right x axis y axis)
$ 

解像度とリフレッシュレートの設定変更

解像度を変更するためには --mode オプションを、 リフレッシュレートを変更するためには --rate オプションを利用します。接続されているモニタを指定するために、 --output オプションを利用します。

リフレッシュレートは指定された値が設定できない場合、最も近い値の設定値に変更します。例えば、上記の出力例では、リフレッシュレートは解像度が3840x2160の時に最大で59.94Hzなので、 xrandr のオプションに --rate 60 と指定した場合、最も近い59.94に設定されます。

モニタ出力がHDMI-1のディスプレイの解像度を3840x2160, リフレッシュレートを60Hzに指定する場合のコマンドは次の通りです。

xrandr --output HDMI-1 --mode 3840x2160 --rate 60

解像度やリフレッシュレートを変更した後に xrandr コマンドでディスプレイの情報を取得すると、変更内容が反映されていることが分かります。

$ xrandr
Screen 0: minimum 8 x 8, current 3840 x 2160, maximum 32767 x 32767
DVI-D-0 disconnected (normal left inverted right x axis y axis)
HDMI-0 disconnected (normal left inverted right x axis y axis)
HDMI-1 connected primary 3840x2160+0+0 (normal left inverted right x axis y axis) 700mm x 400mm
   3840x2160     30.00 +  59.94*   50.00    29.97    25.00    23.98  
   2560x1440     59.95  
   1920x1200     59.88  
   1920x1080     60.00    60.00    59.94    50.00    50.00    23.97    60.00    50.04  
   1680x1050     59.95  
   1600x900      60.00  
   1440x900      59.89  
   1280x1024     75.02    61.00  
   1280x800      59.81  
   1280x720      59.94    50.00  
   1152x864      60.00  
   1024x768      75.03    70.07    60.00  
   800x600       75.00    72.19    60.32    56.25  
   720x576       50.00  
   720x480       59.94  
   640x480       75.00    72.81    59.94    59.93  
DP-0 disconnected (normal left inverted right x axis y axis)
DP-1 disconnected (normal left inverted right x axis y axis)
DP-2 disconnected (normal left inverted right x axis y axis)
DP-3 disconnected (normal left inverted right x axis y axis)
$ 

設定の永続化

シェルから xrandr コマンドで解像度やリフレッシュレートの変更を実行しても、シャットダウンすると設定が消えてしまいます。毎回コマンドを叩くのも面倒なので、保存させる方法がないか調べてみると、X Window Systemの設定ファイルに xrandr コマンドを記述すれば良いようです。ユーザ単位で設定する場合、 .xprofile に解像度やリフレッシュレートを指定する xrandr コマンドを記述します。

ハマった点

解像度は特に何も設定することなく4Kになっていたのですが、リフレッシュレートだけ上げたいと思い、 --mode オプションを指定せずに実行しましたが、画面に変化はなく、 xrandr コマンドの実行結果も変化がありませんでした。

xrandr --output HDMI-1 --rate 60    # 設定が反映されない

リフレッシュレートを指定する際には、 --mode オプションを用いて、どの解像度に対してリフレッシュレートを変更するか明示する必要があるみたいです。

まとめ

Ubuntuでディスプレイのリフレッシュレートを変更するには、 xrandr コマンドを利用して変更します。リフレッシュレートの設定だけでなく、ディスプレイの設定可能な解像度やリフレッシュレートを取得することもできます。

リフレッシュレートを xrandr で設定する際には、 --rate オプションを用いるだけでなく、 --mode オプションで解像度も指定する必要があります。オプションで指定されたリフレッシュレートの値が、指定の解像度で設定可能なリフレッシュレートに存在しない場合、最も近い値を設定します。

シェルから変更された解像度やリフレッシュレートは永続化されないので、Ubuntuの起動時に解像度やリフレッシュレートを自動的に設定させる必要があります。ユーザごとに設定を指定する場合は、 .xprofile に解像度やリフレッシュレートを指定する xrandr コマンドを記述します。 より詳しい xrandr コマンドの使い方は、 xrandr(1) を参照してください。

参考文献

LaravelでREST APIのJSONを柔軟に変形させたい

この記事は Web × PHP TechCafe Advent Calendar 2019 の12/15の記事です。

LaravelでモデルをREST APIのレスポンスとして返す際には、コントローラからモデルのCollectionやインスタンスを返すことで、レスポンスはJSON形式で返されます。しかし、モデルのプロパティを変形してからJSONのレスポンスとして返したいこともあります。

Laravelの API Resource を利用することで、REST APIのレスポンスとして返すJSONの値を柔軟に設定できます。時刻のフォーマットを変換してJSONに埋め込むような場合に便利です。

テーブル構造の例

例として、決まった日付の範囲で、日付ごとのスケジュールを編集するシステムを考えます。日付の範囲を格納するテーブルを dates, スケジュールのデータを格納するテーブルを schedules とします。テーブル構造は以下に示します。

f:id:sierra-kilo:20191214103014p:plain

テーブル定義をマイグレーションファイルに記述します。dates のテーブル構造を CreateDatesTable クラス、 schedules のテーブル構造を CreateSchedulesTable クラスとしてそれぞれ定義します。(use 文やコメントは省略しています)

class CreateDatesTable extends Migration
{
    public function up()
    {
        Schema::create('dates', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->date('date');
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('dates');
    }
}
class CreateSchedulesTable extends Migration
{
    public function up()
    {
        Schema::create('schedules', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('title');
            $table->text('description')->nullable();
            $table->unsignedBigInteger('date_id');
            $table->time('start');
            $table->time('end');
            $table->text('remarks')->nullable();
            $table->timestamps();
            $table->softDeletes();

            $table->foreign('date_id')->references('id')->on('dates');
        });
    }

    public function down()
    {
        Schema::dropIfExists('schedules');
    }
}

モデルとAPI Resourceの実装

モデルの定義

テーブルをPHPのオブジェクトとして扱うために、 datesschedules テーブルに対応するモデルクラス DateSchedule クラスを定義します。外部キー制約を schedules テーブルの date_id カラムに設定したので、モデルにもリレーションを設定します。

class Date extends Model
{
    protected $fillable = ['date'];
}
class Schedule extends Model
{
    use SoftDeletes;

    protected $fillable = [
        'title',
        'description',
        'date_id',
        'start',
        'end',
        'remarks'
    ];

    public function date()
    {
        return $this->belongsTo(Date::class);
    }
}

JSONの構造とAPI Resource

スケジュールを以下のようなJSON形式のレスポンスで返すことを考えます。

{
  "data": [
    {
      "id": 1,
      "title": "PHP Conference Japan 2019",
      "description": "国内最大級のPHPのカンファレンス",
      "date": "2019-12-01",
      "start": "10:00",
      "end": "18:00",
      "remarks": "東京都大田区南蒲田1丁目20-20 大田区産業プラザ PiO"
    }
  ]
}

JSONに含まれる date には Date クラスの date プロパティの値を、 startend はそれぞれ H:i 形式の文字列を設定します。

JSONレスポンスをAPI Resourceとして返すために、 Resource クラスを作成します。雛形を作成するコマンドは以下の通りです。

php artisan make:resource ScheduleResource

雛形を作成したら、 toArray() の内容を以下に示すコード例のように修正します。

class ScheduleResource extends JsonResource
{
    public function toArray($request)
    {
        return [
            'id' => $this->id,
            'title' => $this->title,
            'description' => $this->description,
            'date' => $this->date->date,
            'start' => Carbon::parse($this->start)->format('H:i'),
            'end' => Carbon::parse($this->end)->format('H:i'),
            'remarks' => $this->remarks,
        ];
    }
}

変数 $this には Schedule クラスのインスタンスが格納されているので、リレーション先の Date クラスのオブジェクトも date プロパティから取得できます。日付の文字列を取得するためには $this->date->date を参照します。予定の開始時刻と終了時刻を示すプロパティ startendH:i 形式の文字列に変換するために、Laravelに組み込まれているライブラリ Carbon を利用します。

コントローラからAPI Resourceを用いてレスポンスを生成

API Responseをコントローラのメソッドから返すことで、JSON形式のレスポンスが生成できます。単純にAPI Resourceを返す場合は、ルーティングで直接 Resource クラスのインスタンスを返却することもできます。

モデルのインスタンス単体を Resource として返す場合は、 Resource クラスのインスタンスを返却することで実現できます。複数のモデルのインスタンスCollection として返したい場合は、 collection()を利用します。 Resource クラスのインスタンスnew する際の引数はモデルクラスのインスタンスを、 collection()の引数にはモデルのインスタンスCollection を指定します。

APIコントローラの index() から、スケジュールを開始時刻順にソートしてREST APIとしてJSON形式のレスポンスを返すコード例は以下の通りです。

public function index()
{
    return ScheduleResource::collection(
        Schedule::orderBy('date_id')
            ->orderBy('start')
            ->get()
    );
}

まとめ

LaravelのAPI Resourceを利用すれば、REST APIのレスポンスとして返すJSONを柔軟に変形できます。デフォルトでは JSONdata フィールドに Resource クラスの toArray() で返されるデータが格納されますが、サービスプロバイダで Resource::withoutWrapping()data キーを削除できます。

モデルのフィールドを変形する程度なら、モデルのアクセサを利用するという手段もあります。レスポンスで返すJSONのフィールドをどのように変形したいかによって、モデルのアクセサとAPI Resourceを使い分けると良さそうですね。

参考文献

Vue.jsで要素をドラッグ&ドロップで手軽に動かす方法

Vue.jsでドラッグ&ドロップで要素を動かしたいなと思い、HTML5Drag and Drop APIの利用を考えましたが、移動元の要素の場所をドラッグ開始時(dragstart イベント)に保持して、ドロップ時(drop イベント)に要素を並び替える必要があり、少々実装が複雑です。もっと手軽な方法はないかと探してみると、 Vue.Draggable が見つかりました。

Vue.Draggableを利用すれば、Vue.jsで簡単に要素をドラッグ&ドロップで並び替えることができます。今回は、Vue.Draggableの手軽な使い方について書いていこうと思います。この記事では、Vue.jsのプロジェクトがvue-cliで作成済みであること、Single File Componentで利用することを前提とします。

Vue.Draggableのインストール

手順はVue.jsのプロジェクトに npmyarn 経由で vuedraggable をインストールするだけです。実行コマンドは次のようになります。

npm install vuedraggable -D

要素をドラッグ&ドロップで移動

ドラッグ&ドロップで並び替えたい要素のリストをVue.Draggableのコンポーネントのスロットに入れることで、簡単に要素を動かせるようになります。以降では、Vue.Draggableのコンポーネントdraggable として import します。

リスト内での移動

単一のリスト内で要素を並び替える際には、要素群を draggable のスロットに格納します。リストのデータを保持する配列を並び替え操作に反映させるためには、 draggablev-model ディレクティブに配列を与えます。

f:id:sierra-kilo:20191204214330g:plain

配列 flowers からなるリストについて、ドラッグ&ドロップで要素を動かすための実装例を示します。Vue.Draggableを使ってリストの要素を並び替えるコード例は以下の通りです。

<template>
  <ul>
    <draggable v-model="array">
      <li v-for="flower in flowers" :key="flower.id">{{ flower.name }}</li>
    </draggable>
  </ul>
</template>

<script>
import draggable from 'vuedraggable';

export default {
  components: { draggable },
  data() {
    return {
      flowers: [
        { id: 1, name: 'Wisteria' },
        { id: 2, name: 'Dandelion' },
        { id: 3, name: 'Pansy' },
      ],
    };
  },
};
</script>

上記のコードは、 li 要素がドラッグ&ドロップで並び替えられます。また、 li のデータを格納する配列 flowersdraggable コンポーネントv-model ディレクティブに紐づけられているので、ドラッグ&ドロップで要素の場所が変わった際に、 flowers も自動的に要素が並び替えられます。

別のリストへの移動

Vue.Draggableを使えば、同一のリスト内で要素を動かすだけでなく、別のリストに要素を移動することも可能です。例えば、プロジェクト管理システムでタスクをTo DoのリストからDoingのリストに移すようなことが簡単に実装できます。

f:id:sierra-kilo:20191204215819g:plain

移動する要素を別のリストに移すためには、 group プロパティを利用します。同じ group として指定されていれば、別のリストにも要素をドラッグ&ドロップで移動できます。

ソースコード例は以下の通りです。

<template>
  <ul>
    <draggable v-model="todos" group="tasks">
      <li v-for="task in todos" :key="task.id">{{ task.title }}</li>
    </draggable>
  </ul>
  <ul>
    <draggable v-model="dones" group="tasks">
      <li v-for="task in dones" :key="task.id">{{ task.title }}</li>
    </draggable>
  </ul>
</template>

<script>
import draggable from 'vuedraggable';

export default {
  components: { draggable },
  data() {
    return {
      todos: [
        { id: 1, title: 'Bake cake.' },
        { id: 2, title: 'Harvest raspberry' },
      ],
      dones: [
        { id: 3, title: 'Pour coffee' },
      ],
    };
  },
};
</script>

上記のプログラム例では、配列 todosdones の要素がそれぞれ同じ group に指定されています。同じ group として指定することで、 todos の要素を dones に、 dones の要素を todos に移動できます。

まとめ

Vue.Draggableを利用すれば、簡単に要素をドラッグ&ドロップで移動させることができます。1つのリスト内での移動だけでなく、別のリストに要素を移動させる処理も手軽に書くことができます。

Vue.Draggableは Sortable.js をベースに作られており、要素の移動時のイベント処理を記述することもできます。詳細は Vue.DraggableのGitHubリポジトリを参照してください。

参考文献

LaravelにVuetifyをインストールする方法

Laravelでは、デフォルトでVue.jsをサポートしており、バンドルツールのWebpackも簡単に使えます。ただ、流石にいい感じのUIコンポーネントを自前で1から作るのは大変なので、Vue.jsのフレームワークとして有名な Vuetify を採用してみることにしました。

Vuetifyのドキュメントを参考にしながらインストールや設定を行ったのですが、結構苦労したので、備忘録として残しておくことにします。Laravel, Vue.js, Laravel-Mix, Vuetify のバージョンはそれぞれ以下の通りです。

  • Laravel: 5.8.31
  • Vue.js: 2.6.10
  • Laravel-Mix: 4.1.2
    • Webpack: 4.39.1
  • Vuetify: 2.0.5

Vuetifyのインストールの流れ

Laravelには、デフォルトではVuetifyは入っていないので、パッケージのインストールと設定が必要です。また、今回はLaravel-Mixでコンパイルもしてもらいたいので、Vuetify-LoaderをインストールとWebpackの設定も必要です。

手順の概要は以下の通りです。

  1. VuetifyとVuetify-Loaderをインストール
  2. webpack.mix.js にVuetify-Loaderをプラグインとして設定
  3. Vuetifyの設定ファイルを作成して app.js のVueインスタンスに追加

VuetifyとVuetify-Loaderのインストール

Laravelのプロジェクトディレクトリ配下で npmyarn を使ってインストールします。今回はLaravel-MixでVuetifyのコンポーネントコンパイルしたいので、CDNは用いないことを想定しています。

npm install vuetify -S
npm install vuetify-loader -D

Vuetify はアイコンにアイコンフォントを利用するので、インストールします。

npm install @mdi/font -D

Webpackの設定

VuetifyとVuetify-Loaderをインストールしたら、Webpackの設定をします。Laravel-MixでのWebpackの設定は、 webpack.mix.js に記述します。Webpackの設定は、Laravel-Mixの webpackConfig メソッドを用います。 webpackConfig メソッドの引数には、Webpackの設定で module.export するオブジェクトと同様のものを指定するようです。

Vuetify-Loaderを読みこませるために、 'vuetify-loader/lib/plugin を読み込み、Vuetify-Loaderのインスタンスplugins プロパティに設定します。 webpack.mix.js の中身は以下の通りです。

const mix = require('laravel-mix');
const VuetifyLoaderPlugin = require('vuetify-loader/lib/plugin');

mix.webpackConfig({
  plugins: [
    new VuetifyLoaderPlugin(),
  ],
});

mix.js('resources/js/app.js', 'public/js')
    .sass('resources/sass/app.scss', 'public/css');

Vuetifyの設定

公式ドキュメントでは、 src/plugins/vuetify.js に、VuetifyをVue.jsのプラグインとして読み込ませる方法が書いてありますが、Laravelのディレクトリ構造と異なるので、 resources/js/plugins/vuetify.js に記述することとします。設定ファイルの場所が違うこと以外は、基本的には公式ドキュメントと同様に設定します。

まず、設定ファイルを、公式ドキュメントと同様に resources/js/plugins/vuetify.js に記述します。アイコンフォントの設定もここで行います。アイコンフォントのモジュールを読み込み、Vuetifyインスタンスの引数のオブジェクト内に記述します。

import '@mdi/font/css/materialdesignicons.css';
import Vue from 'vue';
import Vuetify from 'vuetify/lib';

Vue.use(Vuetify);

export default new Vuetify({
  icons: {
    iconfont: 'mdi',
  },
});

Vuetifyをプラグインとして読み込ませるために、 resources/js/app.js を以下のように編集します。

require('./bootstrap');

window.Vue = require('vue');
import vuetify from './plugins/vuetify';

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

これで、Laravel-MixでもVuetifyのコンポーネントが使えるようになります。全てのコンポーネントについて検証したわけではありませんが、明示的にVuetifyのコンポーネントimport していなくても、問題なく利用できるようになっているはずです。

ハマった点

自分で設定をしている際に、以下の2つの点でハマりました。

  1. Vuetifyのコンポーネントが読み込まれないものがある
  2. VuetifyのデフォルトのCSSが効いてない

Vuetifyのコンポーネントが読み込まれないものがある

最初、Webpackの設定しか書いていませんでした。Webpackの設定自体は通り、試しに v-btn コンポーネントを利用した際には、問題なく表示されました。 v-card も同様に使えました。

これで使えるようになったと糠喜びをしていたのも束の間、 v-pagination を試したら画面上に表示されず、コンソールにエラーが出力されていました。

[Vue warn]: Error in render: TypeError: this.$vuetify is undefined v-pagination

どうやら、Vuetify-Loaderの制約で、 Dynamic ComponentFunctional Component は手動で読み込まないと使えないようです。 ドキュメントを読んだのですが、どうしても Functional Component がどのようなものかイマイチはっきり分からず、どのコンポーネントFunctional Component に当たるか区別がつかないので、別の手段を模索しました。

Vuetify-Loader で全部読み込みきれなかったようだったので、次は Vuetify の設定ファイルのみ作って、コンパイルしなおしましたが、今度はVuetifyがうまく動かなくなったようでした。結局、Vuetify-LoaderとVuetifyの設定の両方を書いたら、問題なく動くようになりました。

この辺りについて、自分自身で完全に理解しきれていないので、どなたか助言をいただけると、個人的に大変助かります。

VuetifyのデフォルトのCSSが効いていない

結論から書くと、 v-app の中にVuetifyのコンポーネントを入れ忘れていただけでした。公式ドキュメントにも、Vuetifyのコンポーネントは、 v-app の中に入れろと書いていましたが、とりあえずVuetifyのコンポーネントが動くか試したかったので、そのことをすっかり忘れていました。とても初歩的なミスでした。

v-app を書き忘れていることに気づくまで、Vuetifyの設定を色々いじっていました。 v-btn のデフォルトの背景色はCSS効いてるっぽいけど、フォントが違ったり、 class="primary" を指定しても背景色が変わらなかったりで、コンポーネントCSSを明示的に読み込まなければいけないのかと勘違いしていました。

まとめ

LaravelにVuetifyをインストールする場合、Vue CLI 3を利用するのとは少し勝手が違うので、設定に苦戦しました。とりあえず、 Vuetify-LoaderをLaravel-MixのWebpackにプラグインとして読み込ませ、VueインスタンスにVuetifyをプラグインとして読み込ませれば、各SFCでVuetifyのコンポーネントimport を書かなくても、自動で読み込まれるようになりました。

自分自身、挙動を完璧に理解しきれていないので、もし詳しい方がいらっしゃれば、ご教授願えればと思います。

参考文献