一文了解 requestAnimationFrame

news/2024/4/19 6:38:36/文章来源:https://blog.csdn.net/web220507/article/details/129248765

requestAnimationFrame 的基本使用

requestAnimationFrame 是什么

window.requestAnimationFrame() 告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行 顾名思义,请求动画帧,也称 帧循环。 其实就是该API能以浏览器的显示频率来作为其动画动作的频率,比如浏览器每16.7ms刷新一次,动画回调也每16.7ms调用一次,这样就不会存在过度绘制的问题,动画不会掉帧,自然流畅。

requestAnimationFrame 怎么使用

先来个例子

//html代码
<body><h1>requestAnimationFrame API</h1><button id='begin' class="begin">开始</button><button id='end' class="end">停止</button>
</body>//js
(() => {function test() {console.log('hello ~ requestAnimationFrame');}requestAnimationFrame(test)
})() 

可以看到,控制台成功的输出了一次 log 。 但是它只执行了一次,怎么做动画呢?别急,再看看 MDN 怎么说。

注意:若你想在浏览器下次重绘之前继续更新下一帧动画,那么回调函数自身必须再次调用window.requestAnimationFrame()

修改代码再试一下。

(() => {let n = 0function test() {n++console.log(`hello ~ requestAnimationFrame ${n}`);requestAnimationFrame(test)}requestAnimationFrame(test)
})() 

打开控制器可以看到一种在执行

执行频率

回调函数执行次数通常是每秒 60 次,但在大多数遵循 W3C 建议的浏览器中,回调函数执行次数通常与 浏览器屏幕刷新次数 相匹配。 屏幕刷新频率(次数): 屏幕每秒出现图像的次数。普通笔记本为60Hz。

回调参数

回调函数会被传入DOMHighResTimeStamp参数,DOMHighResTimeStamp指示当前被 requestAnimationFrame() 排序的回调函数被触发的时间。

(() => {function test(timestamp) {console.log(`🚀🚀hello ~ requestAnimationFrame ${timestamp}`);requestAnimationFrame(test)}requestAnimationFrame(test)
})() 

在同一个帧中的 多个回调函数 ,它们每一个都会接受到一个 相同的时间戳 ,即使在计算上一个回调函数的工作负载期间已经 消耗了一些时间 。该时间戳是一个十进制数,单位毫秒,最小精度为1ms(1000μs)。

(() => {function test1(timestamp) {console.log(`hello ~ requestAnimationFrame1 ${timestamp}`);requestAnimationFrame(test1)}function test2(timestamp) {console.log(`ello ~ requestAnimationFrame2 ${timestamp}`);requestAnimationFrame(test2)}requestAnimationFrame(test1)requestAnimationFrame(test2)})() 

可以看到,两个 requestAnimationFrame 在控制台输出的时间戳是一样的。也就是浏览器刷新一次的时候,执行所有的 requestAnimationFrame ,并且它们的回调参数是一模一样的。

ref

requestAnimationFrame()的返回值(上文我称为ref),也是有作用的,代表requestAnimationFrame回调执行的次数, 每执行一次,数值就会 +1

let ref = requestAnimationFrame(one)
function one() { console.log(ref)ref = requestAnimationFrame(one)
} 

终止执行

window.cancelAnimationFrame() 以取消回调函数。 那如果我想要在特定的条件下终止 requestAnimationFrame 怎么办呢,官方也给出了答案,那就是 cancelAnimationFrame API 。 只需要把 requestAnimationFrame 的返回值作为参数传递给 cancelAnimationFrame 就可以了

(() => {const beginBtn = document.querySelector("#begin")const endBtn = document.querySelector("#end")let myRef;beginBtn.addEventListener("click", () => {myRef = requestAnimationFrame(test)})endBtn.addEventListener("click", () => {cancelAnimationFrame(myRef)})function test() {myRef = requestAnimationFrame(test)console.log('~ myRef:', myRef);}
})() 

其实不用这个 API 也可以达到终止执行的目的,比如简单的 if语句 。

(() => {function test(timestamp) {console.log(`hello ~ requestAnimationFrame ${timestamp}`);if (timestamp < 500) {requestAnimationFrame(test)}}requestAnimationFrame(test)
})() 

使用requestAnimationFrame 和 cancelAnimationFrame

所以可以利用 requestAnimationFrame 和 cancelAnimationFrame做一些事情 比如 累积一定时间可以做一些操作

(() => {let startTime = Date.now();function handleTicker() {foo(Date.now() - startTime);startTime = Date.now();requestAnimationFrame(handleTicker);}requestAnimationFrame(handleTicker);let t = 0function foo(timeInterval) {t += timeIntervalconsole.log('~ t:', t);if (t > 1000) {console.log('~ 做事情');t = 0}}
})() 

简单动画演示

<style> #box {width: 0px;height: 50px;background-color: blue;} </style>
<body><h1>requestAnimationFrame API</h1><button id='begin' class="begin">开始</button><button id='end' class="end">停止</button><div id='box'></div>
</body> 
(() => {const beginBtn = document.querySelector("#begin")const endBtn = document.querySelector("#end")const box = document.querySelector("#box")let myRef;beginBtn.addEventListener("click", () => {myRef = requestAnimationFrame(test)})endBtn.addEventListener("click", () => {cancelAnimationFrame(myRef)})function test() {box.style.width = `${myRef}%`myRef = requestAnimationFrame(test)}
})() 

深入了解 requestAnimationFrame

requestAnimationFrame 与 setInterval setTimeout

setTimeout 和 setInterval 的问题是,它们不够精确。它们的内在运行机制决定了 时间间隔参数 实际上只是指定了把动画代码添加到 浏览器UI线程队列 中以等待执行的时间。如果队列前面已经加入了其它任务,那动画代码就要等前面的 任务完成后 再执行,并且如果时间间隔过短(小于16.7ms)会造成丢帧,所以就会导致动画可能不会按照预设的去执行,降低用户体验。

requestAnimationFrame 采用 浏览器时间间隔 ,保持最佳绘制效率,不会因为间隔时间过短,造成过度绘制,消耗性能;也不会因为间隔时间太长,使用动画卡顿不流畅,让各种网页动画效果能够有一个 统一 的刷新机制,从而节省系统资源,提高系统性能,改善视觉效果。

  • 这个其实和 浏览器的事件循环机制是有关系的,在与他们之间执行的方式不同,下边会有介绍 requestAnimationFrame和EventLoop之间的关系

CSS3 的transition 和 animation 搭配使用可以说是非常强大了,但是也有的触手伸不到的地方,比如说 scrollTop,另外 CSS3 动画支持的贝塞尔曲线也是有限的。 那么,CSS3 做不到的就可以用到 requestAnimationFrame 来解决了。

性能对比

来个例子

<div id="test" style="width: 0px; height: 12px; line-height: 12px; margin-bottom: 5px; background: rgb(185, 236, 243);"></div>
当前进度:<span id="progress">0%</span>
<button id="btn">开启</button>
<script> const btn = document.getElementById('btn');
//使用 requestAnimationFrame 实现
btn.onclick = function() {var timer = requestAnimationFrame(function fn() {if (parseInt(test.style.width) < 300) {test.style.width = parseInt(test.style.width) + 3 + 'px';progress.innerHTML = parseInt(test.style.width) / 3 + '%';timer = requestAnimationFrame(fn);} else {cancelAnimationFrame(timer);}});
}
//使用 setInterval 实现
// btn.onclick = function() {
// var timer = setInterval(function () {
// if (parseInt(test.style.width) < 300) {
// test.style.width = parseInt(test.style.width) + 3 + 'px';
// progress.innerHTML = parseInt(test.style.width) / 3 + '%';
// } else {
// clearInterval(timer);
// }
// }, 17);
// }//使用 setTimeout 实现
//btn.onclick = function() {
// var timer = setTimeout(function fn() {
// if (parseInt(test.style.width) < 300) {
// test.style.width = parseInt(test.style.width) + 3 + 'px';
// progress.innerHTML = parseInt(test.style.width) / 3 + '%';
// timer = setTimeout(fn, 17);
// } else {
// clearTimeout(timer);
// }
// }, 17);
// } </script> 

使用setTimeout和setInterval的帧率

使用requestAnimationFrame的帧率

  • 注: 我认为这里可以看 FPS 和GPU的渲染 比较他们渲染方式不同

requestAnimationFrame 和事件循环

Event Loop(事件循环)是用来协调事件、用户交互、脚本、渲染、网络的一种浏览器内部机制。

Event Loop 在浏览器内也分几种:

  • window event loop
  • worker event loop
  • worklet event loop

我们这里主要讨论的是 window event loop。也就是浏览器一个渲染进程内主线程所控制的 Event Loop。 然后大致讲下流程 在这里不详细说明 eventLoop

Event Loop的基本处理过程

1.在所选 task queue (taskQueue)中约定必须包含一个可运行任务。如果没有此类 task queue,则跳转至下面 microtasks 步骤。
2.让 taskQueue 中最老的 task (oldestTask) 变成第一个可执行任务,然后从 taskQueue 中删掉它。
3.将上面 oldestTask 设置为 event loop 中正在运行的 task。
4.执行 oldestTask。
5.将 event loop 中正在运行的 task 设置为 null。
6.执行 microtasks 检查点(也就是执行 microtasks 队列中的任务)。
7.设置 hasARenderingOpportunity 为 false。
8.更新渲染。
9.如果当前是 window event loop 且 task queues 里没有 task 且 microtask queue 是空的,同时渲染时机变量 hasARenderingOpportunity 为 false ,去执行 idle period(requestIdleCallback)。
10.返回到第一步。 下图 是 event loop 在浏览器主线程上运行的一个清晰的流程:

在上面规范的说明中,渲染的流程是在执行 microtasks 队列之后,更进一步,再来看看渲染的处理过程。

更新渲染过程

遍历当前浏览上下文中所有的 document ,必须按在列表中找到的顺序处理每个 document 。 渲染时机(Rendering opportunities):如果当前浏览上下文中没有到渲染时机则将所有 docs 删除,取消渲染(此处是 否存在渲染时机由浏览器自行判断,根据硬件刷新率限制、页面性能或页面是否在后台等因素)。 如果当前文档不为空,设置 hasARenderingOpportunity 为 true 。 不必要的渲染(Unnecessary rendering):如果浏览器认为更新文档的浏览上下文的呈现不会产生可见效果且文档的 animation frame callbacks 是空的,则取消渲染。(终于看见 requestAnimationFrame 的身影了 从 docs 中删除浏览器认为出于其他原因最好跳过更新渲染的文档。 如果文档的浏览上下文是顶级浏览上下文,则刷新该文档的自动对焦候选对象。 处理 resize 事件,传入一个 performance.now() 时间戳。 处理 scroll 事件,传入一个 performance.now() 时间戳。 处理媒体查询,传入一个 performance.now() 时间戳。 运行 CSS 动画,传入一个 performance.now() 时间戳。 处理全屏事件,传入一个 performance.now() 时间戳。 执行 requestAnimationFrame 回调,传入一个 performance.now() 时间戳。 执行 intersectionObserver 回调,传入一个 performance.now() 时间戳。 对每个 document 进行绘制。 更新 ui 并呈现。

流程基本如下图所示

至此,requestAnimationFrame 的回调时机就清楚了,它会在 style/layout/paint 之前调用。

浏览器渲染有个渲染时机(Rendering opportunity)的问题,也就是浏览器会根据当前的浏览上下文判断是否进行渲染,它会尽量高效,只有必要的时候才进行渲染,如果没有界面的改变,就不会渲染。按照规范里说的一样,因为考虑到硬件的刷新频率限制、页面性能以及页面是否存在后台等等因素,有可能执行完 setTimeout 这个 task 之后,发现还没到渲染时机,所以 setTimeout 回调了几次之后才进行渲染

setTimeout 动画比 requestAnimationFrame 动画更快的问题,这就很好解释了。 首先,浏览器渲染有个渲染时机(Rendering opportunity)的问题,也就是浏览器会根据当前的浏览上下文判断是否进行渲染,它会尽量高效,只有必要的时候才进行渲染,如果没有界面的改变,就不会渲染。按照规范里说的一样,因为考虑到硬件的刷新频率限制、页面性能以及页面是否存在后台等等因素,有可能执行完 setTimeout 这个 task 之后,发现还没到渲染时机,所以 setTimeout 回调了几次之后才进行渲染,此时设置的 marginLeft 和上一次渲染前 marginLeft 的差值要大于 1px 的。

下图是 setTimeout 执行情况,红色圆圈处是两次渲染,中间四次是处理 setTimout task,因为屏幕的刷新频率是 60 Hz,所以大致在 16.6ms 之内执行了多次 setTimeout task 之后才到了渲染时机并执行渲染。

requestAnimationFrame 帧动画不同之处在于,每次渲染之前都会调用,此时设置的 marginLeft 和上一次渲染前 marginLeft 的差值为 1px 。

下图是 requestAnimationFrame 执行情况,每次调用完都会执行渲染:

requestAnimationFrame 的使用场景

大数据渲染

在大数据渲染过程中,比如表格的渲染,如果不进行一些性能策略处理,就会出现 UI 冻结现象,用户体验极差。有个场景,将后台返回的十万条记录插入到表格中,如果一次性在循环中生成 DOM 元素,会导致页面卡顿5s左右。这时候我们就可以用 requestAnimationFrame 进行分步渲染,确定最好的时间间隔,使得页面加载过程中很流畅。

关于时间的控制可以看我之前章节的累积时间做一些操作

var total = 100000;
var size = 100;
var count = total / size;
var done = 0;
var ul = document.getElementById('list');function addItems() {var li = null;var fg = document.createDocumentFragment();for (var i = 0; i < size; i++) {li = document.createElement('li');li.innerText = 'item ' + (done * size + i);fg.appendChild(li);}ul.appendChild(fg);done++;if (done < count) {requestAnimationFrame(addItems);}
};
requestAnimationFrame(addItems); 

实现动画

css3实现使得性能和流畅度都得到了很大的提升,但同时局限性也挺大比如不是所有的属性都能参与动画,动画过程不能完全控制,动画缓动效果太小等等。 刚好相反的是setTimeout和setInterval能达成更多的可控性质的自有帧动画,但是由于刷新时间和定时器时间不同会出现掉帧现象,定时器时间设的越短掉帧时间越严重,而且性能牺牲很严重 然而 requestAnimationFrame 的出现让我们有了除了这两种我们常用的方案之外的另一种更优的选择

其他知识 (仅了解)

即图像在屏幕上更新的速度,也即屏幕上的图像每秒钟出现的次数,它的单位是赫兹(Hz)。 对于一般笔记本电脑,这个频率大概是60Hz, 可以在桌面上 右键 > 屏幕分辨率 > 高级设置 > 监视器 中查看和设置。这个值的设定受屏幕分辨率、屏幕尺寸和显卡的影响,原则上设置成让眼睛看着舒适的值都行。 市面上常见的显示器有两种,即 CRT和 LCD, CRT 是一种使用阴极射线管(Cathode Ray Tube)的显示器,LCD 就是我们常说的液晶显示器( Liquid Crystal Display)。 CRT 是一种使用阴极射线管的显示器,屏幕上的图形图像是由一个个因电子束击打而发光的荧光点组成,由于显像管内荧光粉受到电子束击打后发光的时间很短,所以电子束必须不断击打荧光粉使其持续发光。电子束每秒击打荧光粉的次数就是屏幕绘制频率。 而对于 LCD 来说,则不存在绘制频率的问题,因为 LCD 中每个像素都在持续不断地发光,直到不发光的电压改变并被送到控制器中,所以 LCD 不会有电子束击打荧光粉而引起的闪烁现象。 因此,当你对着电脑屏幕什么也不做的情况下,显示器也会以每秒60次的频率正在不断的更新屏幕上的图像。为什么你感觉不到这个变化? 那是因为人的眼睛有视觉停留效应,即前一副画面留在大脑的印象还没消失,紧接着后一副画面就跟上来了,这中间只间隔了16.7ms(1000/60≈16.7), 所以会让你误以为屏幕上的图像是静止不动的。而屏幕给你的这种感觉是对的,试想一下,如果刷新频率变成1次/秒,屏幕上的图像就会出现严重的闪烁,这样就很容易引起眼睛疲劳、酸痛和头晕目眩等症状。

  • CSS 动画原理

根据上面的原理我们知道,你眼前所看到图像正在以每秒 60 次的频率绘制,由于频率很高,所以你感觉不到它在绘制。而 动画本质就是要让人眼看到图像被绘制而引起变化的视觉效果,这个变化要以连贯的、平滑的方式进行过渡。 那怎么样才能做到这种效果呢? 60Hz 的屏幕每 16.7ms 绘制一次,如果在屏幕每次绘制前,将元素的位置向左移动一个像素,即1px,这样一来,屏幕每次绘制出来的图像位置都比前一个要差1px,你就会看到图像在移动;而由于人眼的视觉停留效应,当前位置的图像停留在大脑的印象还没消失,紧接着图像又被移到了下一个位置,这样你所看到的效果就是,图像在流畅的移动。这就是视觉效果上形成的动画。

最后

整理了一套《前端大厂面试宝典》,包含了HTML、CSS、JavaScript、HTTP、TCP协议、浏览器、VUE、React、数据结构和算法,一共201道面试题,并对每个问题作出了回答和解析。

有需要的小伙伴,可以点击文末卡片领取这份文档,无偿分享

部分文档展示:



文章篇幅有限,后面的内容就不一一展示了

有需要的小伙伴,可以点下方卡片免费领取

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_74945.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

腾讯前端二面常考vue面试题(附答案)

虚拟DOM真的比真实DOM性能好吗 首次渲染大量DOM时&#xff0c;由于多了一层虚拟DOM的计算&#xff0c;会比innerHTML插入慢。正如它能保证性能下限&#xff0c;在真实DOM操作的时候进行针对性的优化时&#xff0c;还是更快的。 MVVM的优缺点? 优点: 分离视图&#xff08;V…

MK60DX256VLQ10(256KB)MK60DN256VLQ10 Kinetis K60 MCU FLASH

MK60DX256VLQ10(256KB)MK60DN256VLQ10 Kinetis K60 MCU 32BIT 256KB FLASH 144LQFP【说明】Kinetis K6x MCU系列是一个可扩展的组合&#xff0c;具有不同级别的集成&#xff0c;提供丰富的模拟、通信、定时和控制外设套件&#xff0c;以适应广泛的需求。应用楼宇自动化控制器人…

数仓基础与hive入门

目录1、数仓数据仓库主流开发语言--SQL2、Apache Hive入门2.1 hive定义2.2 为什么使用Hive2.3 Hive和Hadoop关系2.4 场景设计&#xff1a;如何模拟实现Hive功能2.5 Apache Hive架构、组件3、Apache Hive安装部署3.1 metastore配置方式4、Hive SQL语言&#xff1a;DDL建库、建表…

【谷歌grc】recaptcha browser-error 错误

grc 谷歌人机验证错误 https://www.google.com/recaptcha/api/siteverif 返回错误信息 browser-error [{"success": false,"error-codes": ["browser-error"] }]之前都是调通能用的&#xff0c;突然之间就不能用了&#xff0c;查了半天也没有找…

蓝库云|什么是供应链管理?SCM对制造业的重要性

企业在产品的销售经营上&#xff0c;往往不会考量到供应链管理(SCM)的流程规划&#xff0c;但现今的商业环境与以往不同&#xff0c;高度竞争与客户不断提升的期望&#xff0c;藉由做好供应链管理(SCM)&#xff0c;才能更准时的提供优质产品与优良服务&#xff0c;增强企业竞争…

Qt 小项目 图片浏览系统

目录 引言 实现功能&#xff1a; 效果&#xff1a; 实现图片浏览所用知识: 实现流程&#xff1a; 实现环境和UI设计 具体实现 引言 本系统支持&#xff0c;自动播放&#xff0c;左右拖动切换&#xff0c;点击列表切换&#xff0c;点击按钮切换&#xff1b;是一个标准的…

职场性别报告,男女薪酬仍有差距,男性平均薪酬比女性高29.7%

性别是否影响职业&#xff1f;女性求职比男性更加困难&#xff1f;男性薪酬比女性更有优势&#xff1f;人们一说到警察、建筑师通常会想到高大魁梧的男性形象&#xff0c;一说到幼师、护士往往想到的都是温柔的女性形象&#xff0c;职业好似与性别挂钩&#xff1b;女性求职通常…

vue脚手架多页自动化生成实践

前言 在前端开发过程中&#xff0c;常常面对多种业务场景。到目前为止&#xff0c;前端对于不同场景的处理通常会采用不同的渲染方案来组合处理&#xff0c;常见的渲染方案包括&#xff1a;CSR(Client Side Rendering)、SSR(Server Side Rendering)、SSG(Static Site Generati…

LVGL8.3 集成 ST7789V 显示驱动和 CST816T 触摸屏驱动

LVGL8.3 集成 ST7789V 显示驱动和 CTS816S 触摸屏驱动起因效果&#xff08;正常显示&#xff0c;触摸屏可调换X&#xff0c;Y轴&#xff09;使用方式前提操作步骤最后参考起因 LVGL的ESP32 Drivers库中已经包含了大多数显示和触摸芯片的驱动&#xff0c;基本上只需要在MenuCon…

一些无线通信系统模型的概念

一些无线通信系统模型的概念 扩频通信,指的是系统的带宽WWW远大于其信息传输速率R(bits/s)R(bits/s)R(bits/s), 定义展频带因子BeWRB_e \frac{W}{R}Be​RW​, 易知在扩频通信系统中,BeB_eBe​远大于1. 在频率上产生如此大的冗余度,主要是为了减轻无线通信或卫星通信中经常产生…

SpringMVC——响应处理(1)【包含源码分析】

Controller public class JsonReturnController {ResponseBodyGetMapping("/getPet")public Pet getPet(){Pet petnew Pet();pet.setAge(5);pet.setName("lily");return pet;} }项目启动后 浏览器输入 http://localhost:8080/getPet 。 debug DispatcherS…

设备驱动模型--存储技术原理分析笔记 基于2.6.43内核

本文为读书笔记&#xff0c;详细内容参考《存储原理技术分析》1- 驱动模型2- 总线类型2.1- 重要数据结构总线bus_type 和 bus_type_private 互相可以找到对方struct bus_type {const char *name;struct bus_attribute *bus_attrs;struct device_attribute *dev_attrs;s…

BPMN2.0是什么,BPMN能解决企业流程管理中哪些问题?

一、前言&#xff1a; 在任何行业和企业中&#xff0c;一定存在着各式各样的流程&#xff0c;请假流程、报销流程、入职流程、离职流程、出差流程、合同审批流程、出入库流程等等…… 无论是管理者、技术人员还是业务人员&#xff0c;每天肯定也在使用各种流程&#xff0c;但…

Spring彻头彻尾的讲解,按照Spring框架启动流程,逐步剖析问题,不再是大杂烩!

文章目录1. 定义Spring Bean篇1.1 定义Spring Bean的几种方式1.1.1 XML文件定义Spring Bean1.1.2 JavaConfig定义Spring Bean1.1.3 Component注解定义SpringBean1.2 装配Spring Bean的四种常用方式1.2.1 手动装配 XML文件1.2.2 自动装配 XML文件1.2.3 手动装配 JavaConfig文…

C++常见类型及占用内存表

GPS生产厂家在定义数据的时候都会有一定的数据类型&#xff0c;例如double、int、float等&#xff0c;我们知道它们在内存中都对应了一定的字节大小&#xff0c;而我在实际使用时涉及到了端序的问题&#xff08;大端序高字节在前&#xff0c;小端序低字节在前&#xff09;&…

【ICCV2022】 CAPAO:一种高效的单阶段人体姿态估计模型

CAPAO&#xff1a;一种高效的单阶段人体姿态估计模型 重新思考关键点表示&#xff1a;将关键点和姿态建模作为多人姿态估计的对象&#xff08;Rethinking Keypoint Representations: Modeling Keypoints and Poses as Objects for Multi-Person Human Pose Estimation&#xf…

《数据库系统概论》学习笔记——第四章 数据库安全

教材为数据库系统概论第五版&#xff08;王珊&#xff09; 这一章简单记一下那几条sql的用法和两种存取控制和审计&#xff08;今年期末考了&#xff09;吧&#xff0c;不知道有啥好考的 数据库安全性 问题的提出 数据库的一大特点是数据可以共享数据共享必然带来数据库的安全…

idea远程调试线上jar包

有时候本地代码没问题但在线上运行会报错&#xff0c;这时候可以使用idea的remote功能调试线上jar包 步骤1 步骤2&#xff1a;新建remote 步骤3&#xff1a;配置服务器ip和端口 并复制生成的JVM参数供之后使用 步骤4&#xff1a;打jar包&#xff0c;并将生成的jar包放到服务…

Uncaught ReferenceError: jQuery is not defined

今天在拉取项目部署到本地的时候遇到了一个问题特此记录一下 &#xff08;以后闭坑&#xff09; 我和同事同时拉取了一样的代码&#xff0c;结果同事的页面加载正常而我的页面像被狗啃了一样&#xff0c;知道是js的问题但是不知道问题出在哪里&#xff1f;后来还是同事帮我解决…

栈与队列小结

一、理论基础1.队列是先进先出&#xff0c;栈是先进后出2.栈和队列是STL&#xff08;C标准库&#xff09;里面的两个数据结构。栈提供push和pop等等接口&#xff0c;所有元素必须符合先进后出规则&#xff0c;所以栈不提供走访功能&#xff0c;也不提供迭代器。3.栈是以底层容器…