👩‍❤️‍👨

React(Next.js)からSvelte(Sveltekit)に浮気するための参考まとめ

2023/07/01に公開
前書き(ボツ)

前書き

皆さんはUIフレームワーク、何を使っていますか?

どうせReactでしょうね。だってReactみんな使ってますもんね。私もです。仕事でもReactです
Next.js、便利ですもんね。すごくわかります。
私も「駆け出し園児ニア」の頃Next.jsを触ったときにpagesフォルダ配下のファイルパスの構造がそのままurlのpathになるのに感動したものです。

ただ最近、特にNext.js v13から出現してきたApp Routerが発表された後の諸々の新規機能の発表があったりしてからキャッチアップしていくのがしんどくなって来てしまいました。具体的には

React Server Components
Server Actions
Async Component

あたりですかね。これらの出現でReactの設計事情がこれまでとは根本的に変わってくるだろう。という界隈の空気感を感じております。

私もこれらのパラダイムの出現による設計事情を悶々と考えていたのですが。もう、疲れちゃいました。
便利な道具としてのフレームワークがどんどん複雑になってきて、私が好きだった頃の彼がどんどん遠くに行ってしまった気がしたんです。

そんなときなんですよね、そんなときに彼が話しかけてきたんです


Svelte < ドシタン?話キコカ?
https://svelte.dev/

初めはちょっとした気の迷いだったんです。個人ブログを彼(Svelte)で構築することにしたんですよね。最初はReactとの書き方の違いに戸惑ったんですがReactの今後を考えて疲れた私の心を彼は優しく受け止めてくれたんです。

https://svelte.dev/blog/write-less-code

こちらに書いてあるように、彼は少ないコードで私が表現したいことを叶えてくれるんですよね。

ごめんなさい。Reactさん。私はもう彼の、Svelteさんと一緒に生きていきます。仕事であなたを使ってプログラムを書くことは今後もあるし、別にあなたのことは嫌いじゃないんです。ただ私のプライベートはすべて彼に使わせてください。

何を話す?

React(Next.js)で書いてきた人たちがシームレスにSvelte(Sveltekit)に浮気移行するために、 「React,Next.jsでの機能、Svelte(Sveltekit)ではこう書けるよ」をつらつらとまとめていきます。基本的なそれぞれの機能に関しては解説はしませんが、いくつかサンプルコードをcodesandboxで書いたので参考にしてみてください。サンプルコードが書いていないやつに関しても、公式ドキュメントや他の人が書いてくれたREPLなんかを載せていきます。
個人的にはNext.jsやReactの機能の大部分は再現できるし、これで十分なんじゃない?と思っているので、Reactにつかれた皆さん、ぜひSvelte触ってみてください。

とりあえずSvelteのドキュメントおいておきます。ここで紹介している以外にも便利な機能はたくさんあるのでぜひ一度触ってみてください!

Svelte
https://svelte.dev/docs/introduction
Sveltekit
https://kit.svelte.dev/docs/introduction

※追記(2023-07-15)
フレームワークでの書き方を機能ごとに比較できるサイトとしてこんなサイトがあるのを見つけましたので、参考にどうぞ!
https://component-party.dev/

こちらのツイートで知りました
https://twitter.com/shindy_JP/status/1679711320160235520

Reactの〇〇、Svelteだとこう書くよ

useState

react

svelte

https://svelte.dev/examples/reactive-assignments

<script>
	let count = 0;

	function handleClick() {
		count += 1;
	}
</script>

<button on:click={handleClick}>
	Clicked {count}
	{count === 1 ? 'time' : 'times'}
</button>

useEffect

react

svelte
depsをトリガーとしてロジックを実行するようなやつ
https://svelte.dev/examples/reactive-statements

componentのcleanup処理-> onDestoroy関数を使う
https://svelte.dev/tutorial/ondestroy

custom hook

react
/customhook ページに移動すると見れます

svelte
/customhook ページに移動すると見れます

custom hook (global state)

react(ライブラリとしてjotaiを入れてます)

/globalstate ページに移動するとみれます

svelte
/globalstate ページに移動するとみれます

データフェッチング

React

いくつかパターンがありますね

custom hook (swrを例に)

https://swr.vercel.app/ja

import useSWR from 'swr'
 
function Profile() {
  const { data, error, isLoading } = useSWR('/api/user', fetcher)
 
  if (error) return <div>failed to load</div>
  if (isLoading) return <div>loading...</div>
  return <div>hello {data.name}!</div>
}
Suspense

https://react.dev/reference/react/Suspense#displaying-a-fallback-while-content-is-loading

import { Suspense } from 'react';
import Albums from './Albums.js';

export default function ArtistPage({ artist }) {
  return (
    <>
      <h1>{artist.name}</h1>
      <Suspense fallback={<Loading />}>
        <Albums artistId={artist.id} />
      </Suspense>
    </>
  );
}

function Loading() {
  return <h2>🌀 Loading...</h2>;
}
async component

https://nextjs.org/docs/app/building-your-application/data-fetching/fetching#async-and-await-in-server-components

async function getData() {
  const res = await fetch('https://api.example.com/...')
  // The return value is *not* serialized
  // You can return Date, Map, Set, etc.
 
  // Recommendation: handle errors
  if (!res.ok) {
    // This will activate the closest `error.js` Error Boundary
    throw new Error('Failed to fetch data')
  }
 
  return res.json()
}
 
export default async function Page() {
  const data = await getData()
 
  return <main></main>
}

Svelte (おすすめ・超好き)

Svelteのこの書き心地がすごい好きです
https://svelte.dev/docs/logic-blocks#await

https://svelte.dev/repl/70e61d6cc91345cdaca2db9b7077a941?version=4.0.1

<script>
	const fetchImage = (async () => {
		const response = await fetch('https://dog.ceo/api/breeds/image/random')
    return await response.json()
	})()
</script>

{#await fetchImage}
	<p>...waiting</p>
{:then data}
	<img src={data.message} alt="Dog image" />
{:catch error}
	<p>An error occurred!</p>
{/await}

interface HogeProps extends FugaPropsみたいなやつ

これに関してはReactが圧勝ですね。。。

react


interface FugaProps {
	email: string;
}

interface HogeProps extends FugaProps {
	name: string;
}

const Component: FC<HogeProps> = ({email,name}) => {
	return (
		<>
			<div>{name}</div>
			<div>{email}</div>
		</>
	)
}

Svelte (つらい)

Svelteだとこれやるのができないんですよね・・・

https://github.com/sveltejs/svelte/issues/6334#issuecomment-841849463

ただRFC的にはまだ話合われているっぽいので今後出てくるんじゃないかなと思います。
https://github.com/sveltejs/rfcs/pull/38

Next.jsの〇〇、Sveltekitだとこう書くよ

setup

next.js
https://nextjs.org/docs/getting-started/installation

npx create-next-app@latest

sveltekit
https://kit.svelte.dev/docs/creating-a-project

npm create svelte@latest my-app

layout

next.js

https://nextjs.org/docs/app/building-your-application/routing/pages-and-layouts#layouts

/nestlayoutページに移動するとみれます

sveltekit
/nestlayoutページに移動するとみれます
https://kit.svelte.dev/docs/advanced-routing#advanced-layouts

Api Routes

next.js

https://nextjs.org/docs/pages/building-your-application/routing/api-routes

import { serialize, CookieSerializeOptions } from 'cookie'
import { NextApiResponse } from 'next'
 
/**
 * This sets `cookie` using the `res` object
 */
export const setCookie = (
  res: NextApiResponse,
  name: string,
  value: unknown,
  options: CookieSerializeOptions = {}
) => {
  const stringValue =
    typeof value === 'object' ? 'j:' + JSON.stringify(value) : String(value)
 
  if (typeof options.maxAge === 'number') {
    options.expires = new Date(Date.now() + options.maxAge * 1000)
  }
 
  res.setHeader('Set-Cookie', serialize(name, stringValue, options))
}

sveltekit

https://kit.svelte.dev/docs/routing#server

import { error } from '@sveltejs/kit';

/** @type {import('./$types').RequestHandler} */
export function GET({ url }) {
    const min = Number(url.searchParams.get('min') ?? '0');
    const max = Number(url.searchParams.get('max') ?? '1');

    const d = max - min;

    if (isNaN(d) || d < 0) {
        throw error(400, 'min and max must be numbers, and min must be less than max');
    }

    const random = min + Math.random() * d;

    return new Response(String(random));
}

その他

ライブラリ事情

Reactは言わずもがな、UIライブラリ、便利Hooksライブラリ、その他諸々便利ライブラリなんでもござれです。コミュニティの大きさは正義を体現しています。困ってることを調べるとだいたいそれを解決してくれるようなライブラリが見つかります。

Svelteもあるにはあるのですが、色々動かしてみた感じあまりこういうフレームワークにべったりのライブラリは選定しないほうがいいんじゃないかなっと思っています。UIライブラリならTailwindベースのHeadless UIだったりdaisy uiだったりを使うのがいいと思っています。それ以外でも、Javascript純正のライブラリだったりを主に導入するのがいいのかなと思ってます。
あとSvelteはSvelte自身でstoreだったりのモジュールがあったりするので、できる限りSvelteのモジュールだけでかけるように工夫していけるといいかなと思っています

UIフレームワーク

react

chakra-uimuimantine
とりあえずここらへんのライブラリを使えばそれなりのクオリティのuiがパパってできます

svelte

個人的に使いやすかったのは
flowbite-svelte
daisy ui
くらいですかね

Svelteはフレームワーク側でアニメーションに関するモジュールが使えるようになってるので↑のライブラリプラスSvelteのモジュールで攻めていくのがいいのかな?って思っています

https://svelte.dev/docs/svelte-motion
https://svelte.dev/docs/svelte-transition
https://svelte.dev/docs/svelte-animate
https://svelte.dev/docs/svelte-easing

Component Test

reactもsvelteも testing-libraryが使えます。

https://testing-library.com/docs/react-testing-library/intro

https://testing-library.com/docs/svelte-testing-library/intro

Hosting

正直こちらは両者とも十分にサポートされてる感じです

next.js

Vercel様(言わずもがな)、Amplify(AWSのやつ)、Cloudflare Pages、Netlify
とりあえず大体のやつはデプロイできます

sveltekit

Svelte公式がサポートしてるのは
cloudflare pages
cloudflare workers
vercel
netlify

あとはSSG用、nodeサーバー用でアダプターが用意されてます。
https://kit.svelte.dev/docs/adapters

終わりに

Reactの場合はライブラリをたくさん導入して大規模でリッチなアプリケーションを作っていく感じでSvelteの場合は標準機能 + ライブラリちょい足しで小規模 ~ 中規模なアプリケーションを作っていくのがいいのではないかな?って思っています。

Reactのパラダイムシフトの流れに疲れた皆さん、是非一度Svelteを触ってみてはいかがでしょうか?

以上です!

Discussion