HTML
服务端渲染和客户端渲染的区别?
客户端渲染:
- 页面的渲染工作都是由浏览器来完成的,服务器只是负责提供数据。
- 客户端渲染能尽早的把页面展示给用户,用户体验好
- 不容易被爬虫爬取数据,同时也无法被搜索引擎搜索到
服务器渲染:
- 页面渲染的工作都是由服务端来完成的,数据也是由服务端提供的,浏览器只负责展示页面内容
- 容易被爬虫爬取数据,同时能被搜索引擎搜索到,能在搜索引擎中向用户展示数据
那是用客户端渲染好还是服务器渲染好呢?
其实一般的页面中,两种渲染是相结合着使用的,因为我们会有些数据不想过早的传过来,想要被用到时再去拿数据,同时也不想被爬虫那么轻易的爬取,而且也不需要被搜索引擎搜索并展示,我们就可以用客户端渲染,例如我们本文中提到的商品评论信息。
但有时,我们想让我们网页的数据被搜索引擎搜索到,能让用户在使用搜索引擎的时候,查找到我们的数据信息,我们就可以使用服务器渲染。例如我们本文提到的电商网站的商品的基本信息。
onloaded和DOMContentLoaded事件
DOMContentLoaded
当初始html文档完全加载并解析之后触发,无需等待样式、图片、子frame结束。作为明显的对比,load事件只有一个页面完全被加载时才触发。改用DOMContentLoaded的地方常常是load来代替,这是错误的。
load
当一个资源及其依赖的资源结束加载时触发。从这里可以看到需要等待依赖资源的结束加载。
!DOCTYPE 的作用是什么?
在 HTML 中, 声明是文档类型声明(Document Type Declaration)的缩写,用于指示浏览器当前页面使用的HTML版本。
声明位于文档中的最前面的位置,处于 html 标签之前。CSS
改变小图标的颜色,hover时颜色变深
- 思路:使用活跃色的图片素材,给它一个灰色的滤镜。hover的时候取消这个滤镜,还原活跃色。filter:grayscale(x%):将图像转换为灰度图
- 示例:
img {
filter: grayscale(100%);
opacity: 0.6;
}
img:hover {
filter: none;
opacity: 1;
}
效果:
圣杯布局、双飞翼布局
flex布局,一行5个卡片,最后一行左对齐
// 最外层父元素
.container {
display: flex;
flex-wrap: wrap;
align-content: flex-start;
// 卡片
> .card {
height: 150px;
flex: 0 0 18%; //每一个盒子占18%
min-width: 18%;
margin-right: calc(9.99% / 4);
margin-bottom: calc(9.99% / 4);
// 每行最后一个卡片
&:nth-child(5n) {
margin-right: 0;
}
// 最后一个卡片
&:last-child {
margin-right: auto;
}
}
}
css预处理器:less,scss
介绍
- less
- 变量(Variables):你可以使用
@符号来定义和引用变量。 - 嵌套(Nesting):你可以在Less中使用嵌套规则来组织样式规则。
- 运算(Operations):Less允许你在样式中执行简单的算术运算,如加法、减法、乘法和除法。
- 导入(Import):你可以使用
@import指令将其他的Less文件导入到当前文件中。这样可以将样式分为多个文件,并在需要时将它们合并到一个文件中。
- scss
- 变量(Variables):SCSS引入了变量的概念,你可以使用
$符号定义变量,并在整个样式表中引用它们。 - 嵌套(Nesting):SCSS允许在样式规则中使用嵌套的语法,以表示样式的层次结构和关系。
- 继承(Inheritance):SCSS允许通过
@extend关键字实现样式的继承。这样可以减少重复的代码。 - 运算(Operations):SCSS也支持在样式中执行算术运算。你可以在属性值中使用算术表达式,并使用
+、-、*和/等运算符。 - 导入(Import):你可以使用
@import指令将其他的SCSS文件导入到当前文件中。这样可以将样式分为多个文件,并在需要时将它们合并到一个文件中。
区别
-
语法:SCSS是基于CSS的超集,它使用和CSS相同的语法,并通过添加一些新的功能和规则来扩展CSS。因此,任何有效的CSS样式表都是有效的SCSS样式表。相比之下,Less引入了一些新的语法元素,例如使用@符号定义变量和混合等。
-
处理器:SCSS使用的是Sass(Syntactically Awesome Style Sheets)处理器,它在Sass语法的基础上添加了CSS兼容性。Sass提供了两种语法格式:SCSS和Sass(缩进风格)。而Less则使用Less处理器,它的语法更接近于常规的CSS。
-
嵌套规则:SCSS和Less都支持嵌套规则,允许在父选择器下编写样式规则。然而,它们的嵌套语法略有不同。在SCSS中,使用大括号({})来表示嵌套,类似于CSS。而在Less中,使用小括号(())来表示嵌套。
-
变量符号:在SCSS中,变量使用$符号进行定义和引用,例如$primary-color: #ff0000;。而在Less中,变量使用@符号,例如@primary-color: #ff0000;。
-
混合(Mixins):SCSS和Less都支持混合的概念,可以将一组样式规则集合起来并在需要时重用。在SCSS中,使用@mixin关键字定义混合,并使用@include关键字引用它。而在Less中,使用.mixin-name()定义混合,并使用.mixin-name;引用它。
-
继承(Inheritance):SCSS和Less都支持样式的继承。在SCSS中,使用@extend关键字来实现样式的继承。而在Less中,使用&符号来表示父选择器,实现样式的继承。
JS
一次获取一万条数据,如何渲染?
虚拟列表
参考文章:定高的虚拟列表会了,那不定高的...... 哈,我也会!看我详解一波!🤪🤪🤪 - 掘金 (juejin.cn)
可能的拓展问题
如何优化用户体验?(自己整理)
- 滚动条滚动到一半时就向后端请求资源,防止突然的展示和卡顿。
- 滚动条监听事件做防抖处理,防止频繁请求。
- 懒加载
从url中获取指定参数
function getQueryString(name) {
// let url = window.location.href;
const url_string = "https://www.baidu.com/t.html?name=mick&age=20"; // window.location.href
const url = new URL(url_string);
return url.searchParams.get(name);
}
console.log(getQueryString('name')) // mick
用原生JS实现懒加载、轮播图等功能
参考文章:分享一些前端常用功能集合 - 掘金 (juejin.cn)
// 获取所有图片
const imgList = document.querySelectorAll('img')
// 用于记录当前显示到了哪一张图片
let index = 0;
function lazyload() {
// 获取浏览器视口高度,这里写在函数内部是考虑浏览器窗口大小改变的情况
const viewPortHeight = window.innerHeight || document.documentElement.clientHeight
for (let i = index; i < imgList.length; i++) {
// 这里用可视区域高度减去图片顶部距离可视区域顶部的高度
const distance = viewPortHeight - imgList[i].getBoundingClientRect().top;
// 如果可视区域高度大于等于元素顶部距离可视区域顶部的高度,说明图片已经出现在了视口范围内
if (distance >= 0) {
// 给图片赋值真实的src,展示图片
imgList[i].src = imgList[i].getAttribute('data-src');
// 前i张图片已经加载完毕,下次从第i+1张开始检查是否需要显示
index = i + 1;
}
}
}
// 定义一个防抖函数
function debounce(fn, delay = 500) {
let timer = null;
return function (...args) {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
// 页面加载完成执行一次lazyload,渲染第一次打开的网页视口内的图片
window.onload = lazyload;
// 监听Scroll事件,为了防止频繁调用,使用防抖函数进行优化
window.addEventListener("scroll", debounce(lazyload, 600));
// 浏览器窗口大小改变时重新计算
window.addEventListener("resize", debounce(lazyload, 600));
JS为什么是单线程的?
- 可以用webworker开启多线程
- JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的
同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?
为了利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。所以,这个新标准并没有改变JavaScript单线程的本质。
实现sleep函数
方法一:不阻塞同步任务的执行
// sleep 函数--Promise 版本
function sleep(time) {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, time);
});
}
sleep(1000).then(fnA); // 1 秒后输出 A
方法二:阻塞同步任务的执行
// sleep 函数--Promise 版本
function sleep(time) {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, time);
});
}
async function sleepTest() {
fnA(); // 输出 A
await sleep(1000); // 睡眠 1 秒
console.log('E'); // 输出 E
fnB(); // 输出 B
await sleep(1000); // 睡眠 1 秒
fnC(); // 输出 C
await sleep(1000); // 睡眠 1 秒
console.log('G'); // 输出 G
}
虚拟DOM构建为真实DOM
CommonJS和ES6的区别
- 对于模块的依赖,CommonJS是动态的,ES6 Module 是静态的
- CommonJS导入的是值的拷贝,ES6 Module导入的是值的引用
require和import的区别?
规范
require符合CommonJS规范,而import符合ES6标准。因此,在使用时需要根据不同的规范选择相应的引入方式。
静态/动态
require是静态加载,也就是说,在编译时就会加载所需模块。而import支持动态加载,可以在程序运行时根据需要进行加载。
其他
假如说接口响应拦截错误会有全局统一的弹出错误,那如果有些路由他是不需要这个弹出错误的,那么该怎么实现?
axios 的入参扩展一个自定义参数(skipErrorHandler), 在响应拦截器中拿到这个参数, true表示忽略统一的错误处理逻辑, false反之
npm run dev是如何将本地资源打包编译运行在浏览器上的
执行npm run dev命令后会执行一个脚本 使用构建工具对项目文件进行打包编译并输出到特定的目录 然后启动了本地开发服务器并监听本地主机和端口号 浏览器输入http://localhost:xxxx后会发送请求访问本地开发服务器的资源 服务器收到请求就会返回对应的html文件
网络和设备硬件均一致的情况导致首页白屏时间不一致的原因
- 原因:
- 浏览器差异:不同的浏览器或浏览器版本对网页的渲染速度可能有所不同。
- 缓存策略:浏览器的缓存策略可能导致加载速度的差异。例如,如果某个浏览器缓存了某些资源,而其他浏览器没有,那么加载速度就会有所不同。
- JavaScript执行:JavaScript的执行速度和效率可能因浏览器和版本的不同而有所差异,从而影响页面的渲染时间。
- 服务器响应:服务器的响应速度、带宽、负载等因素也会影响页面的加载时间。
- 第三方服务:如果首页依赖了第三方服务(如CDN、字体、统计工具等),这些服务的响应速度和稳定性也会影响首页的加载时间。
- DNS解析:DNS解析的速度和稳定性也可能影响页面加载时间。
- 用户地理位置:用户地理位置的不同可能会导致访问服务器时的网络延迟差异。
- 页面内容动态加载:如果页面内容是通过异步加载(如AJAX)的方式获取的,那么加载速度可能会因网络状况、服务器响应速度等因素而有所不同。
- 方法:
- 使用性能监控工具来分析和定位问题。
- 优化JavaScript代码,减少执行时间。
- 优化图片和其他资源,减少加载时间。
- 考虑使用CDN来加速资源的加载。
- 对服务器进行优化,提高其响应速度和带宽。
- 考虑使用缓存策略来减少重复加载相同资源的时间。
- 如果是CDN(内容分发网络)的原因导致资源加载时间不一致,可能是由以下因素造成的:
- 源站的带宽限制:当源站的带宽达到峰值时,会导致访问源站的速度变慢,从而影响CDN边缘服务器上的资源响应速度。
- CDN配置问题:CDN需要正确配置以实现资源访问与源站一致。如果配置不当,可能会导致资源加载时间不一致。
- 源站和CDN的缓存规则不合适:如果源站资源和CDN的边缘资源未正确缓存,可能会导致资源加载时间不一致。
- 网络抖动或不稳定:网络的不稳定性可能导致CDN的边缘服务器与源站之间的通信受到影响,从而影响资源加载时间。
- CDN服务器数量不足:如果CDN服务器数量不足,可能导致网络容量不足和稳定性下降,从而影响资源加载时间。
为了解决这些问题,可以采取相应的措施,如优化源站服务器性能、确认CDN设置正确、审查源站的防火墙规则、增加CDN服务器数量、优化网络拥塞等。
pc端如何实现扫码登录
轮询或websocket获取二维码状态
参考文章:面试官:如何实现扫码登录功能? - 掘金 (juejin.cn)
如何自动保存表单数据,防止页面崩溃、用户异常退出时数据未保存的问题
自动保存(个人设想,待实践)
localstorage定时存储表单数据,当异常关闭后进入表单填写页面。判断localstorage是否存在该对象,存在则提示用户是否继续填写,该字段在表单正常提交后被清空。
参考链接:vue--localStorage临时存储未提交表单数据,下次登陆后展示上次未提交的数据_vue 离开页面前,实现表单数据保存,回来页面后重新展示-CSDN博客
提交错误时正常回跳
keep-alive包裹(?)
SPA首屏加载慢该如何解决?
首屏时间计算
// 方案一:
document.addEventListener('DOMContentLoaded', (event) => {
console.log('first contentful painting');
});
加载慢的原因
- 网络延时问题
- 资源文件体积是否过大
- 资源是否重复发送请求去加载了
- 加载脚本的时候,渲染内容堵塞了
解决方案
- 减小入口文件体积 常用的手段是路由懒加载,把不同路由对应的组件分割成不同的代码块,待路由被请求的时候会单独打包路由,使得入口文件变小,加载速度大大增加
- 静态资源本地缓存
后端返回资源问题:
- 采用
HTTP缓存,设置Cache-Control,Last-Modified,Etag等响应头 - 采用
Service Worker离线缓存
前端合理利用localStorage
- 图片资源的压缩:字体图标、雪碧图
权限管理相关
参考链接:面试官:vue要做权限管理该怎么做?如果控制到按钮级别的权限怎么做? | web前端面试 - 面试官系列 (vue3js.cn)
Node
Node.js和浏览器之间的区别
1. 环境的不同
Node.js和浏览器的环境非常不同,由于其不同的目的,它们之间的一些关键区别包括:
- 浏览器在宿主环境中执行JavaScript,在客户端执行。浏览器提供了一个表示网页结构的文档对象模型(DOM)。
- Node.js提供了一个运行时环境,允许JavaScript在服务器上运行,不在浏览器中。此外,它没有像Web浏览器一样的DOM。
2. 用户界面
一个主要的区别是执行方式。浏览器在其宿主环境中执行JavaScript并渲染HTML、CSS和JavaScript,并提供图形用户界面(GUI)与网页进行交互,因为用户与网页进行交互,所以使用GUI是必不可少的。但是Node.js使用命令行界面(CLI),因为它与用户没有交互,并且用于服务器端开发构建Web服务器和API。
3. 用法不同
由于两者都执行JavaScript,它们的用途是使它们不同的关键点。浏览器用于客户端,执行JavaScript在浏览器的宿主环境中,开发者使用JavaScript使Web内容动态化。另一方面,Node.js是用于执行JavaScript的运行时环境,用于服务器端开发创建Web服务器和API,因此JavaScript在这里主要用于逻辑构建和算法部分。
NodeJS事件循环
- 6个阶段
Timers:用于存储定时器的回调函数(setlnterval,setTimeout)。Pendingcallbacks:执行与操作系统相关的回调函数,比如启动服务器端应用时监听端口操作的回调函数就在这里调用。idle,prepare:系统内部使用。Poll:存储1/O操作的回调函数队列,比如文件读写操作的回调函数。
在这个阶段需要特别注意,如果事件队列中有回调函数,则执行它们直到清空队列 ,否则事件循环将在此阶段停留一段时间以等待新的回调函数进入。
但是对于这个等待并不是一定的,而是取决于以下两个条件:
-
- 如果setlmmediate队列(check阶段)中存在要执行的调函数。这种情况就不会等待。
-
- timers队列中存在要执行的回调函数,在这种情况下也不会等待。事件循环将移至check阶段,然后移至Closingcallbacks阶段,并最终从timers阶段进入下一次循环。
Check:存储setlmmediate的回调函数。Closingcallbacks:执行与关闭事件相关的回调,例如关闭数据库连接的回调函数等。
- 宏任务与微任务
- 主线程同步代码执行完毕后,会先执行微任务再执行宏任务。
- 虽然
nextTick同属于微任务,但是它的优先级是高于其它微任务,在执行微任务时,只有nextTick中的所有回调函数执行完成后才会开始执行其它微任务。 - 在事件循环的六个阶段每个阶段执行完后会清空微任务队列。
- 与浏览器时间循环的区别?
- 在浏览器事件循环中,每执行完一个宏任务,便要检查并执行微任务队列;而node事件循环中则是在“上一阶段”执行完,“下一阶段”开始前执行微任务队列中的任务。
- 在浏览器事件循环中,process.nextTick()属于微任务,而且和其他微任务的优先级是一样的,不存在哪个微任务的优先级高就先执行谁。但是在node中,process.nextTick()的优先级要高于其他微任务,也就是说,在两个阶段之间执行微任务时,若存在process.nextTick(),则先执行它,然后再执行其他微任务。
多线程实现
方法
1. 使用 Worker 类:通过 Worker 类可以创建和管理工作线程。例如,可以创建一个新的线程,并传递一个 JavaScript 文件给该线程执行:
const { Worker, isMainThread, parentPort } = require('worker_threads');
if (isMainThread) {
const worker = new Worker('./my-worker.js');
// 主线程的逻辑
} else {
// 工作线程的逻辑
parentPort.postMessage('来自工作线程的问候');
}
2. 利用线程池:Node.js 允许创建线程池,为多个任务创建线程并分配给它们,这对于需要执行大量短期任务的情况非常有用。可以使用 workerpool 模块来实现线程池。
const WorkerPool = require('workerpool').pool;
const pool = WorkerPool({ maxWorkers: 4 });
pool.exec(someTask).then(result => {
// 处理结果
});
3. 使用 worker_threads 模块:Node.js 内置的 worker_threads 模块提供了对多线程的支持,通过使用 Worker 类和其他相关的 API 来创建和管理线程。
const { Worker, isMainThread, parentPort } = require('worker_threads');
if (isMainThread) {
const worker = new Worker('./my-worker.js');
// 主线程的逻辑
} else {
// 工作线程的逻辑
parentPort.postMessage('来自工作线程的问候');
}
使用场景
在 Node.js 中,以下几种情况下使用多线程是较为合适的:
- CPU 密集型操作:对于需要大量计算的应用,比如图像处理、加密破解或数据分析,多线程可以大幅提高性能。
- I/O 密集型工作:虽然 Node.js 在处理 I/O 操作上已经相当高效,但在并发处理大量数据库查询或文件操作的场景下,多线程能够提升吞吐量。
- WebSocket 服务器:WebSocket 服务器可以采用多线程来管理连接,确保实时通信的及时响应。
- 网络爬虫与数据抓取:在爬虫和数据抓取等应用中,采用多线程并行处理不同的网页或数据源,能够加快数据获取速度。