🫠
SolidJSでサクッと作るGithubPagesサイト
こんにちは。FEチームのMapleです。私たちのチームは、現在のシステムアーキテクチャを見直し、Reactを用いた新しいアーキテクチャへの移行を検討しています。
他のフレームワークも視野を広げたいので、今回はSolidJSでサクッとGithubPagesにデプロイを行っていこうと思います。
リポジトリ
SolidJSとは
SolidJSは、JavaScriptライブラリの一つで、シンプルで高性能なユーザーインターフェースを構築するために設計されています。以下にSolidJSの特徴をいくつか挙げます。
- リアクティブプログラミング: SolidJSは、リアクティブプログラミングに基づいており、効率的に状態を管理し、UIを更新します。
- 高速なレンダリング: 仮想DOMを使用せずに、直接DOMを操作することで、高速なレンダリングを実現。
- 小さなバンドルサイズ: SolidJSは非常に軽量で、バンドルサイズが小さい。
詳しい内容は以下の記事で解説しておりますので、ご一読ください。
このブログでは、SolidJSを使って簡単なサイトを作成し、GitHub Pagesにデプロイする方法を説明します。
SolidJSプロジェクトのセットアップ
pnpm create solid
あとは質問に回答していくとサクッとプロジェクトが作成されます。
一部プログラム抜粋
以下は、SolidJSの簡単なサンプルコードです。このコードでは、スクロールに応じてプログレスバーが動的に更新されるリアクティブなUIを実装しました。
import { For, createEffect, onCleanup } from "solid-js";
import { css } from "solid-styled";
import logo from "~/assets/logo.jpg";
import nextIcon from "~/assets/nextdotjs.svg";
import reactIcon from "~/assets/react.svg";
import solidIcon from "~/assets/solid.svg";
import typescriptIcon from "~/assets/typescript.svg";
import svelteIcon from "~/assets/svelte.svg";
import pythonIcon from "~/assets/python.svg";
import githubActionIcon from "~/assets/githubactions.svg";
import javascriptIcon from "~/assets/javascript.svg";
import amazonawsIcon from "~/assets/amazonaws.svg";
import goIcon from "~/assets/go.svg";
import kotlinIcon from "~/assets/Kotlin logo.svg";
import vueIcon from "~/assets/Vue.js.svg";
export default function Profile() {
css`
#icon {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
width: 80%;
gap: 12px;
padding: 14px;
border: 0.0625rem solid #dee2e6;
}
#profile {
border-radius: 50%;
width: 100%;
max-width: 12rem;
height: auto;
}
.network-icon {
border-radius: 30%;
width: 100%;
max-width: 3rem;
height: auto;
pointer: cursor;
&:hover {
opacity: 0.5;
}
}
.progress-bar {
width: 100%;
background-color: #f1f1f1;
border-radius: 8px;
overflow: hidden;
}
.progress {
height: 20px;
background-color: #b5b5ff;
text-align: center;
line-height: 20px;
color: white;
border-radius: 8px;
}
#gray {
color: #999;
}
`;
createEffect(() => {
const handleScroll = () => {
// ドキュメントの高さ
const docHeight =
document.documentElement.scrollHeight -
document.documentElement.clientHeight;
// 現在のスクロール位置
const scrollTop = document.documentElement.scrollTop;
// スクロール位置に基づいてプログレスを計算(0から100まで)
const scrollPercent = (scrollTop / docHeight) * 100;
// 各プログレスバーの長さを更新
iconObj.forEach((icon, index) => {
const progressBar = document.querySelector(
`#progress-${index}`
) as HTMLElement;
if (progressBar) {
progressBar.style.width = `${Math.min(scrollPercent, icon.level)}%`;
}
});
};
// スクロールイベントリスナーを追加
window.addEventListener("scroll", handleScroll);
// コンポーネントのアンマウント時にイベントリスナーを削除
onCleanup(() => {
window.removeEventListener("scroll", handleScroll);
});
});
const iconObj = [
{ img: javascriptIcon, name: "JavaScript", level: 95 },
{ img: typescriptIcon, name: "TypeScript", level: 90 },
{ img: reactIcon, name: "React", level: 90 },
{ img: vueIcon, name: "Vue.js", level: 80 },
{ img: nextIcon, name: "Next.js", level: 80 },
{ img: solidIcon, name: "SolidJS", level: 75 },
{ img: svelteIcon, name: "Svelte", level: 60 },
{ img: pythonIcon, name: "Python", level: 70 },
{ img: kotlinIcon, name: "Kotlin", level: 60 },
{ img: goIcon, name: "Go", level: 30 },
{ img: githubActionIcon, name: "GitHub Actions", level: 65 },
{ img: amazonawsIcon, name: "AWS", level: 50 },
];
return (
<section>
<div class="card">
<h3>Skills</h3>
<For each={iconObj}>
{(key, index) => (
<div id="icon">
<img class="network-icon" src={key.img} />
<div class="progress-bar">
<div
id={`progress-${index()}`}
class="progress"
style={{ width: "0%" }}
>
{key.name}
</div>
</div>
</div>
)}
</For>
</div>
</section>
);
}
このサンプルコードは、以下のSolidJSの特徴を示しています。
- リアクティブなUI: createEffectフックを使用して、スクロールイベントに基づいてプログレスバーを動的に更新しています。依存配列は必要ありません。
- より視覚的なJSX:
<For each={iconObj}>
をObject.mapの代わりにしようすることができます。よりわかりやすく良いですね。 - スタイリング: solid-styledを使用して、CSSスタイルをコンポーネント内で定義し、スタイルのスコープを限定しています。
GitHub ActionsでCI/CDを設定
GitHub Actionsを使用して、自動デプロイを設定します。
name: Build and Deploy
on:
push:
branches:
- main # Set a branch to trigger deployment
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout 🛎️
uses: actions/checkout@v4.1.7
- uses: pnpm/action-setup@v4.0.0
with:
version: 8.6.10
- run: |
pnpm install
pnpm build
- name: Move files 📂
run: |
ls -la ./dist/public
cp -R ./dist/public/* ./docs/
- name: Deploy 🚀
uses: JamesIves/github-pages-deploy-action@4.1.0
with:
token: ${{ secrets.MAPLE_TOKEN }}
branch: gh-pages
folder: ./docs
デプロイの確認
コードをmainブランチにプッシュするたびに、GitHub Actionsが自動的にプロジェクトをビルドし、GitHub Pagesにデプロイします。
デプロイ完了後以下のURLを確認します。
まとめ
- リアーキテクチャに向けてより多角的に視野を広げてみるために今回はReact以外の別のフレームワークに触れました。
- 今後もより視野を広げてキャッチアップできたらなと思います。
株式会社SODAの開発組織がお届けするZenn Publicationです。 是非Entrance Bookもご覧ください! → recruit.soda-inc.jp/engineer
Discussion
失礼します。
ご提示されているサンプルコードのように、signalを使っておらず
addEventListener
の実行のみを行う用途では、createEffect
を使用する必要はなく、代わりにonMount
を使用するのが("レンダリング直後に1回のみ実行する"という意図を明確にできるという点で *)適切かと思います。使用例:
(
createEffect
の紹介のためにあえて使用していたのでしたら申し訳ありません🙇)* 返信を受けて追記いたしました
eyemono.moeさん
コメントありがとうございます🙇♀️
ドキュメント確認いたしました。
正しくない書き方になってしまっているので修正いたします。
本日はもう遅いので後日対応させてください🙇♀️
不適切なコードを投稿してしまい大変申し訳ございませんした。
いえいえ、こちらこそ深夜に失礼しました🙇♀️
onMount
がcreateEffect(() => untrack(callbackFn));
のaliasであることを踏まえると、signalが使われていない本サンプルではonMount
とcreateEffect
が等価であるonCleanup
でwindow.removeEventListener
が実行されているためイベントリスナが残ることはない少なくとも本記事のサンプルコードにおいては上記の理由から、
createEffect
/onMount
いずれを使用しても期待通りに動作するため、"正しくない"/"不適切"なコードとまではいかないかと思います👍むしろ あたかも不適切なコードであるかのような指摘になってしまい申し訳ありませんでした🙇♀️
ですので記事内容を修正するか否かは白色さんにおまかせいたします🙇♀️