React + Vite + TypeScript + React router项目搭建教程

一、创建项目

运行项目

二、目录结构

项目目录:
├─node_modules		//第三方依赖
├─public			//静态资源(不参与打包)
└─src
    ├─assets		//静态资源
    ├─components	//组件
    ├─config		//配置
    ├─http			//请求方法封装
    ├─layout		//页面布局
    ├─pages			//页面
    ├─routes		//路由
    ├─service		//请求
    ├─store			//状态管理
    └─util			//通用方法
    └─App.css
    └─App.tsx
    └─index.css
    └─main.tsx
    └─vite-env.d.ts
├─.eslinttrc.cjs
├─.gitignore		
├─index.html		//项目页面总入口
├─package.json	
├─tsconfig.json		//ts配置文件
├─tsconfig.node.json
├─vite.config.ts	//vite配置文件

三、sass安装

1 安装

npm i sass -D

2 创建全局 scss 文件

3 引入

这里的additionalData改为

additionalData: `@import "./src/assets/styles/index.scss";`

4 修改这4个文件

index.css去掉

main.tsx 中 import './index.css' 去掉

app.css 变成 app.scss,内容只剩下,h1{ color:$red; }

app.tsx 代码如下

import './App.scss'

function App() {
  return (
    <>
      <h1>Vite + React</h1>
    </>
  )
}

export default App

sass成功,页面如下

四、写一个函数式组件和类组件

import './App.scss'

// 函数式组件写法
// import React, { useState } from 'react';
// function Example() {
//   const [count, setCount] = useState(0);
//   return (
//     <div>
//       <p>You clicked {count} times</p>
//       <button onClick={() => setCount(count + 1)}>
//         Click me
//       </button>
//     </div>
//   );
// }

// 类组件写法
import React from 'react';
class Example extends React.Component<any, any> {
  constructor(props: any) {
    super(props);
    this.state = {
      count: 0
    }
  }

  render() {
    return (
      <div>
        <p>You clicked {this.state.count} times</p>
        <button onClick={() => this.setState({ count: this.state.count + 1 })}>
          Click me
        </button>
      </div>
    );
  }
}

function App() {
  return (
    <>
      <h1>Vite + React</h1>
      <Example></Example>
    </>
  )
}

export default App

五、路由

1 下载

npm install react-router-dom -S

新建3个页面

HashRouter路由(vue中的hash模式)

main.tsx

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import { HashRouter as Router } from 'react-router-dom';

ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
  <React.StrictMode>
    <Router>
      <App />
    </Router>
  </React.StrictMode>
)

app.tsx

import { Route, Routes, Link, useNavigate } from "react-router-dom";
import Login from "./pages/login";
import Home from "./pages/home";
import User from "./pages/user";
import './App.scss'

function App() {
  const navigate = useNavigate();
  return (
    <div className="App">
      {/* 指定跳转的组件,to 用来配置路由地址 */}
      <Link to="/">首页</Link><br />
      <Link to="/user">用户</Link><br />
      <button onClick={() => navigate('/login')}> 登录 </button>
      <hr />
      {/* 路由出口:路由对应的组件会在这里进行渲染 */}
      <Routes>
        {/* 指定路由路径和组件的对应关系:path 代表路径,element 代表对应的组件,它们成对出现 */}
        <Route path='/' element={<Home />}></Route>
        <Route path='/user' element={<User />}></Route>
        <Route path='/login' element={<Login />}></Route>
      </Routes>
    </div>
  )
}

export default App

成功结果如下

BrowserRouter路由(vue中的history模式?)

main.tsx

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.tsx'
import { BrowserRouter as Router } from 'react-router-dom';

ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
  <React.StrictMode>
    <Router>
      <App />
    </Router>
  </React.StrictMode>,
)

app.tsx

import { Route, Routes, Link, useNavigate } from "react-router-dom";
import Login from "./pages/login";
import Home from "./pages/home";
import User from "./pages/user";
import './App.scss'

function App() {
  const navigate = useNavigate();
  return (
    <div className="App">
      {/* 指定跳转的组件,to 用来配置路由地址 */}
      <Link to="/">首页</Link><br />
      <Link to="/user">用户</Link><br />
      <button onClick={() => navigate('/login')}> 登录 </button>
      <hr />
      {/* 路由出口:路由对应的组件会在这里进行渲染 */}
      <Routes>
        {/* 指定路由路径和组件的对应关系:path 代表路径,element 代表对应的组件,它们成对出现 */}
        <Route path='/' element={<Home />}></Route>
        <Route path='/user' element={<User />}></Route>
        <Route path='/login' element={<Login />}></Route>
      </Routes>
    </div>
  )
}

export default App

嵌套路由

app.tsx

// 嵌套路由
import { Route, Routes, Link, useNavigate } from 'react-router-dom';
import Login from "./pages/login";
import Home from "./pages/home";
import User from "./pages/user";
import './App.scss'
function App() {
  const navigate = useNavigate();
  return (
    <div className="App">
      {/* 指定跳转的组件,to 用来配置路由地址 */}
      <Link to="/home">首页</Link><br />
      <Link to="/home/user">用户</Link><br />
      <button onClick={() => navigate('/home/login')}> 登录 </button>
      {/* 路由出口:路由对应的组件会在这里进行渲染 */}
      <Routes>
        {/* 指定路由路径和组件的对应关系:path 代表路径,element 代表对应的组件,它们成对出现 */}
        <Route path='/home' element={<Home />}>
          <Route path='user' element={<User />}></Route>
          <Route path='login' element={<Login />}></Route>
        </Route>
      </Routes>
    </div>
  )
}
export default App

home.tsx

import { Outlet } from "react-router-dom";
function Home() {
    return (
        <div>
            <div>home页面</div>
            <Outlet />
        </div>
    );
}

export default Home;

页面如下

重定向

import { Navigate } from 'react-router-dom';
<Route path='/' element={<Navigate to="/layout" />}></Route>

useRoutes路由配置

1 app.scss

h1{
  color:$red;
}

body{
  padding: 0;
  margin: 0;
  width: 100vw;
  height: 100vh;
}
#root{
  padding: 0;
  margin: 0;
  width: 100vw;
  height: 100vh;
}

app.tsx

import GetRoutes from "./routes/index";
import './App.scss'

function App() {
  return (
    <GetRoutes></GetRoutes>
  )
}
export default App

home.tsx

function Home() {
    return (
        <div>home页面</div>
    );
}
export default Home;

新增文件

routes/index.tsx 代码如下

import { useRoutes, Navigate, RouteObject } from "react-router-dom";

import Layout from "../layout/index";
import Login from "../pages/login";
import Home from "../pages/home";
import User from "../pages/user";

export const router_item: Array<object> = [
    { path: "/", label: "首页", element: <Navigate to="/layout/home" /> },
    {
        path: "/layout",
        label: "控制台",
        element: <Layout />,
        children: [
            {
                path: "home",
                label: "首页",
                element: <Home />
            },
            {
                path: "login",
                label: "登录页",
                element: <Login />,
            },
            {
                path: "user",
                label: "用户页",
                element: <User />
            },
        ],
    },
];

function GetRoutes() {
    const routes: RouteObject[] = useRoutes(router_item);
    return routes;
}

export default GetRoutes;

layout/index.tsx 代码如下

import { Outlet, Link } from "react-router-dom";
function Layout() {
    return (
        <>
            <div style={{display: 'flex', width: '100%', height: '100%'}}>
                <div style={{width: '200px', background: '#eee'}}>
                    <Link to="/layout/home">home 页</Link><br />
                    <Link to="/layout/login">login 页</Link><br />
                    <Link to="/layout/user">user 页</Link><br />
                </div>
                <div style={{flex: '1'}}>
                    <Outlet />
                </div>
            </div>
        </>
    );
}
export default Layout;

页面成功如下

路由懒加载

有些页面比较大,我们可以使用懒加载,来提升页面加载性能,避免页面卡顿;react官网提供了路由懒加载的完整实例:react路由懒加载。懒加载主要借助lazy、suspense组件来实现。

lazy 能够让你在组件第一次被渲染之前延迟加载组件的代码。<Suspense> 允许您显示临时组件(一般是一个loading状态),直到其子项完成加载。

修改 routes/index.tsx 代码如下

import { useRoutes, Navigate, RouteObject } from "react-router-dom";

import Layout from "../layout/index";
import Login from "../pages/login";
import Home from "../pages/home";
import User from "../pages/user";

import { lazy } from "react";
import lazyLoad from "./lazyLoad";
// 添加一个固定的延迟时间,以便你可以看到加载状态
function delayForDemo(promise: Promise<any>) {
    return new Promise(resolve => {
        setTimeout(resolve, 2000);
    }).then(() => promise);
}

export const router_item: Array<object> = [
    { path: "/", label: "首页", element: <Navigate to="/layout/home" /> },
    {
        path: "/layout",
        label: "控制台",
        element: <Layout />,
        children: [
            {
                path: "home",
                label: "首页",
                // element: <Home />
                element: lazyLoad(lazy(() => delayForDemo(import("../pages/home")))) //故意延迟2s,这里是延迟加载
            },
            {
                path: "login",
                label: "登录页",
                // element: <Login />,
                element: lazyLoad(lazy(() => import("../pages/login"))),	//这里是延迟加载
            },
            {
                path: "user",
                label: "用户页",
                element: <User />
            },
        ],
    },
];

function GetRoutes() {
    const routes: RouteObject[] = useRoutes(router_item);
    return routes;
}

export default GetRoutes;

增加文件

lazyLoad.tsx 代码如下

import { LazyExoticComponent, Suspense } from "react";
import Spinner from "../components/spinner";
/**
 * 实现路由懒加载
 * @param Comp 懒加载组件
 * @returns 
 */
function lazyLoad(Comp: LazyExoticComponent<() => JSX.Element>) {
    return (
        <Suspense fallback={<Spinner />}>
            <Comp />
        </Suspense>
    );
}

export default lazyLoad;

spinner.tsx 代码如下

function Spinner() {
    return (
        <>
            loading...
        </>
    );
}

export default Spinner;
相关推荐
高山我梦口香糖1 小时前
[react]searchParams转普通对象
开发语言·前端·javascript
m0_748235241 小时前
前端实现获取后端返回的文件流并下载
前端·状态模式
m0_748240252 小时前
前端如何检测用户登录状态是否过期
前端
black^sugar2 小时前
纯前端实现更新检测
开发语言·前端·javascript
寻找沙漠的人3 小时前
前端知识补充—CSS
前端·css
GISer_Jing3 小时前
2025前端面试热门题目——计算机网络篇
前端·计算机网络·面试
m0_748245523 小时前
吉利前端、AI面试
前端·面试·职场和发展
理想不理想v3 小时前
webpack最基础的配置
前端·webpack·node.js
pubuzhixing3 小时前
开源白板新方案:Plait 同时支持 Angular 和 React 啦!
前端·开源·github