技術の変遷は秋の空、ワイの興味は飽きの空。時々OSSとテスト。
みなさん初めまして、ダイの大冒険という漫画が大好きなbun913です。
みなさんフロントエンドもキャッチアップしていますか?私は頑張っている最中です。
このブログを作成する際の大きなモチベーションに「静的なサイト作成を通じてReactや周辺技術のキャッチアップをする」というのがありました。
そのため今回は以下の技術を(無駄に)活用しながら、快適に開発ができたので軽くご紹介したいと思います。
「Zennと似たような執筆体験」を目指した結果、本家には劣りますが割と快適に書ける環境ができました。
静的なサイトを作るのに適したなんでも入りのFWとなります。
Astroアイランドという考え方があり、基本は静的なページとしつつも一部を動的なパーツとすることができます。
このサイトは基本的にほとんど静的なページなのですが、ヘッダーなどの一部のパーツについては状態を持つようにしています。
Astro自体でコンポーネントを作成できるのですが、今回はReactで作ったコンポーネントを呼び出してパラメーターを渡す役割に終始してもらうことにしました。
hoge.astro
という形式のファイルにAstron記法で記述するのですが、以下のようにVue
やReact
に慣れている方であればすぐに基本を理解できそうな形でした。
---
import { BlogListElm } from "../../molecules/BlogListElm/BlogListElm"
const { title, tags, date, slug } = Astro.props.blog
---
<BlogListElm title={title}, tags={tags}, date={date}, slug={slug} />
また、Cloudflare Pagesとの兼ね合いもよく、容易にカスタムドメインでサイトを公開できました。(ドメイン購入もCloudflareでできるのも良いですね)
体感ですがスピードもとても早く感じるので、結果的にとても良い選択でした。
今回のブログはこちらを使いたいというのがメインの理由でした。
とはいえ、静的なパーツが多いのでReactの状態管理をガッツリ学習できたわけではありませんが、Storybookとテストを組み合わせることができたのは非常によかったです。
巷ではStorybook駆動開発
が話題になっていますが、その良さを少しでも体感できたのはよかったです。
まずこれまでは小さなコンポーネントを作っても、それを開発画面にレンダリングするまではどのような見た目になるかわかりませんでした。
ところがStoryBookでは以下のようにローカルの環境でもどのようなコンポーネントがあるのか容易にカタログ化できます。
さらにコンポーネントに渡す引数や状態が変わることでどのような見た目の変化(挙動の変化)をするのか容易に把握できます。
試しにパラメーターを切り替えると、見た目が変わることを容易に把握できました。
しかもこの挙動も以下のように数行書いてあげるだけだったので非常に楽です。
const meta = {
title: "atoms/Link/SNSLink",
component: IconLink,
argTypes: {
icon: {
options: iconNames,
control: {
type: "radio",
},
},
url: {
options: [GitHubUrl, ZennUrl, SpeakerDeckUrl, DevIoURL],
control: {
type: "select",
},
},
},
} satisfies Meta<typeof IconLink>
export default meta
type Story = StoryObj<typeof IconLink>
export const DefaultStory: Story = {
args: {
url: GitHubUrl,
icon: "GitHub",
},
}
このStoryでは省いていますが、play
関数を記載することでStorybook上でインタラクションのテストも可能です。
Jest側にStorybookを取り込んでplay関数後のあるべき状態について書くこともできるため、色々とやりやすそうです。
このサイトでは一部のStorybookをJest(testing-library)で利用しています。
describe("NormalStory", () => {
it("前のページのリンクも次のページのリンクもある", async () => {
const { container } = render(<NormalStory />)
// aタグの数が2
await NormalStory.play({ canvasElement: container })
expect(container.querySelectorAll("a")).toHaveLength(2)
// AタグとしてPrevのリンクが取れる
const prev = container.querySelector('a[href="/page/1"]')
expect(prev!.tagName).toBe("A")
// AタグとしてNextのリンクが取れる
const next = container.querySelector('a[href="/page/3"]')
expect(next!.tagName).toBe("A")
})
})
なお、ここでは省きましたがこのサイトではテストの自動化や静的解析の自動化もCIとして組み込んでいます。
開発の初期にやっておいてよかったと思ったので、次回以降ご紹介したいと思います。
最後まで読んでいただきありがとうございました。bun913でした。