面试笔记

150 阅读4分钟

什么是浏览器缓存

浏览器缓存是临时储存网页资源 的机制。 当用户访问一个网页中,会将一些静态资源存储在本地磁盘或内存里面。后续再次加载资源的话就会从缓存中取,就不会重复请求浏览器。

  • 作用:可以加快网页请求速度,减少带宽消耗, 减少服务器压力。
  • 缓存对象: 静态资源img等,html页面资源,API 响应数据
  • 缓存类型
    1. 强缓存: 浏览器会检查资源的Cache-ControlExpires属性,判断资源是否过期。 如果未过期,会从缓存中直接取,不会去请求服务器。 相关 HTTP 头
      • Cache-Control: max-age=3600(单位:秒,优先级更高)
      • Expires: Wed, 21 Oct 2025 07:28:00 GMT
    2. 协商缓存 如果未命中强缓存,浏览器会再次向服务端发起请求,由服务器决定是否继续使用缓存。如果资源未修改,服务器返回 304 Not Modified,会再次使用缓存。 相关 HTTP 头
      • Last-Modified(资源最后修改时间) + If-Modified-Since(浏览器询问)
      • ETag(资源唯一标识符) + If-None-Match(浏览器询问)
    3. 都未命中的话就会触发走服务器逻辑。
  • 总结:浏览器有个缓存机制,可以将首次加载的资源缓存到磁盘或者内存中,当我们再次去加载该资源时,首先会通过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>

  • useNavigate hook useNavigate()(url,{state,replace})
  • <Navigate> 组件

拓展: replace与push的区别 replace:页面跳转后会清除跳转前的历史记录 比如从登录页跳转首页使用 push:页面跳转后支持回退,push(-1),比如 列表 → 详情 → 返回列表

项目中如何去设计路由

  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> 的核心作用

  1. 占位功能
    在父路由组件中放置 <Outlet>,表示"这里将渲染匹配的子路由内容"。
  2. 嵌套路由连接器
    将父路由与子路由的组件结构关联起来,形成完整的 UI 层级。
  3. 布局控制
    允许父组件包裹子组件(比如共享的导航栏、侧边栏等)。

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. 标记清除算法 会从当前的上下文中全局对象中找到所有可以访问的变量,再遍历我们堆内存中可以访问的变量,没有使用的就会被删除。
  2. 引用计数算法 使用到变量就+1,设为null-1. 缺点:比较消耗性能,无法处理循环引用

内存泄露的几个场景

  • 忘记使用var定义变量
  • 闭包的使用
  • 定时器
  • dom应用未释放
  • 事件监听未注销