当前位置: 首页 > article >正文

全球新闻系统发布 -- 项目启动环节

项目搭建和环境配置

和之前学的一样,创建一个新项目,科文老师用的是CRA,我这暂时用Vite,因为CRA我这有点容易卡掉

样式相互影响,可能会导致覆盖

可以把.css改成.module.css

加上class之后,需要加一个变量才可以使用:

.list{
    background-color: blueviolet;
}
import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'
import style from './child.module.css'

function App() {
  return <div>
    app
    <li className={style.list}></li>
    <li ></li>
    <li ></li>
    </div>
}

export default App

 这个猫叫选择器对类有效,标签是力不从心的

项目sass与反向代理

安装sass
npm i --save sass

在Vite中使用sass需要再安装sass-embedded,当项目中没有安装它时,无法正常的编译SCSS文件 ,所以需要安装一下:

npm install -D sass-embedded

然后可以使用语法编写.scss:

我承认我已经忘了啥语法了

首先是可以使用变量:

// 定义变量
$primary-color: #007bff;
$font-size: 16px;

// 使用变量
body {
  color: $primary-color;
  font-size: $font-size;
}

还可以在选择器内嵌套其他的选择器:

nav {
  ul {
    list-style: none;
    margin: 0;
    padding: 0;

    li {
      display: inline-block;
      margin-right: 10px;

      a {
        color: $primary-color;
        text-decoration: none;

        &:hover {
          text-decoration: underline;
        }
      }
    }
  }
}

 可以使用@import指令将多个SCSS合并成一个CSS文件

// _variables.scss 文件
$primary-color: #007bff;

// main.scss 文件
@import 'variables';

body {
  color: $primary-color;
}

tips:下划线开头的文件被视为部分文件,不会被单独编译成CSS文件

混合器(可以定义重复使用的代码样式块):

// 定义混合器
@mixin border-radius($radius) {
  -webkit-border-radius: $radius;
  -moz-border-radius: $radius;
  border-radius: $radius;
}

// 使用混合器
.button {
  @include border-radius(5px);
}

 继承:

.button-base {
  padding: 10px 20px;
  border: none;
  cursor: pointer;
}

.button-primary {
  @extend .button-base;
  background-color: $primary-color;
  color: white;
}

SCSS支持基本的数学运算:

$base-width: 100px;

.box {
  width: $base-width * 2;
  height: $base-width + 50px;
  margin: ($base-width / 2);
}

 还可以使用条件语句。。循环语句:

$type: 'alert';

.message {
  @if $type == 'success' {
    background-color: green;
  } @else if $type == 'warning' {
    background-color: yellow;
  } @else if $type == 'alert' {
    background-color: red;
  } @else {
    background-color: gray;
  }
}
$colors: red, blue, green;

@each $color in $colors {
  .bg-#{$color} {
    background-color: $color;
  }
}
@for $i from 1 through 3 {
  .item-#{$i} {
    width: 20px * $i;
  }
}

我的代码: 


$width:300px;
ul{
    .list{
        width: $width;
        background-color: blueviolet;
    }
}
import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'
import style from './child.module.scss'

function App() {
  return (
    <div>
      app
      <ul>
        <li className={style.list}></li>
        <li></li>
        <li></li>
      </ul>
      <li>111</li>
      <li>111</li>
    </div>
  )
}

export default App
安装axios
npm i axios

安装后在外面找一个没跨域问题的接口,比如猫眼

但是就算是支持跨域,也需要相应的响应字段(后端干的)

科文老师是自己配置的反向代理,然后通过拦截的方式到目标服务器

但是我用的是Vite所以改的文件可能有一些不一样,在尝试了headers里面添加cookie,继续配置更细致的代理,切换协议等方式均不能取得数据的时候我更换了一种策略:

也就是使用Express/Koa代理

首先是安装Express:

npm install express axios cors

第二步创建server.js:

import express from 'express';
import axios from 'axios';
import cors from 'cors';

const app = express();
app.use(cors());

app.get('/api/*', async (req, res) => {
  try {
    const url = `https://m.maoyan.com${req.originalUrl.replace('/api', '')}`;
    const response = await axios.get(url, {
      headers: {
        Referer: 'https://m.maoyan.com/',
        'User-Agent':
          'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36',
      },
    });
    res.json(response.data);
  } catch (error) {
    res.status(500).json({ error: '请求失败', details: error.message });
  }
});

app.listen(3001, () => console.log('代理服务器运行在 http://localhost:3001'));

 在上一次尝试中还出现了差错:packag.json里面有:

"type": "module"

导致node.js把server.js作为ES模块解析,而 require() 语法是 CommonJS(CJS) 的,不能直接使用。

所以改成了import语法

下一步就是启动代理服务器:

node server.js

以及修改Reactdiamante,启用本地的代理:

useEffect(() => {
  axios
    .get('http://localhost:3001/api/mmdb/movie/v3/list/hot.json?ct=西安&ci=42&channelId=4')
    .then((res) => {
      console.log(res.data);
    })
    .catch((error) => {
      console.error('请求出错:', error);
    });
}, []);

 这个和科文老师的代理的配置各有优缺点:

相当于是如果你只需要本地开发,Vite代理就够了

如果想要支持生产环境,需要修改请求头。处理CORS,则用Node.js代理更好

当使用server.js作为代理时,核心机制是中间人代理

前端请求 -> 代理服务器 -> 目标服务器

作用就是:拦截前端请求,然后代替前端去请求目标服务器,再把结果返回给前端(绕过CORS限制)

工作流程:

  1. 前端请求代理服务器http://localhost:3001/api/movies)。

  2. 代理服务器拦截请求

    • 解析 req,检查路径 /api/movies

    • 修改请求头(如 RefererUser-Agent)。

  3. 代理服务器向目标服务器发送请求

    • 请求 https://m.maoyan.com/mmdb/movie/v3/list/hot.json

  4. 代理服务器获取数据

    • 接收 m.maoyan.com 的数据,并返回给前端。

  5. 前端收到代理返回的数据

    • 以为是自己的后端返回的数据,不会触发 CORS 错误

核心作用:

  • 绕过 CORS 限制

    • 目标服务器 m.maoyan.com 不允许直接跨域访问。

    • 代理服务器不受跨域限制,它可以直接访问 m.maoyan.com,然后把数据返回给前端。

  • 隐藏 API Key / 修改请求头

    • 如果目标 API 需要特定的 RefererUser-Agent,代理可以伪装请求,让目标服务器以为请求来自合法客户端。

  • 屏蔽敏感信息

    • 你可以过滤、修改数据,避免泄露 API Key 或其他敏感信息。

  • 缓存 & 降低目标服务器压力

    • 代理服务器可以缓存热门请求,减少对 m.maoyan.com 的访问,提高性能。

CORS机制是一种浏览器安全机制,用来限制跨域请求,防止恶意网站访问你的数据

当前端代码尝试从不同源(域名、端口、协议不同)获取数据的时候,如果服务器没有正确的设置CORS头部,浏览器会拒绝请求,导致CORS错误

浏览器为了安全性,默认禁止跨域请求

如何解决跨域问题呢?

方案一:后端允许跨域(最优解)

 如果你控制后端代码,可以在服务器上添加 CORS 头部,允许跨域访问。

方案二:使用代理服务器:

  • 前端请求代理服务器(localhost:3001)不跨域

  • 代理服务器请求目标 API(m.maoyan.com)没有浏览器 CORS 限制

  • 代理服务器返回数据给前端

如果项目部署子啊服务器上,还可以使用Nginx反向代理实现类似代理服务器的功能

CORS不是bug,而是一种安全机制,防止恶意网站在后台偷偷读取你的数据

项目启动:路由架构

 要授权才可以访问后面的那些东西,否则你刚进去都会到Login组件里面(未授权重定向)

路由也可以直接写到根组件里,但是都说是组件化开发了,还是单独新建一个文件夹写路由比较好

欲使用,先安装:

 npm i --save-dev react-router-dom

令人感到好笑的是,科文老师在项目阶段才讲到了他用的rcc和rfc的插件

Switch 组件是 react-router-dom v5 及之前的版本中的一个重要组件,用于匹配和渲染第一个符合条件的路由。它的作用是保证一次只渲染一个 Route 组件

从上到下检查Route组件的path,渲染第一个匹配的Route,忽略后面的Route

如果要是不加上Switch的话,在访问/login的时候,/也是匹配的(因为/login以/开头)

Login和NewsSandBox都会被渲染,可能导致页面 错误

科文老师的写法是react-router-dom v5

以下的写法是适用于react-router-dom v6

import React from "react";
import { HashRouter, Routes, Route } from "react-router-dom"; 
import NewsSandBox from "../views/sandbox/NewsSandBox";

export default function IndexRouter() {
  return (
    <HashRouter>
      <Routes>  
        <Route path="/login" element={<Login />} /> 
        <Route path="/*" element={<NewsSandBox />} />
      </Routes>
    </HashRouter>
  );
}

但是还没有实现带拦截的功能,所以我们要实现带拦截并且重定向

重定向还是用了新的,从render到element:

import React from "react";
import { HashRouter, Routes, Route, Navigate } from "react-router-dom"; 
import NewsSandBox from "../views/sandbox/NewsSandBox";
import Login from '../views/login/Login';

export default function IndexRouter() {
  return (
    <HashRouter>
      <Routes>  
        {/* 登录页 */}
        <Route path="/login" element={<Login />} /> 
        
        {/* 受保护路由:如果没有 token,重定向到 /login */}
        <Route path="/*" element={
          localStorage.getItem("token") ? 
          <NewsSandBox /> : 
          <Navigate to="/login" replace />
        } />
      </Routes>
    </HashRouter>
  );
}

使用Navigate来代替Redirect,用于进行页面跳转

replace表示替换当前URL,不会留下浏览器历史记录

而render是每次访问路由的时候都会执行的一个函数

v6就不支持render了,用element接收组件JSX,而不会每一次都执行函数

改进的原因:

减少不必要函数执行(很影响性能)

语法更加的直观,更符合JSX的语法

还可以简化API的设计

 项目启动-搭建路由

进行路由的搭建:

import React from 'react'
import SideMenu from '../../components/sandbox/SideMenu'
import TopHeader from '../../components/sandbox/TopHeader'
import {
  BrowserRouter as Router,
  Routes,
  Route,
  Navigate,
} from 'react-router-dom'
import Home from './home/Home'
import RightList from './right-manage/RightList'
import RoleList from './right-manage/RoleList'
import UserList from './user-manage/UserList'

export default function NewsSandBox() {
  return (
    <div>
      <SideMenu></SideMenu>
      <TopHeader></TopHeader>
      <Router>
        <Routes>
          <Route path="/home" element={<Home />} />
          <Route path="/user-manage/list" element={<UserList />} />
          <Route path="/right-manage/role/list" element={<RoleList />} />
          <Route path="/right-manage/right/list" element={<RightList />} />

          <Route path="/" element={<Navigate to="/home" />} />
        </Routes>
      </Router>
    </div>
  )
}

项目目录: 

还要加一个额外的处理:

import React from 'react'
import SideMenu from '../../components/sandbox/SideMenu'
import TopHeader from '../../components/sandbox/TopHeader'
import {
  BrowserRouter as Router,
  Routes,
  Route,
  Navigate,
} from 'react-router-dom'
import Home from './home/Home'
import RightList from './right-manage/RightList'
import RoleList from './right-manage/RoleList'
import UserList from './user-manage/UserList'
import Nopermisson from './nopermisson/Nopermisson'

export default function NewsSandBox() {
  return (
    <div>
      <SideMenu></SideMenu>
      <TopHeader></TopHeader>
      <Router>
        <Routes>
          <Route path="/home" element={<Home />} />
          <Route path="/user-manage/list" element={<UserList />} />
          <Route path="/right-manage/role/list" element={<RoleList />} />
          <Route path="/right-manage/right/list" element={<RightList />} />

          <Route path="/" element={<Navigate to="/home" />} />
          <Route path='*' element={<Nopermisson/>}/>
        </Routes>
      </Router>
    </div>
  )
}

Antd引入

自己写页面不如用别人写好的

站在巨人的肩膀上

我一向是秉承着求己不如求人的观念的

Ant Design - 一套企业级 UI 设计语言和 React 组件库https://ant-design.antgroup.com/index-cn安装Antd:

npm install antd --save

使用的时候就是按需引入

引入的时候抄它代码就好


http://www.kler.cn/a/599017.html

相关文章:

  • 固定翼无人机姿态和自稳模式
  • 区块链技术
  • [笔记] TinyWebServer编译及demo运行过程
  • React-Router路由跳转、传参、抽象封装以及嵌套路由
  • Redisson 分布式锁原理
  • webgl入门实例-06绘制多个大小不同点-深入理解buffer02
  • 【博客节选】再谈Unity 的 root motion
  • Mybatis注解的基础操作——02
  • WPF 样式(Style)和模板(Template)
  • 机器学习课堂4线性回归模型+特征缩放
  • 05STM32定时器-01定时器概述
  • 医学领域的deepseek:Med-R1,用强化学习开启医学视觉语言模型推理
  • Python与区块链隐私保护技术:如何在去中心化世界中保障数据安全
  • MySQL抖动浅析
  • 某个业务采用【规则引擎】重构大幅降低耗时
  • JavaScript |(七)BOM及JSON简介 | 轮播图 | 尚硅谷JavaScript基础实战
  • 蓝桥杯 回文数组
  • VLAN综合实验
  • x86 Docker镜像转换为 ARM 架构镜像
  • 我的Go学习路线概览