03-06 13:42门头沟学院 工学
关注
百度健康春招前端一面
1.介绍一下项目
2.移动端适配
3.css选择器优先级
移动端适配是指将网页或应用程序适配到移动设备(如手机、平板电脑)的屏幕尺寸和分辨率,以提供更好的用户体验。以下是一些常用的移动端适配方法:
-
响应式设计(Responsive Design):
- 使用 CSS3 媒体查询(Media Queries)和弹性布局(Flexbox)等技术,根据设备的屏幕尺寸和分辨率动态调整页面布局和样式,以适应不同大小的屏幕。
-
视口设置(Viewport):
- 使用
<meta>标签设置视口(viewport),指定页面在移动设备上的显示尺寸和缩放比例,以确保页面在移动设备上正常显示。
- 使用
-
REM 或 EM 单位:
- 使用相对单位(如 REM 或 EM)代替固定单位(如 PX),以便根据设备的字体大小和分辨率进行自适应布局,从而实现移动端适配。
-
Flexbox 布局:
- 使用 Flexbox 布局来实现页面的自适应和灵活布局,使页面在不同尺寸的屏幕上均能正确显示,并具有良好的可伸缩性。
-
CSS 媒体查询:
- 使用 CSS3 媒体查询来针对不同的设备尺寸和分辨率应用不同的样式,以适配不同大小的屏幕和设备。
-
移动端布局框架:
- 使用现成的移动端布局框架(如 Bootstrap、Ant Design Mobile 等)来快速搭建适配移动端的网页和应用程序,提高开发效率。
-
设备像素比(Device Pixel Ratio,DPR):
- 根据设备的像素比(DPR)调整页面元素的大小和样式,以适配高分辨率的移动设备,提高页面显示的清晰度和质量。
综合使用以上方法,可以实现有效的移动端适配,提供良好的用户体验和页面显示效果。 CSS 选择器优先级是确定样式应用的重要规则,它决定了当多个规则同时匹配同一个元素时,哪个样式将被应用到元素上。优先级是通过对选择器进行权重计算来确定的,权重高的规则具有更高的优先级。
CSS 选择器优先级由四个部分组成,按重要性递减的顺序是:
-
行内样式(Inline Styles):在元素的 style 属性中直接指定的样式具有最高的优先级,权重为 1000。
-
ID 选择器(ID Selectors):通过元素的 id 属性选择元素的样式,权重为 100。
-
类选择器、属性选择器和伪类选择器(Class Selectors, Attribute Selectors, Pseudo-Class Selectors):包括类选择器(.class)、属性选择器([attribute])和伪类选择器(:hover、:first-child 等),权重为 10。
-
元素选择器和伪元素选择器(Element Selectors, Pseudo-Element Selectors):包括元素选择器(如 p、div 等)和伪元素选择器(::before、::after 等),权重为 1。
如果有多个规则应用到同一个元素上,CSS 解析引擎会根据这些规则的权重来确定最终应用的样式。具体的规则是:
- 样式优先级越高的规则优先被应用。
- 如果权重相同,则后声明的规则会覆盖前面的规则。
- 如果权重和声明顺序都相同,则谁在后声明就会覆盖前面的样式。
例如,行内样式会覆盖外部样式表中的样式,而带有 ID 选择器的样式会覆盖类选择器和元素选择器的样式等。
综上所述,了解 CSS 选择器优先级对于正确理解和应用样式非常重要,可以避免样式冲突和提高样式的可维护性。
4.有没有做过性能优化
5.路由懒加载如何实现的
路由懒加载是一种优化前端性能的技术,它可以在需要时按需加载页面组件,而不是一次性加载所有页面组件,从而减少初始加载时间和网络带宽的消耗。下面是实现路由懒加载的一般步骤:
-
使用动态导入(Dynamic Import):
- 路由懒加载通常使用 JavaScript 的动态导入语法(
import())来实现。在路由配置中,不直接导入组件,而是使用动态导入语法导入组件模块。
- 路由懒加载通常使用 JavaScript 的动态导入语法(
-
将组件定义为函数:
- 将需要延迟加载的路由组件定义为一个返回动态导入模块的函数。这个函数在需要加载组件时被调用,返回一个 Promise,当 Promise 被解析时,会加载并返回组件模块。
-
Webpack 或其他打包工具支持:
- 确保你的打包工具(如 Webpack)支持动态导入功能,并且配置正确。通常情况下,Webpack 会自动将动态导入的模块拆分为单独的代码块(chunk),在需要时按需加载。
下面是一个简单的示例,演示了如何使用 Vue Router 实现路由懒加载:
// 路由配置
const router = new VueRouter({
routes: [
{
path: '/about',
// 将组件定义为函数,返回动态导入模块
component: () => import('./views/About.vue')
},
{
path: '/contact',
// 将组件定义为函数,返回动态导入模块
component: () => import('./views/Contact.vue')
}
]
})
在上述代码中,import('./views/About.vue') 和 import('./views/Contact.vue') 使用了动态导入语法,将组件定义为返回动态导入模块的函数,实现了路由懒加载的效果。
使用路由懒加载可以有效地减少初始加载时间和页面大小,提高页面的性能和用户体验。
6.vite和webpack你是如何如何选型的
选择使用 Vite 还是 Webpack 取决于项目的需求和特点,以及开发团队的技术栈和偏好。下面是一些考虑因素:
-
项目类型:
- 对于传统的大型单页面应用(SPA)或多页面应用(MPA),Webpack 是一个更成熟和稳定的选择,因为它有更多的功能和生态支持。
- 对于轻量级的项目、快速原型开发或小型应用,Vite 提供了更快的开发环境和更快的热更新速度。
-
开发体验:
- Vite 提供了即时编译(Instant Server Start)和快速热更新等功能,使开发者可以享受到更快的开发体验,特别是在开发阶段和调试阶段。
- Webpack 在构建大型项目时可能会有一定的构建时间,但它提供了更丰富的插件和 loader 生态系统,可以满足更多复杂的需求。
-
生态系统:
- Webpack 有一个庞大的生态系统,拥有大量的插件和 loader,可以满足各种不同的需求,例如代码拆分、优化、压缩、模块热替换等。
- Vite 是一个相对较新的工具,其生态系统可能相对较小,但正在迅速发展,社区也在不断壮大,已经拥有了一些核心插件和工具。
-
构建性能:
- Vite 使用了现代的 ES 模块原生支持和基于浏览器原生模块系统的构建方式,因此具有非常快的构建速度,尤其是在开发环境和热更新方面。
- Webpack 的构建速度可能会比较慢,尤其是在大型项目中,但通过一些优化和配置,可以提升构建性能。
-
技术栈和社区支持:
- 如果团队已经熟悉了 Webpack,并且项目对 Webpack 生态系统的某些功能有较高的依赖,那么选择继续使用 Webpack 可能会更加顺畅。
- 如果团队对现代工具链和 JavaScript 生态系统有浓厚的兴趣,并且希望尝试新的技术和工具,那么选择使用 Vite 可能会更加有趣。
综上所述,选择使用 Vite 还是 Webpack 主要取决于项目的需求、团队的技术栈和开发体验的偏好。可以根据具体情况权衡利弊,选择适合自己项目的构建工具。
7.输入url到页面渲染过程(绘制的过程了解吗)
了解 URL 到页面渲染的过程是前端开发中的基础知识之一。以下是一般情况下的整个过程:
-
DNS 解析:
- 当用户在浏览器地址栏中输入 URL 后,浏览器会先进行 DNS 解析,将域名解析成对应的 IP 地址。
- 如果 DNS 缓存中存在对应的 IP 地址,则直接使用缓存,否则会向 DNS 服务器发送 DNS 查询请求。
-
建立 TCP 连接:
- 浏览器通过 IP 地址与服务器建立 TCP 连接,该过程包括 TCP 的三次握手,确保客户端和服务器之间的连接建立成功。
-
发送 HTTP 请求:
- 连接建立成功后,浏览器向服务器发送 HTTP 请求,请求页面资源,包括 HTML、CSS、JavaScript 文件等。
- 请求过程中会包含请求头、请求体等信息。
-
服务器处理请求:
- 服务器接收到浏览器发送的请求后,会根据请求的 URL 和请求头中的信息进行相应的处理,包括查询数据库、执行业务逻辑等。
-
返回 HTTP 响应:
- 服务器处理完成后,会返回 HTTP 响应给浏览器,响应包括响应状态码、响应头、响应体等信息。
- 响应体中通常包含请求的页面内容以及相关资源(如图片、样式表、脚本文件等)的链接。
-
浏览器解析 HTML:
- 浏览器接收到响应后,开始解析 HTML 内容,构建 DOM 树。
- 在解析 HTML 过程中,如果遇到外部资源(如 CSS、JavaScript 文件等),会向服务器发送新的请求,继续加载这些资源。
-
渲染页面:
- 浏览器根据 DOM 树和 CSS 样式表计算出页面的渲染树(Render Tree)。
- 渲染树确定了页面中每个节点的位置和样式,并将其显示在浏览器窗口中,完成页面渲染的过程。
-
执行 JavaScript:
- 如果 HTML 中包含了 JavaScript 代码,浏览器会执行 JavaScript 代码,可能会修改页面的 DOM 结构或样式,进而触发页面重新渲染。
-
绘制页面:
- 浏览器根据渲染树和 JavaScript 的执行结果,将页面内容绘制到屏幕上,完成页面的绘制和显示。
以上是从输入 URL 到页面渲染完成的整个过程,其中涉及了 DNS 解析、建立 TCP 连接、发送 HTTP 请求、服务器处理请求、浏览器解析 HTML、渲染页面等多个步骤。
8.强缓存和协商缓存(301和302状态码是什么)
强缓存和协商缓存是 HTTP 缓存机制的两种实现方式,它们分别通过不同的方式控制缓存的有效性和更新机制。
-
强缓存:
- 当客户端发起请求时,会先检查本地缓存是否存在该资源以及缓存是否过期。
- 如果缓存未过期,则直接从本地缓存中加载资源,不会向服务器发起请求,节省了网络带宽和服务器资源。
- 如果缓存过期,则会向服务器发起请求,服务器会返回状态码为 200(OK),并在响应头中设置缓存控制相关的头部字段(如 Expires、Cache-Control)来指示客户端缓存该资源。
-
协商缓存:
- 当客户端发起请求时,会先向服务器发送一个条件请求,询问服务器是否可以使用缓存。
- 如果服务器认为客户端的缓存仍然有效,则返回状态码为 304(Not Modified),并在响应头中设置缓存控制相关的头部字段(如 Last-Modified、ETag)来指示客户端使用缓存。
- 如果服务器认为客户端的缓存已经失效,则返回状态码为 200(OK),并返回新的资源内容。
-
301 和 302 状态码:
- 301 永久重定向:表示请求的资源已被永久移动到新的 URL,客户端应该更新所有引用该资源的 URL。在浏览器中,会自动跳转到新的 URL。
- 302 临时重定向:表示请求的资源临时移动到了新的 URL,客户端应该继续使用原始 URL。在浏览器中,会暂时跳转到新的 URL,但不会更新地址栏。
这两种状态码主要用于实现 URL 重定向功能,其中 301 是永久性的重定向,而 302 是临时性的重定向。在使用时需要根据具体的业务需求和情境选择合适的状态码。
9.了解性能指标吗,如何减少lcp
了解性能指标对于前端开发非常重要,其中 LCP(Largest Contentful Paint)是页面加载过程中的一个关键性能指标,表示最大内容渲染时间,即页面上最大的可见内容元素渲染完成所需的时间。优化 LCP 可以提升用户体验和页面加载速度。
以下是一些减少 LCP 的方法:
-
优化图片加载:图片通常是页面上最大的可见内容元素之一,因此优化图片加载对于减少 LCP 很重要。可以通过使用适当的图片格式(如 WebP)、懒加载、预加载以及使用正确尺寸的图片等方式来优化图片加载。
-
使用字体的最佳实践:字体的加载也可能影响到 LCP。确保使用的字体文件大小适中,并使用字体的最佳加载方式(如使用字体的本地备份、font-display 属性等),以尽量减少字体的加载时间。
-
减少关键渲染路径上的阻塞资源:通过减少页面加载过程中的阻塞资源(如 CSS 和 JavaScript 文件)的数量和大小,可以加快页面的渲染速度,从而减少 LCP。
-
优化服务器响应时间:优化服务器响应时间可以减少页面加载时间,从而降低 LCP。可以通过优化服务器端代码、使用 CDN 加速、启用 HTTP/2 等方式来减少服务器响应时间。
-
减少渲染阻塞元素的数量和大小:在页面加载过程中,避免渲染阻塞元素(如 JavaScript 和 CSS 文件、未优化的图片等)的数量和大小,以尽量减少页面的渲染时间。
-
使用预渲染技术:对于一些静态页面或者不常变化的页面,可以使用预渲染技术将页面在构建时已经渲染好,从而减少页面加载时间,降低 LCP。
通过以上方法,可以有效减少 LCP,提升页面加载速度和用户体验。
实现a + b === c兼容小数计算
要实现 a + b === c 兼容小数计算,可以使用 JavaScript 中的浮点数运算,并进行适当的精度控制。下面是一个简单的实现:
function floatAdd(a, b) {
const precision = Math.max(getDecimalLength(a), getDecimalLength(b));
const multiple = Math.pow(10, precision);
return (a * multiple + b * multiple) / multiple;
}
function getDecimalLength(num) {
// 将数字转换为字符串,以便处理小数点
const numStr = String(num);
const decimalIndex = numStr.indexOf('.');
if (decimalIndex !== -1) {
// 如果存在小数点,则返回小数点后的位数
return numStr.length - decimalIndex - 1;
} else {
// 如果不存在小数点,则返回 0
return 0;
}
}
// 测试
const a = 0.1;
const b = 0.2;
const c = 0.3;
console.log(floatAdd(a, b) === c); // 输出 true
上述代码中,floatAdd 函数用于进行浮点数相加,其中通过 getDecimalLength 函数获取浮点数的小数位数,并根据最大小数位数进行精度控制,以确保计算结果准确。
实现一个EventEmitter类
下面是一个简单的 EventEmitter 类的实现,包含了 on、off、emit 方法,并在代码中添加了详细的注释说明:
class EventEmitter {
constructor() {
// 使用对象存储事件及对应的回调函数
this.events = {};
}
// 订阅事件
on(event, callback) {
// 如果事件不存在,则创建一个空数组
if (!this.events[event]) {
this.events[event] = [];
}
// 将回调函数添加到对应事件的数组中
this.events[event].push(callback);
}
// 取消订阅事件
off(event, callback) {
// 如果事件不存在,则直接返回
if (!this.events[event]) {
return;
}
// 如果没有传入回调函数,则移除该事件的所有回调函数
if (!callback) {
delete this.events[event];
return;
}
// 移除指定的回调函数
this.events[event] = this.events[event].filter(cb => cb !== callback);
}
// 触发事件
emit(event, ...args) {
// 如果事件不存在,则直接返回
if (!this.events[event]) {
return;
}
// 遍历事件的所有回调函数,并依次执行
this.events[event].forEach(callback => callback.apply(this, args));
}
}
使用示例:
// 创建一个 EventEmitter 实例
const emitter = new EventEmitter();
// 定义一个事件回调函数
const callback1 = (data) => {
console.log('Event 1:', data);
};
// 订阅事件
emitter.on('event1', callback1);
// 触发事件
emitter.emit('event1', 'Hello World');
// 取消订阅事件
emitter.off('event1', callback1);
// 再次触发事件,但因为已经取消订阅,所以不会有输出
emitter.emit('event1', 'Hello World');
以上代码实现了一个简单的 EventEmitter 类,可以用于在 JavaScript 中实现自定义事件的订阅、取消订阅和触发功能。