最近面试了三家公司,记录复盘了一下我没有回答出的地方,对自己的知识面进行查漏补缺,因为各方面都有涉及,并不是系统性的整理,所以希望对大家有所帮助,别忘点个赞,感谢各位靓仔靓女。
HTML
href与src之间的区别
- src是指向物件来源的地址是引入通常在img,script,iframe元素上使用;href是超文本引用,指向需要连接的地方。
- 可替换的元素上使用src,引入资源嵌套当前位置,href用于在文档和外部资源建立一个关系。
- 在请求src资源时,会暂停该资源加载,编译,执行完毕;而浏览器解析href资源时,不会停止对当前文档的处理。
image标签中alt和title的区别
image标签alt可以用图片无法显示时,用文本来占位。title属性时用户鼠标悬停在图像上时,可以显示一个小的文本框,用于描述图片。
iframe相关的知识
<iframe>
元素在 HTML 中用于在当前页面内嵌入另一个文档。它常用于嵌入外部内容,比如视频、广告、其他网页或第三方应用等。
基本用法
<iframe src="https://www.example.com" width="600" height="400" frameborder="0"></iframe>
src
: 指定要嵌入的资源的 URL。width
和height
: 设置iframe
的宽度和高度。frameborder
: 控制是否显示iframe
边框。0
表示没有边框,1
表示有边框。
常用属性
src
: 嵌入的内容的 URL(可以是外部链接或同源页面)。name
: 指定一个名称,允许其他窗口或表单通过target="iframe_name"
指定在哪个iframe
中打开链接。sandbox
: 启用额外的安全限制。允许限制iframe
内部的行为,比如禁用表单提交、脚本执行等。allow
: 允许的权限设置,比如allow-scripts
、allow-same-origin
等,限制或允许某些功能。loading="lazy"
: 延迟加载iframe
,只在用户需要时加载,提高性能。
iframe
的常见用途
- 嵌入第三方内容: 通过
iframe
嵌入来自外部服务的内容,例如视频(YouTube)、地图(Google Maps)或广告。 - 跨域内容显示:
iframe
提供了独立的上下文,可以展示跨域资源,避免直接加载外部内容对主页面的影响。 - 子页面与主页面分离: 当你希望将页面的一部分单独加载或管理时,可以使用
iframe
,例如嵌套页面,在线支付、用户登录等。 - 浏览器兼容性: 通过
iframe
可以有效隔离页面的行为,解决不同浏览器对某些功能的兼容性问题。
优点
- 跨域隔离:
iframe
提供了浏览器级别的隔离,可以在主页面中嵌入来自不同源的内容,而无需担心跨域访问问题。这样可以避免不同来源的 JavaScript 相互干扰。- 例如,嵌入外部站点的内容时,
iframe
会创建一个隔离的上下文,保证外部网站的脚本不会直接访问主页面的 JavaScript 环境。
- 避免主页面影响:
iframe
可以将第三方内容隔离开,避免影响主页面的样式、布局和脚本。例如,广告、社交插件、外部小工具等。
- 内容的独立性:
- 嵌入的
iframe
内容可以是独立的文档(带有自己的样式、JavaScript、框架等),不会与父页面发生冲突。这使得处理动态内容或需要单独控制的部分变得简单。
- 嵌入的
- 加载控制:
- 通过
iframe
可以延迟加载资源(例如,通过loading="lazy"
属性),减少页面初次加载时的性能压力。
- 通过
- 浏览器兼容性:
iframe
是 HTML 标准的一部分,广泛支持,并且能有效解决不同浏览器对某些特性的支持差异。
缺点
- 性能问题:
- 使用
iframe
会增加页面的请求数量和渲染复杂性。每个iframe
都会作为一个独立的浏览上下文来加载和渲染,可能会增加浏览器的负担,影响页面性能。 - 如果页面中嵌套多个
iframe
,可能会导致过多的资源消耗,尤其是在移动端设备上。
- 使用
- 响应式设计问题:
- 由于
iframe
的内容可能来自不同源,它的大小和布局可能无法与父页面的响应式设计完全兼容。需要使用iframe
内的 CSS 或 JavaScript 来调整它的尺寸和布局,增加了开发复杂度。
- 由于
- 安全问题:
iframe
中嵌入的外部内容可能会带来安全隐患。例如,恶意的第三方内容可能利用iframe
进行钓鱼攻击或加载恶意脚本。- 为了增加安全性,HTML5 引入了
sandbox
属性来限制iframe
的行为。sandbox
限制了iframe
中执行的操作,比如禁用表单提交、脚本执行等。
- SEO 问题:
- 搜索引擎可能不会将
iframe
中的内容索引到搜索结果中。这是因为iframe
是嵌套的,内容并不直接属于父页面,所以iframe
中的内容对 SEO 的贡献较小。
- 搜索引擎可能不会将
总结:iframe
的使用建议
- 何时使用
iframe
:- 当需要嵌入外部内容,特别是来自第三方站点的内容时,
iframe
是最好的选择。它可以解决跨域和安全问题,并能提供更好的隔离性。 - 在页面布局中,如果需要将某些内容与页面的其他部分隔离,或者使用独立的 JavaScript 框架时,
iframe
也非常合适。
- 当需要嵌入外部内容,特别是来自第三方站点的内容时,
- 何时避免使用
iframe
:- 如果是需要频繁交互的应用,或者需要高性能的页面,过多的
iframe
嵌套可能会导致性能下降。 - 如果页面的 SEO 依赖于外部内容,嵌入的
iframe
内容可能不会被搜索引擎索引,因此应该谨慎使用。
- 如果是需要频繁交互的应用,或者需要高性能的页面,过多的
- 优化
iframe
的使用:- 避免嵌套过多的
iframe
,确保iframe
的内容尺寸与父页面响应式布局匹配。 - 使用
sandbox
限制iframe
内部的行为,增加安全性。 - 使用
loading="lazy"
属性延迟iframe
的加载,提升页面加载性能。
- 避免嵌套过多的
总体来说,iframe
在实现跨域内容和嵌入第三方资源方面非常有用,但应谨慎使用,以避免性能、SEO、安全等方面的问题。
CSS
浏览器绘制0.5px的方法与实现字体小于12px的解决方法,请列举多个方法
使用 CSS 的 transform 缩放
将1px进行缩放:
.line {
width: 100%;
height: 1px;
background-color: #000;
transform: scaleY(0.5);
transform-origin: top;
}
将12px进行缩放:
.text {
font-size: 12px;
transform: scale(0.5);
transform-origin: left top;
}
使用 border + transform
给元素的边框进行0.5缩放:
.line {
border-top: 1px solid #000;
transform: scaleY(0.5);
}
使用伪元素
通过伪元素设置1px的的高度并缩小:
.line {
position: relative;
}
.line::after {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 1px;
background-color: #000;
transform: scaleY(0.5);
}
绘制三角形的方法有哪些?
常见的是使用border属性绘制一个三角形:
.triangle {
width: 0;
height: 0;
border: 100px solid;
border-color: red blue yellow green;
}
绘制结果
可以给宽度和高度都设置为0的元素,border的任何值都会直接相交,border其实就是用三角形创建的。
根据需要绘制的三角形方向,把其他朝向进行隐藏即可,如三角形朝下:
.triangle {
width: 0;
height: 0;
border-top: 100px solid red;
border-right: 100px solid transparent;
border-left: 100px solid transparent;
border-bottom: 100px solid transparent;
}
三角形朝下的效果
其他方法可以参考我引用的文章,介绍得很详细,但是不太常见,能简要阐述即可。
JavaScript
路由中history模式和hash模式各自的优劣和应用场景
Hash模式
定义:hash模式是一个把前端路由的路径有#号拼接在url后面的模式。当#后面路由发生变化,浏览器不会重新发起请求,而是触发onhashchange事件。
特点:
- hash可以改变url,但是不会触发页面重新加载,<font style="color:rgb(37, 41, 51);">即不会刷新页面。</font>
- <font style="color:rgb(37, 41, 51);">hash通过window.onhashchange的方式,来监听hash的改变,是按无刷新跳转。</font>
- <font style="color:rgb(37, 41, 51);">hash永远不会提交到server端。</font>
History模式
定义:history API是H5提供的新特性,允许开发者直接更改前端路由,即更新浏览器URL地址而不重新发起请求。
特点:
- 没有#号,路由地址看起更加整洁和美观。
- 通过pushState,replaceState来实现无刷新跳转的功能。
- nginx处需要配置相关的路由地址,不配置会出现找不到该页面的错误。
两者的应用场景
- toB的系统推荐使用hash模式,相对简单且容易,hash对url规范不敏感。
- toC的系统,可以考虑选择H5 history,需要服务端的支持与配置。
VUE
Vue3和Vue2之间的差异
- 选项式API升级到组合式API
- 性能改进:更快的虚拟DOM,tree-shaking更好地支持,更高效的响应式系统。
- 响应式系统的升级,Vue2是Object.defineProperty而Vue3使用了Proxy来更好地实现响应式。
- Vue3全面支持typeScript
- 生命周期Vue3引入新的生命周期钩子配合组合式API函数来用语法更加简洁。
- Fragment Vue3的template这次hi包括非单一容器
特性 | Vue2 | Vue3 |
---|---|---|
API风格 | Options API | Composition API |
响应式系统 | Object.defineProperty | 使用Proxy实现响应式 |
性能 | 性能较低 | 优化性能,虚拟DOM更高效 |
TypeScript支持 | 支持较差 | 完全用TypeScript编写 |
生命周期钩子 | 常规钩子 | 新增适配Composition API的钩子 |
多个根元素 | 不支持 | 支持Fragment |
v-model | 只能绑定一个变量 | 支持多个绑定,支持自定义事件名 |
Teleport | 不支持 | 支持元素传送到DOM种其他位置 |
Suspense | 不支持 | 支持一部组件加载时显示加载状态 |
Vue中使用for循环为什么key不能用index
当以数组为下标的index作为key值时,其中一个元素(例如增删改查)发生了变化就有可能导致所有的元素的key值发生改变,diff算法时比较同级之间的不同,以key来进行关联,当对数组进行下标的变换时,比如删除第一条数据,那么以后所有的index都会发生改变,那么key自然也跟着全部发生改变。
所以index作为key值是不稳定的,而这种不稳定性有可能导致性能的浪费,导致diff无法关联起上一次一样的数据。因此,能不使用index作为key就不使用index。
Vue.$nextTick()实现原理简述
Vue.nextTick
是通过微任务队列来实现的,它确保在 Vue 完成 DOM 更新后执行回调函数。- 它的实现依赖于浏览器环境,通常会使用
Promise.then
或MutationObserver
来执行微任务,作为最后手段可以使用setTimeout
(宏任务)。 - 你可以使用
nextTick
来确保在 DOM 更新后执行某些操作,特别是在处理动态内容、动画或依赖更新后的 DOM 的场景中。
原生js,vue,小程序获取url中的携带参数
获取 URL 中携带的参数在原生 JavaScript、Vue 和小程序中实现方法如下:
原生 JavaScript 获取 URL 参数
使用 URLSearchParams
API 或正则解析。
使用 URLSearchParams
function getQueryParams(param) {
const urlParams = new URLSearchParams(window.location.search);
return urlParams.get(param); // 获取指定参数值
}
// 示例
console.log(getQueryParams("name")); // 假设 URL: ?name=John
使用正则解析
function getQueryParams(param) {
const regex = new RegExp(`[?&]${param}=([^&#]*)`, "i");
const match = window.location.search.match(regex);
return match ? decodeURIComponent(match[1]) : null;
}
// 示例
console.log(getQueryParams("name")); // 假设 URL: ?name=John
Vue 中获取 URL 参数
Vue 是基于 JavaScript 的,可以直接使用前述的原生方法获取参数。也可以通过 Vue Router 提供的 $route
对象。
使用 Vue Router 获取参数
export default {
mounted() {
// 假设 URL: /example?name=DJ
const name = this.$route.query.name; // 获取 query 参数
console.log(name);
}
};
不使用 Vue Router
直接使用原生方法,和第一部分类似:
mounted() {
const urlParams = new URLSearchParams(window.location.search);
const name = urlParams.get("name");
console.log(name);
}
小程序中获取 URL 参数
小程序没有直接的 window.location
,需要通过 onLoad
生命周期接收页面跳转时传递的参数。
Page({
onLoad: function (options) {
// options 对象包含页面跳转时传递的参数
console.log(options.name); // 获取参数 name 的值
}
});
总结
- 原生 JS: 使用
URLSearchParams
或正则解析。 - Vue: 推荐通过
$route.query
获取,简单直接。 - 小程序: 在
onLoad
生命周期中通过options
参数获取。
SPA第一次加载过慢如何解决
优化资源加载
- 使用Webpack,Vite等工具进行代码拆分,按需加载。
- 使用动态加载和懒加载。
const Component = () => import('./Component.vue');
- 采用tree-shaking但是基本上打包工具默认开启
- 在nginx端启用Gzip/Brotli压缩
gzip on; #开启压缩
gzip_http_version 1.1; #协议版本配置
gzip_comp_level 9; #压缩等级
gzip_types *;
优化图片和媒体
- 延迟加载
- 图片优化,使用工具压缩图片
减少JS的负担
- 减少第三方库的依赖,避免过重依赖。
- 从组件库按需引入组件
- 缩小JS包,使用工具(如Webpack Bundle Analyzer)分析优化优化打包结果。
- 打包时移除console.log语句。
提高缓存利用率
- 使用强缓存和协商缓存,配置Cache-Control头部
location ~* \.(?:css|js|jpg|png|webp|svg)$ {
expires 1y;
cache-control: public;
}
设计模式
发布订阅模式
定义:引入中间媒介,订阅者可以订阅事件,发布者可以发布事件,事件中心负责通知订阅者。
核心思想:发布者和订阅者解耦,通过中间的消息系统进行通信。
典型应用:事件驱动,消息队列。
class EventBus{
constructor(){
this.event = {}; // 哈希表的思想
}
subscribe(event, callbaack){
if(!this.events[event]){
this.events[event] = [];
}
this.events[event].push(callback);
}
publish(event, data){
if(this.events[event]){
this.events[event].forEach(callback => callback(data));
}
}
}
const eventBus = new EventBus();
eventBus.subscribe('event1', data => console.log(`Subscriber 1 received: ${data}`));
eventBus.subscribe('event1', data => console.log(`Subscriber 2 received: ${data}`));
eventBus.publish('event1', 'Hello Subscribers!');
观察者模式
定义:被观察主体依赖多个关系,如一个对象的状态发生改变时,所有依赖它的对象都会发生变化。
核心思想:被观察者(Subject)直接通知观察者(Observer)。
典型应用:数据绑定,MVC模型
class Subject {
constructor() {
this.observers = [];
}
addObserver(observer) {
this.observers.push(observer);
}
notifyObservers(message) {
this.observers.forEach(observer => observer.update(message));
}
}
class Observer {
update(message) {
console.log(`Observer received: ${message}`);
}
}
// 使用
const subject = new Subject();
const observer1 = new Observer();
const observer2 = new Observer();
subject.addObserver(observer1);
subject.addObserver(observer2);
subject.notifyObservers('Hello Observers!');
对比图
因两者比较相似,以下进行比较
特性 | 观察者模式 | 发布-订阅模式 |
---|---|---|
参与者 | 被观察者、观察者 | 发布者、订阅者、事件中心 |
通信方式 | 被观察者直接通知观察者 | 发布者通过事件中心通知订阅者 |
耦合性 | 耦合度较高 | 解耦性更强 |
通知范围 | 通知的范围较固定,直接绑定关系 | 通知的范围较广,通过事件中心动态管理 |
应用场景 | 数据绑定、依赖更新 | 事件驱动、跨模块通信 |
总结
以上,可以看出前端的面试题涉及的范围还是挺广的,不仅仅涉及到前端基础三件套,还涉及到打包部署和设计模式相关领域,所以还是得从日常工作抓起,养成及时记录问题,整理复盘的习惯,不要寄希望于临时抱佛脚,祝准备金三银四跳槽的各位顺利~