mmyoji's diary

プログラミングとか日々のどうでもいいこととか

Turbolinks有効にした際にVue.jsで気をつけないといけないこと

Railsでデフォルトで入るようになって久しいturbolinks, 有効にした方が速いですよね。

けどいろいろとjsの挙動がおかしくなりがちなのでがんばらないとね。。。ってのを各所で聞いていましたがとうとうVue.jsでも起きたので残しておきます…

Dependency

いつもどおりですが。。。

  • Vue.js
  • lodash.js
  • ES6(babel)

インクリメンタルサーチをするinputのvue configをいろんなページで使いまわしたい

index.html

<div id="incrementalSearchArea">
    <input type="text" v-model="text" v-on="keyup: search" />
    <div v-show="isVisible">
        <ul>
            <li v-repeat="item: results"
                v-on="click: insertItem(item)">{{item}}</li>
        </ul>
    </div>
</div>

incremental_search_form.js

import { isEmpty, filter } from "lodash"

// 簡易化のためにデータは固定してます
const DATA = [
    "mmyoji",
    "coji-coji",
    "jiro",
    "koro-suke",
    "tommy",
    "geran"
]

export default {
    data: {
        text: "",
        isVisible: false,
        results: []
    },
    computed: {
        isVisible() {
            return !isEmpty(this.results)
        }
    },
    methods: {
        search() {
            const val = this.text.trim()
            if (isEmpty(val)) {
                this.results = []
                return
            }
            this.results = filter(DATA, (name) => name.match(val))
        },

        insertItem(item) {
            this.text = item
            this.results = []
        }
    }

}

main.js

import { each } from "lodash"
import Vue from "Vue"
import Form from "./incremental_search_form"

const FORM_IDS = [
  "incrementalSearchArea",
  "incrementalSearchArea2"
]


function execute() {
  
  each(FORM_IDS, (id) => {
    let elem = document.getElementById(id)
    if (!elem) return
    new Vue(Form).$mount(`#${id}`)
  })

}

document.addEventListener("DOMContentLoaded", execute)
document.addEventListener("page:load", execute) // turbolinks用

上記のような構成になってて、このVue configをいろんなページで使いまわしたいとする。(例えば ページ1ページ2

以下のような現象が起きる

  1. ページ1に行く
  2. インクリメンタルサーチを行い、検索結果のリストが表示される
  3. ページ2のリンクをクリック
  4. ページ1のリンクをクリック
  5. 先ほどの検索結果が残っている...

怖いですね。。。

なので以下のように対応しました。

export default {
    // data: {
    
    // 描画されたタイミングでデータを初期化
    ready() {
        this.text = ""
        this.isVisible = false
        this.results = []
    },
    
    // methods: {

うーん、これ毎回やるのめんどくさいぞ...

ちなみに destroyeddetached はページ遷移しても呼ばれないようでした。

data に渡すものが増えれば増えるほど初期化処理がながーくなるのでいつturbolinksオフにするか、って感じですね...w