「.NET 開発基盤部会 Wiki」は、「Open棟梁Project」,「OSSコンソーシアム .NET開発基盤部会」によって運営されています。
2026年、リブート(Vite + React + TypeScript + Tailwind)。
npm create vite@latest my-app -- --template react-ts cd my-app npm install npm run dev
※ 生成AIのサポートを受けつつ実施
PS > npm create vite@latest my-app -- --template react-ts Need to install the following packages: create-vite@9.0.3 Ok to proceed? (y) y > npx > create-vite my-app --template react-ts │ ◇ Install with npm and start now? │ Yes │ ◇ Scaffolding project in D:\temp\my-app... │ ◇ Installing dependencies with npm... added 172 packages, and audited 173 packages in 25s 49 packages are looking for funding run `npm fund` for details found 0 vulnerabilities │ ◇ Starting dev server... > my-app@0.0.0 dev > vite VITE v8.0.2 ready in 940 ms ➜ Local: http://localhost:5173/ ➜ Network: use --host to expose ➜ press h + enter to show help h Shortcuts press r + enter to restart the server press u + enter to show server url press o + enter to open in browser press c + enter to clear console press q + enter to quit
cd my-app
npm install xxxx
npm install
npm run dev
外部からのアクセスを可能にする。
npm run dev -- --host
VSCでデバッグ設定を行ってRuntimeに接続する。
{
// IntelliSense を使用して利用可能な属性を学べます。
// 既存の属性の説明をホバーして表示します。
// 詳細情報は次を確認してください: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "chrome",
"request": "launch",
"name": "localhost に対して Chrome を起動する",
"url": "http://localhost:5173/",
"webRoot": "${workspaceFolder}"
}
]
}フロントエンドの開発を面倒な設定なしでTypeScript, JSX, CSSなどをすぐに使い始められるツール
ほとんどの追加機能(フレームワーク対応、カスタム変換、開発サーバーの拡張など)をプラグインで実現
Rollup/Rolldownに、バンドル、コード分割、minifyなどが同梱される(一部プラグイン)。
npm install tailwindcss npm install @tailwindcss/forms
index.css や globals.css などに追記
css@import "tailwindcss"; @plugin "@tailwindcss/forms";
AI活用(https://claude.ai/chat)で、殆どWebを参照せず、作業を進めることが出来た(Sonnet4.6)。
ココまでの導入手順もAIに生成させている。
以下はプロンプト
以下で生成したコードを修正して3分割のヘッダ、左メニュー、メインエリアの画面を作成したい。 なお、左メニューのリンクを押下するとメインエリアが画面遷移するものとする。 npm create vite@latest my-app -- --template react-ts 修正内容を教えてください。
以下は、出力から修正ポイントを抜粋したもの。
npm install react-router-dom
src/App.tsx、App.css
import { BrowserRouter, Routes, Route } from 'react-router-dom'
import Header from './components/Header'
import Sidebar from './components/Sidebar'
import Home from './pages/Home'
import About from './pages/About'
import Settings from './pages/Settings'
import './App.css'
function App() {
return (
<BrowserRouter>
<div className="app-container">
<Header />
<div className="body-container">
<Sidebar />
<main className="main-area">
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</main>
</div>
</div>
</BrowserRouter>
)
}
export default App/* src/App.css */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.app-container {
display: flex;
flex-direction: column;
height: 100vh;
}
/* ヘッダー */
header {
height: 60px;
background-color: #1e293b;
color: white;
display: flex;
align-items: center;
padding: 0 24px;
font-size: 1.2rem;
font-weight: bold;
flex-shrink: 0;
}
/* ヘッダー下の横並びエリア */
.body-container {
display: flex;
flex: 1;
overflow: hidden;
}
/* 左メニュー */
nav.sidebar {
width: 220px;
background-color: #f1f5f9;
border-right: 1px solid #e2e8f0;
padding: 16px 0;
flex-shrink: 0;
overflow-y: auto;
}
nav.sidebar a {
display: block;
padding: 12px 24px;
text-decoration: none;
color: #334155;
font-size: 0.95rem;
transition: background 0.2s;
}
nav.sidebar a:hover,
nav.sidebar a.active {
background-color: #e0e7ef;
color: #1e40af;
font-weight: 600;
}
/* メインエリア */
.main-area {
flex: 1;
padding: 32px;
overflow-y: auto;
background-color: #ffffff;
}src/components/Header、Sidebar.tsx
export default function Header() {
return <header>My App</header>
}import { NavLink } from 'react-router-dom'
export default function Sidebar() {
return (
<nav className="sidebar">
<NavLink to="/" end className={({ isActive }) => isActive ? 'active' : ''}>
Home
</NavLink>
<NavLink to="/about" className={({ isActive }) => isActive ? 'active' : ''}>
About
</NavLink>
<NavLink to="/settings" className={({ isActive }) => isActive ? 'active' : ''}>
Settings
</NavLink>
</nav>
)
}src/pages/Home、About、Settings.tsx
export default function Home() {
return <div><h1>ホーム</h1><p>ホームページです。</p></div>
}export default function About() {
return <div><h1>アバウト</h1><p>アバウトページです。</p></div>
}export default function Settings() {
return <div><h1>設定</h1><p>設定ページです。</p></div>
}fetchとはWebAPI呼び出し処理のこと。
AIに共通関数を提案させ、
以下の2つの関数から共通部を切り出せますか?
切り出した関数は、ライブラリ化して2つの関数内から呼び出します。
selectAll_DT() {
this.setState({ message: '' });
const method = 'POST';
const headers = oauth_oidc.createHttpRequestHeader(false);
const body =
'ddlDap=' + this.state.ddl.ddlDap
+ '&ddlMode1=' + this.state.ddl.ddlMode1
+ '&ddlMode2=' + this.state.ddl.ddlMode2
+ '&ddlExRollback=' + this.state.ddl.ddlExRollback;
fetch(constants.CrudSampleRootUrl + 'SelectAll_DT', { method, headers, body })
.then(oauth_oidc.fetchStatusHandler)
.then(response => response.json())
.then(data => {
if (data.result) {
this.setState({ message: '', shippers: data.result, loading: false });
} else if (data.errorMSG) {
this.setState({ message: JSON.stringify(data.errorMSG) });
} else if (data.exceptionMSG) {
this.setState({ message: JSON.stringify(data.exceptionMSG) });
}
})
.catch((error: Error) => {
this.setState({ message: JSON.stringify(error.stack) });
});
}
delete() {
this.setState({ message: '' });
const method = 'POST';
const headers = oauth_oidc.createHttpRequestHeader(true);
const body = JSON.stringify({
ddlDap: this.state.ddl.ddlDap,
ddlMode1: this.state.ddl.ddlMode1,
ddlMode2: this.state.ddl.ddlMode2,
ddlExRollback: this.state.ddl.ddlExRollback,
shipper: {
shipperID: this.state.shipper.shipperID,
companyName: '',
phone: '',
},
});
fetch(constants.CrudSampleRootUrl + 'Delete', { method, headers, body })
.then(oauth_oidc.fetchStatusHandler)
.then(response => response.json())
.then(data => {
if (data.message) {
this.setState({ message: data.message });
} else if (data.errorMSG) {
this.setState({ message: JSON.stringify(data.errorMSG) });
} else if (data.exceptionMSG) {
this.setState({ message: JSON.stringify(data.exceptionMSG) });
}
})
.catch((error: Error) => {
this.setState({ message: JSON.stringify(error.stack) });
});
}type SuccessHandler = (data: Record<string, unknown>) => void;
/**
* 共通のfetchラッパー
* @param url リクエスト先URL
* @param headers HTTPヘッダー
* @param body リクエストボディ
* @param onSuccess 成功時のハンドラ(data.errorMSG / data.exceptionMSG 以外の処理)
* @param setMessage メッセージをセットする関数
*/
export function postFetch(
url: string,
headers: HeadersInit,
body: BodyInit,
onSuccess: SuccessHandler,
setMessage: (msg: string) => void,
): void {
setMessage('');
fetch(url, { method: 'POST', headers, body })
.then(oauth_oidc.fetchStatusHandler)
.then(response => response.json())
.then((data: Record<string, unknown>) => {
if (data.errorMSG) {
setMessage(JSON.stringify(data.errorMSG));
} else if (data.exceptionMSG) {
setMessage(JSON.stringify(data.exceptionMSG));
} else {
onSuccess(data);
}
})
.catch((error: Error) => {
setMessage(JSON.stringify(error.stack));
});
}// 元のクラス内
selectAll_DT() {
const headers = oauth_oidc.createHttpRequestHeader(false);
const body =
'ddlDap=' + this.state.ddl.ddlDap
+ '&ddlMode1=' + this.state.ddl.ddlMode1
+ '&ddlMode2=' + this.state.ddl.ddlMode2
+ '&ddlExRollback=' + this.state.ddl.ddlExRollback;
postFetch(
constants.CrudSampleRootUrl + 'SelectAll_DT',
headers,
body,
(data) => {
if (data.result) {
this.setState({ message: '', shippers: data.result, loading: false });
}
},
(msg) => this.setState({ message: msg }),
);
}
delete() {
const headers = oauth_oidc.createHttpRequestHeader(true);
const body = JSON.stringify({
ddlDap: this.state.ddl.ddlDap,
ddlMode1: this.state.ddl.ddlMode1,
ddlMode2: this.state.ddl.ddlMode2,
ddlExRollback: this.state.ddl.ddlExRollback,
shipper: {
shipperID: this.state.shipper.shipperID,
companyName: '',
phone: '',
},
});
postFetch(
constants.CrudSampleRootUrl + 'Delete',
headers,
body,
(data) => {
if (data.message) {
this.setState({ message: data.message });
}
},
(msg) => this.setState({ message: msg }),
);
}デザインをTailwind 拡張にするように修正。