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;