某天,业务线的一个妹子过来找我,说升级到 framework-bom 1.9.3,发布时,应用启动不了,但本地启动是ok的。报错信息如下:
业务团队,某个小伙伴启动时报错的内容
我们中间件团队也对各种 starter 做了二次封装。比如 健康检测模块。
公司中间件团队->内部实现的 starter
但是,在上图中,箭头指向的 package 跟 SpringBoot 框架是一致的。而Spring 框架里面没有 getMaxThreads 方法。
仔细看看上面的两个截图的箭头指向的位置,会发现 ManagementServerProperties 类的全路径一致。
Maven Helper 插件
idea 启动时,可以看到 JAR 包加载顺序
Java -verbose 命令执行效果
Java ClassLoader 加载顺序
JDK 官方文档:https://docs.oracle.com/javase/7/docs/technotes/tools/solaris/classpath.html The order in which the JAR files in a directory are enumerated in the expanded class path is not specified and may vary from platform to platform and even from moment to moment on the same machine. A well-constructed application should not depend upon any particular order. If a specific order is required then the JAR files can be enumerated explicitly in the class path. 翻译: 相同目录下,JAR文件在JVM 中加载的顺序是无法保证,并且可能因平台而异,甚至在同一台计算机上也可能因时间而异。构造良好的应用程序不应依赖于任何特定的顺序。如果需要特定的顺序,那么可以在类路径中显式指定加载JAR文件的顺序。
至此,问题基本明了了,基本底层也搞清楚了。
#类加载#?
作为Python Web 框架,Django 需要一种很便利的方法以动态地生成HTML,最常见的做法是使用模板。模板包含所需HTML 输出的静态部分,以及一些特殊的语法,描述如何将动态内容插入。
Django 项目可以配置一个或多个模板引擎。Django 的模板系统自带内建的后台-称为Django 模板语言(DTL),以及另外一种流行的Jinja2。其他的模板语言的后端,可查找第三方库。
在使用layui的时候,需要使用到layui数据表格的模板,这时候就遇到{{}}转义的问题。在django中{{}}是获取变量值,这就跟前段的layui的模板冲突了,这时候就需要django不转译指定的内容。
<table class="layui-table" lay-data="{width: 'auto', height:'auto', url:'/auto_tasks/task_view/', page:true, id:'autotaskviews'}"
lay-filter="autotaskviews_table" lay-size="xm">
<thead>
<tr>
<th lay-data="{field:'id',sort: true, fixed: true,width:'80'}">编号</th>
<th lay-data="{field:'name', sort: true,width: '180'}">任务名称</th>
<th lay-data="{field:'task_type' , sort: true,width: 140}">任务类型</th>
<th lay-data="{field:'task_custom_parameter' ,sort: true,width: '200'}">自定义参数</th>
<th lay-data="{field:'username' ,sort: true,width: '120'}">创建者</th>
<th lay-data="{field:'status_label' ,sort: true,width: '100'}">执行状态</th>
<th lay-data="{field:'create_time' ,sort: true,width: '190'}">创建时间</th>
<th lay-data="{field:'exec_time' ,sort: true,width: '190'}">执行时间</th>
<th lay-data="{field:'detail_result' ,sort: true,width: '200'}">执行结果</th>
<th lay-data="{fixed: 'right', align:'center',width: '180', toolbar: '#barDemo' }">查看详情</th>
</tr>
</thead>
</table>
<script type="text/html" id="barDemo"> {{# if(d.status=='Y'){ }}
<button class="layui-btn layui-btn-disabled layui-btn-xs">已执行</button>
{{# } else if(d.status=='N') { }}
<a class="layui-btn layui-btn-xs" lay-event="exec">执行</a>
{{# } else if(d.status=='R') { }}
<span class="layui-badge layui-bg-orange layui-btn-xs">执行中</span>
{{# } }}
<a class="layui-btn layui-btn-primary layui-btn-xs" lay-event="detail">查看</a>
</script>
言 Preface
想要成为一名合格的前端工程师,掌握相关浏览器的工作原理是必备的,这样子才会有一个完整知识体系,要是「能参透浏览器的工作原理,你就能解决80%的前端难题」。
今天总结了10道浏览器面试题及解析,作为前端开发工程师的你赶紧来看看吧!
1. 常见的浏览器内核有哪些?
2. 浏览器的主要组成部分是什么?
值得注意的是,和大多数浏览器不同,Chrome 浏览器的每个标签页都分别对应一个呈现引擎实例。每个标签页都是一个独立的进程。
3. 为什么JavaScript是单线程的,与异步冲突吗
补充:JS中其实是没有线程概念的,所谓的单线程也只是相对于多线程而言。JS的设计初衷就没有考虑这些,针对JS这种不具备并行任务处理的特性,我们称之为“单线程”。
JS单线程是指一个浏览器进程中只有一个JS的执行线程,同一时刻内只会有一段代码在执行。
举个通俗例子,假设JS支持多线程操作的话,JS可以操作DOM,那么一个线程在删除DOM,另外一个线程就在获取DOM数据,这样子明显不合理,这算是证明之一。
来看段代码
function foo() { console.log("first");
setTimeout(( function(){ console.log( 'second' );
}),5);
}
for (var i=0; i < 1000000; i++) {
foo();
}复制代码打印结果就是首先是很多个first,然后再是second。
异步机制是浏览器的两个或以上常驻线程共同完成的,举个例子,比如异步请求由两个常驻线程,JS执行线程和事件触发线程共同完成的。
再比如定时器触发(settimeout和setinterval) 是由「浏览器的定时器线程」执行的定时计数,然后在定时时间把定时处理函数的执行请求插入到JS执行队列的尾端(所以用这两个函数的时候,实际的执行时间是大于或等于指定时间的,不保证能准确定时的)。
所以这么说,JS单线程与异步更多是浏览器行为,之间不冲突。
4. CSS加载会造成阻塞吗
先给出结论
先讲一讲CSSOM作用
由之前讲过的浏览器渲染流程我们可以看出:
DOM 和 CSSOM通常是并行构建的,所以「CSS 加载不会阻塞 DOM 的解析」。
然而由于Render Tree 是依赖DOM Tree和 CSSOM Tree的,所以它必须等到两者都加载完毕后,完成相应的构建,才开始渲染,因此,「CSS加载会阻塞DOM渲染」。
由于 JavaScript 是可操纵 DOM 和 css 样式 的,如果在修改这些元素属性同时渲染界面(即 JavaScript 线程和 UI 线程同时运行),那么渲染线程前后获得的元素数据就可能不一致了。
因此为了防止渲染出现不可预期的结果,浏览器设置 「GUI 渲染线程与 JavaScript 引擎为互斥」的关系。
有个需要注意的点就是:
「有时候JS需要等到CSS的下载,这是为什么呢?」
仔细思考一下,其实这样做是有道理的,如果脚本的内容是获取元素的样式,宽高等CSS控制的属性,浏览器是需要计算的,也就是依赖于CSS。浏览器也无法感知脚本内容到底是什么,为避免样式获取,因而只好等前面所有的样式下载完后,再执行JS。
JS文件下载和CSS文件下载是并行的,有时候CSS文件很大,所以JS需要等待。
因此,样式表会在后面的 js 执行前先加载执行完毕,所以「css 会阻塞后面 js 的执行」。
5. 为什么JS会阻塞页面加载
先给出结论
这也是为什么说JS文件放在最下面的原因,那为什么会阻塞DOM解析呢
你可以这样子理解:
由于 JavaScript 是可操纵 DOM 的,如果在修改这些元素属性同时渲染界面(即 JavaScript 线程和 UI 线程同时运行),那么渲染线程前后获得的元素数据就可能不一致了。
因此为了防止渲染出现不可预期的结果,浏览器设置 「GUI 渲染线程与 JavaScript 引擎为互斥」的关系。
当 JavaScript 引擎执行时 GUI 线程会被挂起,GUI 更新会被保存在一个队列中等到引擎线程空闲时立即被执行。
当浏览器在执行 JavaScript 程序的时候,GUI 渲染线程会被保存在一个队列中,直到 JS 程序执行完成,才会接着执行。
因此如果 JS 执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉。
另外,如果 JavaScript 文件中没有操作 DOM 相关代码,就可以将该 JavaScript 脚本设置为异步加载,通过 async 或 defer 来标记代码。
6. defer 和 async 的区别 ?
两者都是异步去加载外部JS文件,不会阻塞DOM解析Async是在外部JS加载完成后,浏览器空闲时,Load事件触发前执行,标记为async的脚本并不保证按照指定他们的先后顺序执行,该属性对于内联脚本无作用 (即没有「src」属性的脚本)。defer是在JS加载完成后,整个文档解析完成后,触发 DOMContentLoaded 事件前执行,如果缺少 src 属性(即内嵌脚本),该属性不应被使用,因为这种情况下它不起作用
7. DOMContentLoaded 与 load 的区别 ?
那么也就是先DOMContentLoaded -> load,那么在Jquery中,使用
(document).load(callback)监听的就是load事件。
那我们可以聊一聊它们与async和defer区别
带async的脚本一定会在load事件之前执行,可能会在DOMContentLoaded之前或之后执行。
如果 script 标签中包含 defer,那么这一块脚本将不会影响 HTML 文档的解析,而是等到HTML 解析完成后才会执行。而 DOMContentLoaded 只有在 defer 脚本执行结束后才会被触发。
8. 为什么CSS动画比JavaScript高效
我觉得这个题目说法上可能就是行不通,不能这么说,如果了解的话,都知道will-change只是一个优化的手段,使用JS改变transform也可以享受这个属性带来的变化,所以这个说法上有点不妥。
所以围绕这个问题展开话,更应该说建议推荐使用CSS动画,至于为什么呢,涉及的知识点大概就是重排重绘,合成,这方面的点,我在浏览器渲染流程中也提及了。
尽可能的避免重排和重绘,具体是哪些操作呢,如果非要去操作JS实现动画的话,有哪些优化的手段呢?
比如
剩下的东西就留给你们思考吧,希望我这是抛砖引玉吧(●’?’●)
9. 能不能实现事件防抖和节流
函数节流(throttle)
节流的意思是让函数有节制地执行,而不是毫无节制的触发一次就执行一次。什么叫有节制呢?就是在一段时间内,只执行一次。
规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效。
抓取一个关键的点:就是执行的时机。要做到控制执行的时机,我们可以通过「一个开关」,与定时器setTimeout结合完成。
function throttle(fn, delay) { let flag=true,
timer=null; return function (...args) { let context=this; if (!flag) return;
flag=false;
clearTimeout(timer)
timer=setTimeout(()=> {
fn.apply(context, args);
flag=true;
}, delay);
};
};复制代码函数防抖(debounce)
在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时。
核心思想:每次事件触发都会删除原有定时器,建立新的定时器。通俗意思就是反复触发函数,只认最后一次,从最后一次开始计时。
代码:
function debounce(fn, delay) { let timer=null
return function (...args) { let context=this
if(timer) clearTimeout(timer)
timer=setTimeout(function() {
fn.apply(context, args)
},delay)
}
}复制代码如何使用 debounce 和 throttle 以及常见的坑
自己造一个 debounce / throttle 的轮子看起来多么诱人,或者随便找个博文复制过来。「我是建议直接使用 underscore 或 Lodash」 。如果仅需要 _.debounce 和 _.throttle 方法,可以使用 Lodash 的自定义构建工具,生成一个 2KB 的压缩库。使用以下的简单命令即可:
npm i -g lodash-cli
npm i -g lodash-clilodash-cli include=debounce,throttle复制代码常见的坑是,不止一次地调用 _.debounce 方法:
// 错误$(window).on('scroll', function() {
_.debounce(doSomething, 300);
});// 正确$(window).on('scroll', _.debounce(doSomething, 200));复制代码debounce 方法保存到一个变量以后,就可以用它的私有方法 debounced_version.cancel(),lodash 和 underscore.js 都有效。
let debounced_version=_.debounce(doSomething, 200);
$(window).on('scroll', debounced_version);// 如果需要的话debounced_version.cancel();复制代码适合应用场景
防抖
节流
10. 谈一谈你对requestAnimationFrame(rAF)理解
正好跟节流有点关系,有点相似处,就准备梳理一下这个知识点。
「高性能动画是什么,那它衡量的标准是什么呢?」
动画帧率可以作为衡量标准,一般来说画面在 60fps 的帧率下效果比较好。
换算一下就是,每一帧要在 16.7ms (16.7=1000/60) 内完成渲染。
我们来看看MDN对它的解释吧
window.requestAnimationFrame() 方法告诉浏览器您希望执行动画并请求浏览器在下一次重绘之前调用指定的函数来更新动画。该方法使用一个回调函数作为参数,这个回调函数会在浏览器重绘之前调用。— MDN
当我们调用这个函数的时候,我们告诉它需要做两件事:
rAF与 setTimeout 相比
rAF(requestAnimationFrame) 最大的优势是「由系统来决定回调函数的执行时机」。
具体一点讲就是,系统每次绘制之前会主动调用 rAF 中的回调函数,如果系统绘制率是 60Hz,那么回调函数就每16.7ms 被执行一次,如果绘制频率是75Hz,那么这个间隔时间就变成了 1000/75=13.3ms。
换句话说就是,rAF 的执行步伐跟着系统的绘制频率走。它能保证回调函数在屏幕每一次的绘制间隔中只被执行一次(上一个知识点刚刚梳理完「函数节流」),这样就不会引起丢帧现象,也不会导致动画出现卡顿的问题。
另外它可以自动调节频率。如果callback工作太多无法在一帧内完成会自动降低为30fps。虽然降低了,但总比掉帧好。
与setTimeout动画对比的话,有以下几点优势
什么时候调用呢
规范中似乎是这么去定义的:
这样子分析的话,似乎很合理嘛,为什么要在重新渲染前去调用呢?因为rAF作为官方推荐的一种做流畅动画所应该使用的API,做动画不可避免的去操作DOM,而如果是在渲染后去修改DOM的话,那就只能等到下一轮渲染机会的时候才能去绘制出来了,这样子似乎不合理。
rAF在浏览器决定渲染之前给你最后一个机会去改变 DOM 属性,然后很快在接下来的绘制中帮你呈现出来,所以这是做流畅动画的不二选择。
至于宏任务,微任务,这可以说起来就要展开篇幅了,暂时不在这里梳理了。
rAF与节流相比
跟 _.throttle(dosomething, 16) 等价。它是高保真的,如果追求更好的精确度的话,可以用浏览器原生的 API 。
可以使用 rAF API 替换 throttle 方法,考虑一下优缺点:
优点
缺点
根据经验,如果 JavaScript 方法需要绘制或者直接改变属性,我会选择 requestAnimationFrame,只要涉及到重新计算元素位置,就可以使用它。
涉及到 AJAX 请求,添加/移除 class (可以触发 CSS 动画),我会选择 _.debounce 或者 _.throttle ,可以设置更低的执行频率(例子中的200ms 换成16ms)。
云和数据HTML5全栈精英班,经过多年的技术迭代和项目革新,逐步发展成为集网站、手机应用、小程序、快应用、桌面应用、后台开发等多领域开发课程,新增Egg、TypeScript、Vue、React、HybridAPP等时下最流行的新技术,结合企业实际用人需求,只为培养更多高端IT技术人才。
*请认真填写需求信息,我们会在24小时内与您取得联系。