浏览器
屏幕刷新频率
屏幕每秒出现图像的次数。(普通笔记本为60Hz)
动画原理
计算机每16.7ms刷新一次,由于人眼的视觉停留,所以看起来是流畅的移动
实现动画的方式
- HTML5:Canvas动画 / requestAnimationFrame API
- CSS3:transition / animation;
- JS:setTimeout / setInterval
- SVG(可伸缩矢量图形)
requestAnimationFrame
(rAF)请求动画帧
requestAnimationFrame
(rAF)是一个用于请求浏览器在下一次重绘之前执行回调函数的方法。它的优势在于可以让系统决定回调函数的执行时机,通常在每次屏幕刷新时执行,这样可以确保动画流畅,避免卡顿、抖动和丢帧。
优势:
-
系统优化: 由于
requestAnimationFrame
由浏览器进行优化,它会在每个屏幕刷新周期中执行回调函数,通常是每秒60次(60Hz),以保证动画的流畅性和稳定性。 -
CPU 节能: 相比于使用
setTimeout
或setInterval
来实现动画,requestAnimationFrame
更加节能。因为它会在浏览器进行绘制时才执行回调函数,避免了不必要的计算。 -
函数节流: 由于
requestAnimationFrame
的执行频率与屏幕刷新频率相匹配,可以避免过于频繁地执行回调函数,实现了函数的节流。
缺点:
优雅降级: 在不支持requestAnimationFrame
的老旧浏览器中,需要考虑优雅降级的策略,通常是通过使用setTimeout
或setInterval
来实现类似的功能。
综上所述,requestAnimationFrame
是实现动画效果的一种高效、流畅的方式,尤其适用于需要保证动画流畅性和节能性的场景。
大屏
适配
数据轮询
const eventSource = new EventSource('http://localhost:3000'); // 建立SSE连接
eventSource.onmessage = (event) => {
this.currentTime = event.data; // 接收到数据时更新currentTime
};
eventSource.onerror = (error) => {
console.error('SSE 连接错误:', error);
eventSource.close(); // 出错时关闭连接
};
Ajax
创建交互式、快速动态网页应用的网页开发技术,无需重新加载整个网页的情况下,能够更新部分网页的技术
优点:减轻服务器的负担,按需取数据,最大程度的减少冗余请求,局部刷新页面,减少用户心理和实际的等待时间,带来更好的用户体验,基于xml标准化,并被广泛支持,不需安装插件等,进一步促进页面和数据的分离
缺点:因为大量的使用了JS
和AJAX
引擎,这些取决于浏览器的支持.在编写的时候考虑对浏览器的兼容性。
如何阻止事件冒泡?
- IE:
e.cancelBubble = true;
- 非IE:
e.stopPropagation();
如何阻止默认事件?
e.preventDefault();
return false;
单点登录SSO(Single Sign-On)
-
Session + Cookie模式
去【认证中心】拿到sessionId,存到cookie里,然后访问【子系统】,让【子系统】拿着sessionId去【认证中心】判断
(优点:认证中心控制权很大 缺点:认证中心压力很大)
-
Token模式
去【认证中心】拿到Token并储存,让【子系统】自行认证
(优点:认证中心压力小 缺点:认证中心没有能力控制用户下线)
-
Token + RefreshToken模式
Token设置10min过期时间,RefreshToken设置一个月过期时间;10min这段时间里访问都由【子系统】通过Token自行认证;过期后由【认证中心】通过RefreshToken认证,重新生成10min新的Token
(结合前两者,延伸出来:减少对【认证中心】的高频认证,又保持【认证中心】的强力控制权)
后端做了认证中心 前端使用了iframe通过window.postmessage实现同域名不同子网的免登陆
工作原理:
-
认证中心(通常是一个独立的后端服务)负责用户身份验证和颁发令牌(Token)。
-
用户访问不同的子系统或应用程序,这些子系统或应用程序都需要验证用户的身份。
-
当用户访问一个需要身份验证的子系统时,子系统会检测用户是否已经登录。
-
如果用户尚未登录,子系统将创建一个 iframe,并将 iframe 的源设置为认证中心的登录页面。这个 iframe 通常跨越了不同的子网。
-
用户在 iframe 中登录,认证中心验证用户身份后,将用户信息和令牌传递回子系统。
-
子系统接收到令牌后,可以验证令牌的有效性,然后允许用户访问应用。
-
子系统中的前端页面可以通过 window.postMessage 将用户信息传递给主页面,以便在主页面中显示用户相关的信息。
这种方式的好处是用户只需一次登录就可以访问多个应用程序,提高了用户体验。但需要注意以下几点:
-
令牌安全性:确保令牌传递过程中的安全性,以免被恶意截取。
-
跨域通信:使用 window.postMessage 需要确保不同子网的域名都信任对方。
-
令牌有效期:令牌应该有一个合理的有效期,以便在一段时间后需要重新登录。
-
会话管理:管理用户会话和单点注销等问题也需要考虑。
总之,SSO 是一种方便用户的身份认证和授权机制,但在实际应用中需要仔细考虑安全性和合理配置。
window.postMessage(message, targetOrigin, [transfer])
message
:要发送的消息,可以是字符串、数字、对象等等。这是必需的参数。targetOrigin
:指定接收消息的窗口的源。这是一个字符串,表示目标窗口所在的域名。通常应指定具体的域名,以增加安全性,而不使用通配符*
。这是必需的参数。transfer
(可选):一个数组,包含要传递给其他窗口的Transferable
对象。Transferable
对象可以是ArrayBuffer
、MessagePort
或者ImageBitmap
。
注意:
window.postMessage
用于实现安全的跨域通信,但要确保目标窗口明确信任来自发送消息的窗口,同时发送消息的窗口也需要明确信任目标窗口。- 跨域
postMessage
应该非常谨慎,以防止跨站点请求伪造(CSRF)攻击。建议在消息中包含令牌或其他验证信息以增加安全性。 - 接收消息的窗口需要添加一个事件监听器来处理传入的消息,通常使用
window.addEventListener("message", handlerFunction)
。
总之,window.postMessage
是 Web 开发中用于实现跨窗口通信的重要方法,它可以帮助不同窗口之间进行安全的数据传输。
事件循环
一种在 JavaScript 运行时环境(如浏览器或Node.js)中管理任务执行的机制。一次事件循环包括以下主要阶段:
-
执行同步任务(Synchronous Tasks): 在事件循环的开始阶段,会执行当前宏任务队列中的所有同步任务,这些任务是按照代码的顺序执行的。
-
执行微任务(Microtasks): 在同步任务执行完成后,会执行微任务队列中的所有微任务。微任务包括Promise回调、MutationObserver回调和
process.nextTick
(在Node.js中)等。微任务通常用于处理与DOM更新相关的操作,以确保在浏览器渲染之前执行。 -
更新渲染(Update Rendering): 这个阶段是浏览器特有的,用于更新页面的渲染。在浏览器中,DOM 更新、布局计算和绘制都在这个阶段完成。在Node.js中,这个阶段通常为空。
-
执行宏任务(Macrotasks): 在微任务执行完毕后,会执行当前宏任务队列中的一个宏任务。宏任务包括定时器回调、事件处理程序和I/O操作等。执行一个宏任务后,会检查是否有微任务需要执行,如果有,就会进入微任务阶段,然后再执行下一个宏任务。这个过程会一直重复,直到所有任务都执行完毕。
-
等待新任务(Waiting for New Tasks): 如果没有新的宏任务被添加到队列中,事件循环会等待新的任务加入,这个等待阶段会持续。
总的来说,事件循环是一个不断循环的过程,它负责执行同步任务、微任务、更新渲染和宏任务,以确保 JavaScript 代码的执行和页面的渲染顺利进行。这种机制使得 JavaScript 能够异步处理各种任务,保持用户界面的响应性。
移动端
inputmode:改变弹出键盘类型
<input type="text" inputmode="" />
<textarea inputmode="" />
如何进行适配
- 使用@media screen + rem
- 使用vw、vh
- 使用flexible.js
- 使用meta标签里的viewport属性
解决1px问题
- 设置 border-image / transform: scale(0.5)
- 使用transfrom + 媒体查询(CSS:@media screen / JS:devicePixelRatio)
- 使用 box-shadow / background-image 渐变 / viewport + rem
点击事件延迟300ms的解决方案:
- 禁用缩放:user-scalable=no
- 使用fastclick.js
点击穿透的解决方案
- 将click事件改成touch事件(touchstart、touchend、tap)
- 阻止默认行为:e.preventDefault()
- 使用fastclick.js
iOS手机容器滚动条滑动不流畅
overflow: auto;
-webkit-overflow-scrolling: touch;
ios的audio无法自动播放、循环播放
var music = document.getElementById('video');
var state = 0;
document.addEventListener('touchstart', function(){
if(state==0){
music.play();
state=1;
}
}, false);
document.addEventListener("WeixinJSBridgeReady", function () {
music.play();
}, false);
//循环播放
music.onended = function () {
music.load();
music.play();
}
小程序
本地缓存
wx.getStorageSync(同步)可以先获取值
wx.getStorage(异步)需要调用success
生命周期
- 应用生命周期(小程序)
- 页面生命周期(页面)
Uni-App
生命周期
- 应用生命周期:与 小程序 应用的生命周期一致(onLaunch、onShow、onHide 等)
- 页面生命周期:与 小程序 页面的生命周期一致(onLoad、onUnload、onShow 等)
- 组件生命周期:与 Vue.js 组件的生命周期一致(mounted、created 等)
JQuery
window.onload
与 jQuery的ready
的区别
JavaScript中的代码:
Window.onload = function(){}
等价于jQuery代码:
$(window).load(function(){});
window.load | $(document).ready() | |
---|---|---|
简化写法 | 无 | $(function(){ }); |
编写个数 | 不能同时编写多个,多个同时存在只运行最后一个 | 能同时编写多个,多个同时存在也会全部运行 |
执行时机 | 必须等待网页中所有的内容加载完毕后(包括图片)才能执行 | 网页中所有DOM结构绘制完毕后就执行,可能DOM元素关联的内容并没有加载完 |
框架
单向数据流和双向数据流
单向数据流 | 双向数据流 | |
---|---|---|
架构 | MVC | MVVM |
框架 | React | Vue、Angular |
例子 | Vue 中 v-bind 、父传子的 props | Vue 中 v-model |
特点 | 所有状态的改变可记录、可跟踪,源头易追溯; 一旦数据变化,就去更新页面(data-页面),但是没有(页面-data); 如果用户在页面上做了变动,那么就手动收集起来(双向是自动),合并到原有的数据中; 所有数据只有一份,组件数据只有唯一的入口和出口,使得程序更直观更容易理解,有利于应用的可维护性 | 无论数据改变,或是用户操作,都能带来互相的变动,并且自动更新 |
性能优化
页面加载及渲染优化
- HTML:文档结构层次尽量少(最好不深于六层)
- CSS:样式结构层次尽量简单;少量首屏样式内联放在标签内;动画尽量使用在绝对定位或固定定位的元素上;隐藏在屏幕外,或在页面滚动时,尽量停止动画;
- JS:在脚本中尽量减少DOM操作,尽量缓存访问DOM的样式信息,避免过度触发回流;减少通过JavaScript修改元素样式,尽量使用修改class名方式操作样式/动画;尽量缓存DOM查找,查找器尽量简洁;
- CSS置顶,JS置底;涉及多域名的网站开启域名预解析
- 静态资源使用CDN
- 使用字体图标、精灵图
- 利用缓存减少重复加载
页面交互优化
懒加载
- 可以通过为图片文件添加loading="lazy"的属性来实现
- 监听srcoll事件
节流防抖
节流
触发高频事件,但在n秒内只会执行一次,在于稀释函数的执行频率,侧重于一段时间内只执行一次
(就像游戏里无论手速多快,多次平A也有频率上限)
应用场景:
- window对象的resize、scroll事件
- 拖拽事件的mousemove
- 射击游戏中的mousedown、keydown事件
- 文字输入、自动完成的keyup事件
防抖
触发高频事件后,n秒内只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间,侧重于一段时间内多次触发只在最后执行一次
(就像游戏里释放技能的时候,再按一次技能就会重新冷却)
应用场景:
- 提交按钮、用户注册时候的手机号验证、邮箱验证
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
div {
height: 4000px;
background-color: #000;
}
</style>
</head>
<body>
<input id="input"></input>
<div id="div"></div>
<script>
function sendRequest() {
console.log("发送请求");
}
// 节流:
// document.getElementById("div").onscroll = throttle(sendRequest, 1000)
const onscroll = throttle(sendRequest, 1000)
function throttle(fn, delayTime) {
let valve = true;
return function () {
if (!valve) {
return;
}
valve = false;
setTimeout(() => {
fn && fn()
valve = true;
}, delayTime)
}
}
// 防抖:输入完成后间隔一秒再发送请求
document.getElementById("input").oninput = antiShake(sendRequest, 1000);
function antiShake(fn, delayTime) {
let timer = null;
return function () {
if (timer) {
clearTimeout(timer)
};
timer = setTimeout(() => {
fn && fn();
}, delayTime)
};
}
</script>
</body>
</html>
服务器
服务的有状态和无状态
都是负载均衡服务
判断两个来自相同发起者的请求在服务器端是否具备上下文关系
有状态 | 无状态 | |
---|---|---|
数据同步 | 需要 | / |
资源消耗 | 保存数据 | / |
带宽消耗 | 数据同步 | / |
部署发布 | 需要额外的数据同步 | 直接部署 |
failover | 数据可能丢失,同步不完全 | 数据不会丢失,负载均衡失效转移 |
Node
package-lock.json 作用为什么要lock,它是怎样保证版本依赖下载正确
通过锁定依赖包的版本和依赖关系,以及记录安全信息,保证了在不同环境下安装依赖时的一致性和安全性
网络
ajax和axios请求的区别
跨文档消息传递
常见的业务
文件下载
文件下载可以通过以下几种方式来实现:
-
基于链接的下载: 在 HTML 中提供一个链接,指向需要下载的文件,用户点击链接后浏览器会自动下载文件。
<a href="/path/to/file.pdf" download="filename.pdf">点击下载文件</a>
这个链接指向了文件的路径,并且使用了
download
属性,这样浏览器在点击链接时会下载文件,而不是打开它。 -
使用后端接口下载: 在后端提供一个接口,接收客户端的下载请求,并返回文件的内容。前端通过发起请求到这个接口来下载文件。
例如,使用 JavaScript 发送 GET 请求来下载文件:
function downloadFile() { fetch('/api/download/file.pdf') .then(response => response.blob()) .then(blob => { const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'filename.pdf'; document.body.appendChild(a); a.click(); window.URL.revokeObjectURL(url); }) .catch(error => console.error('Error downloading file:', error)); }
在后端,需要处理
/api/download/file.pdf
这个路由,根据路由中的文件名找到相应的文件,并将文件内容返回给客户端。 -
使用 Blob 对象: 在前端使用 JavaScript 构造一个 Blob 对象,然后创建一个指向这个 Blob 对象的 URL,并将这个 URL 赋值给
a
标签的href
属性,最后触发点击a
标签的事件,实现下载。function downloadFile() { const fileContent = 'file content here'; const blob = new Blob([fileContent], { type: 'application/octet-stream' }); const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'filename.txt'; document.body.appendChild(a); a.click(); window.URL.revokeObjectURL(url); }
这些方法可以根据需求选择使用,基于链接的下载适用于静态文件,后端接口下载适用于动态生成文件,Blob 对象适用于在前端构造文件内容并下载。
文件预览
文件预览通常需要根据文件类型选择不同的方式来实现。以下是一些常见文件类型的预览方法:
-
图片预览: 对于图片文件,可以直接在网页中使用
<img>
标签来显示。<img src="/path/to/image.jpg" alt="Image Preview">
-
PDF 文件预览: 可以使用
<embed>
或<iframe>
标签来嵌入 PDF 文件,并显示在网页中。<embed src="/path/to/file.pdf" type="application/pdf" width="100%" height="600px" /> <!-- 或者 --> <iframe src="/path/to/file.pdf" width="100%" height="600px"></iframe>
-
视频文件预览: 对于视频文件,可以使用
<video>
标签来播放。<video controls width="100%" height="auto"> <source src="/path/to/video.mp4" type="video/mp4"> Your browser does not support the video tag. </video>
-
音频文件预览: 对于音频文件,可以使用
<audio>
标签来播放。<audio controls> <source src="/path/to/audio.mp3" type="audio/mp3"> Your browser does not support the audio tag. </audio>
-
文本文件预览: 对于文本文件,可以直接在页面中显示文本内容,或者使用
<iframe>
标签嵌入一个包含文本内容的页面。<!-- 直接显示文本内容 --> <pre> Text content here... </pre> <!-- 或者 --> <iframe src="/path/to/textfile.txt" width="100%" height="600px"></iframe>
-
其他文件类型预览: 对于其他类型的文件,可以考虑使用第三方库或在线服务进行预览。例如,使用 Google Docs Viewer 来预览文档文件。
场景题
白屏化
原因:
- 加载速度慢
- 依赖文件丢失
- JS错误
避免措施:
- 优化页面加载:
- 浏览器缓存
- CDN
- 异步加载非首屏需要的资源
- 使用SSR
- 减少频繁的DOM操作
从开发的角度简述手机验证码实现的过程
封装一个sleep 方法,让sleep(5000)后面的代码延迟5s后执行
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function delayedExecution() {
console.log("开始");
await sleep(5000);
console.log("5秒后");
}
delayedExecution();
声明一个变量a,让(a==1&&a==2&&a==3)
条件成立
let a = {
value: 1,
valueOf: function() {
return this.value++;
}
};
if (a == 1 && a == 2 && a == 3) {
console.log("条件成立");
} else {
console.log("条件不成立");
}
假如请求回来 10000 条数据,然后染到页面, 怎么做优化才能使页面不会太卡顿
-
监听用户界面延迟加载数据
使用IntersectionObserver和getBoundingClientRect来判断空白是否在页面底部 -
setTimeOut+分页渲染
const renderList = async () => {
const list = await getList()
const total = list.length
const page = 0
const limit = 200
const totalPage = Math.ceil(total / limit)
const render = (page) => {
if (page >= totalPage) return
setTimeout(() => {
for (let i = page * limit; i < page * limit + limit; i++) {
const item = list[i]
const div = document.createElement('div')
div.className = 'sunshine'
div.innerHTML = `<img src="${item.src}" /><span>${item.text}</span>`
container.appendChild(div)
}
render(page + 1)
}, 0)
}
render(page)
}
- requestAnimationFrame代替setTimeOut(减少reflow次数,提高性能)
const renderList = async () => {
const list = await getList()
const total = list.length
const page = 0
const limit = 200
const totalPage = Math.ceil(total / limit)
const render = (page) => {
if (page >= totalPage) return
requestAnimationFrame(() => {
for (let i = page * limit; i < page * limit + limit; i++) {
const item = list[i]
const div = document.createElement('div')
div.className = 'sunshine'
div.innerHTML = `<img src="${item.src}" /><span>${item.text}</span>`
container.appendChild(div)
}
render(page + 1)
})
}
render(page)
}