1. 什么是单点登录(SSO),前端如何实现单点登录?
单点登录(SSO) 是一种认证机制,用户只需登录一次,就可以访问属于不同系统的多个应用,无需重复登录。
前端实现单点登录的方法:
-
使用共享 Cookie:
- 通过顶级域名共享 Cookie,比如在
example.com
和sub.example.com
间共享。 - 前端通过 Cookie 获取用户认证信息,检查是否已登录。
- 通过顶级域名共享 Cookie,比如在
-
Token 机制(推荐):
- 用户登录时,后端返回 JWT 或其他 Token。
- Token 保存在
localStorage
或sessionStorage
,前端每次请求将 Token 放入 HTTP 头。 - 实现跨系统登录时,统一认证中心管理 Token,前端检测登录状态。
-
OAuth 2.0:
- 使用授权码模式,前端重定向用户到认证中心获取授权,返回
access_token
后完成登录。 - 常用于单点登录和第三方登录。
- 使用授权码模式,前端重定向用户到认证中心获取授权,返回
2. 如何解决前端 SPA 应用首屏加载速度慢的问题?
-
代码分割与懒加载:
- 使用 Webpack/Vite 的动态导入(
import()
)按需加载首屏需要的资源。 - 懒加载非首屏资源,比如图片、第三方库等。
- 使用 Webpack/Vite 的动态导入(
-
SSR/SSG:
- 使用服务端渲染(SSR)或静态生成(SSG)生成首屏 HTML,前端接管后续交互逻辑。
-
预渲染与骨架屏:
- 提供骨架屏,替代空白页面,提高用户感知性能。
- 使用工具(如
React Snapshot
)生成静态 HTML。
-
优化网络请求:
- 启用 HTTP/2,减少请求延迟。
- 配置 CDN 缓存,加快静态资源加载速度。
- 减少 API 请求次数,使用合并或 GraphQL。
-
资源压缩和优化:
- 压缩 JS、CSS 和图片,开启 Gzip/Brotli。
- 使用 Tree Shaking 和 Scope Hoisting 减小打包体积。
3. 如何设计一款能够统计前端页面请求耗时的工具?
功能设计:
-
基础功能:
- 统计页面加载时间(白屏时间、首屏时间、总加载时间)。
- 统计每个请求的耗时,包括 DNS 查询、连接时间、服务器响应时间等。
-
前端实现步骤:
- 利用
Performance
API 获取页面性能指标:const { timing } = performance; const loadTime = timing.loadEventEnd - timing.navigationStart; const whiteScreenTime = timing.domLoading - timing.navigationStart;
- 使用
XMLHttpRequest
或fetch
统计请求时间:const start = Date.now(); fetch(url).then(() => {console.log('Request time:', Date.now() - start); });
- 提供 SDK 接口,支持自定义上报。
- 利用
-
上报机制:
- 使用
beacon
上报数据,确保页面关闭时仍可发送。 - 数据存储在后台,分析生成统计报表。
- 使用
4. 如何设计和优化秒杀系统的前端?
-
性能优化:
- 静态资源预加载:提前加载页面的必要资源。
- 长连接(WebSocket):用于获取实时库存状态和倒计时。
- 请求合并:通过队列合并多用户的抢购请求,减少服务器压力。
-
页面设计:
- 倒计时组件显示抢购时间。
- 库存状态的动态展示(实时更新库存)。
-
防刷设计:
- 前端限制频繁点击,增加按钮冷却时间。
- 加入验证码防止恶意点击。
-
用户体验优化:
- 使用骨架屏加载页面。
- 抢购成功后即时提示,失败时给出重试或候补信息。
5. 如何在前端页面无限滚动加载内容时自动回收上面的内容?
-
虚拟列表技术:
- 渲染只在可视区域内的元素,并移除视窗外的元素。
- 使用库:
react-window
、react-virtualized
。
-
Intersection Observer:
- 监听滚动事件,动态加载新的内容。
- 移除超出视图范围的 DOM 节点,回收资源。
const observer = new IntersectionObserver((entries) => {entries.forEach(entry => {if (!entry.isIntersecting) {entry.target.remove();}}); });
6. 如何在前端实现文件的断点续传,并确保大文件安全可上传?
-
分片上传:
- 将文件分片(如 1MB 一片),每片上传时带有序号和校验信息。
- 使用
File.slice()
方法分割文件:const blob = file.slice(start, end);
-
断点记录:
- 前端记录已上传的分片,上传失败时重新上传。
- 后端保存分片上传的状态(如 MD5 校验)。
-
加密校验:
- 文件上传前进行 MD5 哈希,上传完成后校验完整性。
- 使用 HTTPS 确保传输安全。
-
续传机制:
- 检查服务端是否存在已上传的分片(通过唯一标识),只上传缺失部分。
7. 前端如何实现基于 WebSocket 的实时聊天功能,支持多用户在线聊天并展示消息通知?
-
建立 WebSocket 连接:
- 使用 WebSocket API:
const ws = new WebSocket('wss://example.com/chat'); ws.onmessage = (event) => console.log('Message:', event.data);
- 使用 WebSocket API:
-
多用户支持:
- 使用房间概念,按用户分组聊天。
- 消息结构包含
userId
、roomId
等信息。
-
消息通知:
- 使用浏览器通知(
Notification API
):if (Notification.permission === 'granted') {new Notification('New message', { body: message }); }
- 增加未读消息数提醒。
- 使用浏览器通知(
-
消息可靠性:
- 引入心跳机制检测连接状态。
- 使用消息队列或 Redis 实现消息持久化。
8. 你会如何设计前端日志埋点 SDK?
-
SDK 功能设计:
- 采集用户行为(点击、页面停留时间、浏览路径等)。
- 捕获错误日志。
- 支持自定义事件上报。
-
实现步骤:
- 日志采集:
- 使用事件监听(
addEventListener
)采集用户行为。 - 使用
Performance
和Error
捕获性能数据和异常信息。
- 使用事件监听(
- 日志存储:
- 将日志存储在内存中,达到一定量时批量上报。
- 上报机制:
- 使用
navigator.sendBeacon
或fetch
进行非阻塞上传。
- 使用
- 日志采集:
-
日志分析:
- 后端搭建分析平台,支持按用户、事件、时间过滤查看日志。
9. 前端如何给网页增加水印,并且如何防止水印被移除?
-
实现水印:
- 使用
canvas
动态生成水印:const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); ctx.font = '20px Arial'; ctx.fillText('Watermark', 50, 50); document.body.style.backgroundImage = `url(${canvas.toDataURL()})`;
- 或直接在 DOM 中插入水印元素。
- 使用
-
防止水印被移除:
- 监控 DOM:
- 使用
MutationObserver
监控 DOM 树,发现水印被移除时重新添加。
- 使用
- 样式强化:
- 通过
z-index
、透明度等方式固定水印,不易被覆盖。
- 通过
- 加密混淆:
- 将水印代码与业务逻辑混淆,增加破解难度。
- 监控 DOM:
Vue 2和Vue 3的区别及Vue 3的更新与设计目标、优化
Vue 2和Vue 3的区别:
- 双向数据绑定:Vue 2使用ES5的Object.defineProperty进行劫持,而Vue 3使用ES6的Proxy进行数据代理,提供了更高效的变更侦测和访问拦截。
- 组件结构:Vue 2不支持碎片(Fragments),即每个组件必须有一个根节点,而Vue 3允许组件有多个根节点。
- API类型:Vue 2使用选项类型API(Options API),而Vue 3引入了合成型API(Composition API),使代码逻辑复用更方便,逻辑组织更加清晰。
- 数据建立:Vue 2将数据放入data属性中,而Vue 3使用setup()方法来建立数据。
- 组件特性:Vue 3增加了Teleport组件,允许将子组件渲染到指定的DOM节点外,还引入了Fragments(片段)等新的语法特性。
Vue 3的更新:
- 性能提升:对Virtual DOM进行了优化,引入了静态提升、缓存等技术。
- Composition API:提供了更灵活的方式来组织组件逻辑,并增强了TypeScript支持。
- Teleport组件:允许将子组件渲染到指定的DOM节点外,提升了DOM结构的可管理性。
- Fragments:支持一个组件返回多个根节点,使模板结构更灵活。
- 全局API变更:采用了更加模块化的方式,如Vue.component变为app.component,Vue.use变为app.use,并引入了createApp方法来创建Vue应用实例。
Vue 3的设计目标:
- 提升框架的运行时性能,尤其在渲染、响应式系统和内存管理上。
- 提供新的Composition API,使代码逻辑复用更方便,逻辑组织更加清晰。
- 加强TypeScript支持,提供更强的类型推导和代码提示。
- 改进Vue CLI和开发工具,提供更强大的调试、热重载和构建体验。
- 引入新的组件模型,支持更多的配置方式,尤其是在组合多个逻辑和功能时。
Vue 3在设计过程中做的优化:
- 响应式系统优化:使用Proxy替代Object.defineProperty,提供更高效的变更侦测和访问拦截。
- 编译优化:引入静态模板提升技术,将模板编译为更简洁、更高效的渲染函数。
- 初始化优化:避免Vue2中的原型链查找操作,减少不必要的初始化工作和内存开销。
- Tree-shaking支持:支持更好的Tree-shaking,减少打包体积,提高应用的加载速度。
- 虚拟DOM优化:采用静态标记的方式来跳过静态节点的比对和更新,提高渲染性能。
性能提升的主要体现
Vue 3的性能提升主要体现在响应式系统优化、编译优化、初始化优化、Tree-shaking支持和虚拟DOM优化等方面。这些优化使得Vue 3在处理大量数据、复杂组件以及提高应用加载速度等方面表现出更强大的能力。
设计模式相关问答
什么是设计模式?为什么要用?
设计模式是软件工程中用于解决特定问题的一系列最佳实践。它们是经过时间考验的、被广泛认可的软件设计经验,可以帮助开发者在面对常见问题时做出更好的设计决策。使用设计模式可以提高代码的可重用性、灵活性、可维护性和可扩展性。
最常见的几种设计模式及其应用场景:
- 单例模式:确保一个类只有一个实例,并提供一个全局访问点。常用于管理全局资源,如数据库连接池、日志记录器等。
- 工厂模式:定义一个创建对象的接口,但让子类决定要实例化的类是哪一个。常用于创建具有共同接口的对象,但具体实现可能不同的场景。
- 观察者模式:定义了一个主题和多个观察者之间的关系。当主题发生变化时,它会通知所有注册的观察者并更新它们。常用于实现事件驱动的系统,如GUI框架、消息通知系统等。
- 策略模式:定义了一系列算法,并将它们封装起来,使它们可以互换。常用于实现多种算法或策略的场景,如排序算法、支付策略等。
策略模式:
定义了一系列算法,并将它们封装起来,使它们可以互换。策略模式使得算法可以独立于使用它的客户端而变化。一般用在需要实现多种算法或策略,并且这些算法或策略可以互换使用的场景。
模板方法模式:
定义了一个算法的骨架,而将一些步骤延迟到子类中实现。模板方法使得子类可以不改变算法的结构即可重定义算法的某些特定步骤。一般用在需要将一个复杂的过程分解为多个步骤,并且这些步骤在不同的子类中可能有不同的实现方式的场景。
代理模式:
为其他对象提供一种代理以控制对这个对象的访问。代理对象可以在客户端和目标对象之间起到中介的作用,降低系统的耦合度。一般用在需要为某个对象提供一个代理以控制对该对象的访问,或者需要增强某个对象的功能的场景。
原型模式:
用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式允许一个对象通过复制另一个对象来创建新的实例。一般用在需要创建大量相似对象的场景,或者对象的创建过程比较复杂且耗时的场景。
简单工厂模式的工作原理:
简单工厂模式通过一个工厂类来创建对象,而不需要客户端直接实例化对象。客户端只需要传入一个参数(通常是所需对象的类型或名称),工厂类就会根据这个参数来创建相应的对象并返回。这样,客户端就不需要关心对象的创建过程,只需要知道如何调用工厂类来获取所需的对象即可。
前端实现扫码登录功能
前端实现扫码登录功能通常涉及以下几个步骤:
- 生成二维码:前端通过调用后端接口生成一个包含登录信息的二维码。
- 扫码识别:用户使用手机等移动设备扫描该二维码,并在移动设备上确认登录。
- 后端验证:后端接收到移动设备的登录确认后,验证登录信息并生成一个登录凭证(如token)。
- 前端登录:前端通过调用后端接口并使用该登录凭证进行登录验证,如果验证成功,则用户登录成功。
请注意,具体实现过程可能因项目需求和后端接口的不同而有所差异。
如何在前端实现一个实时自动补全搜索框?
在前端实现实时自动补全搜索框(通常称为“自动补全”或“自动建议”)通常涉及以下几个步骤:
- 监听输入框事件:监听用户在输入框中的输入事件(如
input
或keyup
)。 - 发送请求到后端:将用户输入的关键词发送到后端,后端返回相关的建议列表。
- 显示建议列表:在前端显示从后端返回的建议列表。
- 处理用户选择:当用户从建议列表中选择一个选项时,更新输入框的值。
为了提高性能和用户体验,可以使用防抖(debounce)或节流(throttle)技术来减少请求的频率。
如何在前端实现国际化,并根据用户设置自动切换语言?
前端国际化通常涉及以下步骤:
- 准备多语言资源:创建包含不同语言文本的JSON文件或对象。
- 选择语言:根据用户设置或浏览器语言自动选择语言,或使用选择器让用户手动选择。
- 加载语言资源:动态加载所选语言的资源文件。
- 渲染界面:使用所选语言的资源来渲染界面文本。
可以使用第三方库(如react-i18next
、vue-i18n
)来简化这个过程。
常见的登录鉴权方式有哪些?各自的优缺点是?
-
Session-Cookie:
- 优点:简单、易于理解和实现。
- 缺点:依赖于服务器端的Session存储,容易受到CSRF攻击,且跨域问题复杂。
-
Token-Based(JWT, OAuth):
- 优点:无状态、跨域方便、安全性较高(特别是JWT)。
- 缺点:Token需要妥善存储和管理,JWT的过期和刷新机制需要额外处理。
-
OAuth2:
- 优点:适用于第三方授权,如微信、QQ登录,用户体验较好。
- 缺点:实现复杂,涉及多个系统之间的交互和安全性问题。
-
SSO(单点登录):
- 优点:用户只需一次登录即可访问多个系统,提高用户体验。
- 缺点:实现和维护成本较高,需要统一的认证中心。
前端开发中的 Live-Reload 自动刷新与 HMR 热模块替换有什么区别?
- Live-Reload:每次代码更改后,浏览器会自动刷新整个页面来显示更改。
- HMR(Hot Module Replacement):允许在运行时替换、添加或删除模块,而无需重新加载整个页面。它只更新更改的部分,提供更平滑的开发体验。
如何使用 Webpack 处理内联 CSS?
可以使用style-loader
将CSS作为<style>
标签内联到HTML文档中。在Webpack配置文件中,可以这样配置:
module.exports = {module: {rules: [{test: /\.css$/,use: ['style-loader', 'css-loader'],},],},
};
如何使用 Webpack 解决开发环境中的跨域问题?
在开发环境中,可以使用webpack-dev-server
的proxy
选项来设置代理服务器,从而解决跨域问题。例如:
devServer: {proxy: {'/api': {target: 'http://backend-server.com',changeOrigin: true,},},
},
这样,当前端请求/api
开头的URL时,webpack-dev-server
会将其代理到http://backend-server.com
。
前端开发中如何优化网络请求和资源加载?具体怎么实现?
- 合并和压缩资源:使用Webpack等工具合并和压缩CSS、JavaScript文件。
- 代码拆分:使用Webpack的代码拆分功能(Code Splitting),按需加载代码。
- 缓存:利用HTTP缓存头(如
Cache-Control
、Expires
)和浏览器缓存机制。 - CDN:将静态资源部署到CDN上,加快资源加载速度。
- 懒加载:对于图片等资源,使用懒加载技术,只在需要时加载。
- 减少HTTP请求:合并CSS和JavaScript文件,使用雪碧图等技术减少图片请求。
如何使用 Webpack 优化产出代码?
- Tree Shaking:确保Webpack配置中启用了Tree Shaking,以移除未使用的代码。
- 代码压缩:使用
TerserPlugin
等插件对代码进行压缩。 - Scope Hoisting:虽然Webpack 4及更高版本已经默认启用了Scope Hoisting(也称为模块连接),但它仍然有助于减少输出文件的大小。
- 分离开发环境和生产环境配置:为开发环境和生产环境创建不同的Webpack配置文件,以便在生产环境中启用所有优化选项。
Webpack中,常见的图片处理加载器有哪些?
- file-loader:将文件输出到输出目录,并返回文件的URL。
- url-loader:类似于
file-loader
,但可以将小文件转换为base64编码的URL,以减少HTTP请求。 - image-webpack-loader:用于在Webpack中处理图像文件(如压缩、优化等)。
- imagemin-mozjpeg、imagemin-optipng等:作为插件与
image-webpack-loader
一起使用,以压缩JPEG和PNG图像。
如何使用 Webpack 和 Localstorage 实现静态资源的离线缓存?
- 将静态资源打包成单个文件:使用Webpack将静态资源(如JavaScript、CSS、图片)打包成一个或多个文件。
- 在本地存储资源:在应用程序启动时,检查Localstorage中是否已存储这些资源。如果没有,则从服务器加载它们,并将它们存储在Localstorage中。
- 从本地存储加载资源:如果Localstorage中已存储资源,则直接从Localstorage中加载它们,而不是从服务器加载。
注意:由于浏览器对Localstorage的存储大小和类型有限制(如只能存储字符串),因此可能需要将资源编码为字符串(如使用Base64编码)后再存储。此外,还需要考虑资源的版本控制和更新机制,以确保用户始终使用最新版本的资源。
1. 什么是 Webpack?它有什么作用?
Webpack 是一个现代 JavaScript 应用的静态模块打包工具。
它的作用主要包括:
- 模块化管理:将项目的各种资源(JS、CSS、图片等)作为模块进行处理。
- 代码打包:把模块编译为适合浏览器执行的文件。
- 性能优化:通过代码分割、Tree Shaking、压缩等提升项目性能。
- 开发体验优化:通过热更新、代理、编译错误提示等工具提升开发效率。
- 兼容性处理:将现代 JavaScript 转换为兼容老旧浏览器的代码。
2. 如何使用 Webpack 进行前端性能优化?
- 代码分割:
- 使用
optimization.splitChunks
将公共代码抽离到单独的文件中。
- 使用
- Tree Shaking:
- 通过
sideEffects: false
移除未使用的模块。
- 通过
- 异步加载:
- 动态导入模块,按需加载(
import()
)。
- 动态导入模块,按需加载(
- 资源压缩:
- 使用
TerserPlugin
压缩 JavaScript,css-minimizer-webpack-plugin
压缩 CSS。
- 使用
- 图片优化:
- 使用
image-minimizer-webpack-plugin
压缩图片。
- 使用
- 缓存优化:
- 文件名带有哈希值(如
[contenthash]
)实现长效缓存。
- 文件名带有哈希值(如
3. 如何在 Webpack 中实现条件组件的按需打包?
- 动态导入:
- 根据条件动态加载组件:
if (condition) {import('./ComponentA').then(ComponentA => {new ComponentA.default();}); }
- 根据条件动态加载组件:
require.context
:- 按条件动态引入:
const context = require.context('./components', false, /\.js$/); const module = condition ? context('./ComponentA.js') : context('./ComponentB.js');
- 按条件动态引入:
DefinePlugin
:- 定义环境变量,基于环境选择加载:
if (process.env.NODE_ENV === 'production') {import('./ProdComponent'); } else {import('./DevComponent'); }
- 定义环境变量,基于环境选择加载:
4. 前端如何使用 Webpack 进行高效分包优化?
-
optimization.splitChunks
:- 提取公共依赖库:
optimization: {splitChunks: {chunks: 'all',cacheGroups: {vendors: {test: /[\\/]node_modules[\\/]/,name: 'vendors',chunks: 'all',},},}, }
- 提取公共依赖库:
-
动态加载(按需加载):
- 使用
import()
分割业务代码为独立文件。
- 使用
-
第三方库分包:
- 将第三方库(如 React、Lodash)独立打包,减小主文件体积。
-
按路由分包:
- 配合 React 的
React.lazy()
或 Vue 的路由懒加载实现。
- 配合 React 的
5. 前端项目中资源的缓存配置策略有哪些?
- 静态资源哈希命名:
- 在文件名中添加
contenthash
:output: {filename: '[name].[contenthash].js', }
- 在文件名中添加
- 合理的 Cache-Control 设置:
- 配置
max-age
和immutable
以确保长期有效缓存。
- 配置
- 分离动态和静态资源:
- 动态资源使用短期缓存,静态资源使用长期缓存。
- 服务端支持:
- 使用 ETag 和 Last-Modified 提供资源变更校验。
6. 如何优化 Webpack 打包后文件的体积?
- Tree Shaking:
- 配置
sideEffects: false
,去除未使用的代码。
- 配置
- 减少 Polyfill:
- 使用
@babel/preset-env
按需加载。
- 使用
- 按需引入库:
- 使用插件如
babel-plugin-import
实现按需引入组件。
- 使用插件如
- 代码压缩:
- 配置
TerserPlugin
压缩 JS,使用 CSS 压缩插件。
- 配置
- 图片优化:
- 使用
image-minimizer-webpack-plugin
减少图片体积。
- 使用
- 移除多余插件:
- 避免引入不必要的插件和模块。
7. 同一前端页面的 3 个组件请求同一个 API 并发送了 3 次请求,如何优化?
-
请求合并:
- 使用一个公共服务模块共享请求:
let result; const fetchData = async () => {if (!result) {result = fetch('/api').then(res => res.json());}return result; };
- 使用一个公共服务模块共享请求:
-
共享状态:
- 使用状态管理工具(如 Redux、MobX、Pinia)缓存请求结果。
-
缓存层:
- 利用浏览器缓存或 IndexedDB 存储常用数据,避免重复请求。
8. 什么是 Webpack 的 bundle、chunk 和 module?分别有什么作用?
-
Module(模块):
- Webpack 中的最小代码单元,可以是 JS 文件、CSS 文件、图片等。
- 作用:模块是打包的基本单元。
-
Chunk(代码块):
- 由多个模块组成,用于按需加载或代码分割。
- 作用:实现代码的动态加载和优化。
-
Bundle(包):
- Webpack 打包生成的最终文件。
- 作用:供浏览器直接加载运行。
9. Webpack 插件底层的实现原理是什么?
-
Webpack 插件是一个类或函数:
- 插件通过
apply(compiler)
方法接入 Webpack。 - 通过监听 Webpack 的生命周期钩子(如
emit
、compile
)执行自定义逻辑。
- 插件通过
-
工作流程:
- Webpack 初始化时,调用插件的
apply
方法。 - 插件在特定钩子阶段注入自定义逻辑,操作 Webpack 的内部数据结构(如 AST 或文件列表)。
- Webpack 初始化时,调用插件的
-
简单示例:
class MyPlugin {apply(compiler) {compiler.hooks.emit.tap('MyPlugin', compilation => {console.log('Emit phase');});} }
10. Core Web Vitals 是什么?它包括哪些指标?
Core Web Vitals 是 Google 推出的评估网页用户体验的关键性能指标集,用于衡量加载性能、交互性和视觉稳定性。
包括以下指标:
-
LCP(Largest Contentful Paint):
- 测量最大内容元素的加载时间,目标小于 2.5 秒。
-
FID(First Input Delay):
- 测量用户与页面首次交互的响应时间,目标小于 100 毫秒。
-
CLS(Cumulative Layout Shift):
- 测量页面视觉内容的稳定性,目标小于 0.1。
-
其他相关指标:
- TTFB(Time to First Byte):首字节时间。
- INP(Interaction to Next Paint):全交互响应性测量(实验性)。