公司:脈脈css
分類:Vuehtml
答案&解析前端
指計算機語言中添加的某種語法,這種語法對語言的功能並無影響,可是更方便程序員使用。一般來講使用語法糖可以增長程序的可讀性,從而減小程序代碼出錯的機會。糖在不改變其所在位置的語法結構的前提下,實現了運行時的等價。能夠簡單理解爲,加糖後的代碼編譯後跟加糖前同樣,代碼更簡潔流暢,代碼更語義天然.vue
動態綁定了 input
的 value
指向了 messgae
變量,而且在觸發 input
事件的時候去動態把 message
設置爲目標值node
<input v-model="sth" />
// 等同於
<input v-bind:value="message" v-on:input="message=$event.target.value" > //$event 指代當前觸發的事件對象; //$event.target 指代當前觸發的事件對象的dom; //$event.target.value 就是當前dom的value值; //在@input方法中,value => sth; //在:value中,sth => value; 複製代碼
在自定義組件中,v-model 默認會利用名爲 value 的 prop 和名爲 input 的事件react
本質是一個父子組件通訊的語法糖,經過prop和$.emit實現jquery
所以父組件v-model
語法糖本質上能夠修改成 '<child :value="message" @input="function(e){message = e}"></child>'
webpack
在組件的實現中,咱們是能夠經過 v-model屬性 來配置子組件接收的prop名稱,以及派發的事件名稱。ios
例子git
// 父組件
<aa-input v-model="aa"></aa-input>
// 等價於
<aa-input v-bind:value="aa" v-on:input="aa=$event.target.value"></aa-input>
// 子組件:
<input v-bind:value="aa" v-on:input="onmessage"></aa-input>
props:{value:aa,}
methods:{
onmessage(e){
$emit('input',e.target.value)
}
}
複製代碼
默認狀況下,一個組件上的 v-model 會把 value 用做 prop 且把 input 用做 event
可是一些輸入類型好比單選框和複選框按鈕可能想使用 value prop 來達到不一樣的目的。使用 model 選項能夠迴避這些狀況產生的衝突。
js 監聽input 輸入框輸入數據改變,用oninput ,數據改變之後就會馬上出發這個事件。
經過input事件把數據$emit 出去,在父組件接受。
父組件設置v-model的值爲input$emit過來的值。
公司:微醫
分類:React
在代碼中調用setState函數以後,React 會將傳入的參數對象與組件當前的狀態合併,而後觸發所謂的調和過程(Reconciliation)。
通過調和過程,React 會以相對高效的方式根據新的狀態構建 React 元素樹而且着手從新渲染整個UI界面。
在 React 獲得元素樹以後,React 會自動計算出新的樹與老樹的節點差別,而後根據差別對界面進行最小化重渲染。
在差別計算算法中,React 可以相對精確地知道哪些位置發生了改變以及應該如何改變,這就保證了按需更新,而不是所有從新渲染。
假如全部setState是同步的,意味着每執行一次setState時(有可能一個同步代碼中,屢次setState),都從新vnode diff + dom修改,這對性能來講是極爲很差的。若是是異步,則能夠把一個同步代碼中的多個setState合併成一次組件更新。
在setTimeout或者原生事件中,setState是同步的。
公司:愛範兒
分類:JavaScript
由於每一個請求處理時長不一致,可能會致使先發送的請求後響應,即請求響應順序和請求發送順序不一致,從而致使數據顯示不正確。
便可以理解爲連續觸發多個請求,如何保證請求響應順序和請求發送順序一致。對於問題所在場景,用戶只關心最後數據是否顯示正確,便可以簡化爲:連續觸發多個請求,如何保證最後響應的結果是最後發送的請求(不關注以前的請求是否發送或者響應成功)
相似場景:input輸入框即時搜索,表格快速切換頁碼
防抖(過濾掉一些非必要的請求) + 取消上次未完成的請求(保證最後一次請求的響應順序)
取消請求方法:
XMLHttpRequest
使用 abort
api
取消請求axios
使用 cancel token
取消請求僞代碼(以 setTimeout 模擬請求,clearTimeout 取消請求)
/** * 函數防抖,必定時間內連續觸發事件只執行一次 * @param {*} func 須要防抖的函數 * @param {*} delay 防抖延遲 * @param {*} immediate 是否當即執行,爲true表示連續觸發時當即執行,即執行第一次,爲false表示連續觸發後delay ms後執行一次 */
let debounce = function(func, delay = 100, immediate = false) {
let timeoutId, last, context, args, result
function later() {
const interval = Date.now() - last
if (interval < delay && interval >= 0) {
timeoutId = setTimeout(later, delay - interval)
} else {
timeoutId = null
if (!immediate) {
result = func.apply(context, args)
context = args = null
}
}
}
return function() {
context = this
args = arguments
last = Date.now()
if (immediate && !timeoutId) {
result = func.apply(context, args)
context = args = null // 解除引用
}
if (!timeoutId) {
timeoutId = setTimeout(later, delay)
}
return result
}
}
let flag = false // 標誌位,表示當前是否正在請求數據
let xhr = null
let request = (i) => {
if (flag) {
clearTimeout(xhr)
console.log(`取消第${i - 1}次請求`)
}
flag = true
console.log(`開始第${i}次請求`)
xhr = setTimeout(() => {
console.log(`請求${i}響應成功`)
flag = false
}, Math.random() * 200)
}
let fetchData = debounce(request, 50) // 防抖
// 模擬連續觸發的請求
let count = 1
let getData = () => {
setTimeout(() => {
fetchData(count)
count++
if (count < 11) {
getData()
}
}, Math.random() * 200)
}
getData()
/* 某次測試輸出: 開始第2次請求 請求2響應成功 開始第3次請求 取消第3次請求 開始第4次請求 請求4響應成功 開始第5次請求 請求5響應成功 開始第8次請求 取消第8次請求 開始第9次請求 請求9響應成功 開始第10次請求 請求10響應成功 */
複製代碼
因文章篇幅過長,影響閱讀體驗,接下來的7道題目答案將被摺疊,點擊查看解析,便可查看所有答案,選擇你感興趣的題查看答案吧~
公司:有贊、微醫、58
分類:React、Vue
從本質上來講,Virtual Dom是一個JavaScript對象,經過對象的方式來表示DOM結構。將頁面的狀態抽象爲JS對象的形式,配合不一樣的渲染工具,使跨平臺渲染成爲可能。經過事務處理機制,將屢次DOM修改的結果一次性的更新到頁面上,從而有效的減小頁面渲染的次數,減小修改DOM的重繪重排次數,提升渲染性能。
虛擬dom是對DOM的抽象,這個對象是更加輕量級的對DOM的描述。它設計的最初目的,就是更好的跨平臺,好比Node.js就沒有DOM,若是想實現SSR,那麼一個方式就是藉助虛擬dom, 由於虛擬dom自己是js對象。
在代碼渲染到頁面以前,vue或者react會把代碼轉換成一個對象(虛擬DOM)。以對象的形式來描述真實dom結構,最終渲染到頁面。在每次數據發生變化前,虛擬dom都會緩存一份,變化之時,如今的虛擬dom會與緩存的虛擬dom進行比較。
在vue或者react內部封裝了diff算法,經過這個算法來進行比較,渲染時修改改變的變化,原先沒有發生改變的經過原先的數據進行渲染。
另外現代前端框架的一個基本要求就是無須手動操做DOM,一方面是由於手動操做DOM沒法保證程序性能,多人協做的項目中若是review不嚴格,可能會有開發者寫出性能較低的代碼,另外一方面更重要的是省略手動DOM操做能夠大大提升開發效率。
看一下頁面渲染的一個流程:
下面對比一下修改DOM時真實DOM操做和Virtual DOM的過程,來看一下它們重排重繪的性能消耗:
Virtual DOM的更新DOM的準備工做耗費更多的時間,也就是JS層面,相比於更多的DOM操做它的消費是極其便宜的。尤雨溪在社區論壇中說道: 框架給你的保證是,你不須要手動優化的狀況下,我依然能夠給你提供過得去的性能。
Virtual DOM本質上是JavaScript的對象,它能夠很方便的跨平臺操做,好比服務端渲染、uniapp等。
分類:工程化
webpack是所謂的模塊捆綁器,內部有循環引用來分析模塊間之間的依賴,把文件解析成AST,經過一系類不一樣loader的加工,最後所有打包到一個js文件裏。
webpack4之前在打包速度上沒有作過多的優化手段,編譯慢的大部分時間是花費在不一樣loader編譯過程,webpack4之後,吸取借鑑了不少優秀工具的思路,
如支持0配置,多線程等功能,速度也大幅提高,但依然有一些優化手段。如合理的代碼拆分,公共代碼的提取,css資源的抽離
exclude/include
(肯定 loader 規則範圍)resolve.modules
指明第三方模塊的絕對路徑 (減小沒必要要的查找)resolve.extensions
儘量減小後綴嘗試的可能性noParse
對徹底不須要解析的庫進行忽略 (不去解析但仍會打包到 bundle 中,注意被忽略掉的文件裏不該該包含 import、require、define 等模塊化語句)注意:thread-loader 和 cache-loader 兩個要一塊兒使用的話,請先放 cache-loader 接著是 thread-loader 最後纔是 heavy-loader
來看下具體使用
module.exports = {
module: {
noParse: /jquery/,
rules:[]
}
}
複製代碼
./local
則忽略掉import 'moment/locale/zh-cn'
module.exports = {
plugins: [
new Webpack.IgnorePlugin(/\.\/local/, /moment/),
]
}
複製代碼
webpack.DllPlugin
、Webpack.DllReferencePlugin
thread-loader 會將您的 loader 放置在一個 worker 池裏面運行,以達到多線程構建。
把這個 loader 放置在其餘 loader 以前,放置在這個 loader 以後的 loader 就會在一個單獨的 worker 池(worker pool)中運行。
// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.js$/,
include: path.resolve("src"),
use: [
"thread-loader",
// 你的高開銷的loader放置在此 (e.g babel-loader)
]
}
]
}
}
複製代碼
每一個 worker 都是一個單獨的有 600ms 限制的 node.js 進程。同時跨進程的數據交換也會被限制。請在高開銷的loader中使用,不然效果不佳
不推薦使用 webpack-paralle-uglify-plugin,項目基本處於沒人維護的階段,issue 沒人處理,pr沒人合併。
Webpack 4.0之前:uglifyjs-webpack-plugin,parallel參數
module.exports = {
optimization: {
minimizer: [
new UglifyJsPlugin({
parallel: true,
}),
],
},};
複製代碼
推薦使用 terser-webpack-plugin
module.exports = {
optimization: {
minimizer: [new TerserPlugin(
parallel: true // 多線程
)],
},
};
複製代碼
分類:網絡&安全
瀏覽器緩存策略:
瀏覽器每次發起請求時,先在本地緩存中查找結果以及緩存標識,根據緩存標識來判斷是否使用本地緩存。若是緩存有效,則使用本地緩存;不然,則向服務器發起請求並攜帶緩存標識。根據是否需向服務器發起HTTP請求,將緩存過程劃分爲兩個部分:強制緩存和協商緩存,強緩優先於協商緩存。
HTTP緩存都是從第二次請求開始的
服務器通知瀏覽器一個緩存時間,在緩存時間內,下次請求,直接用緩存,不在時間內,執行比較緩存策略。
強緩存命中則直接讀取瀏覽器本地的資源,在network中顯示的是from memory或者from disk
控制強制緩存的字段有:Cache-Control(http1.1)和Expires(http1.0)
1)強緩存-expires
該字段是服務器響應消息頭字段,告訴瀏覽器在過時時間以前能夠直接從瀏覽器緩存中存取數據。
Expires 是 HTTP 1.0 的字段,表示緩存到期時間,是一個絕對的時間 (當前時間+緩存時間)。在響應消息頭中,設置這個字段以後,就能夠告訴瀏覽器,在未過時以前不須要再次請求。
因爲是絕對時間,用戶可能會將客戶端本地的時間進行修改,而致使瀏覽器判斷緩存失效,從新請求該資源。此外,即便不考慮修改,時差或者偏差等因素也可能形成客戶端與服務端的時間不一致,導致緩存失效。
優點特色:
劣勢問題:
2)強緩存-cache-control
已知Expires的缺點以後,在HTTP/1.1中,增長了一個字段Cache-control,該字段表示資源緩存的最大有效時間,在該時間內,客戶端不須要向服務器發送請求。
這二者的區別就是前者是絕對時間,然後者是相對時間。下面列舉一些 Cache-control
字段經常使用的值:(完整的列表能夠查看MDN)
max-age
:即最大有效時間。must-revalidate
:若是超過了 max-age
的時間,瀏覽器必須向服務器發送請求,驗證資源是否還有效。no-cache
:不使用強緩存,須要與服務器驗證緩存是否新鮮。no-store
: 真正意義上的「不要緩存」。全部內容都不走緩存,包括強制和對比。public
:全部的內容均可以被緩存 (包括客戶端和代理服務器, 如 CDN)private
:全部的內容只有客戶端才能夠緩存,代理服務器不能緩存。默認值。Cache-control 的優先級高於 Expires,爲了兼容 HTTP/1.0 和 HTTP/1.1,實際項目中兩個字段均可以設置。
該字段能夠在請求頭或者響應頭設置,可組合使用多種指令:
max-age=<seconds>
:緩存存儲的最大週期,超過這個週期被認爲過時。s-maxage=<seconds>
:設置共享緩存,好比can。會覆蓋max-age和expires。max-stale[=<seconds>]
:客戶端願意接收一個已通過期的資源min-fresh=<seconds>
:客戶端但願在指定的時間內獲取最新的響應stale-while-revalidate=<seconds>
:客戶端願意接收陳舊的響應,而且在後臺一部檢查新的響應。時間表明客戶端願意接收陳舊響應 的時間長度。stale-if-error=<seconds>
:如新的檢測失敗,客戶端則願意接收陳舊的響應,時間表明等待時間。優點特色:
劣勢問題:
讓客戶端與服務器之間能實現緩存文件是否更新的驗證、提高緩存的複用率,將緩存信息中的Etag和Last-Modified經過請求發送給服務器,由服務器校驗,返回304狀態碼時,瀏覽器直接使用緩存。
1)協商緩存-協商緩存-Last-Modified/If-Modified-since
Last-Modified
字段告知客戶端,資源最後一次被修改的時間,例如 Last-Modified: Mon, 10 Nov 2018 09:10:11 GMT
Last-Modified
的值寫入到請求頭的 If-Modified-Since
字段If-Modified-Since
的值與 Last-Modified
字段進行對比。若是相等,則表示未修改,響應 304;反之,則表示修改了,響應 200 狀態碼,並返回數據。優點特色:
劣勢問題:
2)協商緩存-Etag/If-None-match
Etag
和 If-None-Match
Etag
存儲的是文件的特殊標識(通常都是 hash 生成的),服務器存儲着文件的 Etag
字段。以後的流程和 Last-Modified
一致,只是 Last-Modified
字段和它所表示的更新時間改變成了 Etag
字段和它所表示的文件 hash,把 If-Modified-Since
變成了 If-None-Match
。服務器一樣進行比較,命中返回 304, 不命中返回新資源和 200。優點特色:
劣勢問題:
強緩存:服務器通知瀏覽器一個緩存時間,在緩存時間內,下次請求,直接用緩存,不在時間內,執行其餘緩存策略
Cache-Contrl: max-age=3000
公司:愛範兒
分類:JavaScript
1)懶加載:獲取首屏數據,後邊的數據進行滑動加載請求
2)利用骨架屏提高用戶體驗
3)PreloadJS預加載
使用PreloadJS庫,PreloadJS提供了一種預加載內容的一致方式,以便在HTML應用程序中使用。預加載可使用HTML標籤以及XHR來完成。默認狀況下,PreloadJS會嘗試使用XHR加載內容,由於它提供了對進度和完成事件的更好支持,可是因爲跨域問題,使用基於標記的加載可能更好。
4)除了添加前端loading和超時404頁面外,接口部分能夠添加接口緩存和接口的預加載
上面的方法,能夠根據業務需求選擇組合使用。
因此咱們能夠大體分爲在
首先先上一張經典到不能再經典的圖
其中cnd在dns階段, dom渲染在processing onload階段
上圖從 promot for unload 到 onload 的過程這麼多步驟, 在用戶體驗來講, 一個頁面從加載到展現超過 4 秒, 就會有一種很是直觀的卡頓現象, 其中 load 對應的位置是 onLoad 事件結束後, 纔開始構建 dom 樹, 可是用戶不必定是關心當前頁面是不是完成了資源的下載; 每每是一個頁面開始出現可見元素開始FCP 首次內容繪製或者是FC 首次繪製 此時用戶視覺體驗開始, 到TTI(可交互時間) , 可交互元素的出現, 意味着,用戶交互體驗開始, 這時候用戶就能夠愉快的瀏覽使用咱們的頁面啦;
因此這個問題的主要痛點是須要縮短到達 TTI 和 FCP 的時間
可是這裏已知進入咱們詳情頁面時, 接口數據返回速度是很慢的, FCP 和 FC , 以及加快到達 TTI , 就須要咱們頁面預處理了
第一次 進入詳情頁面, 可使用骨架圖進行模擬 FC 展現, 而且骨架圖, 可以使用背景圖且行內樣式的方式對首次進入詳情頁面進行展現, 對於請求過慢的詳情接口使用 worker 進程, 對詳情的接口請求丟到另一個工做線程進行請求, 頁面渲染其餘已返回數據的元素; 當很慢的數據回來後, 須要對頁面根據商品 id 簽名爲 key 進行 webp 或者是縮略圖商品圖的 cnd 路徑 localStorage 的緩存, 商品 id 的簽名由放在 cookie 並設置成 httpOnly
非第一次 進入詳情頁時, 前端可經過特定的接口請求回來對應的商品 id 簽名的 cookieid, 讀取 localStorage 的商品圖片的緩存數據, 這樣對於第一次骨架圖的展現時間就能夠縮短, 快速到達 TTI 與用戶交互的時間, 再經過 worker 數據, 進行高清圖片的切換
對於緩存圖片地址的處理, 雖然說緩存圖片是放在 localStorage 中, 不會用大小限制, 可是太多也是很差的, 這裏使用 LRU 算法對圖片以及其餘 localStorage 進行清除處理, 對於超過 7 天的數據進行清理 localStorage 詳情頁的數據, 數據結構以下:
"讀取後端的cookieID": {
"path": "對應cdn圖片的地址",
"time": "緩存時間戳",
"size": "大小"
}
複製代碼
這裏有兩種情景
移動端, 對於移動端, 通常不會出現大量圖片, 通常一個商品詳情頁, 不會超過 100 張圖片資源; 這時候, 選擇懶加載方案; 根據 GitHub 現有的不少方案, 當前滑動到可被觀察的元素後才加載當前可視區域的圖片資源, 一樣使用的是 Intersection Observer API ; 好比 vue 的一個庫 vue-lazy , 這個庫就是對 Intersection_Observer_API 進行封裝, 對可視區域的 img 便籤進行 data-src 和 src 屬性替換
第二個狀況, pc 端, 可能會出現大量的 img 標籤, 可能多達 300~400 張, 這時候, 使用懶加載, 用戶體驗就不太好了; 好比說: 當用戶在查看商品說明介紹時, 這些商品說明和介紹有可能只是一張張圖片, 當用戶很快速的滑動時, 頁面還沒懶加載完, 用戶就有可能看不到想看的信息; 鑑於會出現這種狀況, 這裏給出一個方案就是, img 出現一張 load 一張; 實現以下:
// 這裏針對非第一次進入詳情頁,
//當前localStorage已經有了當前詳情頁商品圖片的縮略圖
for(let i = 0; i < worker.img.length; i++) {
// nodeList是對應img標籤,
// 注意, 這裏對應的nodeList必定要使用內聯style把位置大小設置好, 避免大量的重繪重排
const img = nodeList[i]
img.src = worker.img['path'];
img.onerror = () => {
// 將替換失敗或者加載失敗的圖片降級到縮略圖,
// 即緩存到localStorage的縮略圖或者webp圖
// 兼容客戶端處理webp失敗的狀況
}
}
複製代碼
觸發重排的操做主要是幾何因素:
儘可能減小上面這些產生重繪重排的操做
好比說:
這裏產生很大的重繪重排主要發生在 worker 回來的數據替換頁面中的圖片 src 這一步
// 該節點爲img標籤的父節點
const imgParent = docucment.getElementById('imgParent');
// 克隆當前須要替換img標籤的父元素下全部的標籤
const newImgParent = imgParent.cloneNode(true);
const imgParentParent = docucment.getElementById('imgParentParent');
for(let i = 0; i < newImgParent.children.length; i++) {
// 批量獲取完全部img標籤後, 再進行重繪
newImgParent.children[i].src = worker.img[i].path;
}
// 經過img父節點的父節點, 來替換整個img父節點
// 包括對應的全部子節點, 只進行一次重繪操做
imgParentParent.replaceChild(newImgParent, imgParent);
複製代碼
注意被阻塞的css資源
衆所周知, css的加載會阻塞瀏覽器其餘資源的加載, 直至CSSOM CSS OBJECT MODEL 構建完成, 而後再掛在DOM樹上, 瀏覽器依次使用渲染樹來佈局和繪製網頁。
不少人都下意識的知道, 將css文件一概放到head標籤中是比較好的, 可是爲何將css放在head標籤是最後了呢?
咱們用淘寶作例子
好比這種沒有css樣式的頁面稱之爲FOUC(內容樣式短暫失效), 可是這種狀況通常出如今ie系列以及前期的瀏覽器身上; 就是當cssom在domtree生成後, 依然還沒完成加載出來, 先展現純html代碼的頁面一會再出現正確的帶css樣式的頁面;
減小不一樣頁面的css代碼加載
對於電商頁面, 有些在頭部的css代碼有些是首頁展現的有些是特定狀況才展現的, 好比當咱們須要減小一些css文件大小可是當前網站又須要多屏展現, 這時候, 不少人都會想到是媒體查詢, 沒錯方向是對的, 可是怎樣的媒體查詢纔對css文件保持足夠的小呢, 可使用link標籤媒體查詢,看下邊的的例子:
<link href="base.css" rel="stylesheet">
<link href="other.css" rel="stylesheet" media="(min-width: 750px)">
複製代碼
第一個css資源表示全部頁面都會加載, 第二個css資源, 寬度在750px纔會加載, 默認media="all"
在一些需求寫css媒體查詢的網站, 不要在css代碼裏面寫, 最好寫兩套css代碼, 經過link媒體查詢去動態加載, 這樣就能很好的減輕網站加載css文件的壓力
這種js代碼, 是那些關於埋點, 本地日記, 以及動態修改css代碼, 讀取頁面成型後的信息的一些js代碼, 這種一概放在同域下的localStorage上面, 什麼是同域下的localStorage
這裏仍是以天貓爲例
公司:CVTE
分類:JavaScript
單點登陸SSO(Single Sign On),是一個多系統共存的環境下,用戶在一處登陸後,就不用在其餘系統中登陸,也就是用戶的一次登陸獲得其餘全部系統的信任
好比現有業務系統A、B、C以及SSO系統,第一次訪問A系統時,發現沒有登陸,引導用戶到SSO系統登陸,根據用戶的登陸信息,生成惟一的一個憑據token,返回給用戶。後期用戶訪問B、C系統的時候,攜帶上對應的憑證到SSO系統去校驗,校驗經過後,就能夠單點登陸;
單點登陸在大型網站中使用的很是頻繁,例如,阿里旗下有淘寶、天貓、支付寶等網站,其背後的成百上千的子系統,用戶操做一次或者交易可能涉及到不少子系統,每一個子系統都須要驗證,因此提出,用戶登陸一次就能夠訪問相互信任的應用系統
單點登陸有一個獨立的認證中心,只有認證中心才能接受用戶的用戶名和密碼等信息進行認證,其餘系統不提供登陸入口,只接受認證中心的間接受權。間接受權經過令牌實現,當用戶提供的用戶名和密碼經過認證中心認證後,認證中心會建立受權令牌,在接下來的跳轉過程當中,受權令牌做爲參數發送給各個子系統,子系統拿到令牌即獲得了受權,而後建立局部會話。
單點登陸有同域和跨域兩種場景
適用場景:都是企業本身的系統,全部系統都使用同一個一級域名經過不一樣的二級域名來區分。
舉個例子:公司有一個一級域名爲 zlt.com ,咱們有三個系統分別是:門戶系統(sso.zlt.com)、應用1(app1.zlt.com)和應用2(app2.zlt.com),須要實現系統之間的單點登陸,實現架構以下
核心原理:
單點登陸之間的系統域名不同,例如第三方系統。因爲域名不同不能共享Cookie了,須要的一個獨立的受權系統,即一個獨立的認證中心(passport),子系統的登陸都可以經過passport,子系統自己將不參與登陸操做,當一個系統登陸成功後,passprot將會頒發一個令牌給子系統,子系統能夠拿着令牌去獲取各自的保護資源,爲了減小頻繁認證,各個子系統在被passport受權之後,會創建一個局部會話,在必定時間內無需再次向passport發起認證
基本原理
登陸流程
註銷流程
公司:58
分類:Html
當對Dom元素進行一系列操做時,對Dom進行訪問和修改Dom引發的重繪和重排都比較消耗性能,因此關於操做Dom,應該從如下幾點出發:
首先無論在什麼場景下。操做Dom通常首先會去訪問Dom,尤爲是像循環遍歷這種時間複雜度可能會比較高的操做。那麼能夠在循環以前就將主節點,沒必要循環的Dom節點先獲取到,那麼在循環裏就能夠直接引用,而沒必要去從新查詢。
let rootElem = document.querySelector('#app');
let childList = rootElem.child; // 假設全是dom節點
for(let i = 0;i<childList.len;j++){
/** * 根據條件對應操做 */
}
複製代碼
利用document.createDocumentFragment()
方法建立文檔碎片節點,建立的是一個虛擬的節點對象。向這個節點添加dom節點,修改dom節點並不會影響到真實的dom結構。
咱們能夠利用這一點先將咱們須要修改的dom一併修改完,保存至文檔碎片中,而後用文檔碎片一次性的替換真是的dom節點。與虛擬dom相似,一樣達到了不頻繁修改dom而致使的重排跟重繪的過程。
let fragment = document.createDocumentFragment();
const operationDomHandle = (fragment) =>{
// 操做
}
operationDomHandle(fragment);
// 而後最後再替換
rootElem.replaceChild(fragment,oldDom);
複製代碼
這樣就只會觸發一次迴流,效率會獲得很大的提高。若是須要對元素進行復雜的操做(刪減、添加子節點),那麼咱們應當先將元素從頁面中移除,而後再對其進行操做,或者將其複製一個(cloneNode()),在內存中進行操做後再替換原來的節點。
var clone=old.cloneNode(true);
operationDomHandle(clone);
rootElem.replaceChild(clone,oldDom)
複製代碼
批量讀,一次性寫。先對一個不在render tree上的節點進行操做,再把這個節點添加回render tree。這樣只會觸發一次DOM操做。 使用requestAnimationFrame()
,把任何致使重繪的操做放入requestAnimationFrame
js模擬DOM樹並對DOM樹操做的一種技術。virtual DOM是一個純js對象(字符串對象),因此對他操做會高效。
利用virtual dom,將dom抽象爲虛擬dom,在dom發生變化的時候先對虛擬dom進行操做,經過dom diff算法將虛擬dom和原虛擬dom的結構作對比,最終批量的去修改真實的dom結構,儘量的避免了頻繁修改dom而致使的頻繁的重排和重繪。
/* 根據傳入參數n(數字)對一維數組(純數字)按照距離n最近的順序排序。 (距離即數字與n的差值的絕對值) */
var arr = [7, 28, -1, 0, 7, 33];
function sort(n) {
//your code
}
複製代碼
公司:高思教育
分類:算法
var arr = [7, 28, -28, 0, 7, 33];
function sort(n) {
arr.sort((a, b) => {
return Math.abs(a - n) - Math.abs(b - n);
})
console.log(arr);
}
sort(28);
複製代碼
var nearbySort = function (n, arr) {
var splitedArr = [];
for (var i = 0; i < arr.length; i++) {
splitedArr.push([arr[i]]);
}
while(splitedArr.length > 1) { // 兩兩一組,依次歸併 ~ 遞歸就不用了~
var half = Math.ceil(splitedArr.length / 2);
for(var j = 0; j < half; j++) {
splitedArr[j] = mergeArr(n, splitedArr[j], splitedArr[j + half]);
}
console.log(half, splitedArr);
splitedArr.length = half;
}
return (splitedArr.length === 1) ? splitedArr[0] : [];
}
var getDistance = function (n, m){ // 獲取n 與 m數值差別(絕對值)
return n > m ? (n - m) : (m - n);
}
var mergeArr = function(n, left, right){ // left right爲已排序數組
if (!left) {
return right;
}
if (!right) {
return left;
}
var sortedArr = [], leftIndex = 0, rightIndex = 0, leftLen = left.length, rightLen = right.length;
while(leftIndex < leftLen || rightIndex < rightLen){
var leftNum = left[leftIndex], rightNum = right[rightIndex];
if (leftNum === undefined) {
sortedArr.push(rightNum);
rightIndex++;
} else if (rightNum === undefined) {
sortedArr.push(leftNum);
leftIndex++;
} else {
var leftDistance = getDistance(n, leftNum);
var rightDistanc = getDistance(n, rightNum);
if (leftDistance <= rightDistanc) {
sortedArr.push(leftNum);
leftIndex++;
} else {
sortedArr.push(rightNum);
rightIndex++;
}
}
}
return sortedArr;
}
複製代碼
var getDistance = function (n, m){ // 獲取n 與 m數值差別(絕對值)
return n > m ? (n - m) : (m - n);
}
var nearbySort = function (n, arr) {
var distanceObj = {}, len = arr.length, distance, maxDistance = 0, result = [];
for(var i = 0; i < len; i++){
distance = getDistance(n, arr[i]);
if(distance > maxDistance){
maxDistance = distance;
}
if (distanceObj[distance]) {
distanceObj[distance].push(arr[i]);
} else {
distanceObj[distance] = [arr[i]];
}
}
for(var j = 0; j <= maxDistance; j++) { // 兩層for, 但內層循環數固定, 總循環固定O(n);
if (distanceObj[j]) {
var eachDistance = distanceObj[j];
var eachLength = eachDistance.length;
for(var m = 0; m < eachLength; m++) {
result.push(eachDistance[m])
}
}
}
return result;
}
複製代碼