GAミント至上主義

Web Monomaniacal Developer.

このブログについて(先頭固定)

【お約束】 投稿内容は個人の見解であり、所属する(していた)組織の公式見解ではありません。

名前:uyamazak(昔いた会社で上司が開発用Linuxサーバーのユーザー名に「yuyamazaki」が長いので勝手に作ってくれた。読み方わからないけどウヤマザク)

高校あたりからWEBサイトをやったり趣味の延長でWEB系を仕事にした感じの人間。

2020年1月から株式会社シニアジョブにジョイン。
2021/1現在、まだ開発者は3人、これからの会社なのでビジネス貢献しつつ、きれいな設計をしたい

アズールレーン@竹敷でモバイルのUI、UX研究中(初嫁ジャベリン)
フレンド&大艦隊メンバー募集してます ID:939524678 @竹敷



GitHub: https://github.com/uyamazak/

これまでの主なプロジェクト

続きを読む

既存のLaravelプロジェクトに入ってたVueまわりの環境を整備した

そもそも?

シニアジョブの求人サイトはLaravelをメインに一部でVueを使っています。

しかし、すでにVueコンポーネントの多くを書いた人はおらず、最初はLaravel mixで入れたようなので、Vue CLIやNuxtJSだといっしょに入れられるlint、フォーマットルールなども無いためファイルごとにインデントが違うなど混沌としていました。

Google Search ConsoleでCLSの警告が一部ページで出て、改善しようとソースコードをよく見たら「うわぁぁぁぁぁ」となったり、同僚の要望もあったので、これを機に根本的に整えることにしました。

ESlint, Prettierインストール

ここらへんを入れました。NuxtJSの設定をベースに使いました。

package.json

    "eslint-plugin-vue": "^7.0.0-beta.2",
    "eslint": "^7.7.0",
    "prettier": "^2.1.1",
    "eslint-config-prettier": "^6.11.0",
    "eslint-plugin-prettier": "^3.1.4"

eslintの設定

一見シンプルですが、extendsの並び順によって複数回--fixで自動フォーマットを実行するとインデントが崩れるなど問題がありました。正直よくわかってないけどこの並び順なら大丈夫だった。
変数名デフォはcamel caseですが、PHP側のリクエストパラメータに合わせてsnake caseが使われている箇所があったので無効にしました。

eslintrc.js

module.exports = {
  root: true,
  env: {
    browser: true
  },
  extends: [
    'eslint:recommended',
    'plugin:vue/recommended',
    'plugin:prettier/recommended',
    'prettier/vue',
  ],
  // add your custom rules here
  rules: {
    // 変数名をサーバー側の都合に合わせている場合があるのでOFF
    'vue/prop-name-casing': 'off',
  },
}

.prettierrc

これはNuxtJSのまんまだったような

{
  "semi": false,
  "singleQuote": true
}

lintコマンド追加

他のJSもたくさんあるので(混沌)、VueのSFCファイルのみを対象としたコマンドとしました。

{
  "scripts": {
    // 省略
    "lint-vue": "eslint path/to/components/*.vue",
    "lint-vue-fix": "eslint --fix path/to/components/*.vue"
  },

実行

開発環境はDockerで動いているので

docker-compose app exec yarn lint-vue`

で動くようになりました。
インデントやクォーテーションがばっちりそろって気持ちいいです。

あとは表示されたwarning, errorを直しまくって0にしました。

    • fixで直せないのは、ほとんどがv-forにkeyがついてないやつと、propsのデフォルト値周りでした。

JavaScript, CSSの容量削減

SEO、ユーザーメリット等考えるとむしろこっちがメインかも。
webpackでのビルド後のサイズがjsで600KiB、CSSで200KiBを超えてました・・・。
大きいなぁと思ってはいたけど見ないふりしてた。

使ってないパッケージ削除で容量削減

                         Asset       Size  Chunks                    Chunk Names
           /css/portal/app.css    210 KiB       1  [emitted]         /js/portal/app
             /js/portal/app.js    616 KiB       1  [emitted]  [big]  /js/portal/app

これまでの作業でだいたい把握したので、いらなそうなやつを削除することにしました。
まずLaravel mixがデフォルトで入れてたBootstrapやjQueryを削除。

jQueryはHTML側で普通にscriptタグでCDNのを読み込んでいるのでいらないを確信。むしろ2つ読み込んでるのでダメそう。
Bootstrapは一部モーダルで使っていることが判明したけど別のに書き換えたので大丈夫でした。

これだけでCSSはおよそ1/3、 jsも100KiB以上軽くできました。

           /css/portal/app.css   69.1 KiB       1  [emitted]         /js/portal/app  
             /js/portal/app.js    470 KiB       1  [emitted]  [big]  /js/portal/app

どんだけ無駄に読み込んでたんだ・・・。
表示を見ても問題なさそう。

これ以上の最適化は詳しい情報が必要なので、分析ツールを使うことにしました。

やっぱりこれ。
www.npmjs.com

Vue CLIやNuxtJSなら`yarn build --analyze`あたりで実行できるのでありがたいんですが、Laravelなので下記を使いました。

www.npmjs.com

f:id:uyamazak:20200831144744p:plain

jQuery、Bootstrapも消す前の結果ですがVue本体よりfontawesomeとlodashがでかいのが丸わかりです。

fontawesomeの読み込み最適化

fontawesomeは公式Vueのパッケージを使い、下記のようにドキュメントどおりに読み込んでいました。

github.com

import { faChevronCircleLeft, faChevronCircleRight, faMapMarkerAlt } from '@fortawesome/free-solid-svg-icons'

一見これで必要な3つだけ読み込んでいるように思えますが、データ量的にfree-solid-svg-iconsすべてのアイコンデータが読み込まれてしまうようです。

そのため、公式ドキュメントとは異なりますが、ソースコードをみて下記のように変更しました。
動きましたが、おすすめはできません。

import { faChevronCircleLeft } from '@fortawesome/free-solid-svg-icons/faChevronCircleLeft'
import { faChevronCircleRight } from '@fortawesome/free-solid-svg-icons/faChevronCircleRight'
import { faMapMarkerAlt } from '@fortawesome/free-solid-svg-icons/faMapMarkerAlt'

lodash

次にlodash。調べてみるとdebounceしか使ってませんでした。

同じ要領でこんなのだったのを

  const _ = require('lodash');

こうしました。

import debounce from 'lodash/debounce'

結果はこんなの。相対的にVueが一番大きくなったので納得感。この後popper.jsもBootstrapの依存だったので消せました。
f:id:uyamazak:20200831144959p:plain


最終的には

                         Asset       Size  Chunks                    Chunk Names
           /css/portal/app.css   69.2 KiB       1  [emitted]         /js/portal/app
             /js/portal/app.js    380 KiB       1  [emitted]  [big]  /js/portal/app

616 KiB → 380KiBに。1/3強減らせて、ビルド時間も早くなりました。

.editorconfig

ついでにインデントなどのルールがバラバラだったので追加しました。
PHP等他に影響させたくないので、Componentが入っているフォルダに置きました。
VSCode等ほどんどのエディタでプラグインを入れれば使えると思います。

root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

実際のパフォーマンスへの影響

PageSpeed Insightsの結果は、点数の影響は他の要因(タイミングやサーバーレスポンスとか)の方が大きく、平均すると10ぐらい良くなったかな?程度でした。
圧縮後のサイズだと100KBも減ってなさそうなのでそんなものでしょうか。でもいくつかのエラー項目が改善してました。

ユーザー環境での変化は引き続きGoogle Analytics、Search Consoleとかで追って行こうと思います。


ちなみにプルリクはクソでかかったです。
f:id:uyamazak:20200831152248p:plain

レビュー、動作確認に感謝。


Googleスプレッドシートを一行ずつJSONのファイルにしてGoogleドライブに書き出す

前回↓につづきスプレッドシートの処理。
uyamazak.hatenablog.com

前回は1つのJSONだからメッセージに出してコピペで良かった。

でも今回は都道府県ごとに数百文字のテキストがあり、すべてを1ファイルにまとめてしまうと100KB近くいってしまい、Ajaxでの取得に時間がかかりそうなので、都道府県ごとにファイル分割することにしました。

最初はすべてをまとめたJSONを書き出して、ローカルPCでファイルごとに分割する処理をシェルスクリプトかなにかでやろうかと思いましたが、よく考えるとそれ、Googleドライブでできるのでは?と思い調べてみたら簡単そうなのでやりました。

ファイルの書き出し、文字コードの指定はこちらを参考にしました。
hrroct.hatenablog.com


スプレッドシート側のデータ構造はこんな感じ(まだテキストは入ってない)
f:id:uyamazak:20200821163409p:plain

これを1行ずつ都道府県コードをファイル名としてファイル保存にします。

コードはこんな感じ。

function createJsonFiles() {
  const sheet = SpreadsheetApp.getActiveSheet(); 
  const rows = sheet.getSheetValues(1, 1, sheet.getLastRow(), sheet.getLastColumn());  
  const parentFolder = DriveApp.getFolderById('{フォルダのID}');
  const folderName = Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyy-MM-dd_HH-mm-ss');
  const folder = DriveApp.createFolder(folderName).moveTo(parentFolder)
  const contentType = 'text/json';
  const charset = 'utf-8';
  rows.forEach(function(cols, index) {
    if (index === 0) {
      return
    }
    const prefCode = cols[0]
    const prefName = cols[1]
    const textAll = cols[2]
    const text50 = cols[3]
    const text60 = cols[4]
    const text70 = cols[5]
    const fileName = `${prefCode}.json`
    const content = {
      'name': prefName,
      'textAll': textAll,
      'text50': text50,
      'text60': text60,
      'text70': text70,
    }
    const blob = Utilities.newBlob('', contentType, fileName)
      .setDataFromString(JSON.stringify(content), charset);
    
    folder.createFile(blob);
  })
}

最初、一つのフォルダに書き出せば、再度実行した際に上書きされるかと思いましたが、Googleドライブは実際にはIDで管理しているので、実行するたびに同名のファイルができてしまいました。

そのため、指定のフォルダの下に実行日時を元にフォルダを作り、そこに入れることにしました

  const parentFolder = DriveApp.getFolderById('{フォルダのID}');
  const folderName = Utilities.formatDate(new Date(), 'Asia/Tokyo', 'yyyy-MM-dd_HH-mm-ss');
  const folder = DriveApp.createFolder(folderName).moveTo(parentFolder)

DriveApp.createFolder(ファイル名)だけでは、他のフォルダに配置することができないので、作成した後に、moveTo(親フォルダ)が必要でした。
developers.google.com

実行すると日時のフォルダができて、こんな感じで出力されました。
f:id:uyamazak:20200821164313p:plain

更新頻度によってはCMS使ったり、専用アプリ作った方がいいけど、こんな雑なGASとG Suiteで十分な場面は結構あるなぁと思いました。