ホームページ > ウェブフロントエンド > jsチュートリアル > すべての JavaScript 開発者は React フックをブックマークする必要があります

すべての JavaScript 開発者は React フックをブックマークする必要があります

Patricia Arquette
リリース: 2024-11-03 18:00:05
オリジナル
919 人が閲覧しました

 React hooks every JavaScript developer must bookmark

React は、優れた複雑なユーザー インターフェイスを構築するためのオープンソース JavaScript ライブラリであり、JavaScript エコシステムで最も人気のあるライブラリの 1 つです。

TLDR?

React フックは、関数コンポーネント内で React 状態やその他の機能を使用できるようにする関数であり、複雑な React クラスを作成せずに、副作用の処理やコンテキストへのアクセスなどのタスクを可能にします。 React フックを使用すると、開発者のコ​​ードの可読性と保守性も向上します。

この記事では、38 個の React.js フックとそのユースケースのリストを共有します。これは、React および JavaScript 開発者にとって最適なリソースの 1 つと考えられます。

 React hooks every JavaScript developer must bookmark

1. useState

ローカルコンポーネントの状態を管理します。

import { useState } from 'react';
function Counter() {
  const [count, setCount] = useState(0);
  return (
    <div>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <p>Count: {count}</p>
    </div>
  );
}

ログイン後にコピー
ログイン後にコピー

2.エフェクトの使用

関数コンポーネントで副作用を実行します。

import { useEffect, useState } from 'react';

function DataFetcher() {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetch('https://api.example.com/data')
      .then(response => response.json())
      .then(data => setData(data));
  }, []);

  return <div>Data: {data ? JSON.stringify(data) : 'Loading...'}</div>;
}

ログイン後にコピー
ログイン後にコピー

3.コンテキストを使用する

コンポーネント内のコンテキストを消費します。

import { useContext } from 'react';
import { ThemeContext } from './ThemeContext';

function ThemedButton() {
  const theme = useContext(ThemeContext);
  return <button style={{ background: theme.background }}>Click me</button>;
}

ログイン後にコピー
ログイン後にコピー

4.リデューサーの使用

複雑な状態ロジックを管理します

import { useReducer } from 'react';

const initialState = { count: 0 };

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <div>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
      <span>{state.count}</span>
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
    </div>
  );
}

ログイン後にコピー
ログイン後にコピー

5.コールバックを使用する

メモ化されたコールバック関数を返します。

import { useCallback, useState } from 'react';

function CallbackComponent() {
  const [count, setCount] = useState(0);

  const increment = useCallback(() => {
    setCount(count + 1);
  }, [count]);

  return <button onClick={increment}>Count: {count}</button>;
}

ログイン後にコピー
ログイン後にコピー

6.メモを使う

高価な計算をメモ化します。つまり、リソースを大量に消費する計算の結果を将来の使用のために保存します。

import { useMemo, useState } from 'react';

function Fibonacci() {
  const [num, setNum] = useState(1);

  const fib = useMemo(() => {
    const computeFib = (n) => (n <= 1 ? n : computeFib(n - 1) + computeFib(n - 2));
    return computeFib(num);
  }, [num]);

  return (
    <div>
      <button onClick={() => setNum(num + 1)}>Next Fibonacci</button>
      <p>Fibonacci of {num} is {fib}</p>
    </div>
  );
}

ログイン後にコピー
ログイン後にコピー

7.useRef

DOM 要素にアクセスするか、変更可能な値を保存します。

import { useRef } from 'react';

function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    inputEl.current.focus();
  };

  return (
    <div>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </div>
  );
}

ログイン後にコピー
ログイン後にコピー

8.useImperativeHandle

参照によって公開されるインスタンス値をカスタマイズします。

import { forwardRef, useImperativeHandle, useRef } from 'react';

const FancyInput = forwardRef((props, ref) => {
  const inputRef = useRef();
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    },
  }));
  return <input ref={inputRef} />;
});

function App() {
  const fancyInputRef = useRef();
  return (
    <div>
      <FancyInput ref={fancyInputRef} />
      <button onClick={() => fancyInputRef.current.focus()}>Focus input</button>
    </div>
  );
}

ログイン後にコピー
ログイン後にコピー

9.LayoutEffectを使用する

DOM レイアウトと同期します。

import { useEffect, useLayoutEffect, useRef, useState } from 'react';

function MeasureWidth() {
  const ref = useRef();
  const [width, setWidth] = useState(0);

  useLayoutEffect(() => {
    setWidth(ref.current.offsetWidth);
  }, []);

  return (
    <div>
      <div ref={ref} style={{ width: '50%' }}>
        Resize the window to see the effect.
      </div>
      <p>Width: {width}px</p>
    </div>
  );
}

ログイン後にコピー
ログイン後にコピー

10.デバッグ値の使用

React DevTools にカスタム ラベルを表示します。

import { useDebugValue, useState } from 'react';

function useFriendStatus(friendID) {
  const [isOnline, setIsOnline] = useState(null);

  useDebugValue(isOnline ? 'Online' : 'Offline');

  // Simulate an asynchronous operation
  setTimeout(() => setIsOnline(Math.random() > 0.5), 1000);

  return isOnline;
}

function FriendStatus({ friendID }) {
  const isOnline = useFriendStatus(friendID);

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}

ログイン後にコピー
ログイン後にコピー

11.useFetch

API からデータを取得します。

import { useEffect, useState } from 'react';

function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch(url)
      .then(response => response.json())
      .then(data => {
        setData(data);
        setLoading(false);
      });
  }, [url]);

  return { data, loading };
}

function App() {
  const { data, loading } = useFetch('https://jsonplaceholder.typicode.com/posts');

  if (loading) {
    return <p>Loading...</p>;
  }

  return (
    <ul>
      {data.map(post => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}

ログイン後にコピー
ログイン後にコピー

12.ローカルストレージの使用

ローカル ストレージで状態を管理します。

import { useState } from 'react';

function useLocalStorage(key, initialValue) {
  const [storedValue, setStoredValue] = useState(() => {
    try {
      const item = window.localStorage.getItem(key);
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      console.error(error);
      return initialValue;
    }
  });

  const setValue = (value) => {
    try {
      const valueToStore = value instanceof Function ? value(storedValue) : value;
      setStoredValue(valueToStore);
      window.localStorage.setItem(key, JSON.stringify(valueToStore));
    } catch (error) {
      console.error(error);
    }
  };

  return [storedValue, setValue];
}

function App() {
  const [name, setName] = useLocalStorage('name', 'Bob');

  return (
    <div>
      <input value={name} onChange={(e) => setName(e.target.value)} />
      <p>Hello, {name}!</p>
    </div>
  );
}

ログイン後にコピー
ログイン後にコピー

13.デバウンスを使用する

時間の経過とともに値をデバウンスします。

import { useEffect, useState } from 'react';

function useDebounce(value, delay) {
  const [debouncedValue, setDebouncedValue] = useState(value);

  useEffect(() => {
    const handler = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);

    return () => {
      clearTimeout(handler);
    };
  }, [value, delay]);

  return debouncedValue;
}

function App() {
  const [text, setText] = useState('');
  const debouncedText = useDebounce(text, 500);

  return (
    <div>
      <input value={text} onChange={(e) => setText(e.target.value)} />
      <p>Debounced Value: {debouncedText}</p>
    </div>
  );
}

ログイン後にコピー
ログイン後にコピー

14.前を使用

変数の前の値を保存します。

import { useEffect, useRef } from 'react';

function usePrevious(value) {
  const ref = useRef();

  useEffect(() => {
    ref.current = value;
  }, [value]);

  return ref.current;
}

function App() {
  const [count, setCount] = useState(0);
  const previousCount = usePrevious(count);

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>Count: {count}</button>
      <p>Previous Count: {previousCount}</p>
    </div>
  );
}

ログイン後にコピー
ログイン後にコピー

15. useWindowSize

ウィンドウのサイズを追跡します。

import { useEffect, useState } from 'react';

function useWindowSize() {
  const [size, setSize] = useState({ width: window.innerWidth, height: window.innerHeight });

  useEffect(() => {
    const handleResize = () => {
      setSize({ width: window.innerWidth, height: window.innerHeight });
    };

    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);

  return size;
}

function App() {
  const { width, height } = useWindowSize();

  return (
    <div>
      <p>Width: {width}px</p>
      <p>Height: {height}px</p>
    </div>
  );
}

ログイン後にコピー
ログイン後にコピー

16.ホバーを使用する

要素がホバーされているかどうかを検出します。

import { useCallback, useState } from 'react';

function useHover() {
  const [hovered, setHovered] = useState(false);

  const onMouseOver = useCallback(() => setHovered(true), []);
  const onMouseOut = useCallback(() => setHovered(false), []);

  return { hovered, onMouseOver, onMouseOut };
}

function HoverComponent() {
  const { hovered, onMouseOver, onMouseOut } = useHover();

  return (
    <div onMouseOver={onMouseOver} onMouseOut={onMouseOut}>
      {hovered ? 'Hovering' : 'Not Hovering'}
    </div>
  );
}

ログイン後にコピー
ログイン後にコピー

17. useOnlineStatus

オンラインステータスを追跡します。

import { useEffect, useState } from 'react';

function useOnlineStatus() {
  const [isOnline, setIsOnline] = useState(navigator.onLine);

  useEffect(() => {
    const handleOnline = () => setIsOnline(true);
    const handleOffline = () => setIsOnline(false);

    window.addEventListener('online', handleOnline);
    window.addEventListener('offline', handleOffline);

    return () => {
      window.removeEventListener('online', handleOnline);
      window.removeEventListener('offline', handleOffline);
    };
  }, []);

  return isOnline;
}

function App() {
  const isOnline = useOnlineStatus();

  return <div>{isOnline ? 'Online' : 'Offline'}</div>;
}

ログイン後にコピー
ログイン後にコピー

18. useEventListener

イベントリスナーをアタッチします。

import { useEffect, useRef } from 'react';

function useEventListener(eventName, handler, element = window) {
  const savedHandler = useRef();

  useEffect(() => {
    savedHandler.current = handler;
  }, [handler]);

  useEffect(() => {
    const eventListener = (event) => savedHandler.current(event);
    element.addEventListener(eventName, eventListener);

    return () => {
      element.removeEventListener(eventName, eventListener);
    };
  }, [eventName, element]);
}

function App() {
  useEventListener('click', () => alert('Window clicked!'));

  return <div>Click anywhere!</div>;
}

ログイン後にコピー
ログイン後にコピー

19.使用間隔

動的遅延を伴う間隔を設定します。

import { useEffect, useRef } from 'react';

function useInterval(callback, delay) {
  const savedCallback = useRef();

  useEffect(() => {
    savedCallback.current = callback;
  }, [callback]);

  useEffect(() => {
    function tick() {
      savedCallback.current();
    }
    if (delay !== null) {
      const id = setInterval(tick, delay);
      return () => clearInterval(id);
    }
  }, [delay]);
}

function Timer() {
  const [count, setCount] = useState(0);

  useInterval(() => setCount(count + 1), 1000);

  return <div>Count: {count}</div>;
}

ログイン後にコピー
ログイン後にコピー

20. useTimeout

タイムアウトを設定します。

import { useState } from 'react';
function Counter() {
  const [count, setCount] = useState(0);
  return (
    <div>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <p>Count: {count}</p>
    </div>
  );
}

ログイン後にコピー
ログイン後にコピー

21. useOnClickOutside

コンポーネントの外側のクリックを検出します。

import { useEffect, useState } from 'react';

function DataFetcher() {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetch('https://api.example.com/data')
      .then(response => response.json())
      .then(data => setData(data));
  }, []);

  return <div>Data: {data ? JSON.stringify(data) : 'Loading...'}</div>;
}

ログイン後にコピー
ログイン後にコピー

22.クリップボードを使用する

クリップボード操作を処理します。

import { useContext } from 'react';
import { ThemeContext } from './ThemeContext';

function ThemedButton() {
  const theme = useContext(ThemeContext);
  return <button style={{ background: theme.background }}>Click me</button>;
}

ログイン後にコピー
ログイン後にコピー

23.ダークモードを使用する

ダーク モードの設定を管理します。

import { useReducer } from 'react';

const initialState = { count: 0 };

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  return (
    <div>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
      <span>{state.count}</span>
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
    </div>
  );
}

ログイン後にコピー
ログイン後にコピー

24.useToggle

ブール値を切り替えます。

import { useCallback, useState } from 'react';

function CallbackComponent() {
  const [count, setCount] = useState(0);

  const increment = useCallback(() => {
    setCount(count + 1);
  }, [count]);

  return <button onClick={increment}>Count: {count}</button>;
}

ログイン後にコピー
ログイン後にコピー

25.テーマを使用する

明るいテーマと暗いテーマを切り替えます。

import { useMemo, useState } from 'react';

function Fibonacci() {
  const [num, setNum] = useState(1);

  const fib = useMemo(() => {
    const computeFib = (n) => (n <= 1 ? n : computeFib(n - 1) + computeFib(n - 2));
    return computeFib(num);
  }, [num]);

  return (
    <div>
      <button onClick={() => setNum(num + 1)}>Next Fibonacci</button>
      <p>Fibonacci of {num} is {fib}</p>
    </div>
  );
}

ログイン後にコピー
ログイン後にコピー

26.メディアの使用

メディアのプロパティをクエリします。

import { useRef } from 'react';

function TextInputWithFocusButton() {
  const inputEl = useRef(null);
  const onButtonClick = () => {
    inputEl.current.focus();
  };

  return (
    <div>
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>Focus the input</button>
    </div>
  );
}

ログイン後にコピー
ログイン後にコピー

27. useLockBodyScroll

本体スクロールをロックします。

import { forwardRef, useImperativeHandle, useRef } from 'react';

const FancyInput = forwardRef((props, ref) => {
  const inputRef = useRef();
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    },
  }));
  return <input ref={inputRef} />;
});

function App() {
  const fancyInputRef = useRef();
  return (
    <div>
      <FancyInput ref={fancyInputRef} />
      <button onClick={() => fancyInputRef.current.focus()}>Focus input</button>
    </div>
  );
}

ログイン後にコピー
ログイン後にコピー

28.キープレスを使用する

キーの押下を検出します。

import { useEffect, useLayoutEffect, useRef, useState } from 'react';

function MeasureWidth() {
  const ref = useRef();
  const [width, setWidth] = useState(0);

  useLayoutEffect(() => {
    setWidth(ref.current.offsetWidth);
  }, []);

  return (
    <div>
      <div ref={ref} style={{ width: '50%' }}>
        Resize the window to see the effect.
      </div>
      <p>Width: {width}px</p>
    </div>
  );
}

ログイン後にコピー
ログイン後にコピー

29. useDocumentTitle

ドキュメントのタイトルを更新します。

import { useDebugValue, useState } from 'react';

function useFriendStatus(friendID) {
  const [isOnline, setIsOnline] = useState(null);

  useDebugValue(isOnline ? 'Online' : 'Offline');

  // Simulate an asynchronous operation
  setTimeout(() => setIsOnline(Math.random() > 0.5), 1000);

  return isOnline;
}

function FriendStatus({ friendID }) {
  const isOnline = useFriendStatus(friendID);

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}

ログイン後にコピー
ログイン後にコピー

30.ホバーを使用する

ホバー状態を処理します。

import { useEffect, useState } from 'react';

function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch(url)
      .then(response => response.json())
      .then(data => {
        setData(data);
        setLoading(false);
      });
  }, [url]);

  return { data, loading };
}

function App() {
  const { data, loading } = useFetch('https://jsonplaceholder.typicode.com/posts');

  if (loading) {
    return <p>Loading...</p>;
  }

  return (
    <ul>
      {data.map(post => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}

ログイン後にコピー
ログイン後にコピー

31.地理位置情報の使用

地理位置情報を取得します。

import { useState } from 'react';

function useLocalStorage(key, initialValue) {
  const [storedValue, setStoredValue] = useState(() => {
    try {
      const item = window.localStorage.getItem(key);
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      console.error(error);
      return initialValue;
    }
  });

  const setValue = (value) => {
    try {
      const valueToStore = value instanceof Function ? value(storedValue) : value;
      setStoredValue(valueToStore);
      window.localStorage.setItem(key, JSON.stringify(valueToStore));
    } catch (error) {
      console.error(error);
    }
  };

  return [storedValue, setValue];
}

function App() {
  const [name, setName] = useLocalStorage('name', 'Bob');

  return (
    <div>
      <input value={name} onChange={(e) => setName(e.target.value)} />
      <p>Hello, {name}!</p>
    </div>
  );
}

ログイン後にコピー
ログイン後にコピー

32. useScrollPosition

スクロール位置を追跡します。

import { useEffect, useState } from 'react';

function useDebounce(value, delay) {
  const [debouncedValue, setDebouncedValue] = useState(value);

  useEffect(() => {
    const handler = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);

    return () => {
      clearTimeout(handler);
    };
  }, [value, delay]);

  return debouncedValue;
}

function App() {
  const [text, setText] = useState('');
  const debouncedText = useDebounce(text, 500);

  return (
    <div>
      <input value={text} onChange={(e) => setText(e.target.value)} />
      <p>Debounced Value: {debouncedText}</p>
    </div>
  );
}

ログイン後にコピー
ログイン後にコピー

33. アンマウントを使用する

コンポーネントがアンマウントされるときに関数を実行します。

import { useEffect, useRef } from 'react';

function usePrevious(value) {
  const ref = useRef();

  useEffect(() => {
    ref.current = value;
  }, [value]);

  return ref.current;
}

function App() {
  const [count, setCount] = useState(0);
  const previousCount = usePrevious(count);

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>Count: {count}</button>
      <p>Previous Count: {previousCount}</p>
    </div>
  );
}

ログイン後にコピー
ログイン後にコピー

34. useClickOutside

要素の外側のクリックを検出します。

import { useEffect, useState } from 'react';

function useWindowSize() {
  const [size, setSize] = useState({ width: window.innerWidth, height: window.innerHeight });

  useEffect(() => {
    const handleResize = () => {
      setSize({ width: window.innerWidth, height: window.innerHeight });
    };

    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);

  return size;
}

function App() {
  const { width, height } = useWindowSize();

  return (
    <div>
      <p>Width: {width}px</p>
      <p>Height: {height}px</p>
    </div>
  );
}

ログイン後にコピー
ログイン後にコピー

35.デバウンスコールバックを使用する

コールバック関数をデバウンスします。

import { useCallback, useState } from 'react';

function useHover() {
  const [hovered, setHovered] = useState(false);

  const onMouseOver = useCallback(() => setHovered(true), []);
  const onMouseOut = useCallback(() => setHovered(false), []);

  return { hovered, onMouseOver, onMouseOut };
}

function HoverComponent() {
  const { hovered, onMouseOver, onMouseOut } = useHover();

  return (
    <div onMouseOver={onMouseOver} onMouseOut={onMouseOut}>
      {hovered ? 'Hovering' : 'Not Hovering'}
    </div>
  );
}

ログイン後にコピー
ログイン後にコピー

36. スロットルを使用する

時間の経過とともに値を調整します。

import { useEffect, useState } from 'react';

function useOnlineStatus() {
  const [isOnline, setIsOnline] = useState(navigator.onLine);

  useEffect(() => {
    const handleOnline = () => setIsOnline(true);
    const handleOffline = () => setIsOnline(false);

    window.addEventListener('online', handleOnline);
    window.addEventListener('offline', handleOffline);

    return () => {
      window.removeEventListener('online', handleOnline);
      window.removeEventListener('offline', handleOffline);
    };
  }, []);

  return isOnline;
}

function App() {
  const isOnline = useOnlineStatus();

  return <div>{isOnline ? 'Online' : 'Offline'}</div>;
}

ログイン後にコピー
ログイン後にコピー

37.UpdateEffectの使用

マウント時ではなく、更新時にのみエフェクトを実行します。

import { useEffect, useRef } from 'react';

function useEventListener(eventName, handler, element = window) {
  const savedHandler = useRef();

  useEffect(() => {
    savedHandler.current = handler;
  }, [handler]);

  useEffect(() => {
    const eventListener = (event) => savedHandler.current(event);
    element.addEventListener(eventName, eventListener);

    return () => {
      element.removeEventListener(eventName, eventListener);
    };
  }, [eventName, element]);
}

function App() {
  useEventListener('click', () => alert('Window clicked!'));

  return <div>Click anywhere!</div>;
}

ログイン後にコピー
ログイン後にコピー

38.ローカルストレージの使用

ローカルストレージ内の状態を管理します

import { useEffect, useRef } from 'react';

function useInterval(callback, delay) {
  const savedCallback = useRef();

  useEffect(() => {
    savedCallback.current = callback;
  }, [callback]);

  useEffect(() => {
    function tick() {
      savedCallback.current();
    }
    if (delay !== null) {
      const id = setInterval(tick, delay);
      return () => clearInterval(id);
    }
  }, [delay]);
}

function Timer() {
  const [count, setCount] = useState(0);

  useInterval(() => setCount(count + 1), 1000);

  return <div>Count: {count}</div>;
}

ログイン後にコピー
ログイン後にコピー

React.js は学習と習得が簡単で、無料のコースやリソースが多数あり、さらに大規模で活発な開発者コミュニティがあります
次のプロジェクトでこれらの React フックを必ず使用し、このようなコンテンツをもっと見るために私をフォローしてください

 React hooks every JavaScript developer must bookmark

クマール・カリヤン

技術コンテンツの編集者兼ライター

GIFクレジット

このGIFが気に入ったら、インスタグラムでマネージャーのジェレミーをフォローしてください

以上がすべての JavaScript 開発者は React フックをブックマークする必要がありますの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:dev.to
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
著者別の最新記事
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート