Eltrac

極客死亡計劃

不尊重文字的独立博主,胡言乱语的小说家,兴趣使然的神秘学研究者,爱走弯路的半吊子程序员,不务正业的学生,品味小众的游戏爱好者,需要靠早晨一杯咖啡维持生命体征的废物。
twitter

新時代の独立ブログの回り道式構築のベストプラクティス

私はこれまでに何回ブログのコードを書き直したか覚えていませんが、これが(短期間内で)最後の書き直しであることは確かです。とにかく、カスタマイズ性、メンテナンス性、コストの 3 つの間でバランスが取れない完璧主義に一時的な区切りをつける時が来ました。

書き直しの理由#

古いブログのページデザインがますます気に入らなくなったため、私は直接覆して再設計することに決めました。しかし、そうする前に、私はブログを更新していないことに気づきました。最近の更新は、理解しがたい小説のいくつかであり、ほとんどは別のサイトからの作品のコピーです。要するに、私は長い間真剣にブログを書くことができていませんでした。時折、自分の探求の過程を長文で記録することは非常に重要だと思います。これは、手帳を使って日常を記録することとは大きく異なり、物事そのものに焦点を当てた記述は、思考を整理しながら記憶を深めるのに役立ちます。

私はブログを書く習慣をうっかり失ってしまいましたが、その大きな理由は静的ブログの更新時の煩雑な操作プロセスです。前回のブログコードの書き直しでは、純粋な Next.js または Svelte フレームワークを使用しており、いわば「サーバーレス」アプリケーションでした。データベースがないため、ブログのすべての記事は Markdown ファイルとして、ブログのソースコードと一緒に保存されていました。これにより、ブログを更新する必要があるとき、まず Markdown ファイルに記事の内容を書き、ファイルの先頭に記事のタイトル、作成日、タグなどのメタ情報を示すための必須のフロントマターを記述しなければなりません(しかも、毎回フォーマットや属性の命名を忘れてしまい、以前のファイルを探して新しいファイルにコピーする必要があります)。ファイルが準備できたら、それを Git リポジトリの対応するディレクトリに置き、ローカルで npm run dev を実行して問題がないかテストし、問題がなければ GitHub にプッシュして Vercel が新しいブログバージョンを本番環境にデプロイするのを待ちます。その後、記事内のいくつかの誤りを修正したり、追加の更新を行ったりする必要がある場合、私はモバイルデバイスでこれらの操作を行うことができず、コンピュータを開き、GitHub Desktop と VS Code を開いて内容を編集し、テストし、再度プッシュし、再度デプロイを待たなければなりません。

静的ブログの開発者にとっては、これは面白いと感じるかもしれませんが、私はすぐに飽きてしまいました。なぜなら、時にはただ記事を書いて自分の考えを伝えたり、純粋に記録したりしたいだけなのに、バグを書き始める気がするインターフェースを開いて、一連の非常に「ハッカー」な操作を行わなければならないからです。これはまさに非人道的です。

ブログを書く良い習慣を保ちながら、自分を妥協させないために、私は自分がより快適に感じるブログシステムを再構築することに決めました。

私の考え#

私は機能が充実したグラフィカルなブログ管理バックエンドが必要ですが、自分で車輪を作りたくもなく、市場にある派手な CMS を「大砲で蚊を撃つ」ようなこともしたくありません。私はただ、シンプルで使いやすい、個人ブログに適したコンテンツ管理プログラムが欲しいのです。

この説明に合致する答えはもちろん ——Typechoです。

しかし、問題は、Typecho が PHP で書かれたフルスタックのブログプログラムであることです。JavaScript でフロントエンドを書くことを楽しんできた私にとって、PHP の時代に戻ることは、現代人が山の中の岩穴に住むようなものです。私は PHP に再適応し、ブログテーマを再度書き直さなければなりません。それは私には受け入れられません。

私は伝統的なブログの簡単な操作による便利さを享受したい一方で、現代的なフロントエンド開発の優雅さと効率を手放したくありません。したがって、解決策は明らかです —— ヘッドレス CMS を使用し、ブログのフロントエンドを再設計することです。しかし、問題は再び戻ってきました。市場にあるほとんどのヘッドレス CMS は少し重く、私が解決しようとしている問題に対して、必要以上の機能を備えています。しかし、Typecho はヘッドレス CMS として使用できないものの、その規模は私のニーズをちょうど満たしています。

したがって、私は Typecho をヘッドレス CMS に変える方法を考えれば、すべての問題が解決します。

実践を始める#

私は既存のプラグインを簡単に見つけました。これにより、Typecho に RESTful API を提供できます。これにより、Typecho は純粋なバックエンドとして私が設計したフロントエンドにデータを提供でき、私は Typecho のコントロールパネルでブログの内容を更新するだけで済みます。

次に、私はフロントエンドのデザインに重点を置くだけで済みます。

ツールの選択#

私は、フロントエンドを Next.js で書くことに決めました。なぜなら、フロントエンドを Vercel にホスティングすることに決めたからで、Vercel の Next.js のサポートは明らかに優れています。

CSS-in-JS の面では、最近人気のある Tailwind.css を選びました。自分で SCSS で各クラスを書くのではなく、Next.js の新しいバージョンはデフォルトで Tailwind.css をサポートしているため、設定の時間を省けます。また、React のモジュール化開発のサポートにより、同じまたは類似の要素はコンポーネントとして書くことができ、CSS の観点から意味的にすることはあまり必要ではなくなります。この時、より便利で迅速な方法があるのが最善です。

ちなみに、私はしばらく Next.js を使っていませんでした。前のブログ(Isla)は Svelte を使用していました。新しいバージョンの Next.js では、新しいページルーティング方法である App Router が追加され、従来の Page Router と区別されています。理論的には App Router を使用する方が良いですが、戻ってきたばかりの私は明らかにまだ反応できていないので、引き続き Page Router を使用してブログを作成します。しかし、動けばそれで良いのです。

React Icons のような追加のツールについては、特に言及する必要はありません。

記事の取得#

Next.js Page Router が提供する getStaticProps() 関数を使用して、ページが読み込まれる前にヘッドレス CMS からデータを取得できます。fetch() を使用して API の内容を取得し、await キーワードを使用することを忘れないでください。

プラグインが提供する RESTful スタイルの API は直接 json で解析できますが、解析時にも await キーワードを追加することを忘れないでください。

export async function getStaticProps() {
  const res = await fetch('https://blog.guhub.cn/api/posts')
  const posts = await res.json()

  return { props: { posts } }
}

完了したら、記事リストデータを Props としてメイン関数に返すだけです。

ただし、ここで私はバックエンドの問題に直面しました。コードが正常に数十回実行された後、フロントエンドが最初の 5 つの記事しか表示しないことに気づきました。理由は、プラグインが API にページネーション機能を提供しており、デフォルトで 1 ページに 5 つの記事が必要で、URL クエリで ?page= を使用してどのページを表示しているかを示す必要があります。しかし、私の現在のデザインではページネーション機能は必要ないため、API が提供する別の方法を使用して、1 ページあたりの表示記事数を増やしました。これは比較的愚かな解決策です。

const res = await fetch('https://blog.guhub.cn/api/posts?pageSize=9999')

記事の表示#

バックエンドから得られたデータの中で、重要なデータは data.dataSet にあり、記事のタイトル、作成タイムスタンプ、CID、カテゴリ、スラッグなどが含まれています。特に digest という名前の属性は重要です。これは Typecho の設定に関連しており、ホームページに $this->content() の完全な表示を設定した場合、digest には要約だけでなく、全文の HTML 文字列が含まれます。このプラグインは記事リストの API で全文の属性を特に出力していないため、digest が要約だけを出力する場合、スラッグや CID などのユニークな属性を使用して別のパスから詳細な記事情報を取得する必要があります。

これは明らかに面倒すぎるので、私は Typecho の設定を変更せずに digest を全文として使用することに決めました。ただし、記事リストで本当の要約を出力する必要があるため、フロントエンドで要約の一部を切り取る必要があります。

私は次のように実装しました:


function stripDigest(digest) {
    //空行と空白を削除
    digest = digest.replace(/\ +/g,"").replace(/[ ]/g,"").replace(/[\r\n]/g,"")

    //タイトルを削除
    digest = digest.replace( /<h.*?>(\S|\s)*?<\/h.*?>/g,"")
    
    //記事内容の中で <!--more--> タグを探す
    // 存在する場合は、<!--more--> の前の内容を切り取る
    // 存在しない場合は、最初の150文字を切り取る
    var moreTag = digest.search(/<!--more-->/)
    var sliceEnd = (moreTag>0) ? moreTag+2 : 150
    
    // HTML タグを削除し、文字内容のみを保持してから切り取り操作を実行
    digest = digest.slice(0,sliceEnd).replace(/<\/?[^>]+(>|$)/g, "") + "......"

    return digest
}

要約は連続した文字列であるべきで、改行や空白がないため、まずこれらの空白を削除する必要があります。タイトルも削除するのが望ましいです。そして、よく知られている <!--more--> タグを使用して要約を手動で切り取ります。<!--more--> タグがある場合は、それを境界線として前のテキストを要約として切り取ります。ない場合は、最初の 150 文字を切り取ります。その後、HTML 文字列からタグを削除し、純粋な文字内容のみを保持します。

もしあなたが上記のコードを注意深く見ているなら、次のコードに疑問を持つかもしれません:

var moreTag = digest.search(/<!--more-->/)
var sliceEnd = (moreTag>0) ? moreTag+2 : 150

ここで、変数 moreTag<!--more--> の位置のインデックスを示します。存在する場合、インデックスは 0 より大きくなるはずで、理論的にはインデックスを直接 slice() メソッドに使用するべきですが、ここで 2 を加えています。理由は —— この 2 を加えないと、切り取り位置が正しくないからです。

非常に古典的な問題で、なぜこのコードを書く必要があるのか分かりませんが、書かないとプログラムが正常に動作しません。

加えた後も完全に正しくはありませんが、加えないとより大きな問題が発生します。私はその理由をずっと理解できずにいましたが、今思うと、最良の処理方法は RESTful API の設計ロジックに従って、サーバーが提供する要約を直接取得することです。この問題は後で時間があるときに修正しましょう。

ページデザイン#

記事データを取得し、フロントエンドに表示できるようになったので、ブログの基本機能は完成しました。次はページデザインです。

以前のブログデザインのバージョンでは、私は意図的にシンプルさを追求していました(すでに使い古されたデザインスタイルです)。当時のページ構成は白い背景に黒い文字、同じくシンプルな黒い線のアイコン、淡い灰色の色塊で簡単に領域を区切ったものでした。

このようなデザインは、派手なウェブサイトやアプリの中で一抹の爽快感を見出すことができましたが、問題は、この過度にシンプルなデザインは「キッチュ」に陥りやすいことです。より正確には、私は多くの装ったインサイダーのブロガーに広く認められたデザインスタイルに対してキッチュになってしまいました。このスタイルは新しさや個性に欠け、今振り返ると、ブログのフロントエンドを覆して書き直したい主な理由でもあります。

私は何が私にインスピレーションを与えたのか忘れてしまいましたが、数日間の葛藤の後、新しいブログの外観デザインに新しい構想を持ちました。私はシンプルで洗練されているが、同時に特徴が際立ち、色彩が明確で、レイアウトが新しいデザインを望んでいます。いくつかの新聞の見出しやコラージュの要素を取り入れた後、私はまず Figma でコンセプト図を作成しました。

Figma での初期デザイン

その後の実装過程でいくつかの調整を行い、グリッド手帳のページのような背景を追加し、徐々に現在の形になりました。

RSS 订阅#

このブログを読む人が少ない時代に、ブログを書くほとんどの更新も頻繁に行われない中で、自分に関心を持ってくれる読者に購読の手段を提供し、更新した際に読者に通知することは重要です。

最初はこれが難しいとは思いませんでした。なぜなら、Typecho 自体が RSS フィードを提供しているからです。しかし、問題が再び発生しました。バックエンド部分(Typecho)を blog.guhub.cn というドメインに置き、フロントエンドは www.guhub.cn に置いたため、Typecho 自体はフロントエンドとバックエンドを分離するために設計されていません。そのため、Typecho が提供するフィードには、すべての記事リンクが blog.guhub.cn というドメインを指しており、現在使用している www.guhub.cn ではありません。

私は、フロントエンドで RSS の XML を取得し、すべての blog.guhub.cnwww.guhub.cn に置き換えればいいと思っていました。しかし、Next.js の設計者は、道を回り道する愚か者がこのようなことをしようと考えることはなかったでしょう。XML データを直接処理することはできず、ページの内容を直接取得する方法も見つかりませんでした。理論的には可能ですが、このステップで時間をかけたくなかったので、......

npm i rss

私は RSS ライブラリをインストールし、API から取得した記事データを使用して新しいフィードを生成しました。

export default async function generateRssFeed({ posts }) {
    const feedOptions = {
        //...
    }

    const feed = new RSS(feedOptions);

    posts.map((item) => {
        let post = parseBlogPost(item);
        feed.item({
            title: post.title,
            description: post.content,
            url: `${site_url}/blog/${post.slug}`,
            date: post.date,
        });
    });

    fs.writeFileSync('./public/feed/index.xml', feed.xml({ indent: true }));
}

これにより、Next.js は必要に応じて XML ファイルを生成し、RSS フィードとして提供します。今、もしあなたが望むなら、このリンクを使って私のブログを購読できます。

その他#

細かい実装手順についてはここでは詳しく述べません。カテゴリやタグページなどの機能はまだ作成しておらず、記事リストのデザインもやや簡素すぎるため、これらは徐々に後で追加していきます。これらの機能を作るだけでもしばらく忙しくなるでしょう。私はすぐに飽きてブログ全体を削除することはないと思います。

また、このブログのフロントエンドが不快に感じる場合、特に現在ダークモードを実装していないため、Typecho 側で記事を読むことができます。そちらでは Matcha テーマを使用しており、機能が充実していて、読書体験は現在のブログよりもはるかに良いはずです。

ああ、そうだ、私はこのプロジェクトを Taco と呼んでいます。これは Typecho の中間の音素を一部取り除いた言葉です。

备案とサイトの加速#

Typecho 側のサーバーは Tencent Cloud の国内サーバーを使用しているため、私はついに guhub.cn というドメインを登録しました。しかし、主な理由は CDN やオブジェクトストレージなどのサービスを使用してブログのアクセス速度を向上させるためです。

ICP 登録と公安登録の具体的な手順については詳しく述べません。CDN とオブジェクトストレージサービスには又拍云を使用しており、独立ブログのような低アクセス量のサイトにはコストパフォーマンスが非常に高い選択肢です。半月以上使用しても、1 元未満の費用しかかかりませんでした。

サイト Ping テスト結果図

全国が緑色で満たされている感じはとても快適です。


最近のブログの成果は大体これくらいです。

余談ですが、注意深い方は現在ブログにリンク集ページがないことに気づくでしょう。これはできるだけ早く追加します。友達リンクの申請を再開し、あまり交流のない友達リンクを削除するつもりです。ブログの今後の発展についても初歩的な構想があります。これらの内容はおそらく別の記事として書くつもりなので、ここでは詳しく説明しません。

さて、ここまで読んでいただきありがとうございます。あなたが楽しく過ごしていることを願っています。

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。