无脑字节面基🥲

意外收到字节面试,我是惶恐不安呀。😮‍💨刚开始自我介绍,还是小紧张的。不过面着面着就调整了状态。面的感觉不太行,分析了下:一方面是心里觉得大厂面试就是很难的,第二是收到面试的那几天,没启动"临时抱佛脚"模式,还是自己光啃文章了。不过,其实这种面试正常对待就行,日常学的东西总结进行回答差不多了。

项目拷打

通过 prompt 来做模型微调和上传文件做训练有什么区别

  • prompt 微调通过调整输入提示词引导模型输出,不需要修改模型参数,依据较少的数据实例完成快速迭代,适用于快速验证任务可行性,简单任务场景。缺点也有,prompt训练依据prompt 设计能力,输出可能不稳定。
  • 文件上传训练,通过训练数据调整模型参数,直接适配任务。需要大量数据和算力进行训练,依靠数据质量和训练时间,输出稳定,而且对复杂任务也友好。缺点明显,耗费算力,需要大量标注数据,部署复杂。

1. 后端返回数据时间长,如何实现页面快速渲染?

在应对后端数据返回延迟时,前端需要建立多层次的渲染优化体系。核心思路是通过空间换时间 的策略,将数据获取与内容呈现解耦。分块加载(Chunked Loading) 是基础架构层面的解决方案,要求后端API设计时就将数据划分为 元数据层和详情层 , 例如电商场景先返回商品价格、缩略图等核心信息,后续再异步加载用户评价、详情大图。这种分层加载需要前端架构支持异步数据注入,可结合React的Suspense边界或Vue的异步组件实现模块化加载。

针对页面呈现,使用骨架屏。骨架屏(Skeleton Screen)本质是视觉欺骗技术的高级应用,其实现关键在于建立与真实DOM结构一致的占位体系。主流实现方案会通过AST(抽象语法树)分析组件结构自动生成骨架,并配合CSS动画实现渐进式加载效果 (增加闪烁效果)。对于媒体资源的延迟加载,需要引入Intersection Observer API实现视窗检测,并配合响应式图片技术(srcset/sizes)实现分辨率自适应。还可以结合浏览器的Resource Hint(preload/prefetch)机制实现预加载,通过构建工具自动分析关键资源依赖。

最后,服务端渲染(SSR) 是终极解决方案,但需要解决水合 hydration成本问题。现代框架如Next.js采用选择性注水策略 ,将将用户马上要交互的部分优先注水成为交互组件,而非交互区域静态化。让用户感觉页面不卡顿。当然,还可以使用缓存机制。缓存策略应建立分级存储体系,对时效性数据使用Service Worker(Stale-While-Revalidate模式),对元数据可以采用LocalStorage持久化存储。整体方案需要监控系统支持,通过Performance API分析各阶段耗时,建立数据驱动的优化机制。

各级缓存的具体实现

(1) 时效性数据 → Service Worker 的 Stale-While-Revalidate 模式

场景 :股票价格、新闻列表(数据会变,但短时间可用旧值)。 实时性就看先使用缓存还是先发送请求。 工作原理

  1. 用户请求数据时,先返回旧缓存(保证速度)。
  2. 同时偷偷去后台请求新数据(更新缓存)。
  3. 下次请求时直接用新数据。

代码示例

js 复制代码
self.addEventListener('fetch', (event) => {
  // 拦截所有 fetch 请求
  event.respondWith(
    // 步骤1:打开名为 'my-cache' 的缓存仓库
    caches.open('my-cache').then((cache) => {
      // 步骤2:检查当前请求是否有缓存
      return cache.match(event.request).then((response) => {
        // 步骤3:无论是否有缓存,都发起网络请求(更新缓存)
        const fetchPromise = fetch(event.request).then((newResponse) => {
          // 步骤4:将最新响应存入缓存(注意克隆响应流)
          cache.put(event.request, newResponse.clone());
          return newResponse; // 返回新响应
        });
        // 步骤5:有缓存则立即返回,否则等待网络请求
        return response || fetchPromise;
      });
    })
  );
});

优点:用户无感知,页面永远"秒开",数据最终一致。

  • caches api 是浏览器全局对象,专门用于server worker 网络请求代理,可以存储请求和响应的键值对。

  • event.respondWith() 能拦截网络请求,必须传人promise,最后返回一个response 响应对象。替代浏览器正常发送网络请求。

  • newResponse.clone()

    • 必须克隆响应流,因为响应体(Response.body)只能读取一次。
    • 不克隆会导致缓存和页面渲染冲突。
  • cache.match(event.request) : 缓存查询,检查当前拦截的请求(event.request)是否有缓存副本。

(2) 元数据 → LocalStorage 持久化存储

场景:用户昵称、APP主题色(几乎不变)。

实现方式

js 复制代码
// 存数据
localStorage.setItem('userTheme', 'dark');

// 取数据
const theme = localStorage.getItem('userTheme') || 'light';

优化技巧

  • 设置过期时间(比如7天强制更新)。
  • JSON.stringify 存对象(LocalStorage 只支持字符串)

Next.js 通过以下技术实现选择性注水:

  1. 代码分割(Code Splitting)

    • 自动按路由/组件拆分JS包,只加载当前需要的代码。
    • 比如:首页不加载「用户中心」的JS。
  2. 动态导入(Dynamic Import)

    js 复制代码
    // 只有用户点击时,才加载这个组件(并注水)
    const DynamicButton = dynamic(() => import('./InteractiveButton'), {
      loading: () => <p>加载中...</p>,
      ssr: false // 不参与服务端渲染
    });
  3. Intersection Observer API

    • 监测用户滚动位置,延迟注水即将进入视窗的区域。
  4. React 的 Concurrent Features

    • Suspense 划分注水优先级,高优先级组件(如输入框)先注水。

【AST 通过使用工具(如babel)将组件代码转成AST(拆解出标签、样式、层级关系)。如reac里面使用react-content-loader 通过生成svg占位图,模拟占位形状更明显】。

js 复制代码
<img
  srcset="small.jpg 480w, medium.jpg 1024w, large.jpg 1600w"
  sizes="(max-width: 600px) 480px, 800px"
  src="fallback.jpg"
  alt="示例图片"
>
  • srcset:定义不同宽度对应的图片源。
  • sizes:告诉浏览器在不同屏幕宽度下该图片的显示尺寸。

2. 大模型Chat功能的通信协议选择

SSE(Server-Sent Events)在大模型对话场景中的优势源于其协议特性。基于HTTP/2的SSE连接可以复用现有TLS通道,避免了WebSocket的额外握手开销。SSE的text/event-stream格式天然契合文本流式传输,每个chunk包含完整的事件结构,内置重连机制和消息ID追踪功能。但该方案存在两大限制:浏览器并发连接数限制(HTTP/1.1下6个连接)和代理服务器可能中断长连接。

WebSocket适用于需要双向高频通信的场景,如实时协作编辑器,其二进制帧格式在传输效率上优于SSE的文本协议。长轮询作为降级方案,需要通过精心设计超时时间(建议20-30秒)来平衡实时性和服务端压力。现代实践中,混合方案逐渐流行:初始连接使用SSE推送数据,客户端通过独立HTTP通道发送请求,既保证实时性又避免WebSocket的复杂性。

3. SSE传输富媒体文件的工程实践

SSE传输非文本内容需要突破协议层限制。Base64编码虽可行但存在33%体积膨胀,适用于小文件传输。对于大文件,推荐分片传输方案:将文件切割为多个chunk,每个SSE事件携带分片序号和校验码,前端通过Blob API进行重组。更高效的方案是采用混合协议:SSE传输元数据(如文件大小、MIME类型)和CDN地址(内容分发网络),客户端通过Range Request并行下载分片。

对于视频流等实时性要求高的场景,建议改用WebSocket传输二进制数据,或使用MPEG-DASH/HLS等专用流媒体协议。在需要严格时序的场景中,可以通过SSE事件中的序列号字段实现分片排序。实践中需注意设置适当的Content-Security-Policy,防止Base64编码内容被误拦截。

4. SSE在早期Chat系统中的技术选型考量

SSE的早期采用源于其与现有基础设施的兼容性。在HTTP/1.1时代,SSE可以通过分块传输编码(chunked encoding)实现流式传输,无需改造服务端架构。相比WebSocket需要反向代理支持(如Nginx的proxy_http_version 1.1和proxy_set_header Upgrade),SSE的部署成本更低。对于消息可靠性要求高的场景,SSE内置的last-event-id机制能实现断线续传,客户端在重连时会自动发送最后接收的事件ID。

在流量控制方面,SSE基于普通HTTP连接的特性使其更容易集成现有的限流策略(如令牌桶算法)。但需要注意心跳机制的设计,防止代理服务器超时断开连接。现代浏览器虽然全面支持WebSocket,但SSE在移动端弱网环境下的稳定性仍具优势,特别是在需要穿越企业防火墙的场景中表现更好。

5. 流式渲染性能优化体系

高频次DOM更新会导致布局抖动和渲染性能劣化。缓冲队列方案需要动态调整阈值:初始阶段使用小批量快速呈现,后续逐渐增大批量大小。可以通过监控requestAnimationFrame的执行时间动态调整缓冲策略,当帧率低于30fps时自动增大批量处理量。

时间窗口方案需结合空闲期调度,使用requestIdleCallback实现任务分片。对于输入响应类场景,应实现渲染优先级队列:用户当前聚焦区域的更新设为最高优先级,非可视区域内容延迟合并渲染。在Vue/React框架中,可通过自定义hook封装渲染节流逻辑,同时保证状态更新的原子性。

滚动暂停机制需要精细控制,应通过Intersection Observer监测可视区域,结合页面滚动速度预测用户行为。当检测到快速滚动时,暂停渲染并记录最后位置,在滚动停止后通过虚拟列表技术快速定位。

6. 飞书协同系统的增量更新架构

飞书的增量更新体系建立在分布式操作日志(Operation Log)基础上。采用CRDT(Conflict-Free Replicated Data Type)数据结构保证多端一致性,每个操作包含向量时钟(Vector Clock)标记。前端差异检测使用双缓冲策略:比较虚拟DOM快照时,通过最长公共子序列算法(LCS)确定最小变更集。

服务端使用流式处理引擎,将变更事件按时间窗分桶,合并相同路径操作。对于富文本协同,采用Slate.js框架的OT(Operational Transformation)实现,将光标位置转换为路径表达式。增量包采用二进制编码(如MessagePack),字段级补丁通过JSON Pointer定位。

客户端应用更新时,通过代理模式拦截数据访问,在内存中维护版本树。当检测到版本冲突时,根据策略(最后写入优先/手动合并)解决冲突。性能优化方面,采用WebAssembly加速Diff计算,Worker线程处理复杂数据对比,保证主线程流畅性。

  • cursor 或者 windurf 在你开发的过程中占比大吗,你觉得使用他们的缺点是什么
  • 你用过字节的 trace 吗,说说感受

左边固定,右边自适应,该怎么设置?

右边加上多行省略怎么做? 再换成单行,你右边用了flex:1,然后左边现在放在图片,图片突然被压缩了怎么办如何解决然让他不被压缩?

想到 flexbox布局,设置 flex:1

css 复制代码
.container {
  display: flex;
}

.left {
  width: 200px; /* 设置左边固定宽度 */
  background-color: #f0f0f0; /* 示例背景色 */
}

.right {
  flex: 1; /* 右边自适应宽度 */
  background-color: #e0e0e0; /* 示例背景色 */
}
  • 左边图片不压缩,flexbox 默认调整子元素大小适用容器,设置object-fit 保证图片宽高比不变
css 复制代码
.left img{
   width:100%;
   height:100%;
   object-fit:cover; /*保持宽高,超出裁剪 */
  }
  • 右边显示多行省略,设置line-clamp 属性
css 复制代码
.right {
    flex:1;
    display:-webkit-box;
    -webkit-box-orient:vertical;
    -webkit-line-clamp:3 /*设置行数*/
    overflow:hidden;
 }

单行显示 text-overflow

css 复制代码
.right {
    flex:1;
    white-space:nowrap; /*防止内容换行 */
    overflow: hidden;
    text-overflow: ellipsis; /*省略号*/
 }

平板横竖屏广告屏适配

现在有一个场景,我们需要做一个广告屏幕,后面的场景省略,我理解的是做图片适配。 如果是平板呢?因为平板会反转,横屏,竖屏,你会怎么解决?

  • 动态监听屏幕方向并且调整布局,在平板监听屏幕变化动态调整布局。在andriod通过重写onConfigurationChanged。
js 复制代码
@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
        // 横屏时的布局调整
        adjustLayoutForLandscape();
    } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
        // 竖屏时的布局调整
        adjustLayoutForPortrait();
    }
}
  • 媒体查询,进行适配
js 复制代码
/* 默认竖屏样式 */
.ad-container {
    width: 100%;
    height: auto;
}

/* 横屏样式 */
@media only screen and (orientation: landscape) {
    .ad-container {
        width: 50vw; /* 或者根据需要调整 */
        height: auto;
    }
}
  • object-fit 属性,避免压缩

vue2里面是怎么去重写数组的方法的?

Vue2 在 Array.prototype 上重写了七个会改变原数组的方法(pushpopshiftunshiftsplicesortreverse),以便在调用时触发视图更新。其核心在于:

  • 劫持原型:替换对应方法,先调用原生操作,再通知依赖更新。
  • Object.defineProperty:用于对象属性的 getter/setter 拦截,兼顾依赖收集和更新触发。
  • vue 自定义:methods或computed
js 复制代码
export default {
  data() {
    return {
      myArray: [1, 2, 3]
    };
  },
  methods: {
    customPush(value) {
      console.log('Custom push called with:', value);
      this.myArray.push(value); // 调用原生的 push 方法
    }
  }
};


<button @click="customPush(4)">Push 4</button>

vue 响应式原理

  • Vue 2 通过 Object.defineProperty 劫持了对象属性的读取和设置操作。当你访问或修改一个属性时,Vue 可以通过 getter 和 setter 捕获这些操作,并触发视图的更新
  • 依赖收集和更新机制是响应式系统的核心,通过发布-订阅模式实现

依赖收集 track

  • 当你访问一个响应式数据时,Vue 会将当前的视图组件(或计算属性)与该数据建立依赖关系。这个过程称为依赖收集。
  • 每个响应式数据都有一个依赖集合(Dep),用于存储所有依赖于该数据的视图组件或计算属性。
  • 例如,当你在模板中使用 {{ count }},Vue 会将当前组件的渲染函数与 count 属性建立依赖关系

更新机制 trigger

  • 当你修改一个响应式数据时,Vue 会触发 setter,并通知所有依赖于该数据的视图组件或计算属性进行更新。
  • Vue 使用发布-订阅模式Pub/Sub来实现更新机制。每个响应式数据都有一个订阅者列表,当数据发生变化时,Vue 会遍历这个列表并通知每个订阅者。
js 复制代码
function reactive(obj) {
  return new Promise(obj,{
    get(target,key,receiver) {
        track(target,"get",key)
        return Reflect.get(target,key,receiver)
    },
    set(target,key,value,receiver) {
        const oldValue = target[key]
        const res = Reflect.set(target,key,value,receiver)
        if(oldValue !== value) {
            trigger(target,"set",key)
        }
        return res
    }
})}

事件发布-订阅模式

on 订阅,emit 提醒,通过foreach 遍历每个进行订阅的组件或元素,off 取消,使用filter 过滤。

js 复制代码
// 订阅发表
class EventEmitter {
    constructor() {
        this.events = {}
    }
    // 订阅on
    on(event,callback) {
        if(!this.events[event]) {
            this.events[event] = []
        }
        this.events[event].push(callback) // 订阅
    }
    // 发布
    emit(event,...args) {
        if(this.events[event]) {
            this.events[event].forEach(callback => {
                callback(...args)
            });
        }
    }
    // 取消
    off(event,callback) {
        if(this.events[event]) {
            this.events[event] = this.events[event].filter(cb => cb !== callback)
        }
    }
}
const emitter = new EventEmitter();
emitter.on('update', (newVal) => {
  console.log('Data updated to:', newVal);
});
emitter.emit('update', 5); // 输出: Data updated to: 5

koa 洋葱模型

中间件链,使用next() 进行下一个中间件,从外到内,按照注册顺序依次每个中间件"进来";从内到外,当调用next后,进行下一个中间件"进来",逐层返回,当没有next后不能返回之前注册的中间件

js 复制代码
const Koa = require('koa');
const app = new Koa();

app.use(async (ctx, next) => {
  console.log("第一个中间件-进来");
  await next();
  console.log("第一个中间件-出去");
});

app.use(async (ctx, next) => {
  console.log("第二个中间件-进来");
  await next();
  console.log("第二个中间件-出去");
});

app.use(async (ctx, next) => {
  console.log("第三个中间件-进来");
  // 这里没有调用 next(),所以不会继续往下走
  console.log("第三个中间件-出去");
});

app.listen(3000);

打印

js 复制代码
第一个中间件-进来
第二个中间件-进来
第三个中间件-进来
第三个中间件-出去

跨域cors,怎么进行前后端

  • 使用控制台或network,初步查看是哪个响头

  • 如果200,后端没有设置,404或500 请求或后端问题

  • 检查预检请求;对复杂请求(post 带自定义头或content-type:application/json),没有option 请求,简单请求。问题在access--control-allow-origin;- 如果有 OPTIONS 请求,检查它的响应头是否包含 Access-Control-Allow-MethodsAccess-Control-Allow-Headers

  • 如果后端无法修改 CORS 配置,可以在前端开发环境中设置代理。

  • 例如,在 vitewebpack 中配置代理:

    js 复制代码
    // vite.config.js
    export default {
      server: {
        proxy: {
          '/api': {
            target: 'http://example.com',
            changeOrigin: true,
          },
        },
      },
    };
  • 这样,前端请求 /api 时会被代理到目标服务器,避免跨域问题

相关推荐
崔庆才丨静觅6 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60617 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了7 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅7 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅7 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅7 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment8 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅8 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊8 小时前
jwt介绍
前端