Appearance
React环境搭建:Vite与Next.js完全指南
第一章:React开发环境概述
1.1 现代React开发工具链
在React 19时代,开发环境的选择比以往任何时候都更加重要。一个好的开发环境可以:
- 提供快速的开发反馈(毫秒级热更新)
- 自动优化生产构建
- 提供完善的开发工具
- 支持最新的JavaScript特性
- 简化配置和部署
工具链的演进
2016年:Create React App
- 零配置启动React项目
- 隐藏复杂的Webpack配置
- 适合学习和小型项目
2020年:Next.js普及
- 全栈React框架
- 内置SSR和SSG
- 文件系统路由
2021年:Vite兴起
- 极速的开发服务器
- 基于ESM的原生开发
- Rollup生产构建
2024年:React 19时代
- Vite成为主流(快速、现代)
- Next.js 15支持Server Components
- 两者都是官方推荐工具1.2 Vite vs Next.js:如何选择
选择Vite的场景
适用项目:
- 单页应用(SPA)
- 客户端渲染应用
- 需要极速开发体验
- 灵活的架构需求
- 学习React基础
优势:
- 启动速度极快(<1秒)
- 热更新速度快(<50ms)
- 简单易学
- 配置灵活
- 插件生态丰富
劣势:
- 需要自己配置路由
- 需要自己实现SSR(如果需要)
- SEO需要额外配置选择Next.js的场景
适用项目:
- 需要SEO的网站
- 电商平台
- 博客、内容网站
- 全栈应用
- 企业级应用
优势:
- 内置路由系统
- 原生SSR/SSG支持
- Server Components
- API Routes
- 图片优化
- 部署简单
劣势:
- 学习曲线较陡
- 约定大于配置
- 灵活性相对较低快速决策表
问题1:需要SEO吗?
是 → Next.js
否 → 继续
问题2:需要服务端渲染吗?
是 → Next.js
否 → 继续
问题3:是纯前端应用吗?
是 → Vite
否 → Next.js
问题4:刚开始学React?
是 → Vite(更简单)
否 → 根据项目需求选择第二章:使用Vite搭建React环境
2.1 Vite简介
Vite(法语意为"快速")是由Vue.js作者尤雨溪创建的下一代前端构建工具。
Vite的核心优势
- 极速的冷启动
bash
# 传统工具(Webpack)
启动时间:10-30秒
# Vite
启动时间:<1秒- 即时的热更新
bash
# 传统工具
修改代码 → 重新打包 → 刷新页面(2-5秒)
# Vite
修改代码 → 精确热更新(<50ms)- 原生ESM支持
javascript
// 开发环境直接使用原生ES模块
import { useState } from 'react';
import Button from './Button.jsx';
// 浏览器原生解析,无需打包- 优化的生产构建
bash
# 使用Rollup打包
- Tree-shaking
- 代码分割
- 资源压缩
- 现代浏览器优化2.2 创建Vite + React项目
方法1:使用npm
bash
# 创建项目(推荐)
npm create vite@latest my-react-app -- --template react
# 进入项目目录
cd my-react-app
# 安装依赖
npm install
# 启动开发服务器
npm run dev
# 输出:
# VITE v5.0.0 ready in 245 ms
# ➜ Local: http://localhost:5173/
# ➜ Network: use --host to expose方法2:使用yarn
bash
yarn create vite my-react-app --template react
cd my-react-app
yarn
yarn dev方法3:使用pnpm(推荐,速度最快)
bash
pnpm create vite my-react-app --template react
cd my-react-app
pnpm install
pnpm dev可用的React模板
bash
# JavaScript + React
npm create vite@latest -- --template react
# TypeScript + React(推荐)
npm create vite@latest -- --template react-ts
# React + SWC(更快的编译器)
npm create vite@latest -- --template react-swc
npm create vite@latest -- --template react-swc-ts2.3 Vite项目结构详解
默认项目结构
my-react-app/
├── node_modules/ # 依赖包
├── public/ # 静态资源
│ └── vite.svg # 静态文件(不会被处理)
├── src/ # 源代码
│ ├── assets/ # 资源文件(会被处理)
│ │ └── react.svg
│ ├── App.css # 组件样式
│ ├── App.jsx # 根组件
│ ├── index.css # 全局样式
│ └── main.jsx # 入口文件
├── .gitignore # Git忽略文件
├── index.html # HTML入口
├── package.json # 项目配置
├── vite.config.js # Vite配置
└── README.md # 说明文档关键文件详解
index.html(入口HTML)
html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React</title>
</head>
<body>
<!-- React根节点 -->
<div id="root"></div>
<!-- 入口脚本 - 注意type="module" -->
<script type="module" src="/src/main.jsx"></script>
</body>
</html>重点说明:
- Vite将index.html作为入口(不是在public文件夹)
type="module"表示使用ES模块/src/main.jsx会被Vite处理
src/main.jsx(应用入口)
javascript
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
import './index.css'
// React 18+的渲染方式
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<App />
</React.StrictMode>,
)关键点:
- 使用
createRoot(React 18+新API) StrictMode帮助发现潜在问题- 导入CSS文件会被Vite处理
src/App.jsx(根组件)
javascript
import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'
function App() {
const [count, setCount] = useState(0)
return (
<>
<div>
<a href="https://vitejs.dev" target="_blank">
<img src={viteLogo} className="logo" alt="Vite logo" />
</a>
<a href="https://react.dev" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
</div>
<h1>Vite + React</h1>
<div className="card">
<button onClick={() => setCount((count) => count + 1)}>
count is {count}
</button>
<p>
Edit <code>src/App.jsx</code> and save to test HMR
</p>
</div>
<p className="read-the-docs">
Click on the Vite and React logos to learn more
</p>
</>
)
}
export default Apppackage.json(项目配置)
json
{
"name": "my-react-app",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"devDependencies": {
"@types/react": "^18.2.43",
"@types/react-dom": "^18.2.17",
"@vitejs/plugin-react": "^4.2.1",
"vite": "^5.0.0"
}
}脚本说明:
npm run dev:启动开发服务器npm run build:构建生产版本npm run preview:预览生产构建
vite.config.js(Vite配置)
javascript
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
})2.4 Vite配置详解
基础配置
javascript
// vite.config.js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import path from 'path'
export default defineConfig({
// 插件
plugins: [react()],
// 开发服务器配置
server: {
port: 3000, // 端口号
open: true, // 自动打开浏览器
host: true, // 监听所有地址
cors: true, // 启用CORS
proxy: { // 代理配置
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
},
// 构建配置
build: {
outDir: 'dist', // 输出目录
sourcemap: false, // 是否生成sourcemap
minify: 'terser', // 压缩方式
chunkSizeWarningLimit: 1000, // chunk大小警告限制
rollupOptions: {
output: {
// 分包策略
manualChunks: {
vendor: ['react', 'react-dom'],
router: ['react-router-dom']
}
}
}
},
// 路径别名
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
'@components': path.resolve(__dirname, './src/components'),
'@utils': path.resolve(__dirname, './src/utils')
}
},
// CSS配置
css: {
preprocessorOptions: {
scss: {
additionalData: `@import "@/styles/variables.scss";`
}
},
modules: {
localsConvention: 'camelCase' // CSS模块命名转换
}
}
})环境变量配置
创建环境变量文件:
bash
# .env(所有环境)
VITE_APP_TITLE=My React App
# .env.development(开发环境)
VITE_API_URL=http://localhost:8080/api
# .env.production(生产环境)
VITE_API_URL=https://api.production.com使用环境变量:
javascript
// 在代码中使用
const apiUrl = import.meta.env.VITE_API_URL
const appTitle = import.meta.env.VITE_APP_TITLE
console.log('API URL:', apiUrl)
console.log('Mode:', import.meta.env.MODE) // development 或 production
console.log('Is Dev:', import.meta.env.DEV) // 是否开发环境
console.log('Is Prod:', import.meta.env.PROD) // 是否生产环境注意:
- 环境变量必须以
VITE_开头才能暴露给客户端 - 使用
import.meta.env而不是process.env
路径别名使用
javascript
// 配置后可以使用别名导入
import Button from '@/components/Button'
import { formatDate } from '@/utils/date'
import Header from '@components/Header'
// 而不是相对路径
import Button from '../../components/Button'
import { formatDate } from '../../../utils/date'TypeScript支持(tsconfig.json):
json
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@components/*": ["src/components/*"],
"@utils/*": ["src/utils/*"]
}
}
}2.5 Vite插件生态
常用Vite插件
bash
# 安装常用插件
npm install -D vite-plugin-svgr # SVG作为React组件
npm install -D vite-plugin-compression # Gzip压缩
npm install -D vite-plugin-pwa # PWA支持
npm install -D vite-plugin-checker # TypeScript检查配置示例:
javascript
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import svgr from 'vite-plugin-svgr'
import compression from 'vite-plugin-compression'
import { VitePWA } from 'vite-plugin-pwa'
import checker from 'vite-plugin-checker'
export default defineConfig({
plugins: [
react(),
// SVG作为组件导入
svgr(),
// Gzip压缩
compression({
algorithm: 'gzip',
ext: '.gz'
}),
// PWA
VitePWA({
registerType: 'autoUpdate',
manifest: {
name: 'My React App',
short_name: 'ReactApp',
theme_color: '#ffffff'
}
}),
// TypeScript类型检查
checker({
typescript: true,
eslint: {
lintCommand: 'eslint "./src/**/*.{ts,tsx}"'
}
})
]
})2.6 Vite开发技巧
快速刷新(Fast Refresh)
javascript
// Vite自动支持React Fast Refresh
// 修改组件代码时保持状态
function Counter() {
const [count, setCount] = useState(0)
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
</div>
)
}
// 修改按钮文字,count状态保持不变导入静态资源
javascript
// 导入图片
import logo from './assets/logo.png'
<img src={logo} alt="Logo" />
// 导入为URL
import logoUrl from './assets/logo.png?url'
const img = new Image()
img.src = logoUrl
// 导入为字符串
import svg from './assets/icon.svg?raw'
<div dangerouslySetInnerHTML={{ __html: svg }} />
// SVG作为组件(需要vite-plugin-svgr)
import { ReactComponent as Logo } from './assets/logo.svg'
<Logo />
// JSON导入
import data from './data.json'
console.log(data)
// Web Worker
import Worker from './worker?worker'
const worker = new Worker()Glob导入
javascript
// 批量导入文件
const modules = import.meta.glob('./components/*.jsx')
// 动态导入
const modules = import.meta.glob('./components/*.jsx')
for (const path in modules) {
modules[path]().then((mod) => {
console.log(path, mod)
})
}
// 直接导入(同步)
const modules = import.meta.glob('./components/*.jsx', { eager: true })
// 只导入特定内容
const modules = import.meta.glob('./components/*.jsx', {
import: 'default'
})第三章:使用Next.js搭建React环境
3.1 Next.js简介
Next.js是Vercel开发的React全栈框架,提供了生产级的功能和优化。
Next.js的核心特性
- 文件系统路由
app/
page.js → /
about/
page.js → /about
blog/
[slug]/
page.js → /blog/:slug- Server Components
javascript
// 默认是Server Component
async function Page() {
const data = await fetch('https://api.example.com/data')
return <div>{data.title}</div>
}- Server Actions
javascript
async function createUser(formData) {
'use server'
const name = formData.get('name')
await db.user.create({ name })
}- 内置优化
javascript
// 自动图片优化
import Image from 'next/image'
<Image src="/photo.jpg" width={500} height={300} alt="Photo" />
// 自动字体优化
import { Inter } from 'next/font/google'
const inter = Inter({ subsets: ['latin'] })
// 自动代码分割
const DynamicComponent = dynamic(() => import('./Component'))3.2 创建Next.js项目
方法1:交互式创建(推荐)
bash
# 使用npx
npx create-next-app@latest
# 交互式问题:
# ✔ What is your project named? … my-next-app
# ✔ Would you like to use TypeScript? … No / Yes
# ✔ Would you like to use ESLint? … No / Yes
# ✔ Would you like to use Tailwind CSS? … No / Yes
# ✔ Would you like to use `src/` directory? … No / Yes
# ✔ Would you like to use App Router? (recommended) … No / Yes
# ✔ Would you like to customize the default import alias (@/*)? … No / Yes方法2:带参数创建
bash
# TypeScript + App Router + Tailwind
npx create-next-app@latest my-next-app --typescript --tailwind --app --src-dir
# 使用yarn
yarn create next-app
# 使用pnpm
pnpm create next-app启动项目
bash
cd my-next-app
# 安装依赖(如果未自动安装)
npm install
# 启动开发服务器
npm run dev
# 输出:
# ▲ Next.js 15.0.0
# - Local: http://localhost:3000
# - Network: http://192.168.1.100:30003.3 Next.js项目结构(App Router)
推荐项目结构
my-next-app/
├── app/ # App Router目录
│ ├── layout.js # 根布局
│ ├── page.js # 首页
│ ├── globals.css # 全局样式
│ ├── api/ # API路由
│ │ └── users/
│ │ └── route.js # GET /api/users
│ ├── blog/ # 博客路由
│ │ ├── page.js # /blog
│ │ └── [slug]/
│ │ └── page.js # /blog/:slug
│ └── dashboard/ # 仪表盘
│ ├── layout.js # dashboard布局
│ └── page.js # /dashboard
├── components/ # React组件
│ ├── Header.jsx
│ ├── Footer.jsx
│ └── ui/
│ ├── Button.jsx
│ └── Card.jsx
├── lib/ # 工具库
│ ├── db.js
│ └── utils.js
├── public/ # 静态文件
│ ├── images/
│ └── favicon.ico
├── styles/ # 样式文件
│ └── custom.css
├── .env.local # 环境变量
├── next.config.js # Next.js配置
├── package.json
├── tsconfig.json # TypeScript配置
└── tailwind.config.js # Tailwind配置关键文件详解
app/layout.js(根布局)
javascript
import { Inter } from 'next/font/google'
import './globals.css'
const inter = Inter({ subsets: ['latin'] })
export const metadata = {
title: 'My Next App',
description: 'Created with Next.js',
}
export default function RootLayout({ children }) {
return (
<html lang="en">
<body className={inter.className}>
<header>
<nav>Navigation</nav>
</header>
<main>{children}</main>
<footer>Footer</footer>
</body>
</html>
)
}关键点:
- 所有页面共享此布局
- 可以定义metadata(SEO)
- 可以嵌套布局
app/page.js(首页)
javascript
export default function Home() {
return (
<div>
<h1>Welcome to Next.js</h1>
<p>The React Framework for the Web</p>
</div>
)
}app/api/users/route.js(API路由)
javascript
import { NextResponse } from 'next/server'
// GET /api/users
export async function GET(request) {
const users = await db.user.findMany()
return NextResponse.json(users)
}
// POST /api/users
export async function POST(request) {
const body = await request.json()
const user = await db.user.create({
data: body
})
return NextResponse.json(user, { status: 201 })
}next.config.js(Next.js配置)
javascript
/** @type {import('next').NextConfig} */
const nextConfig = {
// 严格模式
reactStrictMode: true,
// 图片域名白名单
images: {
domains: ['example.com', 'cdn.example.com'],
},
// 重定向
async redirects() {
return [
{
source: '/old-path',
destination: '/new-path',
permanent: true,
},
]
},
// 环境变量
env: {
CUSTOM_KEY: 'value',
},
}
module.exports = nextConfig3.4 Next.js路由系统
基础路由
app/
page.js → /
about/page.js → /about
blog/page.js → /blog
blog/[slug]/page.js → /blog/:slug
blog/[...slug]/page.js → /blog/* (catch-all)代码示例:
javascript
// app/blog/[slug]/page.js
export default function BlogPost({ params }) {
return <h1>Post: {params.slug}</h1>
}
// 访问 /blog/hello → params.slug = "hello"动态路由
javascript
// app/blog/[slug]/page.js
export default function BlogPost({ params }) {
return <h1>Blog Post: {params.slug}</h1>
}
// app/shop/[category]/[id]/page.js
export default function Product({ params }) {
return (
<div>
<p>Category: {params.category}</p>
<p>ID: {params.id}</p>
</div>
)
}路由组
app/
(marketing)/ # 路由组(URL中不显示)
about/
page.js → /about
contact/
page.js → /contact
(shop)/
products/
page.js → /products并行路由
app/
@analytics/
page.js
@dashboard/
page.js
layout.js
// layout.js
export default function Layout({ analytics, dashboard }) {
return (
<div>
<div>{analytics}</div>
<div>{dashboard}</div>
</div>
)
}3.5 数据获取
Server Component数据获取
javascript
// app/blog/page.js
async function getPosts() {
const res = await fetch('https://api.example.com/posts', {
cache: 'no-store' // 动态数据
})
return res.json()
}
export default async function Blog() {
const posts = await getPosts()
return (
<ul>
{posts.map(post => (
<li key={post.id}>{post.title}</li>
))}
</ul>
)
}静态生成
javascript
// app/blog/[slug]/page.js
export async function generateStaticParams() {
const posts = await fetch('https://api.example.com/posts').then(r => r.json())
return posts.map(post => ({
slug: post.slug
}))
}
export default async function Post({ params }) {
const post = await fetch(`https://api.example.com/posts/${params.slug}`)
.then(r => r.json())
return <article>{post.content}</article>
}增量静态再生成(ISR)
javascript
// app/blog/page.js
async function getPosts() {
const res = await fetch('https://api.example.com/posts', {
next: { revalidate: 60 } // 60秒后重新验证
})
return res.json()
}3.6 Server Actions
javascript
// app/actions.js
'use server'
export async function createUser(formData) {
const name = formData.get('name')
const email = formData.get('email')
await db.user.create({
data: { name, email }
})
revalidatePath('/users')
}
// app/page.js
import { createUser } from './actions'
export default function Page() {
return (
<form action={createUser}>
<input name="name" />
<input name="email" type="email" />
<button type="submit">Create User</button>
</form>
)
}第四章:环境对比与选择
4.1 性能对比
启动速度:
Vite: 0.5-1秒
Next.js: 2-5秒
热更新:
Vite: <50ms
Next.js: 100-300ms
构建速度(中型项目):
Vite: 10-30秒
Next.js: 30-60秒
包体积(默认):
Vite: 140KB (React + ReactDOM)
Next.js: 250KB+ (包含框架代码)4.2 功能对比
路由:
Vite: 需要React Router
Next.js: 内置文件系统路由
SSR:
Vite: 需要自己实现
Next.js: 原生支持
SEO:
Vite: 需要额外配置
Next.js: 开箱即用
API路由:
Vite: 需要额外服务器
Next.js: 内置API Routes
图片优化:
Vite: 手动配置
Next.js: 自动优化
部署:
Vite: 静态托管
Next.js: Vercel一键部署总结
本章详细介绍了React开发环境搭建:
- 工具选择:Vite适合SPA,Next.js适合全栈
- Vite搭建:极速开发体验,简单灵活
- Next.js搭建:全栈框架,功能丰富
- 配置详解:深入理解各种配置选项
- 最佳实践:项目结构、环境变量、插件使用
选择建议:
- 学习React基础 → Vite
- 需要SEO → Next.js
- 纯前端应用 → Vite
- 全栈应用 → Next.js
下一章我们将创建第一个React应用,开始实战开发。