【Rails】Webpackでvueファイルを表示するまでの処理の流れ。どのファイルを辿っているか?

rails-prograshi(プロぐらし)-kv Rails
記事内に広告が含まれていることがあります。

RailsはWebpackでVue.jsを使うことができます。ですが、Ruby用の.erbとvueファイル用の.jsが混在し、かつ、コンパイルも行われるので、どの順番でファイルを表示しているのかが分かりにくいです。

ここでは、RailsでWebpackerとVue.jsの理解を深めるため、処理の流れをまとめています。

RailsでVue.jsを使う方法については以下をご参考ください。

(参考)Docker上に構築したRails6でVue.jsを表示する方法


ファイルの構成

localhost:3000/home/index にアクセスした時に、Vue.jsで作成した、 main.js を開いてブラウザに表示する例を用います。

使用しているファイル

ファイルの構成は次のようになっています。

app
|- assets
|   ┗ stylesheets
|       ┗ application.css 
|
|- javascript
|    ┗ app.vue
|    ┗ packs
|       ┗ main.js
|
|- views
|    ┗ home
|       ┗ index.html.erb
|    ┗ layouts
|       ┗ application.html.erb
| 
|- controllers
|    ┗ home_controller.rb
|
config
   ┗ routes.rb

  • スタイルシート: app/assets/stylesheets/application.css
  • VueとVuetifyの読み込み: app/javascript/packs/main.js
  • Vue.jsで作成したページ: app/javascript/app.vue
  • ビューファイル: app/views/home/index.html.erb
  • 全体共通のレイアウト: app/views/layouts/application.html.erb
  • コントローラ: app/controllers/home_controller.rb
  • ルーティング: config/routes.rb


ブラウザのアクセスからページ表示までの流れ

ルーティング

localhost:3000/home/indexにアクセスすると、まずルーティングが参照されます。

・ルーティング: config/routes.rb

Rails.application.routes.draw do

  get 'home/index'

end


get 'home/index' の記述に沿って、Homeコントローラのindexアクションが実行されます。

ルーティングの内容についてはこちらをご参考ください。

コントローラ

コントローラのindexアクションはデフォルトの状態です。

  • コントローラ: app/controllers/home_controller.rb
class HomeController < ApplicationController
  def index
  end
end


この場合、viewsディレクトリ配下にある、コントローラ名のディレクトリのアクション名.erb ファイルを開きます。


ビューファイル

開くのはhome/index.html.erbです。

  • ビューファイル: app/views/home/index.html.erb
<%= javascript_packs_with_chunks_tag 'main' %>


ビューファイルの中には、javascript_packs_with_chunks_tagでmainを指定しています。

この場合、app/javascript/packs 配下にある指定したファイル名のJavaScriptファイルにアクセスします。(正確にはそのファイルをコンパイルしたもの)


JavaScriptファイル

javascript_packs_with_chunks_tagで指定したmainは次のファイルになります。

  • VueとVuetifyの読み込み: app/javascript/packs/main.js

このファイルは、VueとVuetifyのリソースを読み込んでいます。

import Vue from 'vue'
import App from '../app.vue'
import Vuetify from 'vuetify'
import 'vuetify/dist/vuetify.min.css'

Vue.use(Vuetify) 
const vuetify = new Vuetify(); 

document.addEventListener('DOMContentLoaded', () => {
  const app = new Vue({
    vuetify, 
    render: h => h(App)
  }).$mount()
  document.body.appendChild(app.$el)

  console.log(app)
})


importでVueやVuetifyを読み込んでいます。

const app = new Vue({ })

Vueのインスタンスを生成し、変数appに代入しています。

カッコの中で、どういった内容でVueインスタンスを生成するかを指定しています。ここでは、render関数で生成したHTMLタグと、Vuetifyのインスタンスを渡しています。

render: h => h(App)

指定したファイルをHTMLに変換(レンダリング)します。

Appはimport App from '../app.vue' で定義しています。つまり、app/javascript/app.vue を読み込んでいます。

render: h => h(App)とは?

render: h => h(App)は Vueのrender関数の表記を短縮したものです。

render: function (createElement) {
  return createElement(App)
}

 ↓

render: function (createElement) {
  return createElement(App)
}

 ↓

render (createElement) {
  return createElement(App)
}

 ↓

render (h) {
  return h(App)
}

 ↓

render: h => h(App)


(参考) Qiita Vue.jsのrender: h => h(App)

document.body.appendChild

対象.appendChild(要素)は、指定した対象のタグの子要素に新たにタグを追加する時に使います。

document.body.appendChild(app) は、生成したVueインスタンス(HTMLタグ)をbodyタグの中に子要素として追加する処理です。


Vueファイルの読み込み

app/javascript/packs/main.js にアクセスし、その処理の中で、app/javascript/app.vue をHTMLにレンダリングしています。

ここでようやく、Vueファイルにたどりつきました。

  • Vue.jsで作成したページ: app/javascript/app.vue

内容は重要ではないのですが、参考までに記載しておきます。

<template>
  <v-app id="app">
    <Header/>

    <p class="page-title">{{ message }}</p>
    <Mountain/>

    <v-alert
      type="success"
      width="400px"
      color="green"
      class="mx-auto"
    >Alert created by Vuetify</v-alert>

  </v-app>
</template>

<script>
import Mountain from "./components/Mountain"
import Header from "./components/Header"

export default {
  components: {
    Mountain,
    Header
  },
  data: function () {
    return {
      message: "Hello Vue!",
    }
  }
}
</script>

<style scoped>
p {
  font-size: 2em;
  text-align: center;
}
.page-title{
  margin-top: 20px;
}
</style>


bodyタグはどこか?

先ほどの、VueとVuetifyをインスタンス化していた、app/javascript/packs/main.jsの中で、document.body.appendChildを使って、HTMLタグに変換ごのVueファイルをbodyタグに追加していました。

このbodyタグは、app/views/layouts/application.html.erbに記載されています。application.html.erbはビュー全体で共通となるheadタグなどのHTMLです。

  • 全体共通のレイアウト: app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>RailsVue</title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
    <link href="https://cdn.jsdelivr.net/npm/@mdi/font@4.x/css/materialdesignicons.min.css" rel="stylesheet">

    <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
  </head>

  <body>
    <%= yield %>
  </body>
</html>


bodyタグの中にある、<%= yield %> にコントローラで指定したページの内容が表示されます。

yieldとは?

yieldとは引き出すという意味で、Rubyでは渡されたブロックの中身を呼び出す処理です。

ブロックとは、do ~ end や { }で囲まれた処理です。

app/views/layouts/application.html.erbにおいては、ルーティングのコントローラで指定したページの内容がブロックとして渡され、それを<%= >で表示しています。


headタグの中では、stylesheet_link_tagを使っています。


スタイルシートの読み込み

app/views/layouts/application.html.erbの中でstylesheet_link_tagを使ってスタイルシートを読み込んでいます。

<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>

対象のスタイルシートは、app/assets/stylesheets/application.css です。これをlinkタグのhref属性で指定しています。

また、属性として、media=”all” と、data-turbolinks-track=reload も合わせて指定しています。

stylesheet_link_tagやmain, data-turbolinks-trackについては下記で詳しく説明しています。

【Rails】javascript_pack_tagやstylesheet_link_tagの’data-turbolinks-track’: ‘reload’とは何か?(media: ‘all’や引数の意味)


以上が、ブラウザでURLを叩いてから、画面にVueで作成したページが表示されるまでの流れになります。


まとめ

最後に流れをまとめると次のようになります。

リクエスト localhost:3000/home/index
↓
ルーティング: config/routes.rb
↓
コントローラ: app/controllers/home_controller.rb
↓
ビューファイル: app/views/home/index.html.erb
↓
VueとVuetifyの読み込み: app/javascript/packs/main.js
     ↓
   Vue.jsで作成したページ: app/javascript/app.vue
↓
全体共通のレイアウト: app/views/layouts/application.html.erb
↓
スタイルシート: app/assets/stylesheets/application.css
↓
ブラウザでページ表示

タイトルとURLをコピーしました