什么是浏览器缓存
浏览器缓存是临时储存网页资源 的机制。 当用户访问一个网页中,会将一些静态资源存储在本地磁盘或内存里面。后续再次加载资源的话就会从缓存中取,就不会重复请求浏览器。
- 作用:可以加快网页请求速度,减少带宽消耗, 减少服务器压力。
- 缓存对象: 静态资源img等,html页面资源,API 响应数据
- 缓存类型:
- 强缓存:
浏览器会检查资源的Cache-Control和Expires属性,判断资源是否过期。
如果未过期,会从缓存中直接取,不会去请求服务器。
相关 HTTP 头:
Cache-Control: max-age=3600(单位:秒,优先级更高)Expires: Wed, 21 Oct 2025 07:28:00 GMT
- 协商缓存
如果未命中强缓存,浏览器会再次向服务端发起请求,由服务器决定是否继续使用缓存。如果资源未修改,服务器返回
304 Not Modified,会再次使用缓存。 相关 HTTP 头:Last-Modified(资源最后修改时间) +If-Modified-Since(浏览器询问)ETag(资源唯一标识符) +If-None-Match(浏览器询问)
- 都未命中的话就会触发走服务器逻辑。
- 强缓存:
浏览器会检查资源的Cache-Control和Expires属性,判断资源是否过期。
如果未过期,会从缓存中直接取,不会去请求服务器。
相关 HTTP 头:
- 总结:浏览器有个缓存机制,可以将首次加载的资源缓存到磁盘或者内存中,当我们再次去加载该资源时,首先会通过cache-control(有一个max-age属性,单位为ms) 或者 expires(他是有一个到期时间) 看看文件是否还在有效期,是的话命中强缓存。直接从缓存里取。如果没有的话,会去走协商缓存的逻辑:会向服务器发送一个请求,服务器会通过 Last-Modified (资源最后修改时间)和 If-Modified-Since (上次请求拿到的 Last-Modified),或者是ETag (文件的key) 和 If-None-Match 上次请求拿到的 ETag),服务器会将两者进行比较来返回304或者200。
有没有封装过Axios
一般会对请求做一下请求拦截与响应拦截。
- 请求拦截:interceptors.request 一般做一些请求之前的一些处理,比如说header里面放token这些。有一些加解密处理,错误处理等
- 响应拦截:interceptors.response 一般对响应数据做处理,1、对不同的code做一个统一处理,比如说没权限,或者未登录等。请求失败做统一的错误提示等。2、对data做处理,成功直接拿data。3、判断文件流 data instanceof Blob 就不处理等。
- get post 封装等。 method,url,params/data
拓展:
config数据结构是什么样的
// 必填字段
url?: string; // 请求地址(若实例化时已配置baseURL,此处可写相对路径)
method?: Method; // HTTP方法(get/post/put/delete等),默认'get'
// 请求数据相关
data?: any; // POST/PUT请求体(对象、FormData、URLSearchParams等)
params?: any; // GET请求的URL参数(会转为?key=value拼接到url)
paramsSerializer?: (params: any) => string; // 自定义params序列化函数
// 请求头
headers?: Record<string, string>; // 自定义请求头(如Content-Type)
// 超时与取消
timeout?: number; // 超时时间(毫秒),默认0(不超时)
timeoutErrorMessage?: string; // 自定义超时错误信息
cancelToken?: CancelToken; // 用于取消请求的Token
// 响应类型
responseType?: ResponseType; // 期望响应格式(json/text/blob/arraybuffer等),默认'json'
// 跨域相关
withCredentials?: boolean; // 是否携带跨域Cookie(默认false)
xsrfCookieName?: string; // CSRF Token的Cookie名
xsrfHeaderName?: string; // CSRF Token的Header名
// 上传/下载进度
onUploadProgress?: (progressEvent: ProgressEvent) => void; // 上传进度回调
onDownloadProgress?: (progressEvent: ProgressEvent) => void; // 下载进度回调
// 适配器(自定义请求处理)
adapter?: AxiosAdapter;
// 其他
signal?: AbortSignal; // 用于取消请求的AbortSignal(替代cancelToken)
validateStatus?: (status: number) => boolean; // 自定义HTTP状态码有效性验证
transitional?: TransitionalOptions; // 过渡期兼容配置
}
如何在header里加token等
this.instance.interceptors.request.use((config) => {
const token = this.getToken();
if (token) {
config.headers.Authorization = `Bearer ${token}`;
config.headers['X-Auth-Source'] = 'web';
}
return config;
});
react-router有几种路由跳转方式
Link基础跳转组件 state:隐式传参 const { state } = useLocation(); //通过useLocation获取
import { Link } from 'react-router-dom';
function Home() {
return (
<Link to="/about">关于我们</Link>
);
}
NavLink高亮跳转组件
<NavLink
to="/about"
className={({ isActive }) => isActive ? 'active' : ''}
>
关于我们
</NavLink>
useNavigatehook useNavigate()(url,{state,replace})<Navigate>组件
拓展: replace与push的区别 replace:页面跳转后会清除跳转前的历史记录 比如从登录页跳转首页使用 push:页面跳转后支持回退,push(-1),比如 列表 → 详情 → 返回列表
项目中如何去设计路由
- 组件式声明: 定义时通过返回: router.tsx:
import {
Routes,
Route,
} from 'react-router-dom';
const HomePage = lazy(() => import('./pages/Home'));
const LoginPage = lazy(() => import('./pages/Login'));
const NotFoundPage = lazy(() => import('./pages/NotFound'));
export default function AppRoutes() {
return (
<Routes>
{/* 公开路由 */}
<Route element={<Layout variant="public" />}>
<Route index element={<HomePage />} />
<Route path="login" element={<LoginPage />} />
</Route>
{/* 错误路由 */}
<Route element={<Layout variant="error" />}>
<Route path="404" element={<NotFoundPage />} />
</Route>
</Routes>
);
}
app.tsx:
import AppRoutes from 'xxx'
import {
BrowserRouter,
} from 'react-router-dom';
function App() {
return (
<BrowserRouter>
{/* 全局加载状态 */}
<Suspense fallback={<LoadingSpinner fullScreen />}>
<AppRoutes />
</Suspense>
{/* 全局弹窗/通知等可以放在这里 */}
<GlobalNotification />
</BrowserRouter>
);
}
export default App;
拓展: 什么是Outlet 一、<Outlet> 的核心作用
- 占位功能
在父路由组件中放置<Outlet>,表示"这里将渲染匹配的子路由内容"。 - 嵌套路由连接器
将父路由与子路由的组件结构关联起来,形成完整的 UI 层级。 - 布局控制
允许父组件包裹子组件(比如共享的导航栏、侧边栏等)。
Outlet总结:因为现在route组件支持嵌套结构,他代表的是我父路由匹配到的子路由的插槽,或者占位的一个功能
for in 和 for of的区别
- for in 遍历对象的 可枚举属性名
注意 for in 会遍历出原型中的属性 可以使用hasOwnProperty过滤 - for of 遍历数组、字符串等线性集合的值。
for of支持遍历可迭代器的数据的数据 比如Map、Set、数组、字符串、
判断数组的几种方式
- Array.isArray()
- 原型判断。 arr instanceof Array
- Object的toString方法: Object.prototype.toString.call() === '[object Array]'
- 构造函数判断: arr.constructor === Array
- Array的isPrototypeOf方法 Array.prototype.isPrototypeOf(arr)
扩展:为什么 instanceof Array 不够可靠?
对象原型链可以被篡改: 比如: obj.proto === Array.prototype
token有用过吗,平时项目中如何使用的。
我们项目一般是存在localStorage中,在每次请求的时候在请求拦截时把token塞到header中去:config.header.Authorization = token
const headers = config.headers || {};
return {
...config,
headers: {
...(getToken() || ''),
...headers,
'biz-system': 'PROJECT',
'legal': localStorage.getItem('legal'),
'org-code': JSON.parse(sessionStorage.getItem("currentUnitInfo") || '{}')?.orgCode || '',
},
};
Vue改深层次对象值
- Vue.set
// 修改嵌套对象
this.$set(this.obj, 'deepProp', newValue);
// 修改数组嵌套对象
this.$set(this.array, index, { ...this.array[index], nestedProp: newValue });
- 创建一个新的引用
// 修改深层属性
this.obj = {
...this.obj,
nested: {
...this.obj.nested,
deepProp: newValue
}
};
// 修改数组中的对象
this.array = this.array.map((item, i) =>
i === index ? { ...item, nestedProp: newValue } : item
);
----------------------------
什么是闭包,闭包的使用场景
创建私有变量 模块化包 函数柯里化 防抖节流函数
什么是垃圾回收机制
- 标记清除算法 会从当前的上下文中全局对象中找到所有可以访问的变量,再遍历我们堆内存中可以访问的变量,没有使用的就会被删除。
- 引用计数算法 使用到变量就+1,设为null-1. 缺点:比较消耗性能,无法处理循环引用
内存泄露的几个场景
- 忘记使用var定义变量
- 闭包的使用
- 定时器
- dom应用未释放
- 事件监听未注销