序言
收集经典面试题
欢迎大家投稿 : 投稿地址
面试官,下午好,我叫高红翔, 我的工作经验是三年左右 ,我用的主要技术站为 react, ts,vue 也熟练使用, 目前在度小满工作,在质量部,负责一些内部系统平台的开发和维护,主要核心为自动化平台,旨在 sso 单点登录接入 xpp 报警平台 sdk 接入
通过平台化操作简化测试用例的 编写和管理流程,
可以直接平台进行
调试,快速定位问题,包括定时批量执行任务,
个人的话平时喜欢写写博客,总结一些工作中遇到的问题或者一些技术点,经常逛 GitHub 及国内前端社区等,合理运用业余时间来学习新知识。
微前端
概念
微前端是一种将前端应用程序拆分成更小、更独立的部分,然后组合成一个整体应用程序的架构方式。
目的
不同团队(技术栈不同),同时开发一个应用
每个团队开发的模块都可以独立开发,独立部署
实现增量迁移
可维护性、可扩展性、灵活性和可重用性。
技术方案
iframe
- 微前端的最简单方案,通过 iframe 加载子应用。
- 通信可以通过 postMessage 进行通信。
- 完美的沙箱机制自带应用隔离。
缺点:用户体验差 (弹框只能在iframe中、在内部切换刷新就会丢失状态)
Web Components
- 将前端应用程序分解为自定义 HTML 元素。
- 基于 CustomEvent 实现通信
- Shadow DOM 天生的作用域隔离
缺点:浏览器支持问题、学习成本、调试困难、修改样式困难等问题。
single-spa
- single-spa 通过路由劫持实现应用的加载(采用 SystemJS),提供应用间公共组件加载及
公共业务逻辑处理。子应用需要暴露固定的钩子 bootstrap、mount、 unmount)接入协
议。
- 基于 props 主子应用间通信
- 无沙箱机制,需要实现自己实现 JS 沙箱以及 CSS 沙箱
缺点:学习成本、无沙箱机制、需要对原有的应用进行改造、子应用间相同资源重复加载问题。
qiankun
- 2019 年 qiankun 基于 single-spa,提供了开箱即用的 api(single-spa + sanbox + import-html-entry)
- 做到了 技术栈无关、并且接入简单
- 实现了样式隔离和 js 隔离
Module federation
- 通过模块联邦将组件进行打包导出使用
- 共享模块的方式进行通信
无 CSS 沙箱和 JS 沙箱缺点:需要webpack5。
js 隔离
- 沙箱防止应用加载的时候对widow造成污染
<!--a->更改了window属性 删掉
<!--b->更改了window属性
<!--先保留a应用的属性 失活的时候 把修改的属性存起来,激活的时候还原回来-->
<!--快照 浪费内存,因为要给window拍照-->
快照
- 浪费内存,因为要给window拍照
基于proxy
- proxy的兼容性不好
在 qiankun 中,JS 的隔离是通过使用沙箱机制来实现的。每个子应用都在自己的沙箱中运行,从而实现了应用之间的隔离。具体来说,沙箱机制通过以下方式实现:
- 沙箱环境的隔离:每个子应用都在自己的 JavaScript 执行环境中运行,沙箱会通过 iframe 来实现应用的隔离。
- 沙箱环境的代理:每个子应用的 window 对象都被代理到 qiankun 的沙箱环境中,这样可以保证子应用只能在自己的沙箱中运行,而不会影响其他子应用。
- 沙箱环境的劫持:沙箱会在子应用的 window 对象中注入一些额外的全局变量和 API,以便子应用可以通过这些 API 来实现和主应用的通信和交互。
通过以上方式,qiankun 实现了应用之间的隔离,保证了子应用之间的相互独立性。同时,沙箱还提供了一些 API 来实现子应用与主应用之间的通信和数据共享
,比如在主应用中通过 props 向子应用传递数据,或者在子应用中通过 emit 方法向主应用发送事件。
样式隔离
qiankun 实现样式隔离的方式是通过 CSS Modules 实现的。具体来说,qiankun 在主应用和子应用中分别使用 CSS Modules 来进行样式管理,确保样式只对当前应用生效。
在主应用中,通过在子应用注册时指定 sandbox
选项为 true
,即可开启样式隔离。当开启样式隔离后,qiankun 会为每个子应用创建一个独立的 CSS Modules 上下文,子应用内部的样式只会在当前应用中生效,不会影响到其他子应用。
同时,qiankun 还提供了 addGlobalStyles
API,可以让主应用设置全局样式,这些样式不会被子应用的 CSS Modules 所覆盖。这样可以确保主应用的全局样式不受子应用的影响。
总的来说,qiankun 的样式隔离方案通过使用 CSS Modules 和全局样式管理来解决了子应用之间的样式冲突问题。
CSS Modules sandbox
选项为 true
, + 选择器 放在body上失效
shadowDOM 来做 css 隔离 影子dom(完全隔离) 3.0废弃
子应用之间的通信和数据共享
在微前端架构中,通常使用以下几种方式来保证子应用之间的通信和数据共享:
- 通过全局状态管理器:在微前端架构中,通常会使用全局状态管理器(如 Redux、Mobx 等)来管理全局状态。子应用可以通过订阅和更新全局状态来实现跨应用的通信和数据共享。
- 通过事件总线:事件总线是一种解耦的通信方式,可以实现应用间的松耦合通信。在微前端架构中,可以使用事件总线(如 EventBus、RxJS 等)来实现子应用之间的通信和数据共享。
- 通过共享数据源:在微前端架构中,可以使用共享数据源的方式来实现子应用之间的数据共享。共享数据源可以是一个独立的数据服务,也可以是一个公共的数据接口,子应用可以通过调用该数据源来获取共享的数据。
- 通过消息队列:在微前端架构中,可以使用消息队列来实现子应用之间的通信和数据共享。消息队列可以解决异步通信和数据共享的问题,可以将数据发送到队列中,其他子应用可以订阅该队列并接收数据。
在实际应用中,需要根据具体的场景和需求选择合适的通信方式和数据共享方式。同时,需要注意通信方式和数据共享方式的性能、可靠性、安全性等问题。
方案 问题 解决 思考
在实际开发中,我曾经应用过微前端方案来解决多个团队协同开发、模块化开发和系统复杂度高的问题。在这个项目中,我们使用了 qiankun 微前端框架。
其中,我们主要遇到了以下问题:
- 代码隔离问题:不同子应用之间的代码可能会相互影响,从而导致不同应用之间的冲突问题。
- 状态管理问题:由于每个子应用都有自己的状态管理,因此状态同步和传递问题也是需要解决的问题。
- 路由和菜单管理问题:由于每个子应用都有自己的路由和菜单管理,因此需要解决菜单和路由的统一管理问题。
针对以上问题,我们采取了以下解决方案:
- 代码隔离:通过将不同子应用的代码打包成独立的 chunk,使用 webpack 的 scope hoisting 特性来实现代码隔离,避免了不同子应用之间的代码冲突。
- 状态管理:我们使用了 Vuex 和 Redux 等状态管理库,并通过 qiankun 提供的 props 和 emit 方法来实现不同子应用之间的状态同步和传递。
- 路由和菜单管理:我们通过 qiankun 提供的 addRoute、removeRoute、addMenu、removeMenu 等方法,实现了统一的路由和菜单管理,从而实现了多个子应用之间的无缝集成。
总的来说,使用微前端方案来解决复杂的系统开发问题可以有效提升开发效率和系统稳定性,但也需要注意各个子应用之间的协作和管理问题,以避免出现混乱和冲突。
脚手架
工具
commander
chalk 是一个终端字符串美化工具。
inquirer交互式命令行界面。提供了询问操作者问题,获取并解析用户输入,多层级的提示,提供错误回调,检测用户回答是否合法等能力。
egs高效的嵌入式 JavaScript 模板引擎。
实现 Creator
Creator 类是脚手架的核心,实现处理用户输入,初始化安装环境,生成项目文件,生成配置文件,生成 readme 文件,初始化 Git 等等功能。lib/create.js 文件引入 Creator 类进行实例化,并调用。
async create(cliOptions = {}, preset = null) {
// 处理用户输入
const preset = await this.promptAndResolvePreset();
// 初始化安装环境
await this.initPackageManagerEnv(preset);
// 生成项目文件,生成配置文件
const generator = await this.generate(preset);
// 生成 readme 文件
await this.generateReadme(generator);
this.finished();
}
}
可插拔的机制设计
这里我们要实现 PromptModuleAPI 类,实现了 injectFeature 方法。该方法被调用时往 Creator 的 featurePrompt.choices 变量填充数据。
GeneratorAPI 是 Generator 辅助类,实现了收集插件的信息,提取文件内容的功能,供各个插件使用。插件调用时将插件的信息,文件内容时,GeneratorAPI 将这些信息记录给 Generator。交由 Generator 实现配置文件,项目文件的生成。
GeneratorAPI 类,它是 Generator 类的辅助,用于提取文件信息,配置信息。主要了解其2个方法
项目
Anycase是一个低代码的在线编辑与实时执行的自动化平台,旨在通过平台化操作简化测试用例的 编写和管理流程,可以直接平台进行调试,快速定位问题,包括定时批量执行任务。
问题
自动化平台,
1. 示例回答
在一个项目中,我们遇到了一个与状态管理相关的复杂问题。具体来说,我们的应用需要在不同组件之间共享状态,并且这些状态的变化需要即时反映在UI上。然而,某些状态的更新在高频操作下导致了性能问题,特别是在大型表单和复杂交互场景中。
问题描述:
我们使用Vue3和Pinia进行状态管理,但在高频状态更新下,UI的响应速度明显变慢。特别是在处理大量表单输入时,UI的卡顿现象严重影响了用户体验。
解决过程:
源码分析:
- 我深入研究了Pinia的源码,发现Pinia使用的是Vue3的响应式系统。在理解了Pinia如何追踪和触发状态变化后,我意识到高频状态更新导致了大量的重新渲染。
优化策略:
- 批处理更新:通过查看Pinia和Vue3响应式系统的实现,我了解到可以利用Vue的
nextTick
机制将多个状态更新合并为一个批次,从而减少不必要的渲染次数。 - 分离关键状态:通过分析,我们将那些不需要频繁更新的状态与需要频繁更新的状态分离开来。这样,我们可以确保只有必要的状态变化才会触发UI更新。
- 批处理更新:通过查看Pinia和Vue3响应式系统的实现,我了解到可以利用Vue的
实际应用:
我们在代码中使用
nextTick
对高频操作进行批处理:
javascript
复制代码
import { nextTick } from 'vue';
const updateState = () => {
// 高频操作状态更新逻辑
store.state.value = newValue;
nextTick(() => {
// 批处理更新后的操作
});
};对状态进行分离和模块化管理,确保只有必要的部分会被重新渲染:
javascript
复制代码
const useHighFrequencyStore = defineStore('highFrequency', {
state: () => ({
frequentState: 0,
// 其他状态...
}),
actions: {
updateFrequentState(newValue) {
this.frequentState = newValue;
},
},
});
结果:
通过以上优化,我们显著提高了应用在高频操作下的性能。具体表现为:
- 表单输入的响应速度提高了约30%。
- 高频状态更新的UI卡顿现象得到明显改善,用户体验显著提升。
这个过程不仅解决了实际问题,还展示了我对Vue3和Pinia底层实现的深入理解,以及如何将理论应用于实际项目中,提升代码性能和用户体验。
数据存储前端存储还是后端存储
原因表单信息量太大
前端存 草稿
- 有事件 数据量的限制
后端存储完整版数据
后端存储部分和前端存储部分 进行合并等问题
团队css样式问题 太多 冲突 推动团队使用tailwind
快速开发:Tailwind CSS的预定义类让开发人员能够更快地构建和修改页面,无需手动编写和管理CSS样式表,从而提高生产力。
灵活性:Tailwind CSS提供了大量的定制化选项,例如自定义颜色、字体、边框等,使开发人员可以轻松地按照自己的需求进行样式设计。
一致性:Tailwind CSS的预定义类是基于一致的设计系统构建的,使得整个应用程序的UI设计能够保持一致,增加了用户体验的可预期性。
可维护性:使用Tailwind CSS的开发人员可以减少样式表中的重复代码和冗余样式,从而使代码更易于维护和修改。
响应式设计:Tailwind CSS提供了一系列响应式类,使开发人员可以轻松地为不同设备和屏幕尺寸进行样式设计。
后期的主题配置,就是居于tailwind
首页白屏 (订单中心 端上内嵌H5)
- 图片压缩:对于网页中的图片,可以使用图片压缩工具将其压缩至最小化,以减少加载时间。
- CSS、JS代码压缩:对于CSS、JS等代码文件,可以使用压缩工具将其压缩至最小化,减少文件大小,提高加载速度。
- 合并文件:对于多个CSS、JS等文件,可以将其合并为一个文件,减少HTTP请求次数,提高加载速度。
- 使用CDN加速:使用CDN(内容分发网络)可以将网页资源分布到多个地点,使用户能够从最近的节点获取资源,从而提高加载速度。
- 利用缓存
- splitChunk代码分割
埋点插件
项目发版是怎么做的?
项目发版是指将代码从开发环境部署到生产环境的过程。以下是一般的项目发版流程:
- 开发人员完成开发、调试、测试,并将代码提交到代码仓库(如 Git)。
- 代码审查员进行代码审核,确保代码规范、质量等达到要求。
- 构建人员使用构建工具(如 Webpack、Gulp、Grunt 等)对代码进行构建、打包、压缩等处理。
- 测试人员进行测试,测试通过后进行预发布(可以是在生产环境的一台服务器上测试)。
- 运维人员进行部署,包括代码上传、服务器环境搭建、配置文件修改等。
- 运维人员进行上线操作,将新代码部署到生产环境,并进行测试。
- 发布人员进行发布操作,通知客户端更新,并对问题进行处理。
在实际操作中,上述流程可能会根据实际情况进行调整,如是否需要进行预发布、上线前是否需要进行回归测试等。
整体性能优化
- 路由懒加载
- 组件懒加载
- 合理使用tree Shacking
- 骨架屏
- 长列表虚拟滚动
- Web worker 优化长任务
- requestAnimationFrame制作动画
- JS的6种加载方式
- 正常模式(阻塞dom渲染)
- async模式(异步加载,不糊阻塞dom渲染,加载完立即执行,场景:与dom无依赖关系,如埋点统计)
- defer模式(异步加载,再DOMContentLoaded 执行之前,有序加载)
- mudule模式 (vite就是利用该模式)
- preload 预加载(依赖插件)
- prefetch 预请求 (webpack支持 空闲事件加载)
- 图片懒加载和压缩 优化 base64
- 压缩文件大小:通过压缩 HTML、CSS、JavaScript 等文件大小,可以减少页面加载时间。可以使用工具如 webpack、gulp 等进行文件压缩。
- 图片优化:通过压缩图片大小、使用 WebP 格式、延迟加载等方式可以减少图片对页面加载速度的影响。
- 使用 CDN:使用内容分发网络(CDN)可以加速网站的访问速度,将静态资源分发到不同的地理位置,让用户从最近的服务器上加载资源,减少加载时间。
- 使用缓存:使用浏览器缓存和服务器缓存可以减少重复请求,提高页面加载速度。可以通过设置 HTTP 缓存头信息来实现。
- 代码优化:减少 HTTP 请求次数,避免页面阻塞,使用异步加载等方式可以提高网站性能。
- 服务器优化:使用高性能的服务器、使用负载均衡、优化数据库等方式可以提高网站性能。
- 网络优化:优化网络传输,使用 HTTP/2、WebSocket 等方式可以提高网络传输效率。
谈什么是jsbridge
JSBridge(JavaScript Bridge)是一种前端与移动端之间交互的技术方案,主要用于解决移动端 WebView 与原生应用之间的通信问题。
在移动端开发中,WebView 是经常用到的组件之一,WebView 的特点是支持 Web 技术栈,而且可以嵌入到原生应用中,但 WebView 是一个独立的运行环境,和原生应用是隔离的,无法直接访问原生应用的资源,比如调用原生的相机、通讯录等。这时候 JSBridge 技术就能派上用场了。
JSBridge 技术通常是通过注入原生提供的 JavaScript 接口到 WebView 的 JavaScript 环境中,使得前端可以直接调用原生提供的 API 来访问设备的硬件和系统资源。同时,JSBridge 技术也可以在 WebView 中注册一个 JavaScript 方法,供原生应用调用,以便实现前后端之间的数据交互和通信。
在实现上,JSBridge 技术主要有两种方式:一种是基于 URL 协议的方式,另一种是基于 WebView 的注入方式。基于 URL 协议的方式通过拦截 WebView 的 URL 请求来实现通信,而基于 WebView 的注入方式则是通过 WebView 的 addJavascriptInterface 方法将原生提供的接口注入到 WebView 的 JavaScript 环境中。
前端登录
前端登录通常指用户在前端页面中输入账号和密码,然后通过前端向后端发送请求验证身份信息,如果验证成功,就会跳转到登录后的页面。
具体的流程一般包括以下步骤:
- 用户在前端页面中输入账号和密码,点击登录按钮。
- 前端通过 Ajax 或表单提交等方式向后端发送验证请求。
- 后端收到请求后,根据请求中的账号密码信息,验证用户身份是否合法。如果合法,后端会生成一个 token 并返回给前端。
- 前端收到后端返回的 token 后,通常会将该 token 存储到 cookie 或 localStorage 中,以便后续的请求可以携带该 token 进行身份验证。
- 用户登录成功后,前端通常会跳转到登录后的页面。
SSO(Single Sign-On)是指一个用户只需要登录一次,就可以访问多个相互信任的应用系统。通常是在一个认证中心登录,然后在其他应用系统中自动登录,而不需要再次输入用户名和密码。
实现 SSO 的方式有多种,其中最常见的是基于 token 的认证方式。具体流程如下:
- 用户在应用 A 中输入用户名和密码进行登录。
- 应用 A 向 SSO 认证中心发送认证请求。
- SSO 认证中心验证用户身份是否合法。如果合法,就生成一个 token,并将该 token 存储在自己的服务器上,并返回该 token 给应用 A。
- 应用 A 收到 token 后,将其存储到 cookie 或 localStorage 中,并在后续的请求中携带该 token。
- 用户在访问应用 B 时,应用 B 会检查用户请求中是否包含有效的 token。如果 token 有效,就会向 SSO 认证中心发送请求,以验证该 token 是否是合法的。
- SSO 认证中心收到请求后,会检查 token 是否合法,如果合法,则返回成功的验证结果给应用 B,用户就可以登录应用 B。
总的来说,前端登录和 SSO 的本质是身份认证和授权,不同之处在于 SSO 适用于多个应用系统之间的用户登录和授权管理。
组件
在平时开发中,我会按照以下步骤去写一个组件:
- 确定组件的功能:首先要明确组件的功能,对组件的作用有一个清晰的认识。
- 设计组件的 API:根据组件的功能,设计组件的 API,包括 props 和方法,使得组件可以满足用户的需求。
- 组件的拆分:根据组件的功能和使用场景,将组件拆分为不同的子组件,便于复用和维护。
- 编写组件的模板和样式:在设计好组件的 API 和拆分好组件的基础上,编写组件的模板和样式,保证组件的外观和交互符合用户的期望。
- 实现组件的逻辑:根据组件的功能和设计好的 API,实现组件的逻辑,包括对 props 的处理、事件的绑定和状态的更新等。
- 组件的测试:编写组件的测试用例,对组件进行测试,确保组件的正确性和稳定性。
- 组件的文档:编写组件的文档,包括组件的使用方法、API、示例和注意事项等,方便其他人使用和维护组件。
在编写组件时,我会考虑以下几点:
- 可维护性:组件的代码应该简洁明了,易于理解和维护。
- 可复用性:组件应该具有一定的通用性,方便在不同的项目中复用。
- 可扩展性:组件应该具有一定的扩展性,方便在需求变化时进行修改和扩展。
- 性能优化:组件的性能应该得到优化,例如通过使用 shouldComponentUpdate 或 PureComponent 来避免不必要的渲染等。
- 用户体验:组件应该具有良好的用户体验,例如通过设计良好的交互和动画效果来提升用户体验。