最近有點想學點不同的東西, 因此想嘗試 vue.js.

在 survey 的過程中發現了 pug 這個東西, 和 ruby 的 slim 類似. 以前在寫 react 的過程中, 我認為最討厭的一點就是寫 jsx 了, 一不小心就會寫得很長, 再加上基本上都會寫在 render 方法中, 換行和縮排就會花費不少空間, 大概像這樣:

class MyComponent extends React.PureComponent {
  render () {
    return (
      <div>Hello</div>
    )
  }
}

當然現在可以寫成純函數的形式的確是比較精簡了, 但還是不喜歡, 長期以來也都沒找到什麼解決辦法, 但是現在有了, 可以使用 babel-plugin-transform-react-pugrender 的時候使用 pug 的語法, 應該能舒服許多.

不過本文的重點不是這個, 因為其實我是先想玩看看 vue.js, 接著看到 vue.js 中可以那樣寫才知道現在有這樣的東西, 所以還沒有嘗試 babel-plugin-transform-react-pug:

<template lang="pug">
#app
  p {{ message }}
</template>

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

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

那麼首先先跑個

rails webpacker:install:vue

它會提供所有基本的設定和一個簡單的範例 (類似上面的), 還會有一則訊息提醒你要記得修改 config/initializers/content_security_policy.rb .

但是這樣還不夠. 根據 packs/hello_vue.js 內所述, 如果有使用 Turbolinks, 需要再額外安裝:

yarn add vue-turbolinks

然後照裡面所寫的, 將相關程式碼複製到 packs/application.js:

import TurbolinksAdapter from 'vue-turbolinks'
import Vue from 'vue/dist/vue.esm'
import App from '../app.vue'

Vue.use(TurbolinksAdapter)

document.addEventListener('turbolinks:load', () => {
  new Vue({ 
    el: '#hello',
    data: () => {
      return {
        message: "Can you say hello?"
      }
    },
    components: { App }
  })
})

然後在頁面中插入 <div id="hello"><app /></div> 就可以看到大大的 Hello Vue! 了.

打開 app.vue 以後會看到:

<template>
  <div id="app">
    <p>{{ message }}</p>
  </div>
</template>

但是我想要:

<template lang="pug">
#app
  p {{ message }}
</template>

但只是想要是沒有用的, 必須讓 webpack 認得 pug 並且編譯成該有的樣子才行, 因此來安裝這些套件:

yarn add pug pug-plain-loader

然後建立一個 config/webpack/loaders/pug.js:

module.exports = {
  test: /\.pug(\.erb)?$/,
  use: [{
    loader: 'pug-plain-loader'
  }]
}

並且修改 config/webpack/environment.js:

const { environment } = require('@rails/webpacker')
const { VueLoaderPlugin } = require('vue-loader')
const vue = require('./loaders/vue')
const pug = require('./loaders/pug')

environment.plugins.prepend('VueLoaderPlugin', new VueLoaderPlugin())
environment.loaders.prepend('vue', vue)
environment.loaders.prepend('pug', pug)
module.exports = environment

這個時候內含 pug template 的 app.vue 就能正確的被編譯了.

再來有個要求是, 我希望能把檔案都放在 components 目錄中, 有新元件就自動載入命名註冊, 我的做法是, 建立一個 components/index.js:

import Vue from 'vue/dist/vue.esm'

const req = require.context('components', true, /\.vue$/)

function capitalize (word) {
  return word.charAt(0).toUpperCase() + word.substring(1)
}

function titleize (words) {
  return words.match(/\w+/g).map(capitalize).join('')
}

function constantize (filename) {
  return titleize(filename.match(/^(?:\.\/)?(.+)(?:\.vue$)$/)[1])
}

req.keys().map(key => Vue.component(constantize(key), req(key).default))

然後修改一下 packs/application.js:

import TurbolinksAdapter from 'vue-turbolinks'
import Vue from 'vue/dist/vue.esm'
import 'components'

Vue.use(TurbolinksAdapter)

document.addEventListener('turbolinks:load', () => {
  new Vue({ el: '#app' })
})

然後把 #app 放在 body 下, 這樣的設置看起來似乎一切都完美了…