fe-migrate
1
总安装量
1
周安装量
#51873
全站排名
安装命令
npx skills add https://github.com/ingpdw/pdw-fe-dev-tool --skill fe-migrate
Agent 安装分布
mcpjam
1
claude-code
1
replit
1
junie
1
zencoder
1
Skill 文档
FE Migration Guide
$ARGUMENTSìì ë§ì´ê·¸ë ì´ì
ìì¤ì íê²ì íì±íì¬ ë¨ê³ë³ ê°ì´ë를 ì ê³µíê³ ì¤ííë¤.
ì§ì ë§ì´ê·¸ë ì´ì
| From | To | í¤ìë |
|---|---|---|
| Pages Router | App Router | pages app-router |
| JavaScript | TypeScript | js typescript, js ts |
| CRA (Create React App) | Vite | cra vite |
| CSS/SCSS | Tailwind CSS | css tailwind |
| Class Components | Hooks | class hooks |
| Redux | Zustand | redux zustand |
| Jest | Vitest | jest vitest |
| Axios | Fetch | axios fetch |
| Moment.js | date-fns | moment date-fns |
ì¸ìê° ìì¼ë©´ ì¬ì©ììê² ì´ë¤ ë§ì´ê·¸ë ì´ì ì ìíëì§ ì§ë¬¸íë¤.
ë§ì´ê·¸ë ì´ì ê³µíµ ì ì°¨
- íì¬ ìí ë¶ì: íë¡ì í¸ êµ¬ì¡°, ìì¡´ì±, ì¤ì íì¼ì íì íë¤
- ìí¥ ë²ì íì : ë³ê²½ì´ íìí íì¼ ëª©ë¡ì Glob/Grepì¼ë¡ ì¶ì¶íë¤
- ë§ì´ê·¸ë ì´ì ê³í ì ì: ë¨ê³ë³ ìì 목ë¡ì ì¬ì©ììê² ë³´ì¬ì¤ë¤
- ë¨ê³ë³ ì¤í: ì¹ì¸ í ìì°¨ì ì¼ë¡ ë³íì ì§ííë¤
- ê²ì¦: ë¹ë ë° í ì¤í¸ íµê³¼ë¥¼ íì¸íë¤
Pages Router â App Router
ë¨ê³ë³ ê°ì´ë
1ë¨ê³: ê¸°ë° ì¤ì
src/app/layout.tsx â pages/_app.tsx ìì ì´ë
src/app/page.tsx â pages/index.tsx ìì ì´ë
src/app/globals.css â styles/globals.css ìì ì´ë
2ë¨ê³: íì´ì§ ë³í ê·ì¹
| Pages Router | App Router |
|---|---|
pages/about.tsx |
app/about/page.tsx |
pages/blog/[slug].tsx |
app/blog/[slug]/page.tsx |
pages/api/users.ts |
app/api/users/route.ts |
pages/_error.tsx |
app/error.tsx |
pages/404.tsx |
app/not-found.tsx |
3ë¨ê³: ë°ì´í° íì¹ ë³í
// Before: getServerSideProps
export async function getServerSideProps() {
const data = await fetchData();
return { props: { data } };
}
export default function Page({ data }) { /* ... */ }
// After: Server Component async
export default async function Page() {
const data = await fetchData();
return /* ... */;
}
// Before: getStaticProps + getStaticPaths
export async function getStaticPaths() {
return { paths: [...], fallback: false };
}
export async function getStaticProps({ params }) {
const data = await fetchData(params.id);
return { props: { data }, revalidate: 60 };
}
// After: generateStaticParams + fetch with revalidate
export async function generateStaticParams() {
return [...];
}
export default async function Page({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
const data = await fetchData(id);
return /* ... */;
}
4ë¨ê³: Hook/ìí ì¬ì© ì»´í¬ëí¸ â “use client”
useState,useEffect,useContext, ì´ë²¤í¸ í¸ë¤ë¬ê° ìë ì»´í¬ëí¸ì"use client"ì¶ê°- ê°ë¥í Client Component를 ìê² ë¶ë¦¬
5ë¨ê³: Head â Metadata
// Before: next/head
import Head from "next/head";
<Head><title>Page</title></Head>
// After: Metadata export
export const metadata: Metadata = {
title: "Page",
};
JavaScript â TypeScript
ë¨ê³ë³ ê°ì´ë
1ë¨ê³: TypeScript ì¤ì¹ & ì¤ì
pnpm add -D typescript @types/react @types/react-dom @types/node
npx tsc --init
2ë¨ê³: íì¼ íì¥ì ë³ê²½
.jsâ.ts(ë¡ì§ íì¼).jsxâ.tsx(JSX í¬í¨ íì¼)
3ë¨ê³: ì ì§ì íì ì¶ê°
// tsconfig.json â ëì¨íê² ìì
{
"compilerOptions": {
"strict": false, // ì ì§ì ì¼ë¡ trueë¡ ì í
"allowJs": true, // JS íì¼ íì©
"noImplicitAny": false // ëì¤ì trueë¡
}
}
4ë¨ê³: strict mode ìì°¨ íì±í
noImplicitAny: truestrictNullChecks: truestrict: true
CRA â Vite
ë³í í¬ì¸í¸
| CRA | Vite |
|---|---|
react-scripts |
vite + @vitejs/plugin-react |
public/index.html |
index.html (루í¸) |
REACT_APP_* íê²½ë³ì |
VITE_* íê²½ë³ì |
process.env.REACT_APP_* |
import.meta.env.VITE_* |
src/setupTests.ts |
vitest.config.ts + setup |
Jest â Vitest
ë³í í¬ì¸í¸
| Jest | Vitest |
|---|---|
jest.config.js |
vitest.config.ts |
jest.fn() |
vi.fn() |
jest.mock() |
vi.mock() |
jest.spyOn() |
vi.spyOn() |
@jest/globals |
vitest |
jest.useFakeTimers() |
vi.useFakeTimers() |
moduleNameMapper |
resolve.alias in vite config |
// vitest.config.ts
import { defineConfig } from "vitest/config";
import react from "@vitejs/plugin-react";
import path from "path";
export default defineConfig({
plugins: [react()],
test: {
globals: true,
environment: "jsdom",
setupFiles: "./src/test/setup.ts",
css: true,
},
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
});
Redux â Zustand
ë³í í¨í´
// Before: Redux Toolkit
const userSlice = createSlice({
name: "user",
initialState: { name: "", email: "" },
reducers: {
setUser: (state, action) => { Object.assign(state, action.payload); },
clearUser: () => ({ name: "", email: "" }),
},
});
// After: Zustand
interface UserState {
name: string;
email: string;
setUser: (user: { name: string; email: string }) => void;
clearUser: () => void;
}
const useUserStore = create<UserState>()((set) => ({
name: "",
email: "",
setUser: (user) => set(user),
clearUser: () => set({ name: "", email: "" }),
}));
ì¤í ê·ì¹
- ì¸ìê° ìì¼ë©´ ì¬ì©ììê² ë§ì´ê·¸ë ì´ì ì¢ ë¥ë¥¼ ì§ë¬¸íë¤
- ë§ì´ê·¸ë ì´ì ì íì¬ íë¡ì í¸ ìí를 ë°ëì ë¶ìíë¤
- ëê·ëª¨ ë³ê²½ì ë¨ê³ë³ë¡ ëë ì ì§ííê³ , ê° ë¨ê³ í ë¹ë/í ì¤í¸ë¥¼ íì¸íë¤
- 기존 ì½ë를 ìì í기 ì ì ì ì½ëê° ëìíëì§ íì¸íë¤
package.jsonë³ê²½ì´ íìíë©´ ëª ë ¹ì´ë¥¼ ìë´íê³ ì¬ì©ì ì¹ì¸ í ì¤ííë¤- ì§ì 목ë¡ì ìë ë§ì´ê·¸ë ì´ì ë ìì² ì ë¶ì í ê°ì´ë를 ì ê³µíë¤