代码仓库

gitee

创建项目

首先保证安装了node, 然后使用vite创建项目

vite

npm create vite react-learn
cd react-learn
npm i 

目录结构

一个完整的前端项目需要:

  • 状态管理
    在全局维护共有的状态(数据), 让页面组件之间共享数据, 我们使用pinia
  • 路由
    路由让页面之间可以进行跳转, 我们使用vue-router
  • 样式
    样式让页面更美观, 我们使用tailwindcss
  • 网络请求
    前端需要通过网络请求的方式和后端进行数据交互, 从而实现功能, 我们使用axios
├── dev-dist/               # 开发环境构建输出目录
├── node_modules/           # Node.js依赖包目录
├── public/                 # 静态资源目录,不会被构建工具处理
├── src/├── admin/                  # 存放后台管理页面├── api/                    # 接口请求逻辑├── assets/                 # 静态资源(图片、字体等)├── components/             # 公共组件├── includes/               # 包含文件,存放外部库├── lib/                    # 存放项目内的一些公共资源├── locales/                # 国际化语言包├── mocks/                  # 模拟数据├── pages/                  # 存放普通的页面组件├── router/                 # 路由配置├── store/                  # 状态管理(Pinia)├── styles/                 # 全局样式或CSS文件├── utils/                  # 工具函数├── App.jsx                 # 根组件├── main.js                 # 项目入口文件├── middleware.js           # 中间件逻辑(如路由守卫)└── settings.js             # 项目设置或配置文件
├── .gitignore              # Git忽略文件配置
├── index.html              # 项目入口HTML文件
├── package.json            # 项目配置及依赖声明
├── postcss.config.js       # PostCSS配置文件
├── README.md               # 项目说明文档
├── tailwind.config.js      # Tailwind CSS配置文件
├── vite.config.js          # Vite构建工具配置文件

配置

路径别名

配置一下路径别名, 用@/表示src/目录

# node的类型声明, 防止使用node的依赖后报错
pnpm i @types/node --save-dev
import {defineConfig} from 'vite'
import {join} from 'path';
import vue from '@vitejs/plugin-vue'// https://vite.dev/config/
export default defineConfig({plugins: [vue()],// 路径别名resolve: {alias: {'@':join(__dirname, 'src'),}}
})

PWA配置

# v1
pnpm i vite-plugin-pwa --save-dev
import {defineConfig} from 'vite'
import {join} from 'path';
import react from '@vitejs/plugin-react'
import {VitePWA} from "vite-plugin-pwa";// https://vite.dev/config/
export default defineConfig({plugins: [react(),VitePWA({registerType: 'autoUpdate',devOptions: {// 生成清单文件enabled: true},manifest: {name: "vue-quick-start",theme_color: '#ff5e3a',icons: [{src: 'assets/logo.png',size: '192x192',type: 'image/png'}]},workbox: {globPatterns: ['**/*.{js,css,html,png,jpg}']}})],resolve: {alias: {'@':join(__dirname, 'src'),}}
})

项目的设置

// settings.js
export default {routeMode: 'history', // 路由模式BaseURL: 'http://localhost:4000', // 后端请求地址timeout: 5000, // 请求超时时间
}

tailwind

使用tailwind3(如果你要用4的也可以, 不过两个安装有点区别)

pnpm install -D tailwindcss@3 postcss autoprefixer
pnpm dlx tailwindcss@3 init -p

tailwind.config.js

/** @type {import('tailwindcss').Config} */
export default {content: ["./index.html","./src/**/*.{vue,js,ts,jsx,tsx}",],theme: {extend: {},},plugins: [],
}

styles/base.css

@tailwind base;@layer base {h1 {@apply text-2xl;}h2 {@apply text-xl;}h3 {@apply text-lg;}h4 {@apply text-base;}h5 {@apply text-sm;}h6 {@apply text-xs;}
}@tailwind components;
@tailwind utilities;body {@apply h-full w-full p-0 m-0;
}/* 覆盖默认的最大宽度限制 */
@media (min-width: 1536px) {.container {max-width: 100%;}
}/* 页面主内容 */
.container {width: 100%;height: 100%;background-color: #f5f7fb;padding: 20px;.container-wrapper {width: 100%;height: 100%;padding: 15px 30px 0;background-color: #fff;border-radius: 8px;display: flex;flex-direction: column;overflow: hidden;}
}/* 设置打印控件样式 */
.plugin-download {width: 500px !important;a:hover {text-decoration: underline;}
}

记得在src/main.js引入

import {StrictMode} from 'react'
import {createRoot} from 'react-dom/client'
import App from './App.jsx'
import '@/styles/base.css'createRoot(document.getElementById('root')).render(<StrictMode><App/></StrictMode>,
)

路由 router

使用react-router-dom进行路由跳转, 并且我们会实现文件路由, 自动扫描目录下的page.jsx文件, 然后注册为路由,
有两个页面目录, 一个是admin, 一个是pages, admin目录是后台管理页面, pages目录是普通的页面组件

# v7
pnpm i react-router-dom@7

文件路由

原生方案

文件路由就是根据目录结构, 自动扫描并注册路由, 不需要我们一个一个手动声明注册
实现的关键是

import.meta.glob("xxx")

这是由vite提供的方法, 可以扫描获取文件, webpack也有类似的方法

require.context()

我们使用的是vite, 使用import.meta.glob就行, 现在来实现文件扫描功能

插件方案

使用vite-plugin-pages, 也可以实现文件路由注册

npm install -D vite-plugin-pages
npm install react-router react-router-dom

vite.config

import {defineConfig} from 'vite'
import react from '@vitejs/plugin-react'
import {VitePWA} from "vite-plugin-pwa";
import {join} from 'path';
import pages from 'vite-plugin-pages'// https://vite.dev/config/
export default defineConfig({plugins: [react(),////*pages({// 自定义配置// dir: 'src/pages',          // 路由组件目录extensions: ['jsx', 'tsx'], // 支持文件后缀// exclude: ['components'],  // 排除组件目录base: process.env.VITE_APP_BASE_URL}),*/pages({// 注册多个目录, 有不同的路由前缀dirs: [// basic{dir: 'src/pages', baseRoute: ''},// with custom file pattern{dir: 'src/admin/', baseRoute: 'admin'},],}),],resolve: {alias: {'@':join(__dirname, 'src'),}},
})

src/components/router-guard.jsx

import {matchPath, Route, Routes, useLocation, useNavigate, useRoutes} from "react-router-dom";
import {Suspense, useEffect} from "react";
import routes from "~react-pages";
import Login from "../pages/login.jsx";
import NotFount from "../pages/not-fount.jsx";const isAuthenticated = () => {return localStorage.getItem("token") !== null; // 示例逻辑
};// 全局路由组件
function RouterGuard() {const navigate = useNavigate();const location = useLocation();// 添加 requiresAuth 属性const toNeedAuth = (r) => {const route = {...r, requiresAuth: true};if (route.children) {route.children = route.children.map(c => toNeedAuth(c));}return route;}const authRoutes = routes.map(toNeedAuth)useEffect(() => {/*// 无法匹配嵌套的路由const currentRoute = authRoutes.find(route =>matchPath({path: route.path, end: true}, location.pathname));console.log(currentRoute)*/console.log(location.pathname)// 全局导航守卫逻辑/*if (currentRoute?.requiresAuth && !isAuthenticated()) {navigate("/login", {replace: true});}*/// 后续添加更多不需要身份验证的页面if (location.pathname !== '/login' && !isAuthenticated()) {navigate("/login", {replace: true});}}, [location, navigate]); // 监听路由变化return (<Suspense fallback={<p>Loading...</p>}>{useRoutes([...authRoutes,{path: 'login',element: <Login/>},{path: "*",element: <NotFount/>}])}</Suspense>);
}export default RouterGuard;

app.jsx

import RouterGuard from "./components/router-guard.jsx";function App() {return (<RouterGuard/>)
}export default App

main.jsx

import {StrictMode} from 'react'
import {createRoot} from 'react-dom/client'
import App from './App.jsx'
import '@/styles/base.css'
import {BrowserRouter} from "react-router-dom";createRoot(document.getElementById('root')).render(<StrictMode><BrowserRouter><App/></BrowserRouter></StrictMode>,
)

状态管理 store

之前学习react的时候会发现原生的组件传值会遇到很麻烦的情况, 比如兄弟/跨级传值, 而我们可以使用全局状态管理的方案了解决
这里我们使用zustand

pnpm i zustand

src/store/count/index.js

import {create} from 'zustand'export const useCountStore = create((set) => ({count: 0,increment: () => set((state) => ({count: state.count + 1})),reset: () => set({count: 0}),
}))

测试 /pages/index.jsx

import React from 'react';
import {useCountStore} from "../store/count/index.js";const Index = () => {const {count, increment, reset} = useCountStore()localStorage.setItem("token", "123")return (<div className={`flex items-center flex-col`}><div className={``}>{count}</div><div className={`flex gap-x-4`}><buttonclassName={`bg-blue-400 py-1 px-2 rounded-lg`}onClick={() => increment()}>+1</button><buttonclassName={`bg-blue-400 py-1 px-2 rounded-lg`}onClick={() => reset()}>reset</button></div></div>);
};export default Index;

网络请求 api

使用axios进行网络请求, 一般情况下, 前后端是分人分组开发的, 前端请求接口地址是后端提供的
如果你此时后端还没有接口, 就需要自己用假数据或者用json-server来模拟测试

# v1
pnpm i axios

封装

// request.js
import axios from 'axios'
import settings from "../settings.js";const request = axios.create({baseURL: settings.BaseURL, // 设置基础URLtimeout: settings.timeout, // 请求超时时间
})// 请求拦截器
request.interceptors.request.use((config) => {// 在发送请求之前做些什么,例如添加 tokenconst token = localStorage.getItem('token')if (token) {config.headers['Authorization'] = `Bearer ${token}`}return config},(error) => {// 对请求错误做些什么return Promise.reject(error)}
)// 响应拦截器
request.interceptors.response.use((response) => {// 对响应数据做处理return response.data},(error) => {// 对响应错误做处理return Promise.reject(error)}
)export default request

测试

/** @Author Malred · Wang* @Date 2025-06-23 16:42:29* @Description * @Path src/pages/login.jsx*/
import React, {useState} from 'react';
import auth from '@/api/auth/index.js'
import {useNavigate} from "react-router-dom";const Login = () => {const navigate = useNavigate();const [username, setUsername] = useState('')const [password, setPassword] = useState('')return (<div className={`flex items-center flex-col`}><h1>login</h1><formonSubmit={async (e) => {e.preventDefault(); // 阻止默认提交行为const res = await auth.login({username, password})localStorage.setItem('token', res.token)navigate('/')}}className={`flex flex-col gap-y-2`}>账号<input className={`px-2 py-1 border rounded-md`} type="text" value={username}onChange={(e) => setUsername(e.target.value)}/>密码<input className={`px-2 py-1 border rounded-md`} type="password" value={password}onChange={(e) => setPassword(e.target.value)}/><buttontype={"submit"}className={`text-white bg-blue-400 py-1 px-2 rounded-lg`}>登录</button></form></div>);
};export default Login;

后端服务可以参考我之前的后端web单体教学代码模板:
rust-web-starter
go-web-starter

下一步

  • json-server模拟接口
  • 代码生成器, 批量生成重复页面和代码

社群

你可以在这些平台联系我:

  • bili: 刚子哥forever
  • 企鹅群: 940263820
  • gitee: gitee
  • 博客: malcode-site
  • 邮箱: malguy2022@163.com
  • 知乎: 乐妙善哉居士
  • csdn: 飞鸟malred

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/86717.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/86717.shtml
英文地址,请注明出处:http://en.pswp.cn/web/86717.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

scrapy+django+pyecharts+mysql 实现西安游客行为分析系统大屏_用户画像_空间分析_路线智能推荐

项目地址 link 此处展示部分结果 思路 描述性统计 可视化大屏 用户画像&#xff08;聚类&#xff0c;情感分析&#xff09; 空间分析

AC-DC-AC间接变频电源设计方案(工频50Hz→20KHz)

AC-DC-AC间接变频电源设计方案(工频50Hz→20KHz) 一、方案分析与选择 1. 可选电路结构分析 方案1:二极管整流+Boost PFC+全桥逆变 优点: 输入功率因数高(>0.99)直流电压稳定可控输出波形质量好缺点: 电路复杂度较高成本相对较高方案2:晶闸管相控整流+电容滤波+半…

七天学完十大机器学习经典算法-09.梯度提升算法:预测艺术的精进之道

接上一篇《七天学完十大机器学习经典算法-08.K均值聚类&#xff1a;无监督学习的万能分箱术》 想象你在教一个学生解决复杂数学题&#xff1a;先让他做基础题&#xff0c;然后针对错误部分强化练习&#xff0c;再针对新错误继续训练...如此反复精进&#xff0c;直到完美掌握——…

数据库(MYsql)

一、Mysql概述 数据库&#xff1a;存储数据的仓库 &#xff0c;数据是有组织的进行存储 数据库管理系统&#xff1a;操纵和管理数据库的大型软件&#xff08;BBMS&#xff09; SQL&#xff1a;定义了一套操作关系型数据库统一标准&#xff0c;操作关系型数据库的编程语言 数…

【SpringBoot】Spring Boot + RESTful 技术实战指南

在当今的软件开发领域&#xff0c;Spring Boot 与 RESTful API 的结合已成为构建高效、可扩展 Web 应用的标配。本文将通过一个完整的项目示例&#xff0c;从知识铺垫到部署上线&#xff0c;带你一步步掌握 Spring Boot RESTful 的开发流程。 一、知识铺垫 1.1 Spring Boot …

安卓中静态和动态添加子 View 到容器

1.静态添加子View 在XML布局文件中直接定义子View&#xff1a; <!-- activity_main.xml --> <LinearLayoutxmlns:android"http://schemas.android.com/apk/res/android"android:id"id/container"android:layout_width"match_parent"a…

【NLP】自然语言项目设计03

目录 03模型构建 代码架构核心设计说明 初步构建模型并进行训练时遇到的一些问题 问题一&#xff1a;模型欠拟合 使用1 model - lstm 解释使用lstm时无法正常的进行cudnn加速 使用2 model - transformer 项目简介 训练一个模型&#xff0c;实现歌词仿写生成 任务类型&am…

WebRTC(十二):DTLS

在 WebRTC 中的作用 DTLS&#xff08;Datagram Transport Layer Security&#xff09;是 TLS 的 UDP 版本&#xff0c;在 WebRTC 中用于&#xff1a; 安全协商加密密钥对等验证&#xff08;基于 X.509 证书 fingerprint&#xff09;为 SRTP/SRTCP 提供密钥材料 WebRTC 不直接…

北大肖臻《区块链技术与应用》学习笔记

区块链学习笔记 \huge{区块链学习笔记} 区块链学习笔记 这是关于北京大学肖臻老师的《区块链技术与应用》课程的学习笔记。 BTC的数据结构 hash pointers&#xff1a;既保存结构体的对应地址位置&#xff08;指针&#xff09;&#xff0c;又保存结构体对应映射的hash值&#…

MongoDB 驱动升级性能测试报告

测试背景 将 MongoDB Java 驱动从 4.11.5 升级至 5.5.1&#xff0c;并配合 Reactor Core 3.8.0-M4 进行性能对比测试。测试主要围绕插入、查询、更新和删除四个核心操作进行。 环境配置 操作系统: Windows 11CPU: Intel Core™ i7-14700F, 28 核心, 2.10 GHzJDK: OpenJDK 21.…

淘宝商品评论实时采集 API 接入指南:从零开始实战开发

在电商数据分析领域&#xff0c;商品评论数据蕴含着用户对产品的真实反馈&#xff0c;对商家优化产品、提升服务质量具有重要价值。本文将详细介绍如何接入淘宝 API&#xff0c;实现商品评论的实时采集&#xff0c;从环境搭建到代码实现进行全流程讲解。 1. 淘宝api概述 淘宝…

ffpaly播放 g711a音频命令

ffpaly播放 g711a音频命令 ffplay 播放 G.711 A-law (8kHz, mono, 16bit) 音频的命令&#xff1a; ffplay -f alaw -ar 8000 -ac 1 input.g711a 或ffplay -f alaw -ar 8000 -ac 1 audio_chn0.g711a 各参数说明&#xff1a; -f alaw&#xff1a;指定输入音频格式为 G.711 A-law…

composer全局配置

composer配置 composer查看全局配置 composer config -l -gcomposer 更新慢 composer下载不下来问题解决 更换composer镜像源&#xff0c;可以执行尝试以下几种&#xff1a; 1、更换成阿里镜像&#xff1a; composer config -g repo.packagist composer https://mirrors.al…

ivx创建一个测试小案例

文章目录 前端后端提交信息服务提交信息事件跳转列表页事件下载事件详情页事件 https://editor.ivx.cn/ 主题选择一下 前端 在前台新建一个页面名为提交页&#xff0c;内边距左和内边距右都设置为40&#xff0c;水平居中和垂直居中设置一下&#xff1b; 新建两个输入框&#x…

【MongoDB】MongoDB从零开始详细教程 核心概念与原理 环境搭建 基础操作

MongoDB从零开始详细教程 核心概念与原理 环境搭建 基础操作 一、核心概念与原理1. 核心组件2. MongoDB vs 关系型数据库 二、环境搭建&#xff08;Windows/Linux/CentOS&#xff09;1. Windows安装2. CentOS安装3. 连接验证 三、基础操作&#xff08;CRUD&#xff09;1. 数据库…

GeoTools 结合 OpenLayers 实现属性查询

前言 在GIS开发中&#xff0c;属性查询是非常普遍的操作&#xff0c;这是每一个GISer都要掌握的必备技能。实现高效的数据查询功能可以提升用户体验&#xff0c;完成数据的快速可视化表达。 本篇教程在之前一系列文章的基础上讲解如何将使用GeoTools工具结合OpenLayers实现Post…

vue-27(实践练习:将现有组件重构为使用组合式 API)

实践练习:将现有组件重构为使用组合式 API 理解重构过程 重构是任何开发者的关键技能,尤其是在采用新范式如 Vue.js 中的 Composition API 时。它涉及在不改变外部行为的情况下重新组织现有代码,旨在提高可读性、可维护性和可重用性。在从 Options API 迁移到 Composition…

基于Uniapp+SpringBoot+Vue 的在线商城小程序

开发系统:Windows10 架构模式:MVC/前后端分离 JDK版本: Java JDK1.8 开发工具:IDEA 数据库版本: mysql8.0 数据库可视化工具: navicat 服务器: SpringBoot自带 apache tomcat 主要技术: Java,Springboot,mybatis,mysql,jquery,html,vue 角色:用户 商家 管理员 用户菜单:首页:商…

华为云Flexus+DeepSeek征文|利用华为云一键部署的Dify平台构建高效智能电商客服系统实战

目录 前言 1 华为云快速搭建 Dify-LLM 应用平台 1.1 一键部署简介 1.2 设置管理员账号登录dify平台 2 接入 DeepSeek 大模型与 Reranker 模型 2.1 接入自定义 LLM 模型 2.2 设置 Reranker 模型 3 构建电商知识库 3.1 数据源选择 3.2 分段设置与清洗 3.3 处理并完成 …

python应用day07---pyechars模块详解

1.pyecharts安装: pip install pyecharts 2.pyecharts入门: # 1.导入模块 from pyecharts.charts import Line# 2.创建Line对象 line Line() # 添加数据 line.add_xaxis(["中国", "美国", "印度"]) line.add_yaxis("GDP数据", [30…