2025-02-20 腾讯微信小店实习生面试

现在刚面完记录一下.

没有问八股,上来直接根据简历进行拷打.

1. Flutter这项技术有什么优缺点

我回答了:

优点:

  1. 一次开发,多端运行
  2. 性能相对Uniapp,React Native来说较好.

缺点:

  1. 开发体验较差,嵌套地狱

GPT总结:

Flutter 作为 Google 推出的跨平台开发框架,近年来备受关注。以下从开发效率、性能、生态、维护成本、学习曲线、应用场景等多个角度分析其优缺点:

一、优点分析

  1. 跨平台能力
    • 单代码库覆盖多平台:通过一套 Dart 代码可同时编译为 iOS、Android、Web、Windows/macOS/Linux 桌面端甚至嵌入式设备应用,大幅减少开发成本。
    • 一致性 UI:自绘引擎(Skia)直接渲染控件,避免平台原生组件差异导致的 UI 不一致问题,例如 Android 和 iOS 的按钮样式差异。
  2. 高性能体验
    • 接近原生性能:通过 AOT(Ahead-of-Time)编译生成原生机器码,性能优于基于 JavaScript 的框架(如 React Native)。
    • 60/120 FPS 流畅度:借助 Skia 图形引擎和高效的渲染管线,可实现流畅的动画和复杂 UI 交互。
  3. 开发效率提升
    • 热重载(Hot Reload):修改代码后立即看到效果,无需重新编译,调试效率提升 30% 以上。
    • 丰富的组件库:提供 Material DesignCupertino 风格组件,内置动画、布局等高级控件,快速构建复杂界面。
  4. 生态与社区
    • 官方强力支持:Google 持续投入,Flutter 3.0 后已支持全平台,长期维护有保障。
    • 插件市场丰富Pub.dev 提供超过 2.5 万个第三方包(如网络请求、状态管理、数据库等),覆盖常见功能需求。
  5. 定制化与灵活性
    • 高度可控的 UI:通过组合 Widget 实现像素级控制,适合需要高度定制化设计的应用(如游戏、教育类 App)。
    • 与原生代码互操作:通过 Platform Channel 可调用 Java/KotlinSwift/Objective-C 代码,兼容现有原生功能。

二、缺点分析

  1. 包体积较大
    • 基础体积偏大:空项目 APK 大小约 15-20MB(React Native 约 7MB),对安装率敏感的应用需优化(如动态交付、代码裁剪)。
  2. Dart 语言限制
    • 生态相对小众:Dart 语言在服务端、Web 等领域应用较少,开发者需额外学习成本(语法类似 Java/JavaScript,但特性如 isolate 需要适应)。
    • 类型系统灵活性不足:相比 TypeScript 或 Kotlin,Dart 的泛型和元编程能力较弱。
  3. 原生功能依赖插件
    • 第三方插件质量参差:部分原生功能(如蓝牙、摄像头高级操作)依赖社区插件,可能存在兼容性问题或维护停滞风险。
    • 平台特定代码仍需编写:深度依赖硬件或系统 API 的功能(如后台服务)仍需原生开发。
  4. Web 和桌面端成熟度
    • Web 端性能瓶颈:复杂动画或大数据量场景下,Web 版本的流畅度可能低于纯前端框架(如 React/Vue)。
    • 桌面端适配问题:Windows/macOS 的窗口管理、菜单栏等功能仍需额外配置。
  5. 内存消耗
    • 自绘引擎开销:渲染复杂界面时内存占用可能高于原生应用,低端设备上需注意优化。

三、适用场景 vs 不适用场景

适合使用 Flutter 不建议使用 Flutter
需要快速开发 MVP 或跨平台应用 强依赖平台特性的应用(如 iOS 小组件开发)
UI 复杂且要求高度一致性的应用 对安装包大小极度敏感的应用(如工具类轻量 App)
中小型团队希望降低长期维护成本 已有成熟原生团队且无需跨平台需求
需要覆盖多平台但资源有限的项目 需要直接调用大量原生 SDK 或底层 API 的场景

四、总结

  • 优势领域:跨平台 UI 密集型应用、快速迭代项目、初创公司 MVP 开发。
  • 挑战:包体积优化、深度原生功能集成、Web/桌面端的性能调优。
  • 未来趋势:随着 Flutter 对折叠屏设备、嵌入式系统(如汽车 OS)的支持扩展,其应用场景可能进一步拓宽。

决策建议:若团队接受 Dart 语言且项目以 UI 交互为核心,Flutter 是高效的选择;若追求极致的原生性能或依赖小众硬件功能,需谨慎评估。

面试官说到了一个什么部署层面的热更新,没有了解过,下去总结一下.

部署层面的热更新(Hot Update)是指在不重新安装或重启应用的情况下,通过远程推送代码、资源或配置的更新包,直接更新已部署应用的某些部分。这种技术常用于快速修复线上问题、动态调整功能或优化用户体验,同时避免依赖应用商店的审核流程。以下是关于热更新的详细解析:


一、热更新的核心原理

  1. 动态加载机制
    • 通过替换或加载远程的代码/资源文件(如 JavaScriptDart 脚本、UI 配置图片等),在运行时动态更新应用逻辑或界面。
    • 例如:React Native 通过 JavaScript 代码的热更新,Flutter 可通过资源文件或部分逻辑的动态化实现。
  2. 增量更新与版本管理
    • 仅下载差异化的更新包(而非全量包),减少用户流量消耗。
    • 服务端管理版本号,客户端根据版本号拉取最新更新。
  3. 安全校验机制
    • 对更新包进行签名、加密,防止篡改或恶意代码注入。

二、热更新的典型实现方式

1. 代码热更新

  • 适用场景:修复逻辑 Bug、调整业务规则。
  • 技术方案
    • React Native:通过 Metro 服务推送 JavaScript 代码包。
    • Flutter:受限于 AOT 编译,原生代码(Dart 编译后的机器码)难以直接热更新,但可通过以下方式部分实现:
      • 动态化脚本:嵌入 LuaJavaScript 引擎,将部分逻辑移至脚本层。
      • 插件化框架:如 Fair(社区方案),通过动态下发 Dart 代码的 JSON 配置实现 UI 更新。
  • 平台限制
    • iOS 严格限制:苹果禁止下载可执行代码(如 JIT 编译的代码),仅允许通过 WebView 或 JavaScriptCore执行脚本(需符合 App Store 审核政策)。

2. 资源热更新

  • 适用场景:更换图片、字体、多语言文案、UI 布局配置。
  • 技术方案
    • 远程下载资源文件(如 JSON、图片、XML),替换本地缓存。
    • Flutter 中可通过 AssetBundle 或自定义资源管理器实现。

3. 配置热更新

  • 适用场景:调整服务地址、功能开关、AB 实验参数。
  • 技术方案
    • 从服务端拉取配置文件(如 Firebase Remote Config、Apollo 配置中心)。
    • 无需发版即可实时生效。

三、热更新的优缺点

优点

  • 快速迭代:修复紧急 Bug 或上线新功能无需等待应用商店审核(尤其对 Android 有效)。
  • 用户无感知:无需用户手动更新,提升用户体验。
  • 灰度发布:可针对特定用户群体逐步推送更新,降低风险。

缺点

  • 技术复杂度高:需设计更新策略、版本兼容性、回滚机制。
  • 平台政策风险:尤其是 iOS,动态下载代码可能违反 App Store 审核指南(如条款 2.5.2)。
  • 安全隐患:若未对更新包严格校验,可能被注入恶意代码。
  • 维护成本:需搭建热更新服务端(如 CDN 分发、版本管理后台)。

四、热更新的合规性考量(以 iOS 为例)

  1. 苹果审核政策
    • 允许:更新资源文件(图片、文案)、配置参数(如开关、URL)。
    • 禁止:下载可执行代码(如修改原生逻辑的机器码、Dart 编译后的代码)。
    • 灰色地带:通过 JavaScriptCoreWebView 运行的脚本需确保不涉及核心功能。
  2. 规避风险的建议
    • 避免直接更新 Dart 代码,转为配置驱动 UI(如 JSON 描述界面)。
    • 使用苹果允许的动态化方案(如 Lottie 动画、服务端下发 HTML5 页面)。

五、Flutter 热更新的实践方案

1. 资源/配置热更新(合规且简单)

dart

复制

1
2
3
4
5
6
7
8
9
// 示例:从远程拉取 JSON 配置更新 UI
void loadRemoteConfig() async {
final response = await http.get(Uri.parse('https://api.example.com/config'));
final config = jsonDecode(response.body);
setState(() {
title = config['title'];
imageUrl = config['imageUrl'];
});
}

2. 动态化 UI 框架(需谨慎)

  • Fair:通过 JSON 描述 UI 和逻辑,动态下发 JSON 实现更新。
  • 限制:仅支持有限控件和逻辑,复杂交互仍需原生代码。

3. 混合开发方案

  • 核心功能使用 Flutter,高频修改部分嵌入 WebView(如活动页面)。
  • 通过 JavaScript 桥接与 Flutter 通信。

六、总结

  • 热更新的核心价值:快速响应、降低发版成本,但对技术合规性要求高。
  • Flutter 的挑战:受限于 AOT 编译和苹果政策,代码热更新难以直接实现,需依赖资源/配置更新或社区动态化方案。
  • 推荐策略
    • Android:可适度使用代码热更新(需注意性能)。
    • iOS:优先采用配置驱动 UI,避免触碰苹果红线。
    • 通用方案:结合服务端动态化(如 API 控制功能开关)+ 资源热更新。

参考百度智能云博客

举一反三: 当问到一个技术有什么优缺点时,可以从哪些角度去考虑?

GPT回答:

1. 性能

  • 优点:技术栈是否可以优化页面加载时间、渲染性能等。例如,虚拟 DOM 技术(如 React、Vue)可以减少实际 DOM 操作,从而提高性能。
  • 缺点:某些框架或技术可能导致较大的资源消耗或性能瓶颈,例如过度渲染、内存泄漏、低效的事件绑定等。

2. 学习曲线

  • 优点:某些技术(如 Vue.js)因其简洁的语法和清晰的文档而适合初学者,入门较为容易。
  • 缺点:一些技术(如 React 或 Angular)可能需要较长时间来掌握,尤其是涉及到高级概念(如状态管理、生命周期、Hooks、路由等)的部分。

3. 开发效率

  • 优点:技术是否能提高开发效率,像是框架提供的 CLI 工具、模板引擎、代码热重载等,都能提升开发者的工作效率。
  • 缺点:如果技术过于复杂或者工具链过于繁琐,可能会影响开发效率。比如,有些前端框架需要配置复杂的构建系统。

4. 社区和生态系统

  • 优点:拥有强大的社区支持和丰富的生态系统,意味着技术可以快速解决问题,并且有大量的第三方库和工具可供使用。
  • 缺点:某些较小或较新的技术可能缺乏社区支持,遇到问题时可能会缺少资源或解决方案。

5. 跨平台和兼容性

  • 优点:如果技术支持跨平台开发,如 React Native 支持 Web、iOS 和 Android 开发,或者 Vue、React 有良好的浏览器兼容性,那么可以减少开发的复杂性。
  • 缺点:一些技术可能在不同平台或浏览器上的兼容性不好,或者需要额外的 polyfill、shims 等来保证兼容性。

6. 可维护性和扩展性

  • 优点:良好的框架通常能帮助开发者保持代码的可维护性,例如通过模块化、组件化等方式,代码结构清晰,便于扩展和维护。
  • 缺点:一些技术可能没有提供好的结构约束或指导,导致项目在长期维护中变得难以扩展和管理。

7. 文档和资源

  • 优点:详细且易懂的文档能让开发者快速入门并理解技术栈的使用。例如,Vue 和 React 都有非常清晰和全面的官方文档。
  • 缺点:缺乏良好文档的技术可能会让开发者花费更多时间去理解和解决问题。

8. SEO(搜索引擎优化)

  • 优点:如果技术支持服务器端渲染(SSR)或静态站点生成(SSG),则可以提升 SEO 性能,如 Next.js(基于 React)、Nuxt.js(基于 Vue)。
  • 缺点:纯客户端渲染的应用(如传统的 SPA)可能会对 SEO 造成负面影响,因为搜索引擎可能无法完全索引动态加载的内容。

9. 安全性

  • 优点:某些框架和库提供内置的安全措施,如防止跨站脚本攻击(XSS)、跨站请求伪造(CSRF)等,帮助提升应用的安全性。
  • 缺点:如果不合理使用某些技术,可能会带来安全隐患。例如,客户端渲染的应用容易受到 XSS 攻击。

10. 支持工具和插件

  • 优点:大部分成熟的框架都支持丰富的插件和开发工具(例如,React DevTools、Vue DevTools),这些工具能帮助开发者调试和优化应用。
  • 缺点:一些技术可能没有相应的开发工具或插件,导致开发者必须依赖手动调试或自己编写工具。

11. 与其他技术的集成

  • 优点:技术的集成能力越强,跨技术栈的协作就越顺畅。例如,React 和 Vue 都能方便地与 REST API、GraphQL 或其他前端/后端技术进行无缝集成。
  • 缺点:如果技术栈封闭或与其他技术兼容性差,可能会导致开发的复杂性增加,甚至需要额外的适配层。

12. 更新频率和版本迭代

  • 优点:技术更新频繁,持续改进,意味着更好的性能、更少的bug、更强的功能支持。像 React 和 Vue 都有较为活跃的更新周期。
  • 缺点:过于频繁的版本更新可能导致开发者需要不断地跟进和适配新版本,可能会破坏已有的开发流程和代码。

2. CommonJS ModuleESModule的区别

我回答了:

CommonJS是Node.js的社区规范的模块化标准.

使用起来比较简单.

导出使用

1
2
3
4
5
6
7
8
module.exports = {

}

// 或者直接使用

exports = {}

导入直接使用

1
const module = require('module')

得到的module就是导出的对象.

ESM是官方的模块化标准

导出有默认导出和具名导出

1
2
export const test = 1;
export default 1;

导入是如果直接导入得到的是默认导出,使用具名导入必须使用与具名导出相同的名称.

还说到了静态导入动态导入,这里其实掌握的不是很好,需要进一步完善.

面试官从ESM支持静态导入分析问到了如何做性能优化,在略微提醒下想起了tree-shaking树摇技术去掉模块中没有实际使用的代码.

感觉对性能优化有很大的兴趣,问的比较多.

GPT回答:

CommonJS (CJS) 和 ES Modules (ESM) 是两种常见的 JavaScript 模块化机制,它们在语法、行为、以及如何与 Node.js 环境或浏览器中的代码交互等方面有所不同。下面从多个角度分析它们的区别:

1. 语法差异

  • CommonJS:

    • 使用 require() 来导入模块,使用 module.exportsexports 来导出模块。
    1
    2
    3
    4
    5
    // 导入模块
    const moduleA = require('./moduleA');

    // 导出模块
    module.exports = function() { ... };
  • ESM:

    • 使用 import 来导入模块,使用 export 来导出模块。
    1
    2
    3
    4
    5
    6
    // 导入模块
    import { something } from './moduleA';

    // 导出模块
    export function myFunction() { ... };
    export default class MyClass { ... };

2. 加载机制(同步 vs 异步)

  • CommonJS:

    • CommonJS 是同步加载模块的。这意味着在加载模块时,执行线程会被阻塞,直到模块完全加载。

    • 适用于 Node.js,因为它通常处理的是文件系统中的模块,因此同步加载的方式可以提升效率。

    • 例如:

      1
      const data = require('./data.json'); // 加载文件是同步的
  • ESM:

    • ESM 是异步加载模块的,这使得它更适合浏览器环境,能够避免阻塞渲染线程。

    • ESM 模块会根据模块的 import 语句异步加载,并且通常会在编译阶段进行静态分析,从而提升性能。

    • 例如:

      1
      2
      3
      import('./moduleA.js').then((module) => {
      console.log(module);
      });

3. 模块解析和执行时机

  • CommonJS:

    • CommonJS 在模块首次被 require() 时会执行并缓存模块。每次 require() 同一模块时,都会返回缓存的模块。
    • 由于模块是按需加载的,它在导入时会立即执行。
    • 这种机制导致 CommonJS 模块是立即执行的,执行的顺序依赖于 require() 的调用顺序。
    1
    2
    3
    // moduleA.js
    console.log('Module A is loaded');
    module.exports = function() { return 'Hello from A'; }
  • ESM:

    • ESM 模块在代码的顶层静态解析时就会被分析,并且模块的执行也遵循声明的顺序。
    • 由于 ESM 支持静态分析,所以它可以实现如 tree shaking(去除未使用的代码)等优化。
    • ESM 模块是惰性加载的,只有在真正需要时才会执行。
    1
    2
    // moduleA.js
    export const greeting = 'Hello from A';

4. 模块缓存

  • CommonJS:
    • CommonJS 会缓存整个模块的内容,因此一个模块只会执行一次。如果你多次 require() 同一个模块,返回的就是缓存的模块对象。
  • ESM:
    • ESM 也会缓存模块,但缓存的内容是模块的“导出”对象,而不是整个模块对象。ESM 模块更偏向于导出固定的对象或值,能够支持更精确的缓存管理。

5. 默认导出

  • CommonJS:

    • CommonJS 模块导出可以是任何值,包括函数、对象等,但没有概念上的“默认导出”。
    • 模块的默认导出通常通过 module.exports 完成,其他模块通过 require 获取。
    1
    module.exports = function() { ... };
  • ESM:

    • ESM 提供了 default 关键字,允许模块有一个默认导出。这使得一个模块可以仅有一个主要的导出值。
    1
    export default function() { ... };
    • 如果其他模块没有显式指定要导入的内容,则默认导出的内容会被导入:
    1
    import myFunction from './moduleA';  // 导入默认导出

6. 跨环境兼容性

  • CommonJS:
    • CommonJS 是为 Node.js 环境设计的,所以它在 Node.js 中非常流行,但在浏览器中不原生支持。为了在浏览器中使用 CommonJS,通常需要使用打包工具(如 WebpackBrowserify)来转换成浏览器可以识别的格式。
  • ESM:
    • ESM 是现代浏览器原生支持的模块化方案,因此它更适合前端开发。通过 <script type="module">,浏览器可以直接解析 ESM 模块。
    • 在 Node.js 中,ESM 直到较新的版本才得到了原生支持。要使用 ESM,通常需要指定 type: "module" 或使用 .mjs 文件扩展名。

7. 动态导入

  • CommonJS:

    • CommonJS 只支持同步的 require() 导入,没有内建的机制来处理动态导入。
  • ESM:

    • ESM 支持动态导入,使用 import() 来异步加载模块,这对于懒加载按需加载非常有用。
    1
    2
    3
    import('./moduleA.js').then((module) => {
    module.someFunction();
    });

8. 模块路径解析

  • CommonJS:
    • CommonJS 使用相对路径或绝对路径(如 require('./moduleA'))来解析模块。Node.js 会根据文件系统结构来查找模块。
  • ESM:
    • ESM 的路径解析更严格一些,特别是在浏览器中。例如,导入的路径必须是明确的相对路径或完整的 URL,不能像 CommonJS 那样省略文件扩展名(除非是指向一个 URL)。

9. 支持的环境

  • CommonJS:
    • 主要用于 Node.js 环境中,广泛支持在服务器端。
  • ESM:
    • 适用于浏览器和 Node.js,尤其是浏览器原生支持,Node.js 从 v12 开始提供对 ESM 的正式支持。

总结

CommonJS 更适合服务器端的 Node.js 开发,采用同步加载和模块缓存的机制,适合处理文件系统等同步任务。

  • ESM 是标准的 ES6 模块,支持静态分析、异步加载、动态导入,适合前端开发并且在现代浏览器中原生支持。

选择哪个模块化方案取决于项目的需求

  • 如果是 Node.js 服务端应用,CommonJS 可能更方便。
  • 如果是现代浏览器应用,ESM 提供更好的性能和兼容性。

3. 实习项目中主要负责了什么内容

4. SSE技术详细说一下

5. 团队沟通协调具体用了什么方法,有什么好处

6. 讲一下项目亮点

Scss函数实现响应式设计

tsx和pkgroll打包工具的优缺点

由此引申到了其他打包工具,webpack和vite,问了他们的区别和优缺点

7. 简历拷打完之后问从所有可能得方式,比如图片,网络,框架等等方面如何进行前端优化.

回答了树摇和代码压缩

追问了代码压缩有什么格式

回答了gzip,不知道对不对,完了研究一下

笔试题

1. 版本号大小比较

2. 基于Promise实现setInterval

3. 买卖股票的最佳时间

2025-02-28 腾讯金融实习生

笔试题

  1. 括号匹配
  2. 数字千分位分割
  3. 图片懒加载

2025-03-10 天美客户端一面

1. 介绍一下实习项目,以及主要负责的工作

在四川品忆科技实习期间,我参与了“力喵软件”(腾讯应用宝上架)的开发,这是一款面向游戏玩家的数据库工具,提供游戏比价、攻略、日志管理等功能。
主要负责:​

  • 游戏日志模块:独立开发日志记录、分类检索功能,并新增“名称模糊搜索”功能,通过用户调研优化交互,提升满意度23%。
  • 米奥智能体模块:基于性能与成本考量,采用SSE(Server-Sent Events)技术实现流式数据推送,节省30%服务器资源。
    成果:​ 1周自学Flutter并交付核心模块,代码通过率100%;主动联调接口,推动版本提前1周上线。

2. 对Flutter技术的感受

Flutter的跨平台一致性高性能渲染引擎(Skia)让我印象深刻。

  • 优点:热重载极大提升开发效率;Widget树的设计使UI高度可控;Dart语言的AOT/JIT结合兼顾开发与发布性能。
  • 实战价值:在“力喵”项目中,通过单代码库覆盖Android/iOS,降低维护成本,且流畅度接近原生。

3.追问:Flutter热更新的原理

热重载(Hot Reload)通过增量编译实现:

  1. 修改代码后,Dart编译器生成增量Kernel文件(仅含变更部分)。
  2. VM将新代码注入正在运行的Dart虚拟机,重建Widget树并保留应用状态(如变量值)。
  3. 界面即时刷新,无需重启应用。
    与热重启(Hot Restart)的区别:热重启会重置应用状态,重新加载整个Dart代码。

4. 追问: 如何解决Flutter的嵌套地狱问题

通过组合与抽象降低嵌套复杂度:

  • 组件封装:将复杂UI拆分为StatelessWidget/函数组件(如buildHeader())。
  • 语法优化:使用..级联运算符或Column/Rowchildren参数简化嵌套。
  • 状态管理:引入Provider/Riverpod,将逻辑与UI解耦,减少setState导致的嵌套膨胀。
解决方法 优点 缺点
组件抽离:将嵌套层级较深的UI部分提取wield独立的StatelessWidgetStatefulWidget 代码复用
可读性提升
组件数量增加
拆分不当可能会影响重建效率
使用动态构建组件:利用ListView.builderGridView.builder 高效渲染
减少冗余代码
仅适用于动态列表场景,无法解决一般的布局嵌套
第三方库辅助:引入flutter_hooksfunctional_widget等库 代码简洁:函数式语法减少模板代码
Hooks可替代setState
依赖风险:需维护第三方库版本
需要熟悉新API和概念
状态管理:使用Provider,Block等状态管理库,将业务逻辑与UI分离 关注点分离:UI仅负责渲染
减少传递嵌套:避免逐层传递状态
初始化成本:需要配置状态管理框架
Dart语法特性:使用级联运算符(..)或箭头函数简化代码 代码简洁:减少括号嵌套 适用性有限:仅适用于属性赋值场景

5. AOT和JIT的区别

  • JIT(Just-In-Time):运行时编译(如开发阶段),支持热重载,适合快速迭代,但启动稍慢。
  • AOT(Ahead-Of-Time):发布前编译为机器码(如Flutter生产包),启动快、性能高,但失去动态调试能力。
    Flutter应用:开发阶段用JIT+热重载,发布时用AOT优化性能。
AOT JIT 解释执行
原理 静态编译整个程序,无需运行时编译.例如,C/C++直接编译为机器码,.NET NativeJavaGraalVM Native Image将字节码AOT编译为本地可执行文件. 结合解释器和编译器,初始阶段解释执行,随后通过计数器或采样识别热点代码,触发即时编译,例如Java的HotSpot JVM使用分层编译(C1/C2编译器),JavaScript的V8引擎采用Ignition+TurboFan组合 解析器将代码解析为抽象语法树(AST)或直接执行预编译的字节码,每一步操作都需要动态查找语义并执行.例如,Python和早期的JavaScript引擎直接解释源码,而Java的字节码解释器执行.class文件
执行过程 在程序运行前将源代码或中间表示(如字节码)完全编译为平台相关的机器码 在运行时监控代码执行,将热点代码(如多次调用的方法)动态编译为机器码 逐行解释并执行源代码或中间表示(如字节码),不生成机器码
特点 无法利用运行时信息进行动态优化,可能牺牲部分灵活性(如反射) 利用运行时信息(如类型特化,分支预测)生成优化后的机器码 执行时需频繁处理类型检查,动态绑定等,效率较低.
启动时间 最快,直接执行预编译的机器码 较慢,需要初始解释阶段,随后触发编译 最快,直接开始执行,无编译开销
运行效率 高,但静态优化可能不如JIT的动态优化充分 高,热点代码经优化后接近原生性能 最低,频繁解析和动态查找
内存占用 中等,仅存储编译后的机器码 较高,需存储机器码和运行时数据(如编译缓存) 最低,仅需存储源码或字节码
优化 静态优化
基于编译时的静态分析,如常量传播,死代码消除
无法处理动态特性(如多态调用,反射)可能导致保守优化
利用运行时信息进行激进优化,如内联缓存(Inline Caching),逃逸分析
支持逆优化(Deoptimization),当假设失效时,回退到解释器
无深度优化,仅执行基本逻辑,效率依赖解释器实现
跨平台支持与部署 弱,需为每个目标平台单独编译 中等,依赖运行时环境(如JVM),但能生成平台相关的机器码 强,同一份字节码可在不同平台运行
应用场景 资源受限环境(嵌入式系统,移动端),追求启动速度的应用
安全敏感的场景,避免运行时编译攻击
适合长期运行的服务端应用(Java微服务,Nodejs)可最大化运行时优化
动态语言(JavaScript)的性能关键场景
适合脚本语言,配置性代码或快速迭代开发
对启动时间敏感的场景.

6. 追问: JIT和解释执行有什么区别

  • 解释执行:逐行翻译字节码为机器码执行(如JavaScript引擎),效率较低。
  • JIT:运行时将高频代码编译为机器码缓存(如V8引擎),后续直接执行,平衡启动速度与长期性能。
    核心差异:JIT通过编译优化提升性能,解释执行无此过程。

7. TypeScript和Dart有什么区别

  • 类型系统:TS是渐进类型(可选),Dart是强类型(必选)。
  • 生态:TS面向Web/Node.js,Dart专注Flutter跨端开发。
  • 编译:TS转译为JS;Dart支持JIT(开发)和AOT(发布)。
    选择场景:TS适合前端工程化,Dart适合高性能跨端应用。

8. 追问TypeScript是怎么编译成JavaScript的

通过tsc编译器:

  1. 类型擦除:移除TS类型注解(如number)。
  2. 语法降级:将ES6+语法(如async/await)转换为目标版本(ES5)。
  3. 模块解析:处理import/export为CJS/ESM格式。
    工具链:配合tsconfig.json配置编译规则(如路径别名、目标环境)。

9. Flutter中的StatefulWidgetStatelessWidget有什么区别?

  • StatelessWidget:静态UI,无内部状态(如纯展示组件),build方法依赖外部参数。
  • StatefulWidget:动态UI,通过State对象管理可变状态(如计数器),可触发setState重建视图。
    生命周期StatefulWidgetinitStatedispose等钩子,用于资源管理。

10.介绍一下SSE技术

SSE(Server-Sent Events) 基于HTTP的单向通信协议,服务器可主动推送数据到客户端(如实时日志、价格更新)。

  • 与WebSocket对比:SSE更轻量(无需双向通信),自动重连,但仅支持文本数据。
    应用场景:在“米奥智能体”模块中,用SSE接收AI生成的流式响应,降低服务器开销。

11. 你说你用过Uniapp去开发原生应用,那它是如何打包成原生应用的?原理是什么?

Uniapp通过编译时转换生成原生代码:

  1. 代码转换:将Vue组件编译为JS Bundle,通过WebView渲染(或调用原生组件)。
  2. 原生封装:使用各平台(如微信小程序、Android/iOS)的SDK打包为对应格式。
    底层原理:基于V8/JavaScriptCore引擎运行JS,通过桥接层调用原生API(如相机、GPS)。

12. 原生应用和Web应用有什么区别?

  • 性能:原生直接调用系统API(如GPU渲染),Web依赖浏览器引擎,性能较低。
  • 权限:原生可访问硬件(传感器、文件系统),Web受沙盒限制(如无法直接读写本地文件)。
  • 体验:原生支持系统级交互(如手势、通知栏),Web依赖浏览器行为。

13. 追问: 你说原生引用权限更高?为什么?

  • 系统API直接调用:原生应用通过SDK访问硬件(如蓝牙、摄像头)。
  • 沙盒机制:Web应用运行在浏览器隔离环境,仅能通过有限API(如Web Bluetooth)交互,需用户显式授权。

14. 简单说一下你在你的”学习岛”项目中是如何进行技术选型的?

  • 小程序端:选Uniapp+Vue3,因跨端能力(覆盖微信/支付宝)和开发效率;TypeScript提升代码质量。
  • 管理后台Art Design Pro开箱即用,快速搭建企业级中台。
  • 后端Express+Sequelize轻量灵活,TS强化类型安全;pkgroll解决路径别名和打包问题。
    核心考量:平衡开发效率、维护成本与性能需求。

15. 在该项目中,你前后端通信使用的数据格式是什么?

使用JSON,因其通用性强、可读性好,且主流框架(如Axios)原生支持序列化/反序列化。

16. 追问: 除了JSON外,你使用过哪些数据格式?各有什么优缺点?为什么使用JSON?

  • XML:冗余标签多,解析复杂,适合配置文件和旧系统(如SOAP)。
  • Protobuf:二进制编码,体积小、解析快,但需预定义Schema,适合高性能场景(如游戏通信)。
  • 选择JSON:无模式约束、开发便捷,与RESTful API天然契合,生态完善(如Swagger文档生成)。

2025-3-19 腾讯文档一面

面试提问

1. 数组和链表有什么区别,各自的优缺点

2. 什么是哈希表?如何处理哈希冲突?

3. GET请求和POST请求有什么区别?

追问:他们如何传参?参数大小有没有限制?

特性 GET请求 POST请求
参数位置 URL查询字符串 请求体(Body)
可见性 高(URL暴露) 低(Body隐藏)
数据大小 小(受URL长度限制) 大(受服务器配置限制)
安全性 低(适合非敏感数据) 较高(仍需HTTPS加密)
缓存/书签 支持 不支持
幂等性 是(多次请求结果相同) 否(可能修改资源)

4. http会引起哪些安全问题?https是怎么解决这些安全问题的?

安全问题 HTTP的风险场景 HTTPS的解决方案 技术原理
数据窃听 数据明文传输,攻击者可通过网络抓包(如Wireshark)直接读取敏感信息(密码、Cookie等)。 使用SSL/TLS加密,所有数据在传输前加密,即使被截获也无法解密。 对称加密(如AES)和非对称加密(如RSA)结合,建立安全通道后使用对称密钥高效加密通信数据。
数据篡改 攻击者可修改传输中的内容(如插入广告、劫持跳转链接)。 通过数据完整性校验(如HMAC、SHA-256哈希),确保数据未被篡改。 哈希算法生成数据摘要,接收方验证摘要一致性,若不一致则拒绝数据。
身份冒充(中间人攻击) 攻击者伪造服务器身份(如钓鱼网站),用户无法验证对方真实性。 使用数字证书验证服务器身份,证书由可信的证书颁发机构(CA)签发。 服务器证书包含公钥和域名信息,浏览器验证证书有效性(是否过期、是否被吊销、是否由可信CA签发)。
会话劫持 Cookie和Session ID明文传输,攻击者窃取后可冒充用户身份。 加密所有通信(包括Cookie),防止会话标识泄露。 通过SSL/TLS加密整个HTTP会话,确保Cookie和令牌等敏感信息在传输中不可见。
隐私泄露 URL参数和请求头明文传输,暴露用户行为(如搜索关键词、资源路径)。 加密URL路径和参数(仅在初始DNS查询可能泄露域名)。 TLS 1.3及以上版本支持加密整个HTTP报文,包括URL路径(部分浏览器和服务器已支持)。
重放攻击 攻击者截获合法请求后重复发送(如重复转账)。 通过随机数(Nonce)时间戳防止重放。 每次通信生成唯一随机数,服务端校验请求的时效性和唯一性。
协议降级攻击 攻击者迫使客户端和服务端使用低安全性协议(如SSL 2.0)。 使用TLS协议扩展(如HSTS),强制浏览器与服务端使用高安全性协议。 服务器配置HSTS头,浏览器在指定时间内仅通过HTTPS连接,避免降级到HTTP。

5. 常见的web攻击方式有哪些?如何防御?

攻击类型 攻击原理 防御措施
XSS(跨站脚本攻击) 攻击者注入恶意脚本到网页中,用户浏览时执行(如窃取Cookie、劫持会话)。 - 对用户输入进行过滤和转义(如HTML实体编码)
- 设置Content-Security-Policy(CSP)头限制脚本来源
- 使用HttpOnly标记Cookie防止JS访问。
SQL注入 通过用户输入构造恶意SQL语句,篡改数据库查询逻辑(如删除表、窃取数据)。 - 使用参数化查询(Prepared Statements)或ORM框架(如Hibernate)
- 对输入进行白名单验证,避免拼接SQL语句。
CSRF(跨站请求伪造) 诱导用户访问恶意页面,伪造合法请求(如自动转账、修改密码)。 - 服务端生成并验证CSRF Token
- 设置SameSite Cookie属性为StrictLax
- 验证请求头中的RefererOrigin
点击劫持 通过透明iframe覆盖诱导用户点击隐藏按钮(如触发敏感操作)。 - 设置HTTP头X-Frame-Options: DENYSAMEORIGIN
- 使用CSP的frame-ancestors指令限制页面嵌套。
文件上传漏洞 上传恶意文件(如PHP、JSP脚本)到服务器,执行任意代码。 - 限制文件类型(检查MIME类型和扩展名)
- 重命名文件并使用随机存储路径
- 禁用上传目录的执行权限。
DDoS攻击 通过大量请求耗尽服务器资源(如带宽、连接数、CPU)。 - 使用CDN和负载均衡分散流量
- 配置Web应用防火墙(WAF)和速率限制(Rate Limiting)
- 启用自动扩容和流量清洗服务。
OS命令注入 用户输入被拼接到系统命令中执行(如rm -rf /)。 - 避免直接调用系统命令,使用安全的API替代
- 对用户输入进行白名单过滤,禁止特殊字符(如;&、`
`)。
SSRF(服务端请求伪造) 攻击者利用服务器发起内部网络请求(如访问内网接口、读取本地文件)。 - 禁止用户控制请求的URL(如协议、域名、端口)
- 使用白名单验证目标地址 - 禁用不必要的URL协议(如file://gopher://)。
IDOR(不安全的直接对象引用) 通过修改参数(如用户ID、订单号)访问未授权资源。 - 实施严格的访问控制(ACL),验证用户权限
- 使用间接引用(如映射表)替代直接暴露数据库ID。

6. 常用的设计模式有哪些?举例说明观察者模式和单例模式的应用?

类型 模式名称 核心作用
创建型 单例模式、工厂模式、建造者模式 解耦对象的创建与使用,控制实例化过程
结构型 适配器模式、代理模式、装饰器模式 通过组合对象/类,优化系统结构以增强扩展性
行为型 观察者模式、策略模式、责任链模式 定义对象间的交互方式,提升代码灵活性和可维护性

7. JS和TS有什么区别?为什么要有TS产生?

特性 JavaScript TypeScript
类型系统 动态类型(运行时检查类型) 静态类型(编译时检查类型)
代码错误检测 运行时暴露错误(如未定义变量) 开发阶段即时提示错误(如类型不匹配)
语言能力 原生支持ES标准语法 支持ES语法 + 类型系统、接口、泛型等增强特性
编译过程 无需编译,直接执行 需编译为JS代码(通过tsc或Babel)
开发工具支持 基础代码提示 强大的IDE支持(如自动补全、类型推导、重构)
适用场景 小型项目、快速原型开发 中大型项目、长期维护的复杂系统

8. 什么是内存泄漏?平时在web前端中哪些代码容易引起内存泄漏?如何去避免或排查内存泄漏问题?

一、内存泄漏(Memory Leak)的定义

当程序不再需要的内存未被释放,且无法被垃圾回收机制(GC)回收时,称为内存泄漏。长期累积会导致应用卡顿、崩溃或浏览器标签页崩溃。

内存泄漏 vs 内存膨胀

  • 内存泄漏:程序逻辑缺陷导致内存无法回收(需修复代码)。
  • 内存膨胀:合理占用但内存使用过高(需优化算法或数据结构)。

二、Web前端常见内存泄漏场景与示例

1. 未解绑的事件监听器

javascript

1
2
3
4
5
// 错误示例:元素移除后未移除事件监听
const button = document.createElement('button');
button.addEventListener('click', handleClick);
document.body.appendChild(button);
// 若后续执行 document.body.removeChild(button),但未调用 removeEventListener,handleClick 仍被引用

修复方法

javascript

1
2
3
4
5
// 正确做法:移除元素前解绑事件
const removeButton = () => {
button.removeEventListener('click', handleClick);
document.body.removeChild(button);
};

2. 未被清理的定时器或回调函数

javascript

1
2
3
4
5
6
7
// 错误示例:组件销毁后定时器仍在运行
function Component() {
const timer = setInterval(() => {
console.log('Running...');
}, 1000);
return () => {/* 未清除定时器 */};
}

修复方法

javascript

1
2
3
4
5
6
7
8
// 正确做法:在组件卸载时清除定时器
function Component() {
const timer = useRef();
useEffect(() => {
timer.current = setInterval(() => {}, 1000);
return () => clearInterval(timer.current); // React useEffect清理函数
}, []);
}

3. 闭包意外捕获外部变量

javascript

1
2
3
4
5
6
7
8
// 错误示例:闭包长期持有大对象引用
function processData() {
const largeData = new Array(1000000).fill('data');
return function () {
console.log('处理完成'); // largeData 被闭包间接引用,无法释放
};
}
const callback = processData();

修复方法

javascript

1
2
3
4
5
6
7
8
// 正确做法:使用后置空引用
function processData() {
const largeData = new Array(1000000).fill('data');
return function () {
console.log('处理完成');
largeData = null; // 显式释放
};
}

4. 全局变量或缓存不当使用

javascript

1
2
3
4
5
// 错误示例:全局变量持有DOM引用
let cachedElement = document.getElementById('list');
function update() {
cachedElement.innerHTML = '...'; // 即使页面移除#list,cachedElement仍存在
}

修复方法

javascript

1
2
3
4
5
6
// 正确做法:使用弱引用或及时置空
const cachedElement = new WeakRef(document.getElementById('list')); // WeakRef允许被GC
function update() {
const element = cachedElement.deref();
if (element) element.innerHTML = '...';
}

5. 框架特定问题(如React/Vue)

  • React:未在useEffect中清理订阅或异步操作。
  • Vue:未在beforeUnmount钩子中解绑全局事件。

三、排查内存泄漏的工具与方法

1. 浏览器开发者工具

  • 内存快照(Heap Snapshot)
    1. 打开DevTools → Memory → 拍摄初始堆快照。
    2. 执行可能泄漏的操作(如创建/销毁组件)。
    3. 拍摄第二次快照,对比差异,查找未被释放的对象。
  • 性能监控(Performance Monitor)
    实时观察JS堆大小、DOM节点数、事件监听器数量是否持续上升。

2. 代码审查关键点

  • 检查事件监听器、定时器、全局变量、闭包的使用。
  • 使用WeakMap/WeakSet代替Map/Set管理临时引用。

3. 自动化检测(仅限Node.js环境)

  • 使用

    1
    --expose-gc

    参数手动触发GC:

    javascript

    1
    2
    global.gc(); // Node.js中调用GC
    console.log(process.memoryUsage());

四、预防内存泄漏的最佳实践

  1. 代码规范
    • 使用addEventListener时,配套编写removeEventListener
    • 框架中利用生命周期钩子(如React的useEffect清理函数)。
  2. 引用管理
    • 对缓存数据设置上限或过期时间(如LRU缓存算法)。
    • 使用WeakRefFinalizationRegistry管理弱引用。
  3. 工具辅助
    • 集成Linter规则(如ESLint no-unused-vars)。
    • 使用TypeScript检测潜在无效引用。
  4. 定期检测
    • 在持续集成(CI)中添加内存泄漏测试用例。
    • 使用自动化工具(如Chrome Headless模式 + Puppeteer)。

五、典型案例分析

场景:SPA页面切换后内存持续增长

  • 原因:路由组件卸载时未销毁第三方图表库实例。
  • 排查:对比快照发现多个图表对象未释放。
  • 修复:在React的useEffect返回函数中调用图表dispose()方法。

总结

阶段 关键行动
开发阶段 遵循事件/资源清理规范,使用弱引用和类型检查
测试阶段 利用浏览器工具拍摄堆快照,监控内存变化
运维阶段 监控线上应用内存指标,设置异常报警阈值

通过规范编码、工具检测和持续监控,可显著降低内存泄漏风险,保障Web应用流畅性。

9.Flutter是什么?他和原生开发相比有什么优缺点?

以下是关于 Flutter 及其与原生开发对比的详细分析,以表格和结构化内容呈现:


一、Flutter 是什么?

Flutter 是由 Google 开发的开源 UI 工具包,用于通过单一代码库构建高性能、高保真的 iOS、Android、Web、桌面(Windows/macOS/Linux) 应用。其核心特点包括:

  • 编程语言:使用 Dart(支持 JIT 和 AOT 编译)。
  • 渲染引擎:基于 Skia 实现自绘引擎,不依赖平台原生组件。
  • 开发效率:支持热重载(Hot Reload),实时更新代码效果。
  • UI 组件:提供丰富的 Material Design(Android风格)Cupertino(iOS风格) 组件库。

二、Flutter vs 原生开发:优缺点对比

维度 Flutter 原生开发
跨平台能力 ✅ 一套代码支持多平台(移动端、Web、桌面) ❌ 需分别为 Android(Java/Kotlin)和 iOS(Swift/Obj-C)编写代码
开发效率 ✅ 热重载快速迭代;统一代码库减少维护成本 ❌ 双倍开发时间;需维护两套独立代码库
性能 ⚡️ 接近原生(游戏/复杂动画略逊);AOT编译优化 ⚡️ 最佳性能(直接调用硬件和系统API)
UI 一致性 ✅ 全平台UI完全一致(自绘引擎控制) ❌ 需分别适配iOS/Android设计规范
生态与支持 🌱 生态快速增长,但第三方库数量少于原生 🌳 成熟生态,海量官方库和社区支持
学习曲线 📚 需学习 Dart 和 Flutter 框架(对新手友好) 📚 需掌握平台特定语言和工具(Android Studio/Xcode)
平台特定功能 ⚠️ 依赖插件实现(如相机、蓝牙),可能有延迟 ✅ 直接调用系统API,第一时间支持新功能
应用体积 📦 较大(包含引擎和Dart运行时),基础约 5-10MB 📦 较小(仅业务代码),基础约 1-3MB

三、Flutter 的核心优势

  1. 热重载(Hot Reload)
    修改代码后立即看到效果,无需重新编译,提升调试效率。
  2. 高性能自绘引擎
    通过 Skia 直接渲染到画布,避免平台组件差异,保证流畅的 60/120 FPS 动画。
  3. 丰富的组件库
    提供高度定制化的 Widget(如ListView.builderHero动画),快速实现复杂交互。
  4. 跨平台成本节约
    适合预算有限或希望快速验证产品的团队,节省 30%-50% 开发时间。

四、Flutter 的局限性

  1. 性能天花板
    对图形密集型应用(如 3D 游戏)或超高频计算场景,原生开发仍具优势。
  2. 生态系统依赖
    部分冷门硬件功能(如特定传感器)需自行开发插件,增加适配成本。
  3. 动态化限制
    苹果对热更新的审核限制可能导致动态化方案受阻。

五、适用场景建议

场景 推荐技术 理由
MVP(最小可行性产品) Flutter 快速验证想法,低成本覆盖全平台
企业级跨平台应用 Flutter 统一代码库维护,如电商、社交、工具类应用
高频交易/复杂动画应用 原生开发 需要极致性能或深度依赖硬件加速(如金融交易、AR/VR)
强依赖平台特性的功能 原生开发 如 iOS 的 Live Activity 或 Android 的深度后台服务

六、典型案例

  • Flutter 成功应用:Google Ads、阿里巴巴闲鱼、腾讯会议(部分模块)、eBay Motors。
  • 原生开发主导应用:Instagram(重度依赖iOS/Android相机优化)、PUBG Mobile(高性能3D渲染)。

总结

Flutter 是平衡效率与性能的跨平台解决方案,尤其适合追求快速迭代和成本控制的场景;原生开发仍是高性能、深度系统集成领域的首选。随着 Flutter 生态的完善(如 Flutter 3 对桌面的支持),其应用范围正逐步扩大,但技术选型需结合实际需求权衡利弊。

追问: 为什么Flutter的包会比原生开发的大?

以下是 Flutter 应用包体积大于原生开发的详细原因及优化策略:


一、Flutter 包体积更大的核心原因

因素 Flutter 应用 原生应用
引擎与运行时 包含完整的 Flutter 引擎(Skia、Dart VM)和框架代码 直接使用系统原生组件,无需额外引擎
编译方式 AOT 编译生成机器码,自带 Dart 运行时 仅编译业务代码,依赖系统提供的运行时环境
资源冗余 默认包含多分辨率资源(如字体、图标) 按平台规范按需引入资源,优化更灵活
插件依赖 插件可能携带平台特定的原生代码和库 直接调用系统API,依赖更少

二、具体分析

1. Flutter 引擎与框架

  • 引擎体积:Flutter 引擎(包括 Skia 图形库、Dart 运行时)约占 4-7 MB
  • 框架代码:基础 Widget 库(Material/Cupertino)和 Dart SDK 核心功能增加约 3-5 MB

2. AOT 编译机制

  • Dart → Native:Flutter 发布模式通过 AOT 将 Dart 代码编译为 ARM 机器码,生成 libapp.so(约 5-15 MB,取决于业务复杂度)。
  • 对比原生:Kotlin/Swift 编译后代码更精简,且无需携带语言运行时。

3. 资源文件

  • 默认多分辨率资源:Flutter 可能包含未优化的图片和字体(如 MaterialIcons-Regular.ttf150 KB)。
  • 原生优化:Android 可使用 WebP 格式,iOS 用 Asset Catalogs 按设备分发资源。

4. 插件与依赖

  • 插件开销:例如 camera 插件包含 Android/iOS 原生库,增加约 1-2 MB
  • 原生直接调用:如 Android 相机直接使用 Camera2 API,无需额外库。

三、包体积优化策略

1. 代码优化

  • 移除未使用的代码

    yaml

    1
    2
    3
    4
    5
    # pubspec.yaml 中仅引入必要依赖
    dependencies:
    flutter:
    sdk: flutter
    # 避免引入无关插件(如google_fonts仅在需要时使用)
  • 启用代码压缩

    (仅限 Android):

    gradle

    1
    2
    3
    4
    5
    6
    7
    // android/app/build.gradle
    buildTypes {
    release {
    minifyEnabled true // 启用 R8/ProGuard
    shrinkResources true // 移除未使用资源
    }
    }

2. 资源精简

  • 压缩图片:使用 flutter pub run flutter_launcher_icons 生成适配图标,避免冗余。

  • 按需引入字体

    yaml

    1
    2
    3
    4
    5
    # pubspec.yaml
    fonts:
    - family: Roboto
    fonts:
    - asset: fonts/Roboto-Regular.ttf # 仅保留必要字重

3. 构建配置优化

  • 分离ABI

    (Android):

    gradle

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // android/app/build.gradle
    splits {
    abi {
    enable true
    reset()
    include 'armeabi-v7a', 'arm64-v8a' // 仅打包主流架构
    universalApk false
    }
    }
  • 启用 Flutter 的 —split-debug-info

    (减少符号表):

    bash

    1
    flutter build apk --split-per-abi --split-debug-info=debug_info

4. 插件管理

  • 评估插件必要性:如非必须,用纯 Dart 实现替代原生插件。
  • 选择轻量插件:对比插件体积(如 http 包比 dio 更小)。

四、体积对比示例

应用类型 基础体积(Release) 优化后体积
Flutter 空项目 ~16 MB (Android APK) ~12 MB (ABI分离后)
原生 Android 空项目 ~1.5 MB (APK) ~1 MB (代码压缩后)

五、适用场景建议

  • 追求极致体积:选择原生开发(如工具类小型应用)。
  • 功能复杂且跨平台:接受 Flutter 体积代价,换取开发效率。

通过针对性优化,Flutter 应用体积可缩减 20%-40%,但仍需权衡跨平台优势与体积成本。

10. Vue项目开发有哪些常见的性能优化手段?

以下是 Vue 项目开发中常见的性能优化手段,结合代码示例和场景分析,帮助系统性提升应用性能:


一、组件级优化

1. 减少不必要的渲染
  • 问题:频繁的组件重渲染导致性能浪费。

  • 解决:

    • 使用v-once标记静态内容:

      vue

      1
      2
      3
      <template>
      <div v-once>{{ staticContent }}</div> <!-- 仅渲染一次 -->
      </template>
    • 合理使用 v-ifv-show

      1
      <div v-if="isNeeded">高频切换用 v-show</div> <!-- 频繁切换时用 v-show 减少销毁开销 -->
2. 优化列表渲染
  • 问题:大数据列表导致 DOM 节点过多。

  • 解决:

    • 使用virtual-scroller虚拟滚动:

      1
      2
      3
      4
      5
      <virtual-scroller :items="largeList" item-height="50">
      <template #default="{ item }">
      <div>{{ item.name }}</div>
      </template>
      </virtual-scroller>
    • 避免在v-for中使用复杂表达式:

      1
      2
      3
      4
      <!-- 错误示例 -->
      <div v-for="item in list.filter(x => x.active)">...</div>
      <!-- 正确做法:提前计算 -->
      <div v-for="item in filteredList">...</div>

二、状态管理优化

1. 避免全局状态滥用
  • 问题:Vuex/Pinia 中存储非全局数据导致响应式追踪开销。

  • 解决:

    • 按模块拆分状态:

      1
      2
      3
      4
      5
      // store/modules/user.js
      export default {
      namespaced: true,
      state: { ... }
      }
    • 使用shallowRefmarkRaw减少深度响应:

      1
      2
      3
      import { shallowRef, markRaw } from 'vue';
      const largeData = shallowRef(rawData); // 浅层响应
      const config = markRaw({ ... }); // 标记为非响应式
2. 优化计算属性
  • 问题:复杂计算属性重复执行。

  • 解决使用缓存或计算属性(computed)

    1
    2
    3
    const expensiveValue = computed(() => {
    return heavyCalculation(state.data);
    });

三、构建与资源优化

1. 代码分割与懒加载
  • 问题:打包后的主文件过大导致首屏加载慢。

  • 解决:

    • 路由级懒加载:

      1
      const Home = () => import('./views/Home.vue'); // 动态导入
    • 组件级懒加载:

      1
      2
      3
      <script setup>
      const Modal = defineAsyncComponent(() => import('./Modal.vue'));
      </script>
2. 优化第三方库
  • 问题:全量引入 UI 库增加打包体积。

  • 解决:

    • 按需引入 Element Plus:

      1
      2
      3
      4
      5
      // vite.config.js
      import ElementPlus from 'unplugin-element-plus/vite';
      export default {
      plugins: [ElementPlus({ /* 按需导入样式 */ })],
      }
    • 使用轻量替代方案(如 Lodash → Lodash-es):

      1
      import { debounce } from 'lodash-es'; // 支持 Tree Shaking
3. 资源压缩与 CDN
  • 问题:未压缩资源导致传输缓慢。

  • 解决

    • Vite 配置生产压缩:

      1
      2
      3
      4
      5
      // vite.config.js
      import viteCompression from 'vite-plugin-compression';
      export default {
      plugins: [viteCompression({ algorithm: 'gzip' })],
      }
    • 静态资源上传 CDN:

      1
      <img :src="`https://cdn.example.com/${imagePath}`" />

四、运行时优化

1. 事件与监听器优化
  • 问题:高频事件(如 resizescroll)导致性能问题。

  • 解决:

    • 使用防抖/节流:

      1
      2
      import { debounce } from 'lodash-es';
      window.addEventListener('resize', debounce(handleResize, 200));
    • 及时销毁事件监听:

      1
      2
      3
      onBeforeUnmount(() => {
      window.removeEventListener('resize', handleResize);
      });
2. 优化长任务
  • 问题:同步阻塞代码导致界面卡顿。

  • 解决:

    • 分片处理大数据操作:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      function processChunk(data, chunkSize) {
      let index = 0;
      const process = () => {
      const chunk = data.slice(index, index + chunkSize);
      requestIdleCallback(() => {
      // 处理当前分片
      if (index < data.length) process();
      });
      };
      process();
      }

五、工具与监控

1. 性能分析工具
  • Vue Devtools:检测组件渲染时间和依赖追踪。
  • Lighthouse:生成性能报告并给出优化建议。
  • Chrome Performance Tab:录制运行时性能瓶颈。
2. 错误与性能监控
  • 集成 Sentry 或 LogRocket:

    1
    2
    3
    4
    5
    6
    import * as Sentry from '@sentry/vue';
    Sentry.init({
    Vue,
    dsn: 'YOUR_DSN',
    tracesSampleRate: 1.0,
    });

总结:性能优化优先级

优化阶段 高 ROI 手段
首屏加载 代码分割、CDN、Gzip 压缩、预渲染(Prerender)
运行时性能 虚拟滚动、减少重渲染、防抖/节流
长期维护 状态管理模块化、移除未使用代码、监控告警

通过结合上述策略,可系统性提升 Vue 应用的性能表现,应对不同阶段的性能挑战。

追问: 长列表的虚拟滚动如何实现?

以下是虚拟滚动(Virtual Scrolling)的详细实现原理及基于 Vue 3 的代码示例,帮助你高效处理长列表渲染:


一、虚拟滚动的核心原理

  1. 按需渲染:仅渲染可视区域内的列表项,避免全量 DOM 节点创建。
  2. 滚动占位:使用空白占位元素(Placeholder)模拟完整列表高度,维持滚动条行为。
  3. 动态计算:根据滚动位置计算当前应渲染的起始和结束索引。

二、实现步骤与代码示例

1. 组件结构设计
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<template>
<div class="virtual-scroller" @scroll="handleScroll" ref="scroller">
<!-- 占位元素,模拟总高度 -->
<div class="scroll-phantom" :style="{ height: totalHeight + 'px' }"></div>

<!-- 实际渲染的可见项 -->
<div class="scroll-content" :style="{ transform: contentTransform }">
<div
v-for="item in visibleItems"
:key="item.id"
class="item"
:style="{ height: itemHeight + 'px' }"
>
{{ item.content }}
</div>
</div>
</div>
</template>
2. 逻辑实现(Vue 3 Composition API)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<script setup>
import { ref, computed, onMounted } from 'vue';

const props = defineProps({
items: { type: Array, required: true }, // 完整数据列表
itemHeight: { type: Number, default: 50 },// 每项高度(假设固定)
buffer: { type: Number, default: 5 }, // 缓冲项数(减少滚动白屏)
});

const scroller = ref(null); // 滚动容器引用
const scrollTop = ref(0); // 当前滚动位置
const viewportHeight = ref(0); // 可视区域高度

// 计算总高度(用于占位)
const totalHeight = computed(() => props.items.length * props.itemHeight);

// 计算可见项的起始和结束索引
const startIndex = computed(() => {
return Math.max(0, Math.floor(scrollTop.value / props.itemHeight) - props.buffer);
});
const endIndex = computed(() => {
const visibleItemCount = Math.ceil(viewportHeight.value / props.itemHeight);
return Math.min(
props.items.length,
startIndex.value + visibleItemCount + props.buffer * 2
);
});

// 当前需要渲染的可见项子集
const visibleItems = computed(() => {
return props.items.slice(startIndex.value, endIndex.value);
});

// 内容区域的偏移量(通过transform定位)
const contentTransform = computed(() => {
const offsetY = startIndex.value * props.itemHeight;
return `translateY(${offsetY}px)`;
});

// 处理滚动事件
const handleScroll = () => {
scrollTop.value = scroller.value.scrollTop;
};

// 初始化时获取可视区域高度
onMounted(() => {
viewportHeight.value = scroller.value.clientHeight;
});
</script>
3. 样式定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
.virtual-scroller {
position: relative;
height: 600px; /* 容器固定高度 */
overflow-y: auto; /* 启用垂直滚动 */
border: 1px solid #eee;
}

.scroll-phantom {
position: absolute;
left: 0;
right: 0;
top: 0;
z-index: -1;
}

.scroll-content {
position: absolute;
left: 0;
right: 0;
top: 0;
}

.item {
padding: 10px;
border-bottom: 1px solid #ddd;
box-sizing: border-box;
}

三、使用示例

vue

1
2
3
4
5
6
7
8
9
10
11
12
13
<script setup>
import VirtualScroller from './VirtualScroller.vue';

// 生成 10000 条测试数据
const mockItems = Array.from({ length: 10000 }, (_, index) => ({
id: index,
content: `Item ${index + 1}`,
}));
</script>

<template>
<VirtualScroller :items="mockItems" :item-height="50" />
</template>

四、关键优化点

  1. 缓冲区(Buffer):在可视区域外预渲染若干项,减少快速滚动时的空白(如上例中的 buffer=5)。
  2. CSS 硬件加速:使用 transform: translateY 代替 top 定位,触发 GPU 加速。
  3. 避免重复计算:通过计算属性(computed)缓存计算结果。
  4. 固定项高度:假设项高度固定,简化位置计算(若需动态高度需额外计算)。

五、动态高度扩展思路

若列表项高度不固定,需额外步骤:

  1. 测量实际高度:渲染后通过 ResizeObserver 获取项高度并缓存。
  2. 计算累加位置:维护一个数组存储每项的累计高度,用于快速查找滚动位置对应的索引。
  3. 动态调整偏移量:根据实际高度调整 translateY 的值。

六、性能对比

方案 10000 项 DOM 节点数 滚动流畅度 内存占用
传统渲染 10000 卡顿
虚拟滚动 ~30(可视区域) 流畅

通过虚拟滚动,可以显著减少 DOM 操作和内存消耗,适用于数据量大的表格、聊天记录等场景。

笔试题

大数相加

最长重复子字符串

2025-3-21 腾讯文档二面

面试题

笔试题

1. 数组去重

2. 两数之和

2025-04-01 京东工业一面

1. 基本数据类型

  • String
  • Number
  • BigInt
  • Boolean
  • Symbol
  • null
  • undefined

2. 基本类型和引用类型有什么区别

一.基本类型

  1. 类型范围
  • String
  • Number
  • BigInt
  • Boolean
  • Symbol
  • Undefined
  • Null
  1. 存储方式
  • 值直接存储在栈内存中
  • 变量直接保持数据的”值”
  1. 操作特性
  • 按值访问
  • 不可变性
  • 复制行为
  1. 比较方式

    比较的是值的等价性

二. 引用类型

  1. 类型范围
    • Object
    • Array
    • Function
    • Date
    • RegExp
  2. 存储方式
    • 值存储在堆内存中
    • 变量保存的是堆内存地址(即引用)而非实际数据
  3. 操作特性
    • 按引用访问:通过地址间接操作堆内存中的数据
    • 可变性:修改对象的属性时,原对象会改变
    • 复制行为:赋值或传参时,复制的是内存地址(指向同一对象)
  4. 比较方式
    • 比较的是引用的等价性(地址相同时才为true)

3. 数组的常用方法

4. Vue2和Vue3的区别

一. 响应式系统

Vue2:

  • 使用Object.defineProperty实现数据劫持
  • 缺点:无法检测对象属性的动态添加/删除(需要通过Vue.set/Vue.delete处理),对数组的监测需要特殊处理(如覆盖数组方法)

Vue3:

  • 基于Proxy重构响应式系统,支持动态新增/删除属性和数组索引的直接修改
  • 优点:性能更高,支持更全面的数据劫持,无需手动处理响应式问题.

二. API设计模式

  • Vue2(选项式API)
    • 代码按选项类型(data,method,computed)阻止,逻辑分散
    • 痛点:复杂组件代码可读性差,逻辑复用困难
  • Vue3(组合式API)
    • 引入setup函数和CompositionAPI.(如ref,reactive,computed)
    • 优点:按逻辑功能组织代码,提升可维护性,支持更加灵活的逻辑复用(自定义Hook)

三.性能优化

  1. 虚拟DOM优化
    • 静态节点提升(Hoist Static):编译时标记静态节点,避免重复渲染.
    • 补丁标记(Patch Flag):动态节点标记差异类型,减少Diff复杂度
  2. Tree-shaking支持
    • 按需编译代码,打包体积更小
  3. SSR优化
    • 服务端渲染性能提升,支持流式渲染.

三. 生命周期与组件

  • 生命周期钩子
    • Vue3组合式API移除beforeCreatecreated,由setup代替
    • 其他钩子更名为前缀on(如onMounted),需要在setup中使用
  • Fragment支持
    • Vue3允许组件有多个根节点,无需外层包裹<div>
  • Teleport组件
    • 将子组件渲染到DOM任意位置(如模态框,弹窗)
  • Suspense组件
    • 处理异步组件加载状态,提供占位内容

五. TypeScript支持

  • Vue3
    • 源码用TypeScript重写,提供更完善的类型推断
    • 组合式API天然支持TS,无需额外配置.
  • Vue2
    • 需通过vue-class-componentvue-property-decorator实现类组件语法,类型支持较弱

2025-04-02 腾讯音乐一面

1. 什么是语义化

2. 什么是盒模型

3. CSS优先级计算

4. 常用的CSS布局

5. js的数据类型

追问: undefined和none有什么区别?

6. Array的常用api

7. 说一下ES5?

8. 说一下JS的原型链

9. 说一下JS中this的指向问题

10. 说一下JS的同步异步以及事件循环的理解