🚀 ViteでNodeblocksをスクラッチから設定
Viteを使用して超高速開発でモダンなReact 18アプリケーションをNodeblocksコンポーネントで構築 ⚡
この包括的なガイドでは、Vite、React 18、Nodeblocksフロントエンドコンポーネントを使用してスクラッチから完全なアプリケーションを作成する方法を説明します。NodeblocksはReact 19をまだサポートしていないため、React 19(Viteの現在のデフォルト)からReact 18にダウングレードします。最適なパフォーマンスのためにcreateRoot APIを含むReact 18の機能を活用します。
📋 前提条件
🎯 プロジェクト設定
ステップ1: Viteプロジェクトの作成
# ReactとTypeScriptで新しいViteプロジェクトを作成
npm create vite@latest my-nodeblocks-app -- --template react-ts
# プロジェクトディレクトリに移動
cd my-nodeblocks-app
# React 18にダウングレード(Nodeblocks互換性要件)
npm install react@^18.0.0 react-dom@^18.0.0
# TypeScript型をReact 18に更新
npm install --save-dev @types/react@^18.0.0 @types/react-dom@^18.0.0
# 他の依存関係をインストール
npm install
# React 18がインストールされていることを確認
npm list react react-dom
⚠️ React 19サポート: NodeblocksはReact 19をまだサポートしていません。React 19の互換性に取り組んでおり、近日中にパッケージを更新予定です。今のところ、上記のようにReact 18を使用してください。
ステップ2: NPMレジストリアクセスの設定
Nodeblocksパッケージをインストールする前に、プライベートnpmレジストリへのアクセスを設定する必要があります。プロジェクトルートに.npmrc
ファイルを作成します:
# プロジェクトルートに.npmrcファイルを作成
touch .npmrc
.npmrc
ファイルに以下の設定を追加します:
@nodeblocks:registry=https://registry.npmjs.org
@basaldev:registry=https://registry.npmjs.org
//registry.npmjs.org/:_authToken=${NODEBLOCKS_DEV_TOKEN}
重要: アクセストークンでNODEBLOCKS_DEV_TOKEN
環境変数を設定する必要があります。開発トークンを取得するには、Nodeblocksチームにお問い合わせください。
# Nodeblocks開発トークンを設定
export NODEBLOCKS_DEV_TOKEN=your_token_here
ステップ3: Nodeblocks依存関係のインストール
必須のNodeblocksパッケージをインストールします:
# 状態管理のためのコアドメインパッケージ
npm install @nodeblocks/matching-app-domain
# 必須UIブロック
npm install @nodeblocks/frontend-navbar-block
npm install @nodeblocks/frontend-footer-block
npm install @nodeblocks/frontend-hero-block
npm install @nodeblocks/frontend-signin-block
npm install @nodeblocks/frontend-signup-block
# 商品管理ブロック
npm install @nodeblocks/frontend-create-product-block
npm install @nodeblocks/frontend-list-products-grid-block
# 組織管理ブロック
npm install @nodeblocks/frontend-create-organization-block
# ユーザー管理ブロック
npm install @nodeblocks/frontend-basic-information-block
npm install @nodeblocks/frontend-list-users-block
🏗️ 基本的なアプリケーション構造
ステップ4: メインアプリケーションコンポーネントの作成
src/App.tsx
の内容を置き換えます:
import React from 'react'
import { Navbar } from '@nodeblocks/frontend-navbar-block'
import { Footer } from '@nodeblocks/frontend-footer-block'
import { MatchingAppDomain } from '@nodeblocks/matching-app-domain'
import { HomePage } from './pages/HomePage'
import { LoginPage } from './pages/LoginPage'
import { SignUpPage } from './pages/SignUpPage'
import { ProductsPage } from './pages/ProductsPage'
import './App.css'
function App() {
return (
<MatchingAppDomain
applicationType={['demand']}
serviceEndpoints={{
user: import.meta.env.VITE_USER_SERVICE_URL || 'https://user-service.example.com',
auth: import.meta.env.VITE_AUTH_SERVICE_URL || 'https://auth-service.example.com',
organization: import.meta.env.VITE_ORG_SERVICE_URL || 'https://org-service.example.com',
catalog: import.meta.env.VITE_CATALOG_SERVICE_URL || 'https://catalog-service.example.com',
chat: import.meta.env.VITE_CHAT_SERVICE_URL || 'https://chat-service.example.com',
order: import.meta.env.VITE_ORDER_SERVICE_URL || 'https://order-service.example.com',
}}
logoutRedirectUrl={'/login'}
loggedInRedirectUrl={'/products'}
sessionType="local_storage">
{({state, middleware, updateState}) => {
return (
<div className="app">
{/* ナビゲーションバー */}
<Navbar
leftContent={
<div className="navbar-logo">
<img
src="/vite.svg"
alt="会社ロゴ"
style={{height: '32px'}}
/>
<span className="brand-name">マイアプリ</span>
</div>
}
centerContent={
<nav className="main-navigation">
<a href="/" className="nav-link">ホーム</a>
<a href="/positions" className="nav-link">商品</a>
</nav>
}
rightContent={
<div className="auth-buttons">
{state.loggedInUser ? (
<button
className="btn-logout"
onClick={() => middleware.onActionLogout()}
>
ログアウト
</button>
) : (
<>
<a href="/login" className="btn-login">ログイン</a>
<a href="/sign-up" className="btn-signup">サインアップ</a>
</>
)}
</div>
}
/>
{/* メインコンテンツ */}
<main className="main-content">
{renderPage(state.currentPage, state, middleware, updateState)}
</main>
{/* フッター */}
<Footer
logoSrc="/vite.svg"
content={
<div className="footer-content">
<div className="footer-section">
<h4>会社</h4>
<ul>
<li><a href="/about">私たちについて</a></li>
<li><a href="/careers">採用情報</a></li>
<li><a href="/contact">お問い合わせ</a></li>
</ul>
</div>
<div className="footer-section">
<h4>サポート</h4>
<ul>
<li><a href="/help">ヘルプセンター</a></li>
<li><a href="/docs">ドキュメント</a></li>
<li><a href="/api">API</a></li>
</ul>
</div>
</div>
}
copyright="© 2024 Your Company Name. All rights reserved."
/>
</div>
)
}}
</MatchingAppDomain>
)
}
// ページルーティング関数
function renderPage(currentPage: string, state: any, middleware: any, updateState: any) {
switch (currentPage) {
case 'home':
return <HomePage />
case 'login':
return <LoginPage middleware={middleware} />
case 'sign_up':
return <SignUpPage middleware={middleware} />
case 'positions_list':
return <ProductsPage />
default:
return <div>見つかりません</div>
}
}
export default App
📱 ページコンポーネントの作成
ステップ5: ヒーローブロック付きホームページ
src/pages/HomePage.tsx
を作成:
import React from 'react'
import { Hero } from '@nodeblocks/frontend-hero-block'
export function HomePage() {
return (
<div className="home-page">
<Hero
title="Nodeblocksへようこそ"
secondaryText="包括的なコンポーネントライブラリでモダンなアプリケーションをより速く構築"
imageUrl='https://images.unsplash.com/photo-1557804506-669a67965ba0?auto=format&fit=crop&w=1920&q=80'
buttonText="始める"
byline="包括的なコンポーネントライブラリでモダンなアプリケーションをより速く構築"
onClickButton={() => {
console.log("開始")
}}
/>
<section className="features-section">
<div className="container">
<h2>なぜNodeblocksを選ぶのか?</h2>
<div className="features-grid">
<div className="feature-card">
<h3>🚀 高速開発</h3>
<p>事前構築されたコンポーネントで開発時間を70%短縮</p>
</div>
<div className="feature-card">
<h3>🎨 カスタマイズ可能</h3>
<p>ブランドに適応する完全にカスタマイズ可能なコンポーネント</p>
</div>
<div className="feature-card">
<h3>⚡ パフォーマンス</h3>
<p>速度のために最適化され、モダンなReactプラクティスで構築</p>
</div>
</div>
</div>
</section>
</div>
)
}
ステップ6: 認証ページ
src/pages/LoginPage.tsx
を作成:
import React from 'react'
import { SignIn } from '@nodeblocks/frontend-signin-block'
interface LoginPageProps {
middleware: {
onActionLogin: (email: string, password: string) => Promise<void>
}
}
export function LoginPage({ middleware }: LoginPageProps) {
return (
<div className="auth-page">
<div className="auth-container">
<SignIn
onChange={(setError, getValues, clearErrors) => {
const values = getValues()
console.log('フォーム値:', values)
// カスタムバリデーション
if (!values.email) {
setError('email', {message: 'メールアドレスは必須です', type: 'required'})
} else if (!/\S+@\S+\.\S+/.test(values.email)) {
setError('email', {message: 'メールアドレスが無効です', type: 'pattern'})
} else {
clearErrors('email')
}
}}
onSubmit={async (formData) => {
try {
await middleware.onActionLogin(formData.email || '', formData.password || '')
console.log('ログイン成功')
} catch (error) {
console.error('ログイン失敗:', error)
}
}}
style={{
maxWidth: '400px',
margin: '2rem auto',
padding: '2rem',
borderRadius: '12px',
boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)',
backgroundColor: 'white'
}}>
<SignIn.SignInTitle>おかえりなさい</SignIn.SignInTitle>
<SignIn.EmailField
label="メールアドレス"
placeholder="メールアドレスを入力"
/>
<SignIn.PasswordField
label="パスワード"
placeholder="パスワードを入力"
/>
<SignIn.SignInButton>サインイン</SignIn.SignInButton>
<SignIn.GotoSignUp>
<a href="/signup">アカウントをお持ちでない方はこちら</a>
</SignIn.GotoSignUp>
<SignIn.ResetPassword>
<a href="/reset-password">パスワードをお忘れですか?</a>
</SignIn.ResetPassword>
</SignIn>
</div>
</div>
)
}
src/pages/SignUpPage.tsx
を作成:
import React from 'react'
import { SignUp } from '@nodeblocks/frontend-signup-block'
interface SignUpPageProps {
middleware: {
onActionRegister: (formData: {
email?: string;
password?: string;
agreesPrivacyPolicy?: boolean;
agreesUserAgreement?: boolean;
}) => Promise<void>
}
}
export function SignUpPage({ middleware }: SignUpPageProps) {
return (
<div className="auth-page">
<div className="auth-container">
<SignUp
onChange={(setError, getValues, clearErrors) => {
const values = getValues()
// カスタムバリデーションロジック
if (values.password && values.confirmPassword &&
values.password !== values.confirmPassword) {
setError('confirmPassword', {
message: 'パスワードが一致しません',
type: 'validate'
})
} else {
clearErrors('confirmPassword')
}
}}
onSubmit={async (formData) => {
try {
await middleware.onActionRegister(formData)
console.log('登録成功')
} catch (error) {
console.error('登録失敗:', error)
}
}}
style={{
maxWidth: '400px',
margin: '2rem auto',
padding: '2rem',
borderRadius: '12px',
boxShadow: '0 4px 6px rgba(0, 0, 0, 0.1)',
backgroundColor: 'white'
}}>
<SignUp.SignUpTitle>アカウントを作成</SignUp.SignUpTitle>
<SignUp.EmailField name="email" label="メールアドレス" placeholder="メールアドレスを入力" />
<SignUp.PasswordField name="password" label="パスワード" placeholder="パスワードを作成" />
<SignUp.PasswordField name="confirmPassword" label="パスワード確認" placeholder="パスワードを確認" inputType="password" />
<SignUp.TermsOfUse name="agreesUserAgreement" label="利用規約に同意します" />
<SignUp.PrivacyPolicy name="agreesPrivacyPolicy" label="プライバシーポリシーに同意します" />
<SignUp.SignUpButton>アカウント作成</SignUp.SignUpButton>
<SignUp.GotoSignIn>
<a href="/login">既にアカウントをお持ちの方はこちら</a>
</SignUp.GotoSignIn>
</SignUp>
</div>
</div>
)
}
ステップ7: 商品ページ
src/pages/ProductsPage.tsx
を作成:
import React, { useState } from 'react'
import { CreateProduct } from '@nodeblocks/frontend-create-product-block'
import { ListProductsGrid } from '@nodeblocks/frontend-list-products-grid-block'
export function ProductsPage() {
const [showCreateForm, setShowCreateForm] = useState(false)
const [products, setProducts] = useState([
{
id: '1',
name: 'モダンなラップトップ',
price: 1299.99,
category: 'electronics',
imageUrl: 'https://images.unsplash.com/photo-1496181133206-80ce9b88a853?w=300',
description: 'プロフェッショナル向け高性能ラップトップ'
},
{
id: '2',
name: 'ワイヤレスヘッドフォン',
price: 199.99,
category: 'electronics',
imageUrl: 'https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=300',
description: 'ノイズキャンセリング機能付きプレミアムワイヤレスヘッドフォン'
}
])
const categoryOptions = [
{ value: 'electronics', label: '電子機器' },
{ value: 'clothing', label: '衣類' },
{ value: 'home', label: 'ホーム&ガーデン' },
{ value: 'books', label: '書籍' },
{ value: 'sports', label: 'スポーツ&レクリエーション' }
]
return (
<div className="products-page">
<div className="container">
<div className="page-header">
<h1>商品管理</h1>
<button
className="btn-primary"
onClick={() => setShowCreateForm(!showCreateForm)}
>
{showCreateForm ? '商品を表示' : '商品を追加'}
</button>
</div>
{showCreateForm ? (
<div className="create-product-section">
<h2>新しい商品を作成</h2>
<CreateProduct
categoryOptions={categoryOptions}
onSubmit={(formData) => {
console.log('商品作成中:', formData)
// 商品をリストに追加
const newProduct = {
id: Date.now().toString(),
name: formData.name || '',
price: Number(formData.price) || 0,
category: String(formData.category) || '',
imageUrl: formData.image?.url || 'https://via.placeholder.com/300',
description: formData.description || ''
}
setProducts([...products, newProduct])
setShowCreateForm(false)
}}
onChange={(setError, getValues, clearErrors) => {
const values = getValues()
console.log('フォーム値:', values)
}}
onAcceptAttachment={(files) => {
console.log('ファイル受信:', files)
}}
onUploadAttachment={(file) => {
console.log('ファイルアップロード中:', file)
// ファイルアップロードロジックをここで処理
return Promise.resolve({
url: 'https://via.placeholder.com/300',
name: file.name
})
}}
onClearAttachment={() => {
console.log('添付ファイルクリア')
}}
/>
</div>
) : (
<div className="products-list-section">
<ListProductsGrid listProductsGridTitle="注目商品" subtitle="最新コレクション">
<ListProductsGrid.Items>
{products.map(product => (
<ListProductsGrid.Items.GridCard key={product.id} title={product.name} subtitle={product.description} imageUrl={product.imageUrl} />
))}
</ListProductsGrid.Items>
</ListProductsGrid>
</div>
)}
</div>
</div>
)
}
🎨 アプリケーションのスタイリング
ステップ8: カスタムスタイルの追加
src/App.css
を更新:
/* リセットとベーススタイル */
root {
width: 100%;
max-width: 100%;
margin: 0 auto;
padding: 0;
text-align: center;
}
.card {
padding: 2em;
}
.read-the-docs {
color: #888;
}
/* リセットとベーススタイル */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
line-height: 1.6;
color: #333;
background-color: #f8fafc;
}
.app {
min-height: 100vh;
display: flex;
flex-direction: column;
}
.main-content {
flex: 1;
}
/* コンテナ */
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 1rem;
}
/* ナビゲーションスタイル */
.navbar-logo {
display: flex;
align-items: center;
gap: 0.5rem;
font-weight: bold;
color: #1a202c;
}
.brand-name {
font-size: 1.5rem;
}
.main-navigation {
display: flex;
gap: 2rem;
}
.nav-link {
text-decoration: none;
color: #4a5568;
font-weight: 500;
transition: color 0.2s;
}
.nav-link:hover {
color: #2d3748;
}
.auth-buttons {
display: flex;
gap: 1rem;
align-items: center;
}
.btn-login, .btn-signup, .btn-logout {
padding: 0.5rem 1rem;
border-radius: 6px;
text-decoration: none;
font-weight: 500;
transition: all 0.2s;
border: none;
cursor: pointer;
}
.btn-login {
color: #4a5568;
background: transparent;
}
.btn-login:hover {
background-color: #f7fafc;
}
.btn-signup, .btn-logout {
background-color: #3182ce;
color: white;
}
.btn-signup:hover, .btn-logout:hover {
background-color: #fff;
color: #3182ce;
}
/* ページスタイル */
.home-page {
min-height: calc(100vh - 160px);
}
.features-section {
padding: 4rem 0;
background: white;
}
.features-section h2 {
text-align: center;
margin-bottom: 3rem;
font-size: 2.5rem;
color: #2d3748;
}
.features-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
margin-top: 2rem;
}
.feature-card {
padding: 2rem;
border-radius: 12px;
background: #f7fafc;
border: 1px solid #e2e8f0;
text-align: center;
transition: transform 0.2s, box-shadow 0.2s;
}
.feature-card:hover {
transform: translateY(-4px);
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
}
.feature-card h3 {
margin-bottom: 1rem;
color: #2d3748;
}
/* 認証ページ */
.auth-page {
min-height: calc(100vh - 160px);
display: flex;
align-items: center;
justify-content: center;
padding: 2rem;
}
.auth-container {
width: 100%;
max-width: 500px;
}
/* 商品ページ */
.products-page {
padding: 2rem 0;
}
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 2rem;
padding-bottom: 1rem;
border-bottom: 1px solid #e2e8f0;
}
.page-header h1 {
color: #2d3748;
font-size: 2.5rem;
}
.btn-primary {
background-color: #3182ce;
color: white;
border: none;
padding: 0.75rem 1.5rem;
border-radius: 8px;
font-weight: 500;
cursor: pointer;
transition: background-color 0.2s;
}
.btn-primary:hover {
background-color: #2c5282;
}
input {
background-color: #fff;
}
.create-product-section, .products-list-section {
background: white;
padding: 2rem;
border-radius: 12px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
/* フッタースタイル */
.footer-content {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 2rem;
margin-bottom: 2rem;
}
.footer-section h4 {
margin-bottom: 1rem;
color: white;
}
.footer-section ul {
list-style: none;
}
.footer-section li {
margin-bottom: 0.5rem;
}
.footer-section a {
color: #4a5568;
text-decoration: none;
transition: color 0.2s;
}
.footer-section a:hover {
color: #2d3748;
}
/* レスポンシブデザイン */
@media (max-width: 768px) {
.main-navigation {
display: none;
}
.features-grid {
grid-template-columns: 1fr;
}
.page-header {
flex-direction: column;
gap: 1rem;
align-items: stretch;
}
.auth-buttons {
flex-direction: column;
}
}
🔧 環境設定
ステップ9: 環境変数
プロジェクトルートに.env
ファイルを作成:
# APIエンドポイント
VITE_USER_SERVICE_URL=https://user-service.example.com
VITE_AUTH_SERVICE_URL=https://auth-service.example.com
VITE_ORG_SERVICE_URL=https://organization-service.example.com
VITE_CATALOG_SERVICE_URL=https://catalog-service.example.com
VITE_CHAT_SERVICE_URL=https://chat-service.example.com
VITE_ORDER_SERVICE_URL=https://order-service.example.com
# アプリケーション設定
VITE_APP_NAME=MyNodeBlocksApp
VITE_APP_VERSION=1.0.0
開発用に.env.development
を作成:
# 開発用APIエンドポイント
VITE_USER_SERVICE_URL=http://localhost:8001
VITE_AUTH_SERVICE_URL=http://localhost:8002
VITE_ORG_SERVICE_URL=http://localhost:8003
VITE_CATALOG_SERVICE_URL=http://localhost:8004
VITE_CHAT_SERVICE_URL=http://localhost:8005
VITE_ORDER_SERVICE_URL=http://localhost:8006
🚀 アプリケーションの実行
ステップ10: 開発サーバーの起動
# 開発サーバーを起動
npm run dev
# アプリケーションは以下で利用可能になります:
# http://localhost:3000
ステップ11: 本番ビルド
# 本番用ビルド
npm run build
# 本番ビルドをプレビュー
npm run preview
📈 高度な機能
さらなるブロックの追加
アプリケーションが成長するにつれて、より専門的なブロックを追加できます:
# 組織管理
npm install @nodeblocks/frontend-list-organizations-block
# 注文管理
npm install @nodeblocks/frontend-create-order-block
npm install @nodeblocks/frontend-list-orders-block
# チャット機能
npm install @nodeblocks/frontend-chat-conversation-block
npm install @nodeblocks/frontend-chat-conversation-list-block
# 高度なUIコンポーネント
npm install @nodeblocks/frontend-filter-search-panel-block
npm install @nodeblocks/frontend-breadcrumb-block
npm install @nodeblocks/frontend-side-navigation-block
カスタムブロックオーバーライド
すべてのNodeblocksコンポーネントは強力なブロックオーバーライドパターンをサポートしています:
<CreateProduct categoryOptions={categoryOptions}>
{({defaultBlocks, defaultBlockOrder}) => ({
blocks: {
...defaultBlocks,
// 特定のブロックをオーバーライド
productName: {
...defaultBlocks.productName,
props: {
...defaultBlocks.productName.props,
placeholder: "素晴らしい商品名を入力してください",
style: { fontSize: '1.2rem' }
}
}
},
blockOrder: defaultBlockOrder
})}
</CreateProduct>
詳細を学ぶ: 包括的な例と高度なブロックオーバーライド技術については、高度な使用法ガイドをご覧ください。
🔗 有用なリソース
最大の開発速度とパフォーマンスのためにNodeblocksとViteを使用して❤️で構築