react-bn-面试

1.主要内容

工作台待办

在这里插入图片描述
在这里插入图片描述
实现思路:

1,待办list由后端返回,固定需要的字段有id(查详细)、type(本条待办的类型),还可能需要时间,状态等
2,一个集中处理待办中转路由页,所有待办都跳转到这个页面然后用逻辑控制显示内容
3,动态待办子页面单独处理需要的逻辑
4,每次改变type的值都在redux中取
5,所有跟type相关的组件都做成一个配置文件,方便后续添加新组件

代码:
在这里插入图片描述

const toDoTask=(item)=>{console.log(item);const isComp=type2Comp[item.type]console.log(isComp);dispatch(setDyComp(item.type))if(isComp){//跳转路由navigate(`/todo/${item.id}`)}else{console.error(`Unknown type: ${item.type}`);}
}

中转页核心代码:

function withDynamicComponent(Component, parentProps) {if (!Component) {return <div>Component not found</div>;}return (<React.Suspense fallback={<div>Loading...</div>}><Component props={parentProps} /></React.Suspense>);
}

type配置:

import Tax from '../tax/index'
import Apply from '../apply/index'
import Mail from '../mail/index'
import Text from '../text/index'
export const type2Comp = {tax: Tax,mail: Mail,text: Text,apply: Apply,// 增加一个路由就增加一个配置组件//这里还应该考虑一个type 可能有多个子页面,这种情况可以在子页面通过status再写子页面,类似树结构};

动态子组件

function Apply({props}) {return <div>apply<div>{props.name}</div><div>{props.id}</div></div>;}export default Apply;

登录

Cookie + Session 登录
Token 登录(jwt)
SSO 单点登录

相关资料:https://blog.csdn.net/beekim/article/details/135130179

session、cookie

session
在这里插入图片描述
cookie
在这里插入图片描述

token(jwt)

json web token (jwt)
在这里插入图片描述
在这里插入图片描述

1,用户登录后,服务器生成一个 JWT,包含用户信息和签名。2,服务器将 JWT 返回给客户端,客户端存储(如 localStorage 或 Cookie)。3,客户端每次请求时在 Authorization 头中携带 JWT,服务器验证签名并解析用户信息。

单点登录方案

1.cookie+session
在这里插入图片描述

第一次登录
1.用户登录某个子系统时,将用户引导到认证中心登录
2.验证通过在服务器(一般在redis)的表中创建该用户的键值对(sid,info),有失效时间。
3.用户拿到认证中心返回的sid并保存在本地cookie登录过,访问其他系统
到认证中心区查服务器表中有没有值,有通过,没有就执行登录操作优点:强控制力,可以直接拉黑某用户不让登录,让其强制下线
缺点:成本高,烧钱。大型项目中,如果子系统有几十个,用户量又大,每次访问都得要经过认证中心

2.双token
在这里插入图片描述

登录
1.用户登录访问认证中心,并接收到token保存在本地
2.用户访问子系统携带token,子系统可以独立校验该token缺点:失去强控制力

在这里插入图片描述
在这里插入图片描述

10-20min失效
短token	+	refresh
1.用户登录访问认证中心,并接收到token+refresh保存在本地
2.用户访问子系统携带token,子系统可以独立校验该token短token失效
3.用户用refresh(刷新token)再去认证中心获取新的短token

权限管理

1.接口权限(登录后存token,通过axios请求拦截器进行拦截,每次请求的时候头部携带token)
2.路由权限\菜单权限:a.路由的name、path、component统一存在后端,由后端返回。配置信息也可以提供界面统一管理。b.后端返回后,路由通过addRoute动态挂载,将component字段换为真正的组件(注意vite项目:import.meta.glob)c.将菜单和路由存在vuex/pinia实时变更页面3.按钮权限a.在登录的时候拉取按钮权限编码code['EXPORT_LIST','OPEN_MODAL']b.将编码缓存本地c.页面通过v-if控制按钮或是自义定指令控制按钮extra:
前端控制权限
//1.在路由配置中配置白名单
{name: "Login",path: "/login",component: () => import("@/views/Login.vue"),meta:{whiteList:['admin','tom']}},//2.在路由守卫beforeEach中判断当前用户角色是否在meta中,是就next()

正式的项目中,权限管理一般都是在后台维护的,也就是用户登录后,从接口获取该用户权限信息(菜单权限,按钮权限等)。要实现该功能,前端项目得提供基础功能用于维护,功能如下:

必备:
1,用户管理
简介:用户基础信息2,角色管理
简介:配置每个用户的角色,一个用户可以有多个角色
作用:
a,可以在给每个角色配置上他能看到的菜单
b,后台可以根据角色来控制列表的数据权限,列表根绝相应的角色和部门信息返回不同的值
c,按钮权限控制也是要通过角色来配置3,菜单管理
简介:配置项目中的菜单信息和按钮权限
参数:
*菜单类型:一级菜单、子菜单、按钮权限
*菜单名称:xxx
*访问路径:xxx(path)
*前端组件:xxx(component/element的位置)
其他参数:角色、图标、序号等注:
type为子菜单时要显示父级菜单。
type按钮权限时要配置权限的编码code,方便使用时匹配对应的权限其他:
部门管理
数据字典
分类字典

在项目中具体怎么使用?

1,获取菜单后怎么渲染?
2,怎么控制按钮权限?

1,获取菜单后怎么渲染?

通过接口返回后,就前端代码只需要写一个公共方法处理返回的data->项目能识别的路由

怎么识别?

import React, { Suspense } from 'react';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';// 模拟从接口返回的字符串
const getElementFromApi = async () => {// 假设接口返回的字符串是组件的路径return 'pages/Home'; // 例如:'pages/Home'
};// 动态导入组件的函数
const loadComponent = async (componentPath) => {try {const { default: Component } = await import(`./${componentPath}`);return Component;} catch (error) {console.error('Error loading component:', error);return null;}
};const App = () => {const [routes, setRoutes] = React.useState([]);React.useEffect(() => {const fetchRoutes = async () => {const elementPath = await getElementFromApi();const Component = await loadComponent(elementPath);if (Component) {setRoutes([{path: '/',element: <Component />,},]);}};fetchRoutes();}, []);return (<Router><Suspense fallback={<div>Loading...</div>}><Routes>{routes.map((route, index) => (<Route key={index} path={route.path} element={route.element} />))}</Routes></Suspense></Router>);
};export default App;

2,怎么控制按钮权限?

写一个hooks接受按钮权限编码code
a,在hooks查接口判断有没有权限
b,在登录时查询所有按钮权限编码['EXPORT_LIST','OPEN_MODAL'],缓存在本地,然后再在hooks去查本地有没有权限

文件上传

普通的文件上传

核心部分:
//创建流式读取器
const reader=new FileReader();
reader.readAsDataURL(this.files[0])//input读取完成后自动执行onload函数reader.onload=function(e){preview.src=e.target.resultconsole.log(e.target.result);//读取完成后将e.target.result传给服务器//ajax....
}const formData = new FormData();
formData.append('BizType', props.bizType);//设置文件类型(合同材料,)
formData.append('file', resultFile);//文件
formData.append('businessId', props.businessId);//其他参数const temp: any = await useUpLoadingUrl(fileParam.value);上传成功后,会返回对应文件的下载地址用于回显注意:这里没有考虑大文件上传,后台限制文件最大为50M

大文件上传(分片):

  <input type="file" id="fileInput" /><button id="uploadButton">上传</button><div id="progress"></div><script>
document.getElementById('uploadButton').addEventListener('click', async () => {const fileInput = document.getElementById('fileInput') as HTMLInputElement;const file = fileInput.files?.[0];if (!file) {alert('请选择一个文件');return;}const chunkSize = 1024 * 1024; // 每个分片的大小,1MBconst totalChunks = Math.ceil(file.size / chunkSize);const uploadProgress = document.getElementById('progress') as HTMLDivElement;for (let i = 0; i < totalChunks; i++) {const start = i * chunkSize;const end = Math.min(start + chunkSize, file.size);const chunk = file.slice(start, end);const formData = new FormData();formData.append('file', chunk, file.name);formData.append('chunkIndex', i.toString());formData.append('totalChunks', totalChunks.toString());try {const response = await fetch('/upload', {method: 'POST',body: formData,});if (!response.ok) {throw new Error('上传失败');}const progress = ((i + 1) / totalChunks * 100).toFixed(2);uploadProgress.innerText = `上传进度: ${progress}%`;} catch (error) {console.error('上传出错:', error);alert('上传失败');return;}}alert('上传完成');
});
</script>

改造?
老的文件上传所有文件存在阿里云上的,后续的投标时,需要批量下载投标材料,阿里云上无法确认文件的准确性。
改造后将所有上传的文件分类(身份证信息,毕业证信息等),存在公司自己的nas盘方便集中管理

如果文件为图片,后台有限制不能超过固定宽高,不同手机拍的照片上传后会有翻转的情况限制宽高:
exif.js插件获取到旋转参数
判断图片方向,重置canvas大小,确定旋转角度,
image.onload里面获取图片宽高,判断是否满足,不满足的话就就创建canvas裁剪
var canvas = document.createElement('canvas');
var context: any = canvas.getContext('2d');
context.drawImage(image, 0, 0, drawWidth * 0.8, drawHeight);
//返回校正图片
next(canvas.toDataURL(v.file.type));

socket聊天

其他常见场景:
https://vue3js.cn/interview/http/WebSocket.html#%E4%BA%8C%E3%80%81%E7%89%B9%E7%82%B9

项目中用到的:advance-chat+socket.io
难点:
1.替换advance-chat源码中后台的firebase
看了源码,firebase有普通接口请求和ws事件请求两种功能,所以将相关代码拆成两块:普通接口,ws事件。再基于对soket.io的封装对firebase中关于ws事件那一块改写
2.socket.io的封装

1.本地创建了一个简单的node服务
/* 这里就是当客户端socket连接到服务端socket的生命周期 */
io.on("connection", function (socket) {/* io.emit(事件名,参数) */io.emit("message", "恭喜连接成功" );// 接受前台发过来的消息socket.on("sendToServer", (message) => {console.log(message);// 向客户端发送消息// socket.emit("sendToClient", {//   message: "你好我是服务端,让我们来聊天呀",// });});
});/* 指定连接的地址 */this.socket = io('ws://localhost:3333');this.socket.connect();//监听后台给前台发的消息this.socket.on('message', (message) => {console.log('我是客户端,接收到了数据', message);//业务逻辑if (message.type === 'getMsg') {console.log(message.id);this.getAutoChatData(message.roomId);}});//前台给后台发的消息this.socket.emit('sendToServer', {message: '我是客户端,来陪我聊聊',});

更多细节见之前写的文章:
https://blog.csdn.net/beekim/article/details/135130179

几个重要的ws事件:
MessageUpdate 监听房间内的消息
RoomUpdate 监听左侧房间list
LastMessageUpdate 监听最后一条消息
如果ws监听到变化,就在相应的回调函数中更新页面上的数据

加解密

这一块主要看计算机网络方面的非对称加密:

非对称加密是一种加密方法,使用一对密钥:公钥和私钥。公钥用于加密数据,私钥用于解密数据。
公钥可以公开发布,而私钥必须保密。加密过程:发送方使用接收方的公钥对数据进行加密,加密后的数据只能通过接收方的私钥解密。
解密过程:接收方使用自己的私钥对加密数据进行解密,恢复原始数据。

H5-原生相机

需求:在h5做身份证件拍照然后上传
方案:
利用 navigator.mediaDevices.getUserMedia 打开摄像头,将视频流放入 video 标签的 src 中,再通过 canvas.drawImage 的方法,以 video 对象为画布源,绘制最终拍照的图
问题:
1,安卓可以正常用video打开相机,ios有问题,打开时全屏的。
解决方案:

 <video ref="video"  id="video-fix" :width="width" :height="height"autoplay   webkit-playsinline playsinline></video>ios端video标签必须加webkit-playsinline、playsinline属性。
android端部分视频也会存在自动全屏问题,添加webkit-playsinline属性。

2,拍出来的照片默认是640*480 ,照片不清晰
解决方案:

<video ref="video"  id="video-fix" width="1280" height="720" autoplay   webkit-playsinline playsinline></video>
<canvas ref="canvas" style="display: none" width="1280" height="720"></canvas>video宽高要设置成 4:3或16:9才行,这里我设置成了1280*720

3,本地local能打开电脑前置,不是最终效果
localhost 只能调起电脑的前置摄像头,无法在手机本地调试。这是因为浏览器的安全限制,必须使用 https 才可以。所以需要让运维升级测试环境为https。所在在使用后置摄像头调试时非常麻烦,建议将需要调试的参数都设置成变量再逐一调试。

4,部分手机打开相机默认是放大的(手动设置焦距)

5,ios的css样式bug

原因是ios的游览器识别不到video实时的offsetHeight的值,所以在识别不到的时候,手动设置一下遮罩层的高度就可以了。
videoHeight.value=video.value.offsetHeight
if(video.value.offsetHeight<400){//解决ios不能获取到实时的offsetHeight的问题videoHeight.value=600
}

在这里插入图片描述

详细文档:https://blog.csdn.net/beekim/article/details/143680213?spm=1001.2014.3001.5502

H5-pdf文件预览

需求:有一个H5项目要嵌入我们的主项目,嵌入方案是用的iframe,但是在H5项目中需要预览pdf文件,这里再用Iframe就会出问题。
在这里插入图片描述
在多次查找和尝试后解决了预览pdf的问题:
方案:借助pdfjs插件,将pdf文件流/pdf下载链接,传入pdfjs,插件会读取文件并识别出一些文件信息,把pdf转为图片。我们再将图片绘制到cavans上就可以完美解决这个问题了。

核心代码如下:

<template><div ref="showpdfRef"></div>
</template><script setup>
import { ref } from 'vue';
import { getDocument } from 'pdfjs-dist/legacy/build/pdf.mjs';
import 'pdfjs-dist/build/pdf.worker.mjs';const showpdfRef = ref(null);const pdfPath ='xxxxxxxx'const loadingTask = getDocument(pdfPath);
loadingTask.promise.then(async (pdf) => {const canvas = document.createElement('canvas');const context = canvas.getContext('2d');// 循环遍历每一页pdf,将其转成图片for (let i = 1; i <= pdf._pdfInfo.numPages; i++) {// 获取pdf页const page = await pdf.getPage(i);// 获取页的尺寸const viewport = page.getViewport({ scale: 1 });// 设置canvas的尺寸canvas.width = viewport.width;canvas.height = viewport.height;// 将pdf页渲染到canvas上await page.render({ canvasContext: context, viewport: viewport }).promise;// 将canvas转成图片,并添加到页面上const img = document.createElement('img');img.src = canvas.toDataURL('image/png');showpdfRef.value.appendChild(img);}}).then(function () {console.log('# End of Document');},function (err) {console.error('Error: ' + err);},);
</script><style scoped></style>

详细文档:https://blog.csdn.net/beekim/article/details/144857593?spm=1001.2014.3001.5502

3.补充内容

flex

//容器属性
flex-direction
flex-wrap
flex-flow//flex-direction属性和flex-wrap属性的简写形式,默认值为row nowrap
justify-content
align-items//flex-start、flex-end、center、baseline、stretch
align-content//属性定义了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用//项目属性
order//设置顺序
flex-grow//设置放大比例(剩余空间分配比例(默认为0,不扩展))
flex-shrink//宽度不够时设置缩小比列(空间不足时收缩比例(默认为1,可收缩))
flex-basis//项目在分配剩余空间前的初始大小
flex//flex-grow, flex-shrink 和 flex-basis的简写,默认值为0 1 auto。后两个属性可选
align-self//允许单个项目有与其他项目不一样的对齐方式

常见问题:
如何实现水平垂直居中?

.container {display: flex;justify-content: center; /* 主轴居中 */align-items: center;     /* 交叉轴居中 */
}

如何实现「圣杯布局」(Header + Footer + 自适应内容区)?

.container {display: flex;flex-direction: column;min-height: 100vh;
}
.header { height: 60px; }
.content { flex: 1; } /* 占据剩余空间 */
.footer { height: 100px; }

flex-basis 和 width 的优先级

若 flex-direction: row,flex-basis 优先级高于 width若 flex-basis 为 auto,则使用 width 的值例外:min-width/max-width 会限制 flex-basis。

flex-shrink: 0 的作用

禁止项目在空间不足时收缩,常用于固定侧边栏:
.sidebar {flex: 0 0 250px; /* flex-grow:0, flex-shrink:0, flex-basis:250px */
}

为什么设置 flex:1 的项目宽度不一致?
原因:若项目内容长度差异大,且 flex-basis:0(即 flex:1),浏览器会优先按内容比例分配空间。

解决:设置 min-width: 0 或 overflow: hidden 重置内容最小尺寸。
Flex 布局中 margin: auto 的特殊效果
现象:在 Flex 项目中,margin: auto 会吸收剩余空间,实现特定对齐效果。

示例:单个项目右对齐 → margin-left: auto。

line-height:120% 和line-height:1.2区别?

在这里插入图片描述

iframe

用途:

嵌入第三方内容(如地图、广告、视频)实现微前端架构中的子应用隔离跨域通信的桥梁(如 OAuth 授权登录)沙箱隔离(运行不受信任的代码)

优缺点:

优点:隔离性:CSS/JS 作用域隔离,避免全局污染跨域支持:通过 postMessage 实现跨域通信并行加载:iframe 内容可异步加载,不阻塞主页面缺点:性能开销:每个 iframe 是独立文档,消耗内存和 CPUSEO 不友好:搜索引擎可能不抓取 iframe 内容安全性风险:可能引入 XSS 攻击或点击劫持

如何通讯
同域通信:直接访问dom

// 父页面访问子页面
const iframeWindow = document.getElementById('myIframe').contentWindow;
iframeWindow.document.getElementById('childElement');// 子页面访问父页面
parent.document.getElementById('parentElement');

跨域:postMessage

// 父页面向子页面发送消息
const iframe = document.getElementById('myIframe');
iframe.contentWindow.postMessage('Hello from parent', 'https://child-domain.com');// 子页面接收消息
window.addEventListener('message', (event) => {if (event.origin !== 'https://parent-domain.com') return;console.log('Received:', event.data);
});

在这里插入图片描述
在这里插入图片描述

ts最有可能问到的几个

1.交叉类型和联合类型区别?

交叉类型((&)将多个类型合并为一个类型,要求同时满足所有类型,
联合类型(1)表示可以是多个类型中的任意一个

type A ={ a: number };
type B ={b: string };
type c=A&B;//交叉类型,C必须同时有 a和 b
type D=A|B;//联合类型,D 可以是A或 B

2.ts中有哪些数据类型?

number/string/boolean/null/undefined
对象
元组
联合类型
Never
unkonw等

3. interface和type

在Typescript中,type和interface都可以用来定义类型的别名。然而,它们之间有些关键的区别
1,灵活性和使用范围:type更加灵活,它可以用来定义任意类型的别名,包括原始类型、函数、对象等。相比之下,interface只能用来定义对象类型的别名

2,类型组合:type可以使用交叉类型(intersection type)和联合类型(uniontype)来组合多个类型,而interface不能。
3,继承:type可以使用继承和扩展类型的语法来继承和扩展另一个类型,而interface不能。

总的来说,type在灵活性、类型组合和继承方面都优于interface。然而,interface在定义对象类型的别名时具有更明确的作用。

4. any和unkown

any可以给任意类型值赋值,也可以被任意类型赋值。
unknonw只能被赋值,不能赋值别人。

5.infer有什么用

在这里插入图片描述

type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;function foo() {return 123;
}type FooReturnType = ReturnType<typeof foo>; // number
type ElementType<T> = T extends (infer U)[] ? U : never;type NumArray = number[];
type Num = ElementType<NumArray>; // number
type UnwrapPromise<T> = T extends Promise<infer U> ? U : never;type PromiseResult = Promise<string>;
type Result = UnwrapPromise<PromiseResult>; // string

6.ts常见的工具类型

Partial<T>
Required<T>
Readonly<T>
Record<K, T>
Pick<T, K>
Omit<T, K>
Exclude<T, U>
interface User {name: string;age: number;
}type PartialUser = Partial<User>;
// 等价于
// interface PartialUser {
//   name?: string;
//   age?: number;
// }
type RequiredUser = Required<PartialUser>;
// 等价于
// interface RequiredUser {
//   name: string;
//   age: number;
// }
type ReadonlyUser = Readonly<User>;
// 等价于
// interface ReadonlyUser {
//   readonly name: string;
//   readonly age: number;
// }
type UserMap = Record<string, User>;
// 等价于
// interface UserMap {
//   [key: string]: User;
// }
type UserName = Pick<User, 'name'>;
// 等价于
// interface UserName {
//   name: string;
// }

7.逆变和协变?

保证类型使用是安全的
在这里插入图片描述

// 参数类型允许为 Dog 的父类型,不允许为 Dog 的子类型。
//返回值类型允许为 Dog 的子类型,不允许为 Dog 的父类型,逆变和协变
//函数类型的参数类型使用子类型逆变的方式确定是否成立
//返回值使用子类型协变的方式确定是否成立

函数参数(逆变)

interface Animal {}
interface Dog extends Animal {}type Handler<T> = (arg: T) => void;let animalHandler: Handler<Animal> = (animal: Animal) => {};
let dogHandler: Handler<Dog> = (dog: Dog) => {};dogHandler = animalHandler; // 合法,因为 Animal 可以处理 Dog
animalHandler = dogHandler; // 非法,因为 Dog 不能处理所有 Animal

函数返回值(协变)

interface Animal {}
interface Dog extends Animal {}type Creator<T> = () => T;let createAnimal: Creator<Animal> = () => new Animal();
let createDog: Creator<Dog> = () => new Dog();createAnimal = createDog; // 合法,因为 Dog 是 Animal 的子类型
createDog = createAnimal; // 非法,因为 Animal 不一定是 Dog

数组(协变)

interface Animal {}
interface Dog extends Animal {}let animals: Animal[] = [];
let dogs: Dog[] = [];
animals = dogs; // 合法,因为 Dog[] 是 Animal[] 的子类型
dogs = animals; // 非法,因为 Animal[] 不一定是 Dog[]

react相关

为什么要用hooks?

答:

1,Hooks 的引入主要是为了解决类组件的一些痛点。比如逻辑分散、代码复用困难、this 绑定问题等。
2,允许我们在函数组件中使用状态和生命周期特性,通过自定义 Hooks 可以更好地复用逻辑。
3,学习成本更低,代码更简洁,
4,并且 React 团队也明确表示函数组件和 Hooks 是未来的发展方向。

在这里插入图片描述

useReducer

在这里插入图片描述
在这里插入图片描述
使用

import React, { createContext, useContext, useReducer } from 'react';// 创建 Context
const CounterContext = createContext();// 初始状态和 reducer
const initialState = { count: 0 };
function counterReducer(state, action) {switch (action.type) {case 'increment':return { count: state.count + 1 };case 'decrement':return { count: state.count - 1 };default:throw new Error();}
}// 子组件
function Counter() {const { state, dispatch } = useContext(CounterContext);return (<div><p>Count: {state.count}</p><button onClick={() => dispatch({ type: 'increment' })}>+</button><button onClick={() => dispatch({ type: 'decrement' })}>-</button></div>);
}// 父组件
function App() {const [state, dispatch] = useReducer(counterReducer, initialState);return (<CounterContext.Provider value={{ state, dispatch }}><Counter /></CounterContext.Provider>);
}export default App;

路由相关

如何跳转?
在这里插入图片描述
定义路由
在这里插入图片描述
路由传值
在这里插入图片描述
在这里插入图片描述
嵌套路由
在这里插入图片描述
渲染子路由
v5:children
在这里插入图片描述
v6:outlet
在这里插入图片描述
路由守卫

在这里插入图片描述
全局路由守卫
在这里插入图片描述
具体实现
高阶组件

import React from 'react';
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom';// 高阶组件:路由守卫
function AuthWrapper({ children }) {const isLoggedIn = localStorage.getItem('isLoggedIn'); // 假设登录状态存储在 localStorageif (!isLoggedIn) {return <Navigate to="/login" />; // 未登录则跳转到登录页}return children; // 已登录则渲染子组件
}// 页面组件
function Home() {return <div>Home Page</div>;
}
function Login() {return <div>Login Page</div>;
}function App() {return (<BrowserRouter><Routes><Routepath="/"element={<AuthWrapper><Home /></AuthWrapper>}/><Route path="/login" element={<Login />} /></Routes></BrowserRouter>);
}export default App;```使用 useLocation 和 useNavigate
```js
import React, { useEffect } from 'react';
import { BrowserRouter, Routes, Route, useLocation, useNavigate } from 'react-router-dom';// 全局路由守卫组件
function AuthGuard({ children }) {const location = useLocation();const navigate = useNavigate();const isLoggedIn = localStorage.getItem('isLoggedIn');useEffect(() => {if (!isLoggedIn && location.pathname !== '/login') {navigate('/login'); // 未登录则跳转到登录页}}, [location, navigate, isLoggedIn]);return isLoggedIn ? children : null; // 已登录则渲染子组件
}// 页面组件
function Home() {return <div>Home Page</div>;
}
function Login() {return <div>Login Page</div>;
}function App() {return (<BrowserRouter><Routes><Routepath="/"element={<AuthGuard><Home /></AuthGuard>}/><Route path="/login" element={<Login />} /></Routes></BrowserRouter>);
}export default App;

路由懒加载
路由懒加载(Lazy Loading)是一种优化技术,用于延迟加载应用程序中的某些模块或组件,直到它们真正需要时才加载。这种方式可以显著提升应用的初始加载速度和性能
在这里插入图片描述

常见问题
在这里插入图片描述

什么是合成事件?

调用合成事件后发生了什么

react为什么有两种优先级?

怎么理解并发更新?

useEffect和useLayoutEffect区别?

useEffect
js操作dom之前调用,页面渲染完成后执行
异步执行,不会阻塞渲染,适合大多数场景。

用于数据加载后的状态更新

useLayoutEffect
js操作dom之后执行,页面渲染之前执行

由于是同步执行,可能会阻塞页面渲染,导致性能问题

在表格组件中,使用 useLayoutEffect 动态调整列宽,确保布局一致

useLayoutEffect有替代方案吗?
1.useEffect + requestAnimationFrame:在下一帧执行逻辑,避免阻塞渲染。

2.CSS 替代:优先使用 CSS 实现布局调整。

3.ResizeObserver:监听 DOM 尺寸变化,动态调整布局。

4.getBoundingClientRect:异步测量 DOM 尺寸。

5.CSS 变量:通过 CSS 变量动态调整布局。

hooks原理?

在这里插入图片描述

☆react的diff?

diff比较三大原则:

1,更新前后,层级一致
2,类型type一致
3,key相同
否则,都算作新节点

通常我们常说的diff算法,实际上重点在于for循环遍历出来的元素,进行移动的过程。

在react中对比前后的数组元素,会有一个map数据结构存储老的节点fiber对象。
如果在遍历新老节点的过程,发现能复用,则从map数据结构中删除老节点。
剩下则是新节点没有用到的,最后会打上标记,并在commit阶段删除掉

diff移动节点:

123 ->321遍历移动后的:从左到右新	  老
index下标 0 => 2
遍历到谁,谁就是最右节点
2	=> 老index 1 		2>1		不移动
3	=> 老index 2		3>2		不移动
2	=> 老index 0		2<0?	打上标记,移动一次

删除节点:

123 ->32diff的map数据结构中,每遍历一下新的,就会吧map中老的删掉。最后map中剩下的元素dom移除

新增节点:

123 ->3214遍历到新的,发现老的没有就创建

什么是module?chunk?bundle?

https://blog.csdn.net/Jasonslw/article/details/124028176

module,chunk 和 bundle 其实就是同一份逻辑代码在不同转换场景下的取了三个名字:
我们直接写出来的是 module,webpack 处理时是 chunk,最后生成浏览器可以直接运行的 bundle

☆webpack和vite打包优化有哪些?分别从构建优化,打包速度优化和前端性能优化三个方面来说?

构建优化

Webpack

代码分割:通过 SplitChunksPlugin 分割公共代码和第三方依赖,结合动态导入实现按需加载。Tree Shaking:移除未使用代码,确保代码为 ESM 格式。模块联邦:实现微前端架构,多个应用共享模块,减少重复代码。WASM 加速:使用 @swc/core 替代 Babel,提升转译速度。

Vite

依赖预构建:将 CommonJS 转换为 ESM,合并小文件,减少请求数量。按需编译:开发环境基于浏览器原生 ESM,按需加载模块。Rollup 集成:生产模式下利用 Rollup 的 Tree Shaking 和代码压缩能力。

打包速度优化

Webpack

持久化缓存:Webpack 5 内置文件系统缓存,减少重复构建时间。多线程处理:使用 thread-loader 或 HappyPack 并行处理任务。DLL 预编译:预编译不常变化的第三方库,减少构建时间。Rust 工具链:使用 swc-loader 和 esbuild 替代 Babel 和 Terser,提升构建速度。

Vite

开发零打包:直接使用浏览器加载 ESM 模块,启动极快。依赖预构建缓存:首次构建后缓存第三方依赖,后续启动直接复用。增量编译:仅重新编译变化的模块,热更新响应速度极快。

前端性能优化

Webpack

资源压缩:使用 TerserPlugin 压缩 JS,CssMinimizerPlugin 压缩 CSSCDN 引入:通过 externals 排除第三方库,改为 CDN 引入。懒加载:动态导入非关键代码(如路由组件)。预加载与预取:使用 webpackPreload 和 webpackPrefetch 提示浏览器加载关键资源。

Vite

异步组件加载:动态导入组件,按需加载资源。浏览器缓存策略:利用 HTTP 强缓存缓存静态资源。生产代码优化:生产模式下 Rollup 自动压缩代码、分割代码块。资源内联:小文件(如 CSS、图片)内联为 Base64,减少请求数。

在这里插入图片描述

4.小知识

游览器控制台特性

const arr=[{n:1},{n:2}]console.log(arr);arr[0].n++console.log(arr);

这段代码游览器打印
在这里插入图片描述

原因:第一次展开三角的时候会重新计算

在这里插入图片描述

js文件下载

普通的下载用a标签就可以了

<a  download=‘abc.pdf’ href=“xxx.pdf”>下载</a>

但是这里有个需要要在下载文件是把token传给后端

const down=async()=>{const resp=await fetch('xxx.pdf',{headers:{authorization:localStorage.getItem('token')}})const blob=await resp.bolo()//等待二进制文件全部传输完成const url=URL.createObjectUrl(blob)//创建临时的url地址const a=document.createElement('a')a.href=urla.download='xxxxx.pdf'//下载文件命名a.click()
}

在这里插入图片描述

这样虽然可以但是有两个问题:
1,如果文件比较大,游览器缓存可能会出问题
2,如果文件有100g,等游览器缓存好要一个小时,才会弹出选择文件存储位置的框,用户点了就会发下没反应。

解决办法:
让后端配合,下载文件单独写个接口把token传过去,验证通过后再返回一个一次性的下载地址

如何实现私有属性

1,命名规范约束

class A{
__key=123
#name='tom'
}A.__key

缺点:全靠开发人员自觉

2,Symbol
A.js

cost key =Symbol('key')
export class A{[key]=123
}

B.js

import {A} from'./A'
const a=new A()
console.log(a[key])//报错const key2=Object.getOwnProperttySymbols[a][0]
console.log(a[key2])//可以获取到

3.ts约束

 class A{private key=123
}

这种约束虽然会被ts检测到(编译时),但是强行运行还是可以
4.原生语法

class A{#key=123
}

这是新语法,肯呢个会有兼容性问题

5.终极方案
其原理就是在babel中把“#key”新语法降级后的代码

const fieldMap=new WeakMap()
export class A{construct(){fieldMap.set(this,{key:123})}print(){//内部使用console.log(fieldMap.get(this).key)}
}

如何理解js的异步

在这里插入图片描述

执行结果

在这里插入图片描述
5,4,3,1,2

阐述js事件循环(宏任务,微任务)

1
微队列:promise
延时队列:定时器
交互队列:点击事件,滚动事件等

在这里插入图片描述

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

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

相关文章

GRN前沿:利用DigNet从scRNA-seq数据中生成基于扩散的基因调控网络

1.论文原名&#xff1a;Diffusion-based generation of gene regulatory network from scRNA-seq data with DigNet 2.出版时间&#xff1a;2024.12.18 3.doi: 10.1101/gr.279551.124 摘要&#xff1a; 基因调控网络&#xff08;GRN&#xff09;在细胞内基因的身份和功能之间…

AnswerRocket:通过 AI 辅助简化分析

AnswerRocket是一家专注于人工智能驱动数据分析和商业智能的领先企业&#xff0c;其核心产品是一款增强型分析平台&#xff0c;旨在通过自然语言处理&#xff08;NLP&#xff09;、机器学习&#xff08;ML&#xff09;和生成式AI技术&#xff0c;简化复杂数据的分析过程&#x…

小程序设计和开发:如何研究同类型小程序的优点和不足。

一、确定研究目标和范围 明确研究目的 在开始研究同类型小程序之前&#xff0c;首先需要明确研究的目的。是为了改进自己的小程序设计和开发&#xff0c;还是为了了解市场趋势和用户需求&#xff1f;不同的研究目的会影响研究的方法和重点。例如&#xff0c;如果研究目的是为了…

我的AI工具箱Tauri版-ZoomImageSDXL全图超清放大TILE+SDXL

本教程基于自研的AI工具箱Tauri版进行ComfyUI工作流ZoomImageSDXL全图超清放大TILESDXL。 ZoomImageSDXL全图超清放大TILESDXL 借助ControlNet的Tile技术与SDXL大模型&#xff0c;该工具能够在放大图像的同时&#xff0c;精准还原细节和纹理&#xff0c;确保输出效果既清晰锐利…

Java设计模式:行为型模式→状态模式

Java 状态模式详解 1. 定义 状态模式&#xff08;State Pattern&#xff09;是一种行为型设计模式&#xff0c;它允许对象在内部状态改变时改变其行为。状态模式通过将状态需要的行为封装在不同的状态类中&#xff0c;实现对象行为的动态改变。该模式的核心思想是分离不同状态…

蓝桥与力扣刷题(234 回文链表)

题目&#xff1a;给你一个单链表的头节点 head &#xff0c;请你判断该链表是否为回文链表。如果是&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,2,1] 输出&#xff1a;true示例 2&#xff1a; 输入&…

【面经】字节南京一面部分题目记录

南京字节一面题&#xff0c;可能因为项目不太匹配&#xff0c;全程八股比较多&#xff0c;也有两道手撕代码题&#xff0c;强度还是有的。为了方便大家学习&#xff0c;大部分答案由GPT整理&#xff0c;有些题给出了我认为回答比较好的博客链接。 文章目录 一、python2 和 pyth…

【C语言篇】“三子棋”

一、游戏介绍 三子棋&#xff0c;英文名为 Tic - Tac - Toe&#xff0c;是一款简单而经典的棋类游戏。游戏在一个 33 的棋盘上进行&#xff0c;两名玩家轮流在棋盘的空位上放置自己的棋子&#xff08;通常用 * 和 # 表示&#xff09;&#xff0c;率先在横、竖或斜方向上连成三个…

vscode软件操作界面UI布局@各个功能区域划分及其名称称呼

文章目录 abstract检查用户界面的主要区域官方文档关于UI的介绍 abstract 检查 Visual Studio Code 用户界面 - Training | Microsoft Learn 本质上&#xff0c;Visual Studio Code 是一个代码编辑器&#xff0c;其用户界面和布局与许多其他代码编辑器相似。 界面左侧是用于访…

【B站保姆级视频教程:Jetson配置YOLOv11环境(六)PyTorchTorchvision安装】

Jetson配置YOLOv11环境&#xff08;6&#xff09;PyTorch&Torchvision安装 文章目录 1. 安装PyTorch1.1安装依赖项1.2 下载torch wheel 安装包1.3 安装 2. 安装torchvisiion2.1 安装依赖2.2 编译安装torchvision2.2.1 Torchvisiion版本选择2.2.2 下载torchvisiion到Downloa…

于动态规划的启幕之章,借 C++ 笔触绘就算法新篇

注意&#xff1a;代码由易到难 P1216 [IOI 1994] 数字三角形 Number Triangles 题目链接&#xff1a;[IOI 1994] 数字三角形 Number Triangles - 洛谷 题目描述 观察下面的数字金字塔。 写一个程序来查找从最高点到底部任意处结束的路径&#xff0c;使路径经过数字的和最大。每…

分页按钮功能

前言 在前端开发中&#xff0c;分页功能是一个常见的需求&#xff0c;特别是当需要展示大量数据时&#xff0c;它能有效提升用户体验。该文章结合运用了HTML&#xff0c;CSS&#xff0c;JS实现网页的分页按钮功能&#xff0c;并且可以选择每页显示的条数试试更新总页数及显示当…

SAP HCM 回溯分析

最近总有人问回溯问题&#xff0c;今天把12年总结的笔记在这共享下&#xff1a; 12年开这个图的时候总是不明白是什么原理&#xff0c;教程看N次&#xff0c;网上资料找一大堆&#xff0c;就是不明白原理&#xff0c;后来为搞明白逻辑&#xff0c;按照教材的数据一样做&#xf…

gitea - fatal: Authentication failed

文章目录 gitea - fatal: Authentication failed概述run_gitea_on_my_pkm.bat 笔记删除windows凭证管理器中对应的url认证凭证启动gitea服务端的命令行正常用 TortoiseGit 提交代码备注END gitea - fatal: Authentication failed 概述 本地的git归档服务端使用gitea. 原来的用…

X Window System 架构概述

X Window System 架构概述 1. X Server 与 X Client ​ 这里引入一张维基百科的图&#xff0c;在Linux系统中&#xff0c;若用户需要图形化界面&#xff0c;则可以使用X Window System&#xff0c;其使用**Client-Server**架构&#xff0c;并通过网络传输相关信息。 ​ ​ X…

Linux防火墙基础

一、Linux防火墙的状态机制 1.iptables是可以配置有状态的防火墙&#xff0c;其有状态的特点是能够指定并记住发送或者接收信息包所建立的连接状态&#xff0c;其一共有四种状态&#xff0c;分别为established invalid new related。 established:该信息包已建立连接&#x…

[论文学习]Adaptively Perturbed Mirror Descent for Learning in Games

[论文学习]Adaptively Perturbed Mirror Descent for Learning in Games 前言概述前置知识和问题约定单调博弈&#xff08;monotone game&#xff09;Nash均衡和Gap函数文章问题定义Mirror Descent 方法评价 前言 文章链接 我们称集合是紧的&#xff0c;则集合满足&#xff1…

Go学习:类型转换需注意的点 以及 类型别名

目录 1. 类型转换 2. 类型别名 1. 类型转换 在从前的学习中&#xff0c;知道布尔bool类型变量只有两种值true或false&#xff0c;C/C、Python、JAVA等编程语言中&#xff0c;如果将布尔类型bool变量转换为整型int变量&#xff0c;通常采用 “0为假&#xff0c;非0为真”的方…

使用Pygame制作“吃豆人”游戏

本篇博客展示如何使用 Python Pygame 编写一个简易版的“吃豆人&#xff08;Pac-Man&#xff09;” 风格游戏。这里我们暂且命名为 Py-Man。玩家需要控制主角在一个网格地图里移动、吃掉散布在各处的豆子&#xff0c;并躲避在地图中巡逻的幽灵。此示例可帮助你理解网格地图、角…

ubuntu磁盘扩容

ubuntu磁盘扩容 描述先在虚拟机设置里面扩容进入Ubuntu 配置使用命令行工具parted进行分区输出如下完成 描述 执行命令,查看 fs 类型是什么 lsblk -o NAME,FSTYPE,MOUNTPOINT将60G扩容到100G&#xff0c;其中有些操作我也不知道什么意思&#xff0c;反正就是成功了&#xff0…