Vue CLIで自動生成されるmain.jsを読む


はじめに

この記事ではVue CLIのvue createコマンドでプロジェクトを作成した際に自動で生成されるmain.jsの中に記述されている数行のコードは何をする処理なのか読み解きます。
render関数の使い方やVue.configの各種設定には踏み込みません。

利用するVueのバージョンは2.x、Vue CLIは3.xです。

読む前にVue CLIを利用して適当なプロジェクトを作成することをおすすめします。
vue create プロジェクトの名前でプロジェクトを作成。
cd プロジェクトの名前
npm run serveでローカルで開発サーバーを利用して起動。

main.js ?

Vue CLIを利用してvueのプロジェクトを作成すると自動で./src/main.jsというファイルが作成されます。
作成したプロジェクトはmain.jsをエントリーポイントとして動きます。

main.js
import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
}).$mount('#app')

6行をほどの記述の中のimport文を除く以下の4つに分けて何をしているのか読んでいきます。
Vue.config.productionTip
new Vue
render: h => h(App)
.$mount('#app')

Vue.config.productionTip

公式ドキュメントによると
"これを false に設定すると、 Vue の起動時のプロダクションのヒントが表示されなくなります。"とのこと。
https://jp.vuejs.org/v2/api/index.html#productionTip

ここで言われるプロダクションのヒントとは何かを見てみます。
Vue.config.productionTipにtrueを指定してブラウザの開発者ツールでconsoleを見ると下のように
development modeで起動していることを知らせるメッセージが出ます。
これが「起動時のプロダクションのヒント」です。

productionモードにするとこのような警告の出力はなくなりアプリの負荷を軽減できます。
https://vuejs.org/v2/guide/deployment.html#Turn-on-Production-Mode

productionTipのON/OFFで表示を切り替えられるメッセージは上であげたもの以外に見つかりませんでした。
メッセージ1つだけを切り替えるだけの設定でもなさそうなんですが...
(Vueのソース内でも探しましたが上記のメッセージ以外見つけられず...知ってるかた教えてください...)

new Vue

シンプル。
Vueのインスタンスを作成しています。

render: h => h(App)

まず書き方が構文的に分かりづらいので書き下します。
ちょうどこの部分について触れたissueがありました。
https://github.com/vuejs-templates/webpack-simple/issues/29#issuecomment-312902539
issue内であるように、下のように書き下すことができます。

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

render関数の引数にはVueインスタンスのcreateElement関数が来ていて、hと省略されています。
hはHyperscriptの略で仮想dom実装の中で一般的に使われるんだとか。
https://github.com/vuejs/babel-plugin-transform-vue-jsx/issues/6#issuecomment-232994673

ここで登場するrenderはhtmlを描画するための関数です。
公式ドキュメントによるとrendertemplateの代替として利用できるものとされており
templateを利用して以下のように書き換えることもできます。

main.js
// import Vue from 'vue'
import Vue from "vue/dist/vue.esm.js";
import App from './App.vue'

Vue.config.productionTip = true

// new Vue({
//   render: h => h(App),
// }).$mount('#test')

new Vue({
  components: { App },
  template: '<App />'
}).$mount('#test')

templateを用いて書いた場合、事前にJavaScriptレンダリング関数にコンパイルされる必要があります。
importでVueにvue.esm.jsを利用するように書き変えたのはvueのビルドがランタイム限定ビルドを利用するのを回避するためで、この記述がなければ画面には何も表示されなくなります。

vueのランタイム限定ビルドは完全版(コンパイラ込み)のビルドに比べておよそ30%軽量なため利用可能なら利用することが推奨されています。
ランタイム + コンパイラとランタイム限定の違い

描画関数とJSXの項目にVue ではほとんどの場合 HTML をビルドするためにテンプレートを使うことが推奨されますと書かれているため、なぜmain.jsでrender関数を利用するのか疑問でしたが
ランタイム限定ビルドを利用するのがモチベーションかなと考えています。
(明確な理由を探したが見つからないので知ってる方教えてください)

引数のcreateElementはVueインスタンスの関数で、指定されたパラメータの情報を元に生成した仮想DOMを返します。

.$mount('#app')

vm.$mount( [elementOrSelector] )によると
Vue インスタンスがインスタンス化において el オプションを受け取らない場合は、DOM 要素は関連付けなしで、”アンマウント(マウントされていない)” 状態になります。vm.$mount() は アンマウントな Vue インスタンスのマウンティングを手動で開始するために使用することができます。
とのこと。

これだけ読んでもわかりにくいので
わかりやすくするために、少しコードを書き換えます。
./public/index.htmlを見ると下のような記述があります

index.html
  <body>
    <noscript>
      <strong>We're sorry but vue-banner doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>

ここの

<div id="app">

を下のように書き換えてみてください

<div id="hoge">

何も表示されなくなります。
続けてmain.js

.$mount('#app')

を下のように書き換えてみてください。

.$mount('#hoge')

再びApp.vueの内容が表示されるようになります。
この時点で気づくかもしれませんが
このvm.$mount()は引数のセレクタで指定したhtml要素の子要素にVueインスタンスを挿入します。
main.jsを下のようにelオプションを利用する形式に書き換えても同様に<div id="hoge">の子要素としてVueインスタンスが挿入されます。

main.js
import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = true

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

また、elオプションvm.$mount()が同居した場合はelオプションが優先されます。

main.js
import Vue from 'vue'
import App from './App.vue'

Vue.config.productionTip = true

new Vue({
  el: '#hoge',
  render: h => h(App),
}).$mount('#fugafuga')

※優先されるもののvm.$mount()が無効化されたわけではないようで、警告が出ました。

たった6行のコードしか書かれていないmain.jsでしたが、ちゃんと読むとなると結構読み応えあって面白かったです。
年末年始の暇な時にでも、もっと深く読み込んでみたい。

それではよいクリスマスを

$\huge{🎅メリークリスマス!!!!🎄}$

【参照】
renderの実装
createElementの実装