universalモードでのaxiosのエラーハンドリングを共通化する
universalモードの場合、SPAのハンドリング、SSRのハンドリングをしなければならない。
SPAの場合はビューで制御して
ビュー - NuxtJS
SSRの場合はnginxのerror_pageディレクティブとか使って制御する必要がある
。
フロントもSSR、SPAを判定してエラーハンドリングをしなければいけないのですが、めんどくさいのでそこらへんの判定を無視する雑なハンドリング方法を書く。雑実装なので非推奨
static/error.html
<body> エラーっす </body>
plugins/axios.js
export default function({ $axios, redirect }) { $axios.onError((err) => { const code = parseInt(err.response && err.response.status) redirect(code, '/static/error.html') }) }
nuxt.config.js
plugins: [ { src: '~/plugins/axios.js' }, ]
あとはnginxとかでerror_pageディレクティブとか使ってハンドリングする。errorメソッドでハンドリングしても良いが、axiosの例外はSSRのときに500で拾ってしまうので正しいステータスコードをサーバーに伝えるため無理やり指定のstatusコードにredirectしてる。
雑すぎるのでおすすめしない。
[NustJS]外部ファイルからstoreにアクセスする
NuxtJSではコンポーネントやplugins、middlewareなどなど、contextにアクセス出来ないことがある。つまりstoreにアクセス出来ない。
やり方はいろいろあるが、自分なりの落とし所を書く。
globalで使う(やばい)
.eslintrc.js
globals: { $store: true, ... }
plugins/global_store.js
export default function({ store }) { global.$store = store }
nuxt.config.js
plugins: [ { src: '@/plugins/global_store.js', ssr: false }, ]
これでこう使う
export default class Hoge { static hoge() { $store.dispatch('hogehoge'); } }
シャローコピーだしまぁ良いかなーと思ったけど、状態のように常に変わるようなものをグローバルに置くのはまぁヤバイ。コンポーネントではthis.$store
と$store
どっちも使えるのがシビれる(悪い意味で
onNuxtReadyで初期化
let store if (process.client) { window.onNuxtReady(({$store}) => { store = $store }) } export default class Hoge { static hoge() { store.dispatch('hogehoge'); } }
なんか絶対よくない気がするけど、ググってもonNuxtReady
がなんだかわからない。公式のドキュメントがあれば読みたい。却下。
constructorで初期化
export default class Hoge { constructor(store) { this._store = store } get $store() { return this._store } }
無難。staticで使えないので却下。
シンプルにインスタンス作る
export default class Hoge { static get $store() { if (!this._store) { this._store = new Vuex.Store(...) } return this._store } }
無難でシンプル、が結果的には最初の実装と同じようにインスタンスはグローバルに。インスタンスメソッドからはthis.constructor.$store
で使う。this.$store
とインスタンスが変わるのでシュチュエーションによっては悪い例だけど、今回は特定のclass経由でのみstoreをいじるのでまぁおっけいかも?
pluginsでstoreを注入
plugins/hoge.js
import Vue from 'vue' export default ({ store }) => { Hoge.$store = store // Vueインスタンスとして使いたければ // Vue.prototype.$hoge = Hoge // Vue.prototype.$hoge.$store = store }
export default class Hoge { static hoge() { $store.dispatch('hogehoge'); } }
Nuxtの初期化フェーズでクリーンなcontextとして扱えるのでこの方法が一番しっくりくる。別インスタンス作ってしまうとchromeのvuedevtoolでstateが確認出来ないので困る。
[参考]
Accessing the store from an external file · Issue #2005 · nuxt/nuxt.js · GitHub
正規表現を使ってディレクトリ内のファイルをrequire出来るようにする
requireでは、ファイル名を直接指定する必要がある。
require
const hogehoge = require('./hoge/hogehoge.js')
たまーに動的に使いたいときがある(NuxtJSでVeeValidateでカスタムルール作るときとか)。
こう使ってたりすることが多い。
names.forEach(name => require(`./hoge/${name}.js`))
これだとnamesはどこで作るのか迷う。余談だがNuxtJSは設定より規約を重視してるので規約をつくった方がよい。だからnames作りたくない
require.context
独自のコンテキストを作れる。早速使い方。
const context = require.context('./hoge/', true, /\.js$/) context.keys().forEach((key) => { // const hoge = require(`./hoge/{key}`) これとほぼ同じ(これじゃ実際動かない const hoge = context(key).default })
左からディレクトリパス、サブディレクトリも検索するかどうか、ファイルを照合する正規表現。
これをうまく使えばNuxtJSのpagesでroutingを生成するように規約ベースでなんやかんや出来る!
dotnet build、runで起きるエラーの対処法
いつも忘れるいつもググるから備忘録
コピーできません
warning MSB3026: "obj/Debug/{app}" を "bin/Debug/{app}" にコピーできませんでした。1000 ミリ秒以内に 1 回目の再試行を開始します。
多分どっかのプロセスが握ってるので、ブチ消す。
rm -rf bin
Kestrel動きません
crit: Microsoft.AspNetCore.Server.Kestrel[0] Unable to start Kestrel. System.InvalidOperationException: HTTPS endpoints can only be configured using KestrelServerOptions.Listen().
使いたいポートが空いてない。開ける
lsof -i:5000 kill PID
10万PVの収益や掛かる費用など公開する
先月、約8万PV、2.2万ユーザー訪問がありました(タイトル嘘
※2月は10万PVちゃんと超えました(パチパチ👏
10万PVも十分見込める範囲内なのでここまできた考察と収益やサーバーの維持費、その他サーバーにおけるコストなど公開します。
対象サイト
アナリティクス
このサイトは1人のユーザーが何ページも跨ぐサイトではないので、ユーザー数が実際の流入になります。現状、流入は多いけど直帰、離脱率が比較的高い傾向にあります。1人のユーザーが3.5ページ閲覧しているということになります。見てもらえればわかるのですが、SEOが強く、その他の流入(SNS等)は弱いです。
ユーザー数
PV数
Search Console
クリックされているキーワードを確認します。「洋楽ランキング 2020」というキーワードが多くクリックされていることがわかります。ユーザーが最新情報を検索する場合は「最新」ではなく西暦を検索する傾向ですが、年初ということもあるので中盤に差し掛かると「最新」と検索する人が増えるかもしれません。「洋楽ランキング」でも3位と上位に位置しているものの、クリック数がそこまで多くないので「最新の洋楽ランキングを知りたい」と思っているユーザーが多いことがわかります。
SEO頑張った過程については以下をご覧ください。
tekitoumemo.hatenablog.com
サーバー代
サーバーとデータベースはAzureを利用しているのですが、月2000円程度かかっています。VPSなど使えばまだ安くできると思うのですが、CIとかその他もろもろのインフラを管理するのが大変なのでPaas使えると楽ですね。現状このプランでも十分すぎるほど捌けているのでまだまだ余裕があります(後ほど負荷について記載します)。ちなみにLinuxを選んでいるのでここまで安いのですが、Windowsサーバーになるとこれの4〜5倍します。
負荷
平均応答時間
大体300ミリ秒前後なので、そこまでストレスが掛かる時間ではないのかなと思います。ただし、PageSpeed Insightsでは200ミリ秒以下は遅いと判断されます。
サーバーの応答時間は 200 ミリ秒以下に抑える必要があります。
CPU
10%以下に抑えられているので大丈夫でしょう。グラフでちょくちょく負荷が多くみられるのは夜間バッチによる高負荷が原因です。今後改善の予知がありそう。
メモリ
60〜70%の間を行き来しています。プランでは1.75 GBなので約1GB使っていることになります。リアルタイムの訪問数が40近くいく時があるのですが、まぁ耐えられているんだろうなぁというところです。
データベース
DTUはAzure特有の単位で以下の通りになります。
DTU(Database Transaction Unit)という単位で定義され、CPU、メモリ、I/Oの組み合わせからなります。
DTUが大きくなるほど、コンピューティングリソースが多く使えるようになり、標準で付属するストレージ、追加できるストレージも大きくなります。後述するように、ダウンタイムを発生させずにスケールの変更ができます。
1%台と全然問題ない結果となっています。
その他
パフォーマンス改善については以下に書きましたので興味があれば
tekitoumemo.hatenablog.com
収益
一番気になるところだと思います。ずばり
3,882円
でした。結局はクリックが増えれば収益も増えるので大きく前後しそうです。PVに大してクリック数が160と相当少ないので、ここら辺が増えればもっと収益が上がるハズです。
Adsenseの画像は載せられないのでご了承ください。
利益
3,882 - 2,229 = 1,653円
夢がないですねー。逆にいうと元を取るのに5万PVぐらい稼がなければいけないので広告で利益をあげるのはかなり厳しい気がします。ここら辺は胡散臭いい記事が多すぎるので、騙されないようにしましょう。
Javascriptで基底クラスで子クラスのインスタンスを返す
class Parent { static getChildClass() { return new this() } } class Child extends Parent { static start() { console.log(this.getChildClass()) } } Child.start() // Child {}
C#でmarkdownを書く(Markdig)
サイトを運用する人あるあるで
「ガイドとかヘルプ、マジめんどくせ〜」
ってなりませんか?僕はめっちゃなります、
CSSとかマークアップがとりあえずだるいし、wordpress運用してAPIで取り込むとかもだるいです。
今回はC#でmarkdownが書けるライブラリを紹介します。ちなみに今のご時世MVCが少なくなってきているので
フロントエンドだったらmarkedおすすめです(僕はブログのネタ用でC#で書きました。)
C#でMarkdownが書けるライブラリ
Markdigってやつです。
github.com
とりあえずNuget
dotnet add package Markdig
使う
using Markdig; var markdown = File.ReadAllText(path); var html = Markdown.ToHtml(markdown); Console.Write(html); /* markdown -> #見出しです。 html -> <h1>見出しです。</h1> */
ちなみに拡張機能を使いたい場合はこうやるらしいです。
// Configure the pipeline with all advanced extensions active var pipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().Build(); var result = Markdown.ToHtml("This is a text with some *emphasis*", pipeline);
拡張機能はこちらに載ってます。UseAutoLinkst
とか便利そうね使わないけど。
実際作ったやつはこんな感じになりました。