某天,業務線的一個妹子過來找我,說升級到 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小時內與您取得聯系。