Skip to content

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的核心优势

  1. 极速的冷启动
bash
# 传统工具(Webpack)
启动时间:10-30秒

# Vite
启动时间:<1秒
  1. 即时的热更新
bash
# 传统工具
修改代码 重新打包 刷新页面(2-5秒)

# Vite
修改代码 精确热更新(<50ms)
  1. 原生ESM支持
javascript
// 开发环境直接使用原生ES模块
import { useState } from 'react';
import Button from './Button.jsx';

// 浏览器原生解析,无需打包
  1. 优化的生产构建
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-ts

2.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 App

package.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的核心特性

  1. 文件系统路由
app/
  page.js           → /
  about/
    page.js         → /about
  blog/
    [slug]/
      page.js       → /blog/:slug
  1. Server Components
javascript
// 默认是Server Component
async function Page() {
  const data = await fetch('https://api.example.com/data')
  return <div>{data.title}</div>
}
  1. Server Actions
javascript
async function createUser(formData) {
  'use server'
  
  const name = formData.get('name')
  await db.user.create({ name })
}
  1. 内置优化
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:3000

3.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 = nextConfig

3.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开发环境搭建:

  1. 工具选择:Vite适合SPA,Next.js适合全栈
  2. Vite搭建:极速开发体验,简单灵活
  3. Next.js搭建:全栈框架,功能丰富
  4. 配置详解:深入理解各种配置选项
  5. 最佳实践:项目结构、环境变量、插件使用

选择建议:

  • 学习React基础 → Vite
  • 需要SEO → Next.js
  • 纯前端应用 → Vite
  • 全栈应用 → Next.js

下一章我们将创建第一个React应用,开始实战开发。