親バカエンジニアのナレッジ帳

webのエンジニアをやっており、日頃の開発で詰まったことや書き残しておきたいことを載せています。育児のイロハという育児サイト(https://ikujip.jp)の開発も行っているため、その開発で使用されている技術についても掲載しています。

Sequel Proが重くなった場合の対処法

Sequel Proが重くなる事象

https://sequelpro.com

MacMySQLクライアントソフトとして大変便利なSequel Proですが、長く使っていると突然動作がどっしりと重くなってしまう事象が発生します。
そんな時の解決方法をここでは紹介したいと思います。

一応前提条件ですが、Sequel Proを使わずにMySQLに接続した場合は重さを感じずに通常通り利用できる状態にあることとします。
Sequel Proを使わずにMySQLに接続した場合でも重い場合は、Sequel Proとは関係ない別な原因で発生していると考えられるので、MySQLの状態などを確認してください。

また、Sequel Proのバージョンとして1.1.2を使いますが、どのバージョンでも同じ手順で解決できると思います。

どんな動作が重くなる?

操作全般が重くなり、例えば下記のような事象が発生します。

・クエリを実行させてから結果が返って来るまでの時間が異常に長くかかる(タイムアウトも起きる)
・新しくタブを開くまで長時間かかる
・テーブルの定義情報の閲覧は重くならないが、データの閲覧は重い
・再起動しても治らない
・ローカル環境でも、外部のサーバにネットワーク経由で接続した場合でも関係なく重くなる

ちなみにMySQLに繋がらないといったような事象はないです。

解決手順

解決方法は至ってシンプルで、何か設定を変えたりする必要はありません。
下記のように「クエリ履歴」を開き、「共用の履歴を消去」を選択すれば良いのです。

f:id:tomotomo1129:20180628012948p:plain

これで解決するはずです。
私の記憶では、使っているうちに徐々に重くなるわけではなく、突然重くなり、PCの不調を疑うほどでした。

Sequel Proでは「環境設定」-> 「一般」と進むと、以下のようにどれだけ履歴を残しておきたいか設定することができます。
私の場合はデフォルトから触っておらず20クエリにしていたはずなので、そんなに残していないはずですが。。。

f:id:tomotomo1129:20180628012954p:plain

考えられることとしては、一度に大量のINSERT文やUPDATE文を発行することがあるので、それが原因かもしれません。
たまに数百、数千のDMLクエリを同時に発行していたので、その履歴が全て残ってしまったのかなぁとなんとなく予想しています。

みなさんも、突然Sequel Proが重くなった場合は今回の手順を試してみてください。

エキスパートのためのMySQL[運用+管理]トラブルシューティングガイド

エキスパートのためのMySQL[運用+管理]トラブルシューティングガイド

SPAサイトがGoogleにインデックスされない時の解決法

今回はSPAサイトがGoogleのSearch Consoleで「Fetch as Google」をしてもレンダリングされず、サイトがインデックスされない時の対応方法です。
Googlebotがサイトのクロール時に使うブラウザは、Javascriptも動作するために、SPAサイトでも(ページの表示に多少タイムラグがあるサイトでも)問題なくインデックスすると発表しています。

ところが僕がVue.jsで書いたソースがいつまで経っても(約1ヶ月)インデックスされないという事象が発生しました。
さすがにおかしいと思い、Search Consoleで「Fetch as Google」を使用してレンダリングをしたところ、空白ページが…

f:id:tomotomo1129:20180621111825p:plain

何回やっても同じ結果で、このままではGooglebotにもただの空白ページと認識されてしまいます。
結果的にいつまで経ってもGoogleにインデックスされないため、検索されることはなくなってしまうのです。

Search Consoleを見ない人は、インデックスされていないことにしばらく気づかないかもしれませんね。。。

SPAでも大丈夫ってGoogleは言ってるのになんで?って感じですが、結論からするとGooglebotで使われているブラウザのバージョンが原因のようです。
botのブラウザはバージョンが高くないようで、ES2015 (ES6)で書かれているソースコードだとそのままではレンダリングできないようです。
Javascriptであればなんでもいいという訳ではないんですね。

よって解決策としては単純に、古いブラウザでもES2015(ES6)を実行できるようにしなければいけません。
一番簡単な方法としては、babel-polyfillをかませる方法です。

ここでbabel-polyfillとは何かということを簡単に説明します。
babelとpolyfillに分けて説明しますが、
まずbabelについてですが、これは最新の新しい文法(ES2015(ES6))で記述したJavascriptファイルを古い文法の記述に変換(コンパイル)してくれるコンパイラです。
新しい文法で記述されたJavascriptで100%動くブラウザはまだまだ多くないようで、IEや先に挙げたGooglebotがクロール時に使っているブラウザはまず動かないです。
そのようなブラウザに合わせてJavascriptが吐き出されるようにbabelがコンパイルしてくれます。

ただ、古いブラウザでは新しい文法(ES2015(ES6))の機能に対応していないものがそもそもあり、例えbabelでコンパイルしてもそのブラウザでは動作しません。
そこは標準オブジェクトを拡張し、古いブラウザでも新しい機能が動作するように変換する必要があり、polyfillの出番になります。

話が難しくなりましたが、結論としてはGooglebotが使っている古いバージョンのブラウザでも動作するようにソースをコンパイルしてやればいいのです。


参考として、Vue.jsで対応した時の方法を載せておきます。
※モジュールはnpmで管理しているものとします。

まずはモジュールのインストールです。

npm install babel-polyfill --save

次に、webpack.base.conf.jsで以下のような記述箇所を、

app: ['./src/main.js']

以下のようにすればOKです。
とても簡単ですね!

app: ['babel-polyfill', './src/main.js']

vue.jsでは、ビルドをするとmain.jsをコンパイルしてapp.jsに吐き出されますが、
この時にbabel-polyfillをかませてやれば、古いブラウザでも動くように変換してくれるのです。

対応後、Search Consoleをチェックしますと、
無事にレンダリングできていました。
読み込めないリンク先などはどうしてもあるため、ステータスが完全ではなく一部になってしまうことがありますが、そこは気にしなくても大丈夫なようです。

これでGooglebotもちゃんとしたサイトとみなしてくれるので、無事にインデックスされます。


基礎から学ぶ Vue.js

基礎から学ぶ Vue.js

Vue.jsのv-bind:classで動的なクラス割り当て

Vue.jsで動的(条件を満たす場合)にクラスを割り当てる方法として、v-bind:classがあります(v-bindは省略可能)。
動的に割り当てるパターンとしては色々考えられますよね。
単純にある条件を満たした時にクラスを割り当てるパターン、条件Aを満たした時にクラスAを割り当て、条件Bを満たした時にクラスBを追加で割り当てるパターンなどなど...
今回はそのパターンに合わせた入力方法をご紹介します。

ある条件を満たした時にクラスを割り当てる方法

今回は例として、isActiveAがtrueの場合にclassAというクラスが割り当たるとします。
一番シンプルなパターンで、動的に割り当てる時に最もよく使われる書き方です。
ログインしている時にあるクラスを割り当ててスタイルを適用させたい、など使用用途は様々考えられますね。
書き方としては以下のようにコロン(:)で挟んで割り当たるクラスをコロンの前、条件を後ろに書くことになります。

<div v-bind:class="{classA: isActiveA}"></div>

クラスはシングルコーテーションで挟んでも問題ありません。

<div v-bind:class="{'classA': isActiveA}"></div>

クラス名の中に「-」などの記号が入る場合は、むしろシングルコーテーションで囲まないと以下のようなエラーになります。

- invalid expression: Unexpected token - in

v-bindは省略可能なので、以下の書き方も可能です。

<div :class="{classA: isActiveA}"></div>
<div :class="{'classA': isActiveA}"></div>



ある条件を満たした時にクラスを複数割り当てる方法

これは上記のやり方の応用になりますが、isActiveAがtrueの場合にclassAというクラスとclassBというクラスの両方が割り当たるとします。
クラス1つ1つに細かくスタイルを割り当てている場合にあり得るかもしれませんね。
この場合、単純に両方をシングルコーテーションで囲ってしまえば大丈夫です。

<div v-bind:class="{'classA classB': isActiveA}"></div>

シングルコーテーションで囲まない場合は以下のようなエラーが発生するので要注意です。

- invalid expression: Unexpected identifier in



ある条件Aを満たした時にクラスAを割り当て、ある条件Bを満たした時にクラスBを割り当てるなど、複数のクラスを割り当てる方法

先ほどの複数パターンですね。
今回は例として、isActiveAがtrueの場合にclassAというクラスが割り当たり、isActiveBがtrueの場合にclassBというクラスが割り当たるとします。
これもログインしている時にあるクラスを割り当ててスタイルを適用させたい、メニューを開いている時に別なクラスを割り当ててスタイルを適用させたい...などなど使用用途はたくさん考えられます。
書き方としては、以下のようにカンマ(,)で連結させれば複数パターンに対応できます。

<div v-bind:class="{classA: isActiveA, classB: isActiveB}"></div>

これも同じようにv-bindは省略可能なので、以下の書き方も可能です。

<div :class="{classA: isActiveA, classB: isActiveB}"></div>


三項演算子で条件次第で別々のクラスを割り当てる方法

例えば、isActiveがtrueの場合はclassA、falseの場合はclassBが割り当たるなど、条件によって別々なクラスを割り当てたい場合などに使えます。
先ほどの複数パターンで割り当てる方法でも以下のようにすればできます。

<div v-bind:class="{classA: isActiveA === true, classB: isActiveA === false}"></div>

しかしこれだとどう見ても冗長で可読性が悪いですね。
こんな時は三項演算子を使用して以下のように書くことができます。
三項演算子を使用する時はクラス名をシングルコーテーションで囲み、かっこは「{}」に代わって「[]」で囲まなければ反映されないので、そこだけ注意してください。

<div v-bind:class="[isActiveA === true ? 'classA' : 'classB']"></div>

シンプルに書けて可読性があがりましたね!
さらにv-bindを省略して

<div :class="[isActiveA === true ? 'classA' : 'classB']"></div>

となります。

三項演算子を複数使いたい時は、これも以下のようにカンマで繋げばOKです。

<div v-bind:class="[isActiveA === true ? 'classA' : 'classB', isActiveC === true ? 'classC' : 'classD]"></div>

慣れてしまえば問題ないですが、パターンによってはシングルコーテーションで囲まないと反映されないことがあるなど、詰まってしまうポイントはいくつかあるのでそこだけ要注意です。

基礎から学ぶ Vue.js

基礎から学ぶ Vue.js

Vue.jsでScroll Depthが計測されない時の対処法

サイト分析において、ヘッダー内での読了率(つまりどこまで読まれたか)を分析することは重要ですよね。
そんな時に便利なライブラリはたくさんあるのですが、今回はその中の1つ、Scroll Depthについて、vue.js内で使用して上手く計測されない場合の対処法について説明します。
※analytics-scroll-depthのモジュールを使うパターンでお話しますが、このモジュールを使わない場合でも対処法は一緒になります。

まずはnpmでモジュールのインストールからです。

npm install analytics-scroll-depth --save

次に早速ソースコードの記載ですが、今回はmain.jsに記載します。また、GoogleAnalyticsのトラッキングコードもソースコード内に記載されているものとします。

まず上手く動かなかった時の例です。マニュアルには、以下のようにすると動くとあるのですが、

import scrollDepth from 'analytics-scroll-depth'

scrollDepth()

コード埋め込み後にGoogleAnalyticsでチェックしてもなぜか計測されませんでした。
反映されるまでタイムラグがあるのかな、と思ったのですが、何時間待っても何日待っても反映されずです。
scrollDepth内にconsole.logを入れてデバッグもしたのですが、イベント自体は発生している模様です。
色々試したのですが、結局以下のようにタイムラグを置いて関数を読ませると上手くいきました。

import scrollDepth from 'analytics-scroll-depth'

setTimeout(() => {
  scrollDepth()
}, 1000)

どうしてタイムラグがないと上手くいかないのかはちょっとわからないのですが…
scrollDepthとトラッキングIDの紐付けに時間かかるんめすかね。
さすがに1秒以下でページを閉じる人の読了率まで分析しても仕方がないので、このくらいのタイムラグは発生させても大丈夫かと思います。

基礎から学ぶ Vue.js

基礎から学ぶ Vue.js

Vue.jsのプロジェクトでGoogleAnalyticsの設置

プロジェクトをデプロイした後は、アナリティクスを設置してサイト分析をしますよね。
ここではそのための方法を2つほどご紹介したいと思います。

まず1つ目は、vue-analyticsというモジュールを組み込む方法です。

まずはモジュールをインストールしましょう。

npm install vue-analytics —save

コードを記載する場所はどこでもいいですが、今回は全ページの計測をしたいので、main.jsに記載しようと思います。
また、vue-routerを使用してページのルーティングを行っているものとします。

main.js

import Vue from 'vue'
import router from './router'
import VueAnalytics from 'vue-analytics'

Vue.use(VueAnalytics, {
  id: ‘UA-XXXXXXXXX-X',
  router
})

コードはこれだけでOKです!
簡単ですよね。

この方法だとソース内にはアナリティクスのコードが表示されないために、あれ?ちゃんと計測できてる?と疑問に思うのですが、GoogleAnalyticsの画面で確認するとしっかり計測されています。

もう1つの方法は、headタグ内に通常通りscriptで囲ってコードを書き込む方法です。
そんなの当たり前じゃん、と思われるかもしれませんが、SPAを使っているとheadタグ内にコードを書き込むのは大変だったりするんですよね。
そんな時はvue-headを使いましょう。
こちらでも詳しく述べてますが、まずはvue-headのインストールです。

npm install vue-head —save

そして以下のように例えばApp.vueのhead内のscript欄に直接記入してしまえばOKです。

import Vue from 'vue'
import VueHead from 'vue-head'

Vue.use(VueHead)

export default {
  head: {
    script: [
      { type: 'text/javascript', inner: (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');ga('create', 'UA-XXXXXX-X', 'auto');ga('send', 'pageview');, async: true},
    ]
  }
}

どちらの方法を使うかはプロジェクトの状況次第だと思いますが、個人的にはvue-headを使う方が手間もかからずに楽だと思いました。

基礎から学ぶ Vue.js

基礎から学ぶ Vue.js

Intellijでプロジェクトを開く時は、Import Project

Intellijで新規でプロジェクトを作る時ではなく、既存のソースを初めて開く時の話です。
Intellijを立ち上げた時にCreate New Project、Import Project、Open…とメニューが出て来ますが、フレームワークを使う時はImport Projectを選択しましょう。

f:id:tomotomo1129:20180621110012j:plain

Openでもソースは開けるのですが、開いた後にフレームワークの設定などをしなければならず、結構面倒です。場合によっては長時間詰まることもあり、効率の良いやり方ではありません。

Import Projectをすれば、ソースを開くまでにフレームワークを選択する画面などがあるために、ソースを開いた後の設定がほとんど要らなくなるのです。

ほとんどの人がImport Projectで選んでると思いますが、僕がOpenでソースを開いて色々詰まったので備忘のために書きました笑

vue.jsで全ページ共通コンポーネントのまとめ方

コンポーネントでヘッダーやフッターなど、どのページでも共通となる部分をまとめることってありますよね。
Vue.jsではその時、どのようにまとめるのがいいのか自分なりに調べた方法を共有します。
まずは全ページで毎回コンポーネントを書くやり方です。
今回ページ構成はトップとPage1とPage2の2つがあるとします。


※ヘッダーのコンポーネントをshared-header、フッターのコンポーネントをshared-footerとしています。

<template>
  <div>
    <shared-header></shared-header>
    <div>
        // Topの処理内容
    </div>
    <shared-footer></shared-footer>
  </div>
</template>

Page1コンポーネント、Page2コンポーネント

<template>
  <div>
    <shared-header></shared-header>
    <div>
      // Page1またはPage2の処理内容
    </div>
    <shared-footer></shared-footer>
  </div>
</template>

例えばヘッダー、フッターに毎度引数を渡して処理する場合などはこの方法が無難かもしれませんね。
ただこの方法では、全ページに毎回毎回ヘッダー、フッターなどの共通コンポーネントを忘れずに記載する必要があり、漏れのリスクも発生します。
また、毎度コンポーネントをインポートするのも面倒ですね。

僕がおすすめのやり方は、それぞれのページをrouterの階層で子コンポーネントにしてしまう方法です。
まずはIndex.vueを作り(名前は何でもいいですが)、その中で共通のコンポーネントを記載しておきます。
router-viewの中身が子コンポーネントの内容となり、子コンポーネント(各ページのコンポーネント)には、共通のコンポーネントの記載が不要となります。

具体的なコードです。

まずはrouter.jsです。

import Router from 'vue-router'
import Index from '@/pages/Index'
import Top from '@/pages/Top'
import Page1 from '@/pages/Page1'
import Page2 from '@/pages/Page2'

Vue.use(Router)

export default new Router({
  mode: 'history',
  routes: [
    {
      path: '/',
      component: Index,
      children: [
        {
          path: '',
          name: 'Top',
          component: Top
        },
        {
          path: 'page1',
          name: 'page1',
          component: Page1
        },
        {
          path: 'page2',
          name: 'page2',
          component: Page2
        }
      ]
    }
  ]
})

App.vueに関しては、元々の記述から変更する必要はありません。

<template>
  <router-view></router-view>
</template>

そしてIndex.vueです。
ここに共通となるコンポーネントを記載しておき、router-viewは子コンポーネントの内容となります。

<template>
  <div>
    <shared-header></shared-header>
    <router-view></router-view>
    <shared-footer></shared-footer>
  </div>
</template>

あとは各ページのコンポーネントを普通に書いて大丈夫です。共通コンポーネントの記載はIndex.vueに集約されているために、ヘッダーやフッターは反映されるようになります。

Topコンポーネント

<template>
    <div>
        // Topの処理内容
    </div>
</template>

Page1コンポーネント、Page2コンポーネント

<template>
    <div>
        // Page1またはPage2の処理内容
    </div>
</template>

欠点となるポイントとしては、Index.vueで共通コンポーネントやrouter-viewの外枠をdivなどで囲わなければいけなくなる点です。body直下にヘッダーやフッターを置きたい人にとっては余計なタグを入れなければいけなくなる点が気持ち悪いかもしれませんね。

蛇足となりますが、これを応用して複数の別なデザイン構成のサイトを作る時にも使えます。
例えばCMS機能を実装する時に、ユーザーが閲覧する画面に加えて管理画面を作りたい時などに使えます。管理画面とユーザーが閲覧
する画面は、当然ヘッダー要素やフッター要素などパーツが異なりますよね。
方法を簡単に言いますと、親コンポーネントを複数作るのです。

まずはrouterです

import Router from 'vue-router'
import IndexAdmin from '@/pages/IndexAdmin'
import TopAdmin from '@/pages/TopAdmin'
import PageAdmin1 from '@/pages/PageAdmin1'
import PageAdmin2 from '@/pages/PageAdmin2'
import Index from '@/pages/Index'
import Top from '@/pages/Top'
import Page1 from '@/pages/Page1'
import Page2 from '@/pages/Page2'

Vue.use(Router)

export default new Router({
  mode: 'history',
  routes: [
    {
      path: '/admin',
      component: IndexAdmin,
      children: [
        {
          path: '',
          name: 'TopAdmin',
          component: TopAdmin
        },
        {
          path: 'pageAdmin1',
          name: 'pageAdmin1',
          component: PageAdmin1
        },
        {
          path: 'pageAdmin2',
          name: 'pageAdmin2',
          component: PageAdmin2
        }
      ]
    },
    {
      path: '/',
      component: Index,
      children: [
        {
          path: '',
          name: 'Top',
          component: Top
        },
        {
          path: 'page1',
          name: 'page1',
          component: Page1
        },
        {
          path: 'page2',
          name: 'page2',
          component: Page2
        }
      ]
    }
  ]
})

管理画面はpathに/adminを入れて、ドメイン/adminとURLに入力すれば管理画面のトップ(TopAdminコンポーネント)が表示されるようになります。
同様に/admin/pageAdmin1、/admin/pageAdmin2...と入力するとそれぞれのページが表示されます。

そしてIndex.vueとは別にIndexAdmin.vueを作成します。
※管理画面のヘッダー、フッターのコンポーネントをshared-admin-header、shared-admin-footerとします。

<template>
  <div>
    <shared-admin-header></shared-admin-header>
    <router-view></router-view>
    <shared-admin-footer></shared-admin-footer>
  </div>
</template>

このようにすれば、管理画面とユーザーの画面それぞれで独立して実装しやすくなりますね。

基礎から学ぶ Vue.js

基礎から学ぶ Vue.js