① 程序前端跟後端有什麼區別呀
前端開發主要做的是用戶所能看到的前端展示界面;後端開發主要做的是邏輯功能等模塊。其實主要區別體現在以下兩個方面:知識結構與實現和工作職責。
1、知識結構
(1)展示的方式不同
前端指的是用戶可見的界面,網站前端頁面也就是網頁的頁面開發,比如網頁上的特效、布局、圖片、視頻,音頻等內容。前端的工作內容就是將美工設計的效果圖的設計成瀏覽器可以運行的網頁,並配合後端做網頁的數據顯示和交互等可視方面的工作內容。
後端是指用戶看不見的東西,通常是與前端工程師進行數據交互及網站數據的保存和讀取,相對來說後端涉及到的邏輯代碼比前端要多得多,後端考慮的是底層業務邏輯的實現,平台的穩定性與性能等。
2、工作職責
前端工程師主要的工作職責分為三大部分,分別是傳統的Web前端開發,移動端開發和大數據呈現端開發。Web前端開發主要針對的是PC端開發任務;
移動端開發則包括Android開發、iOS開發和各種小程序開發,在移動互聯網迅速發展的帶動下,移動端的開發任務量是比較大的,隨著5G標準的落地,未來移動端的開發任務將得到進一步的拓展;大數據呈現則主要是基於已有的平台完成最終分析結果的呈現,呈現方式通常也有多種選擇,比如大屏展示等。
後端工程師的主要職責也集中在三大部分,分別是平台設計、介面設計和功能實現。平台設計主要是搭建後端的支撐服務容器;介面設計主要針對於不同行業進行相應的功能介面設計,通常一個平台有多套介面,就像衛星導航平台設有民用和軍用兩套介面一樣;功能實現則是完成具體的業務邏輯實現。
② 深入理解V8的垃圾回收原理
V8的垃圾回收策略基於分代回收機制,該機制又基於 世代假說 。該假說有兩個特點:
基於這個理論,現代垃圾回收演算法根據對象的存活時間將內存進行了分代,並對不同分代的內存採用不同的高效演算法進行垃圾回收。
在V8中,將內存分為了新生代(new space)和老生代(old space)。它們特點如下:
V8堆的空間等於新生代空間加上老生代空間。我們可以通過 --max-old-space-size 命令設置老生代空間的最大值, --max-new-space-size 命令設置新生代空間的最大值。老生代與新生代的空間大小在程序初始化時設置,一旦生效則不能動態改變。
默認設置下,64位系統的老生代大小為1400M,32位系統為700M。
對於新生代,它由兩個 reserved_semispace_size 組成。每個 reserved_semispace_size 的大小在不同位數的機器上大小不同。默認設置下,在64位與32位的系統下分別為16MB和8MB。我們將新生代、老生代、 reserved_semispace_size 空間大小總結如下表。
在介紹垃圾回收演算法之前,我們先了解一下「全停頓」。垃圾回收演算法在執行前,需要將應用邏輯暫停,執行完垃圾回收後再執行應用邏輯,這種行為稱為 「全停頓」(Stop The World)。例如,如果一次GC需要50ms,應用邏輯就會暫停50ms。
全停頓的目的,是為了解決應用邏輯與垃圾回收器看到的情況不一致的問題。舉個例子,在自助餐廳吃飯,高高興興地取完食物回來時,結果發現自己餐具被服務員收走了。這里,服務員好比垃圾回收器,餐具就像是分配的對象,我們就是應用邏輯。在我們看來,只是將餐具臨時放在桌上,但是服務員看來覺得你已經不需要使用了,因此就收走了。你與服務員對於同一個事物看到的情況是不一致,導致服務員做了與我們不期望的事情。因此,為避免應用邏輯與垃圾回收器看到的情況不一致,垃圾回收演算法在執行時,需要停止應用邏輯。
新生代中的對象主要通過 Scavenge 演算法進行垃圾回收。Scavenge 的具體實現,主要採用了Cheney演算法。
Cheney演算法採用復制的方式進行垃圾回收。它將堆內存一分為二,每一部分空間稱為 semispace。這兩個空間,只有一個空間處於使用中,另一個則處於閑置。使用中的 semispace 稱為 「From 空間」,閑置的 semispace 稱為 「To 空間」。
過程如下:
對象晉升的條件有兩個:
1)對象是否經歷過Scavenge回收。對象從 From 空間復制 To 空間時,會檢查對象的內存地址來判斷對象是否已經經過一次Scavenge回收。若經歷過,則將對象從 From 空間復制到老生代中;若沒有經歷,則復制到 To 空間。
2)To 空間的內存使用佔比是否超過限制。當對象從From 空間復制到 To 空間時,若 To 空間使用超過 25%,則對象直接晉升到老生代中。設置為25%的比例的原因是,當完成 Scavenge 回收後,To 空間將翻轉成From 空間,繼續進行對象內存的分配。若佔比過大,將影響後續內存分配。
對象晉升到老生代後,將接受新的垃圾回收演算法處理。下圖為Scavenge演算法中,對象晉升流程圖。
Scavenge 演算法的缺點是,它的演算法機制決定了只能利用一半的內存空間。但是新生代中的對象生存周期短、存活對象少,進行對象復制的成本不是很高,因而非常適合這種場景。
老生代中的對象有兩個特點,第一是存活對象多,第二個存活時間長。若在老生代中使用 Scavenge 演算法進行垃圾回收,將會導致復制存活對象的效率不高,且還會浪費一半的空間。因而,V8在老生代採用Mark-Sweep 和 Mark-Compact 演算法進行垃圾回收。
Mark-Sweep,是標記清除的意思。它主要分為標記和清除兩個階段。
與 Scavenge 演算法不同,Mark-Sweep 不會對內存一分為二,因此不會浪費空間。但是,經歷過一次 Mark-Sweep 之後,內存的空間將會變得不連續,這樣會對後續內存分配造成問題。比如,當需要分配一個比較大的對象時,沒有任何一個碎片內支持分配,這將提前觸發一次垃圾回收,盡管這次垃圾回收是沒有必要的。
為了解決內存碎片的問題,提高對內存的利用,引入了 Mark-Compact (標記整理)演算法。Mark-Compact 是在 Mark-Sweep 演算法上進行了改進,標記階段與Mark-Sweep相同,但是對未標記的對象處理方式不同。與Mark-Sweep是對未標記的對象立即進行回收,Mark-Compact則是將存活的對象移動到一邊,然後再清理端邊界外的內存。
由於Mark-Compact需要移動對象,所以執行速度上,比Mark-Sweep要慢。所以,V8主要使用Mark-Sweep演算法,然後在當空間內存分配不足時,採用Mark-Compact演算法。
在新生代中,由於存活對象少,垃圾回收效率高,全停頓時間短,造成的影響小。但是老生代中,存活對象多,垃圾回收時間長,全停頓造成的影響大。為了減少全停頓的時間,V8對標記進行了優化,將一次停頓進行的標記過程,分成了很多小步。每執行完一小步就讓應用邏輯執行一會兒,這樣交替多次後完成標記。如下圖所示:
長時間的GC,會導致應用暫停和無響應,將會導致糟糕的用戶體驗。從2011年起,v8就將「全暫停」標記換成了增量標記。改進後的標記方式,最大停頓時間減少到原來的1/6。
垃圾回收的原理較為復雜,在理解上需要花費一些功夫。了解GC原理,有助於我們對Nodejs項目進行性能瓶頸定位與調優。文章所描述的演算法為V8中使用的基礎演算法,現代V8引擎對垃圾回收進行了很多改進,比如,在Chrome 64和Node.js v10中V8啟用了「並行標記」技術,將標記時間縮短了60%~70%。還有「Parallel Scavenger」技術,它將新生代的垃圾回收時間縮短了20%~50%。
垃圾回收是影響服務性能的因素之一,為了提高服務性能,應盡量減少垃圾回收的次數。
V8堆內存最大值在64位系統上為1464MB,在32位系統上為732MB。計算公式如下:
③ 我們還能在哪些方面進行webpack性能優化
Bigo前端組計算平台前端組基於amis框架,參考之前的文章:https%3A%2F%2Fgithub.com%2Fbigo-frontend%2Fblog%2Fissues%2F17 ;有很好的研發效率提升,但是構建速度卻很慢,亟需進行優化。優化之後達到了將webpack構建速度提升80%左右的一個成績,以下是優化前後的對比
團隊做了3件事情來達到這樣的一個效果:
基於這次優化做了功課,看了一些資料,看看還有哪些可以優化的地方。
官網的定義:
webpack is a static mole bundler for modern javaScript applications. When webpack processes your application, it internally builds a dependency graph which maps every mole your project needs and generates one or more bundles.
也就是說 webpack 是一個用於現代 JavaScript 應用程序的靜態模塊打包工具,從入口出發,找到入口文件所有的依賴,生成瀏覽器可以用的bundle文件。webpack的出現使得前端的工程化更加地豐富。從webpack在2013的第一次release(v1.0.0-beta2)開始,至今已經有8、9年的 歷史 了,是一個相當成熟的工具,其生態也比較完善,所以前端圈用webpack也是非常地廣泛。
盡量用較新的版本,新版本相較之前都會有一定的性能提升和優化,包括Node和Webpack。要注意的是 Node.js v8.9.10 - v9.11.1 ES6的 Set 和 Map 會有性能回退問題,現在LTS的node已經是 v14.16.0 ,所以假設 Node 版本已經較新,並且用的是 WP4 ( webpack4 )。目前還不建議對求穩的或者已經很龐大的項目立即升級到 WP5 ,其一是因為webpack生態裡面並不一定所有的插件都能跟的上最新的版本,可能會出現兼容性的問題;其二由於webpack5還並未被廣泛地應用,到新版本的穩定和成熟還是需要一定的時間,為避免不必要的bug,建議暫時使用 webpack4 。
對於開發者來說,每次在build的時候不希望花費較長的時間,優化構建速度能夠減少開發成本;對於用戶而言,優化bundle文件的數量和大小能減少用戶的流失率,提升用戶體驗。所以webpack的性能優化是一個非常關鍵的技術手段。
webpack構建大概可分為 loader解析 -> 依賴搜索 -> 打包 等三個階段,就這三個階段我們分別展開闡述如何去優化。
loader解析:
依賴搜索:
打包: Smaller = Faster
當然需要在 index.html 裡面引入cdn依賴,否則在runtime無法找到相應的模塊: 。
開發環境* *:** 同樣地,生產環境有些配置也不適用於開發環境,比如 TerserPlugin 就不需要,因為在開發環境中壓縮代碼是沒有意義的;devTools的最佳實踐是 eval-cheap-mole-source-map ,我現在的項目比較輕量,但是也能看出對比:
雖然是不到1000ms的差距,蒼蠅肉也是肉不是?而且將來代碼量越來越龐大的時候,差距就更明顯了。
當然還有其他的可以優化的方法,比如使用ES mole,能更好地利用webpack的tree shaking功能;Dll,為更改不頻繁的代碼生成單獨的編譯結果,但卻是一個配置比較復雜的過程;還有對圖片的壓縮等等。以上是對於webpack4性能優化基本的配置,期待webpack5成熟穩定的那一天。
④ nodejs的優勢和劣勢是什麼
nodejs優勢在於原型開發快,學習門檻低,簡單業務運行效率高於java等後台腳本語言的vm。
劣勢同樣明顯,javascript引擎基於事件的函數回調模型既是優勢又是劣勢:導致復雜邏輯失控,不能用於生產環境。此架構並非新興事物,早年的windows 3的消息模型類似:所謂的協同式多任務(不展開)。最終不得不讓步於搶佔多任務。導致入門易,優化難,難調試,大型業務框架不易成型
⑤ Web前端性能優化的實用技巧匯總
今天小編要跟大家分享的文章是關於Web前端性能優化的實用技巧匯總。javascript在瀏覽器中運行的性能,可以認為是開發者所面臨的最嚴重的可用性問題。這個問題因為javascript的阻塞性而變得復雜,事實上,多數瀏覽器使用單一進程來處理用戶界面和js腳本執行,所以同一時刻只能做一件事。js執行過程耗時越久,瀏覽器等待響應的時間越長。
一.提高載入性能
1.IE8,FF,3.5,Safari4和Chrome都允許並行下載js文件,當script下載資源時不會阻塞其他script的下載。但是js下載仍然會阻塞其他資源的下載,如圖片。盡管腳本下載不會互相影響,但頁面仍然必須等待所有js代碼下載並執行完才能繼續。因此仍然存在腳本阻塞問題.推薦將所有js文件放在body標簽底部以減少對整個頁面的影響。
2.減少頁面外鏈腳本文件的數量將會提高頁面性能:
http請求會帶來額外的開銷,因此下載單個300k的文件將比下載10個30k的文件效率更高。
3.動態腳本載入技術:
無論何時啟動下載,文件的下載和執行都不會阻塞頁面其他進程。
functionlaodScript(url,callback){
varscript=document.createElement('script');_
_cript.type='text/javascript'__f(script.readyState){//ie
____cript.onreadystatechange=function(){_____
if(script.readyState=='loaded'||script.readyState=='complete'){_______
_cript.onreadystatechange=null;_______
callback()_____
____
__
}else{//其他瀏覽器___
script.onload=function(){_____
_allback()
___}_
}_
script.src=url;_
document.getElementsByTagName('head')[0].appendChild(script);
}
//使用
loadScript('./a.js',function(){_
loadScript('./b.js',function(){___
loadScript('./c.js',function(){_____
console.log('載入完成')___
})_
})
})
4.無阻塞載入類庫——LABjs,使用方法如下:
//鏈式調用時文件逐個下載,.wait()用來指定文件下載並執行完畢後所調用的函數
$LAB.script('./a.js')_
.script('./b.js')_
.wait(function(){__
_pp.init();
})
//為了保證執行順序,可以這么做,此時a必定在b前執行
$LAB.script('./a.js').wait()_
.script('./b.js')_
.wait(function(){___
_pp.init();
})
二.數據存取與JS性能
1.在js中,數據存儲的位置會對代碼整體性能產生重大影響。數據存儲共有4種方式:字面量,變數,數組項,對象成員。他們有著各自的性能特點。
2.訪問字面量和局部變數的速度最快,相反,訪問數組和對象相對較慢
3.由於局部變數存在於作用域鏈的起始位置,因此訪問局部變數的比訪問跨域作用變數更快
4.嵌套的對象成員會明顯影響性能,應盡量避免
5.屬性和方法在原型鏈位置越深,訪問他的速度越慢
6.通常我們可以把需要多次使用的對象成員,數組元素,跨域變數保存在局部變數中來改善js性能
三.DOM編程
1.訪問DOM會影響瀏覽器性能,修改DOM則更耗費性能,因為他會導致瀏覽器重新計算頁面的幾何變化。<通常的做法是減少訪問DOM的次數,把運算盡量留在JS這一端。
註:如過在一個對性能要求比較高的操作中更新一段HTML,推薦使用innerHTML,因為它在絕大多數瀏覽器中運行的都很快。但對於大多數日常操作而言,並沒有太大區別,所以你更應該根據可讀性,穩定性,團隊習慣,代碼風格來綜合決定使用innerHTML還是createElement()
2.HTML集合優化
HTML集合包含了DOM節點引用的類數組對象,一直與文檔保持連接,每次你需要最新的信息時,都會重復執行查詢操作,哪怕只是獲取集合里元素的個數。
①_優化一——集合轉數組collToArr
functioncollToArr(coll){_
for(vari=0,a=[],len=coll.length;i
a._ush(coll[i]);
__
returna
}
②緩存集合length
③訪問集合元素時使用局部變數(即將重復的集合訪問緩存到局部變數中,用局部變數來操作)
3.遍歷DOM
①使用只返回元素節點的API遍歷DOM,因為這些API的執行效率比自己實現的效率更高:
td{border:1pxsolid#ccc;padding:5px;margin:auto;}
td>p{text-align:left;}
td>pspan{text-align:center;display:block;}
屬性名
被替代屬性
children
childNodes
childElementCount
childNodes.length
firstElementChild
firstChild
lastElementChild
lastChild
nextElementSibling
nextSibling
previousElementSibling
previousSibling
_諮≡衿_PI——querySelectorAll()
querySelectorAll()方法使用css選擇器作為參數並返回一個NodeList——包含著匹配節點的類數組對象,該方法不會返回HTML集合,因此返回的節點不會對應實時文檔結構,著也避免了HTML集合引起的性能問題。
let_rr=_ocument.querySelectorAll('div.warning,_iv.notice>_')
4.重繪和重排
瀏覽器在下載完頁面的所有組件——html,js,css,圖片等之後,會解析並生成兩個內部數據結構——_OM樹,渲染樹.一旦DOM樹和渲染樹構建完成,瀏覽器就開始繪制頁面元素(paint).
①重排發生的條件:
添加或刪除可見的DOM元素位置變化元素尺寸改變內容改變頁面渲染器初始化瀏覽器窗口尺寸變化出現滾動條時會觸發整個頁面的重排_嘏瘧囟ㄖ鞀
5.渲染樹變化的排列和刷新
大多數瀏覽器通過隊列化修改並批量執行來優化重排過程,然而獲取布局信息的操作會導致隊列強制刷新。
offsetTop,offsetWidth...
scrollTop,scrollHeight...
clientTop,clientHeight...
getComputedStyle()
一些優化建議:將設置樣式的操作和獲取樣式的操作分開:
//設置樣式
body.style.color='red'
body.style.fontSize=པpx'
//讀取樣式
letcolor=body.style.color
let_ontSize=_ody.style.fontSize
另外,獲取計算屬性的兼容寫法:
functiongetComputedStyle(el){_
varcomputed=(document.body.currentStyle?el.currentStyle:document.defaultView.getComputedStyle(el,'');_
returncomputed
}
6.最小化重繪和重排
①.批量改變樣式
/*使用cssText
*/el.style.cssText='border-left:1px;_order-right:2px;_adding:20px'
②.批量修改dom的優化方案——使元素脫離文檔流-對其應用多重改變-把元素帶迴文檔
functionappendDataToEl(option){
vartargetEl=option.target||document.body,___
createEl,___
data=option.data||[];_//讓容器脫離文檔流,減少重繪重排_
vartargetEl_display=targetEl.style.display;_
targetEl.style.display='none'
_
//*****創建文檔片段來優化Dom操作****_
varfragment=document.createDocumentFragment();_//給元素填充數據_
for(vari=0,max=data.length;i
createEl=
document.createElement(option.createEl);___
for(varitemindata[i]){_____
if(item.toString()==='text'){_______
createEl.appendChild(document.createTextNode(data[i][item]));________ontinue;___________
_f(item.toString()==='html'){_______
createEl.innerHTML=item,data[i][item];_______
continue;_____
}_____
_reateEl.setAttribute(item,data[i][item]);_______
//****將填充好的node插入文檔片段****___
fragment.appendChild(createEl);___
//****將文檔片段統一插入目標容器****_
targetEl.appendChild(fragment);_
//顯示容器,完成數據填充_
targetEl.style.display=
targetEl_display;
}
//使用
varwrap=document.querySelectorAll('.wrap')[0];
vardata=[_
_name:'xujaing',text:'選景',title:'xuanfij'},_
{name:'xujaing',text:'選景',title:'xuanfij'},_
{name:'xujaing',text:'選景',title:'xuanfij'}];
appendDataToEl({_
target:wrap,_
createEl:'div',
_ata:data
});
上面的優化方法使用了文檔片段:_蔽頤前鹽牡燈尾迦氳澆詰闃惺保導噬媳惶砑擁鬧皇歉悶蔚淖詠詰悖皇瞧偽舊懟?梢允溝_om操作更有效率。
②.緩存布局信息
//緩存布局信息
letcurrent=el.offsetLeft;
current++;
el.style.left=current+'px'
if(current>300){_
stop();
}
④.慎用:hover
如果有大量元素使用:hover,那麼會降低相應速度,CPU升高
⑤.使用事件委託(通過事件冒泡實現)來減少事件處理器的數量,減少內存和處理時間
functiondelegation(e,selector,callback){_
e=e||window.event;_
vartarget=e.target||e.srcElement;
_if(target.nodeName!==selector||
target.className!==selector||target.id!==selector){___
return;
_}_
if(typeofe.preventDefault==='function'){__
_.preventDefault();___
e.stopPropagation();
}else{___
e.returnValue=false;
e.cancelBubble=true;_
}
__allback()}
四.演算法和流程式控制制
1.循環中減少屬性查找並反轉(可以提升50%-60%的性能)
//for循環
for(vari=item.length;i--){_
process(item[i]);
}
//while循環
varj=item.length;
while(j--){_
process(item[i]);
}
2.使用Duff裝置來優化循環(該方法在後面的文章中會詳細介紹)
3.基於函數的迭代(比基於循環的迭代慢)
items.forEach(function(value,index,array){__rocess(value);})
4.通常情況下switch總比if-else快,但是不是最佳方案
五.字元串和正則表達式
1.除了IE外,其他瀏覽器會嘗試為表達式左側的字元串分配更多的內存,然後簡單的將第二個字元串拷貝到他的末尾,如果在一個循環中,基礎字元串位於最左側,就可以避免重復拷貝一個逐漸變大的基礎字元串。2.使用[sS]來匹配任意字元串3.去除尾部空白的常用做法:
if(!String.prototype.trim){_
String.prototype.trim=function(){___
returnthis.replace(/^s+/,'').replace(/ss*$/,'')_
}
}
六.快速響應的用戶界面
1.瀏覽器的UI線程:用於執行javascript和更新用戶界面的進程。
2.在windows系統中定時器解析度為15毫秒,因此設置小於15毫秒將會使IE鎖定,延時的最小值建議為25ms.
3.用延時數組分割耗時任務:
functionmultistep(steps,args,callback){_
vartasks=steps.concat();
__etTimeout(function(){___
vartask=tasks.shift();___
task.apply(null,args||[]);_//調用Apply參數必須是數組
___
if(tasks.length>0){_____
setTimeout(arguments.callee,25);
___else{_____
_allback();___
__
},25);
}
4.記錄代碼運行時間批處理任務:
functiontimeProcessArray(items,process,callback){_
vartodo=item.concat();__etTimeout(function(){___
varstart=+newDate();
__o{_____
_rocess(todo.shift());___
}while(todo.length>0&&(+newDate()-start<50));
____f(todo.length>0){_____
_etTimeout(arguments.callee,25);
___else{____
_allback(items);_
}_
_,25)
}
5.使用WebWorker:它引入了一個介面,能使代碼運行且不佔用瀏覽器UI線程的時間。一個Worker由如下部分組成:
①一個navigator對象,包括app