聊聊Web程序員所具有的基本技能
前言:同步一篇2017年1月在QQ空間寫(xiě)的隨筆,2017年,我還在實(shí)習(xí),現(xiàn)在回首,已經(jīng)好幾年過(guò)去,現(xiàn)在看起來(lái),還挺有意思的。
web相對(duì)于其他方向來(lái)說(shuō),會(huì)簡(jiǎn)單點(diǎn),但是涉及的東西會(huì)多點(diǎn),如今的web程序員,所需的基本技能應(yīng)該有三劍客,nodejs,php,mysql,http系列協(xié)議,網(wǎng)絡(luò),瀏覽器基本原理,apache/nginx,其中包括幾十個(gè)javascript庫(kù)和幾百個(gè)框架,沒(méi)準(zhǔn)還需要個(gè)shell和linux常用命令,如果這些都涉獵過(guò)了,沒(méi)事,面試人家還會(huì)問(wèn)各種源碼,原理,機(jī)制有沒(méi)有。不過(guò)需要的技能其實(shí)很大程度上依賴(lài)于公司,具體來(lái)說(shuō)應(yīng)該有這么幾種情況 ,參與小系統(tǒng)或者內(nèi)部系統(tǒng)的開(kāi)發(fā)時(shí),前端程序員其實(shí)就是個(gè)web程序員,需要自己建表,寫(xiě)服務(wù)端邏輯,前端邏輯,寫(xiě)頁(yè)面樣式,交互,俗稱(chēng)全棧,如果有個(gè)后臺(tái)和你搭配,或許你可以免去寫(xiě)后端的代碼。參與中型系統(tǒng)開(kāi)發(fā)時(shí),前端程序員需要做的是寫(xiě)交互,頁(yè)面和樣式有重構(gòu),前端只需要處理用戶(hù)操作然后和后臺(tái)搞好接口就行,參加大型系統(tǒng)開(kāi)發(fā)時(shí),前端程序員還需要寫(xiě)中間轉(zhuǎn)發(fā)層,因?yàn)榍岸擞袀€(gè)同源策略,對(duì)于一般的系統(tǒng),整個(gè)系統(tǒng)都是由一個(gè)團(tuán)隊(duì)負(fù)責(zé)的,前后端的代碼再怎么分離,也還是有辦法解決同源問(wèn)題,但是大型系統(tǒng)的后臺(tái)有時(shí)候并不是由一個(gè)團(tuán)隊(duì)負(fù)責(zé)的,這時(shí)候,前端看到的就是一個(gè)黑盒子,我們只是根據(jù)后臺(tái)給的接口,把前端的數(shù)據(jù)傳到我們的后臺(tái),也就是中間層(中間層可以是nodejs或者php來(lái)做),然后在后臺(tái)去訪問(wèn)別的后臺(tái),而且后面可能有好幾十個(gè)后臺(tái)串起來(lái),這時(shí)候就沒(méi)有什么前端和后臺(tái)了,或許叫客戶(hù)端和服務(wù)端更加好,利用中間層,我們解決了同源問(wèn)題,但是中間層不僅僅是為了解決同源問(wèn)題?,F(xiàn)在很多公司的系統(tǒng)都拆成服務(wù)化,比如做一個(gè)登錄的子系統(tǒng),然后一大波平臺(tái)都統(tǒng)一去那校驗(yàn)登錄態(tài)。中間層也是為了業(yè)務(wù)的接入。所以,參與開(kāi)發(fā)的系統(tǒng)不同,所需要的技能也不一樣。
但是,作為web程序員,最核心的應(yīng)該還是javascript,javascript這門(mén)語(yǔ)言,套路很深,而且和很多傳統(tǒng)的語(yǔ)言差別很大,無(wú)論是鼻祖c語(yǔ)言,還是各種面向?qū)ο蟮恼Z(yǔ)言,我覺(jué)得函數(shù)里分配的變量在函數(shù)執(zhí)行完還能被訪問(wèn),這個(gè)點(diǎn)就很不一樣,javascript的知識(shí)點(diǎn)不算是很多,核心的大概有詞法作用域,原型鏈,this問(wèn)題,bind/call/apply系列函數(shù),閉包,變量提升,正則,函數(shù)是一等公民,變量對(duì)象,執(zhí)行上下文,arguments,setTimeout系列函數(shù)等,因?yàn)橛欣厥掌?,?nèi)存管理這個(gè)大難題我們就不需要過(guò)于擔(dān)心了,但是javascript的知識(shí)只是前端的其中一部分,接下來(lái)的還有BOM和DOM的知識(shí)還有API非常多,雖然常用的都不會(huì)很難,但是一不小心還是會(huì)踩坑,高級(jí)點(diǎn)的還有v8的一些知識(shí),包括javascript編譯和執(zhí)行的過(guò)程原理,js引擎是單線程,瀏覽器是多線程,事件驅(qū)動(dòng)模型,事件隊(duì)列,頁(yè)面加載和渲染原理,瀏覽器的工作原理。
網(wǎng)絡(luò)協(xié)議也是很重要的一部分,應(yīng)用層現(xiàn)在說(shuō)的基本都是http1.1,https和http2,http2的前身spyd已經(jīng)在chrome里部署,http2目測(cè)也要來(lái)臨了,不過(guò)現(xiàn)在討論比較多的還是https,畢竟安全也是很重要的, http1.1主要需要搞一下rfc文檔和http權(quán)威指南就功力大增了。http協(xié)議的核心知識(shí)應(yīng)該有緩存機(jī)制,常見(jiàn)的請(qǐng)求方法,cookie機(jī)制,持久連接和管道化機(jī)制,常見(jiàn)的http響應(yīng)碼,請(qǐng)求頭,響應(yīng)頭的意義。還包括一些安全的,協(xié)議升級(jí)協(xié)商,解決跨域的頭部。https協(xié)議的知識(shí)包括ssl握手機(jī)制和過(guò)程,connect方法實(shí)現(xiàn)https流量的轉(zhuǎn)發(fā),https服務(wù)器的搭建。http2加了很多高大上的特性,提供了很多新的功能和對(duì)http1.1的性能進(jìn)行了優(yōu)化。tcp/ip,dns協(xié)議也是前端需要學(xué)習(xí)的知識(shí),瀏覽器對(duì)域名進(jìn)行解析或預(yù)解析時(shí)需要使用dns協(xié)議。需要了解瀏覽器解析域名的機(jī)制,比如瀏覽器里有對(duì)域名的解析進(jìn)行緩存,這會(huì)導(dǎo)致使用dns服務(wù)器進(jìn)行負(fù)載均衡時(shí)達(dá)不到效果,host文件里也有域名的映射,利用host文件可以配置不同的域名指向127.0.0.1來(lái)進(jìn)行同源策略的學(xué)習(xí),然后dns服務(wù)器自然也有緩存或者域名對(duì)應(yīng)的ip,最后還需要了解dns服務(wù)器進(jìn)行域名解析的機(jī)制,這些在計(jì)算機(jī)網(wǎng)絡(luò)里都會(huì)說(shuō)到。tcp/ip協(xié)議里,前端的知識(shí)里似乎沒(méi)有太多涉及到ip協(xié)議的,tcp卻是很重要的一部分,tcp協(xié)議是ssl和http協(xié)議的基礎(chǔ),后者都是需要在建立tcp連接的基礎(chǔ)上進(jìn)行工作的,理解tcp協(xié)議對(duì)前端的優(yōu)化有很大幫助。
瀏覽器基本工作原理是前端比較重要的一塊,webkit內(nèi)部這本書(shū)講了很多這方面的知識(shí),而且也很全面, 瀏覽器工作原理包括域名解析和緩存,tcp連接的管理,cookie管理,同源策略機(jī)制,緩存管理,頁(yè)面代碼的分析,布局計(jì)算,渲染,js引擎和渲染引擎的互斥工作,dom樹(shù)和cssom樹(shù)的構(gòu)建,dns預(yù)解析,頁(yè)面預(yù)渲染,tcp預(yù)連接,其實(shí)js引擎是單線程和瀏覽器是多線程這個(gè)知識(shí)也比較重要,這個(gè)對(duì)理解為什么大量的cpu密集型的計(jì)算會(huì)導(dǎo)致瀏覽器掛掉。為什么js引擎正在執(zhí)行代碼時(shí),點(diǎn)擊了按鈕,點(diǎn)擊事件的回調(diào)還可以被會(huì)加到事件隊(duì)列里。還有setTimeout(0)的原理有很大的幫助。瀏覽器什么時(shí)候會(huì)進(jìn)行布局計(jì)算和重繪影響我們?nèi)绾螌?xiě)代碼,以免引起性能問(wèn)題。輸入url時(shí),瀏覽器發(fā)生了什么,這個(gè)面試題基本是前端面試必問(wèn)??梢?jiàn)瀏覽器的原理也是需要了解的。
代理/web服務(wù)器也是比較重要的一部分,因?yàn)榇蟛糠值膶?duì)外系統(tǒng),因?yàn)榘踩阅艿葐?wèn)題,都是有代理的,squid和nginx是比較重要的代理服務(wù)器,squid是緩存代理,是對(duì)靜態(tài)資源進(jìn)行緩存以提高性能,nginx是神器,功能太多,接觸到的都是做反向代理的,nginx的代碼和架構(gòu)都是享有美譽(yù)的,看代碼是理解軟件的最好方式,作為一個(gè)前端,其實(shí)去深入研究nginx代碼或許暫時(shí)沒(méi)有必要,但是大概看一些工作原理和代碼的設(shè)計(jì)架構(gòu),對(duì)自己的水平和對(duì)nginx的工作原理都是有好處的,tnginx是淘寶基于nginx開(kāi)發(fā)的一個(gè)服務(wù)器,他們把研究的思路和心得都發(fā)布到他們的網(wǎng)站上,有興趣的可以去瞅瞅,理解了nginx的基本工作原理,nodejs和nginx都是用了一種神技,就是事件驅(qū)動(dòng)模型+單線程,應(yīng)對(duì)網(wǎng)絡(luò)高并發(fā)無(wú)壓力,和傳統(tǒng)的一言不合就開(kāi)進(jìn)程和線程不同,進(jìn)程線程的切換花銷(xiāo)很大,而且同步互斥問(wèn)題很復(fù)雜,事件驅(qū)動(dòng)是通過(guò)注冊(cè)事件,事件發(fā)生時(shí)把回調(diào)扔到事件隊(duì)列里去排隊(duì)執(zhí)行,單線程就去不斷輪詢(xún)事件隊(duì)列,執(zhí)行回調(diào),其實(shí)js也是這樣。但這種模式不適合cpu密集型的應(yīng)用,因?yàn)閱尉€程,傷不起。nginx的功能很多,可以做代理服務(wù)器,也可以做web服務(wù)器(比如配置一個(gè)https服務(wù)器),郵件服務(wù)器,緩存服務(wù)器等等,使用nginx主要是需要學(xué)會(huì)使用nginx.conf。至于參數(shù)調(diào)優(yōu),開(kāi)發(fā)自己的模塊,這都是高級(jí)話題。說(shuō)到服務(wù)器,誰(shuí)敢不說(shuō)老前輩apache,apache一般作為web服務(wù)器使用,管理靜態(tài)頁(yè)面和和后端的cgi程序通信,把前端的請(qǐng)求分配給cgi程序處理,一般使用的是php,apache一般是以一種預(yù)先派生進(jìn)程/線程的方式工作,這樣前端的請(qǐng)求來(lái)的時(shí)候,就不需要急忙地創(chuàng)建進(jìn)程/線程,來(lái)一個(gè)請(qǐng)求,從線程池里拿出一個(gè)進(jìn)程/線程,所以高并發(fā)會(huì)爆,使用apache一般也是熟悉httpd.conf文件,里面包括配置虛擬主機(jī),需要監(jiān)聽(tīng)的ip和端口,每個(gè)目錄的權(quán)限控制,文件權(quán)限控制,url重寫(xiě),打log,對(duì)靜態(tài)文件進(jìn)行緩存管理,配置一些http頭,壓縮,加載相應(yīng)的模塊等,和nginx類(lèi)似但是語(yǔ)法不太一樣。
后端,作為一個(gè)前端,有時(shí)候也需要知道后端的知識(shí),因?yàn)槟悴恢朗裁磿r(shí)候你需要去寫(xiě)后端,學(xué)習(xí)點(diǎn)后端不至于到時(shí)候一臉懵逼,后端現(xiàn)在一般是使用nodejs和php,python也有人用,很多人說(shuō)對(duì)于前端來(lái)說(shuō),學(xué)習(xí)nodejs可能不是很難,但其實(shí)也不是這么簡(jiǎn)單,至少不會(huì)比php簡(jiǎn)單,因?yàn)閷?duì)于lamp下的網(wǎng)站,前端發(fā)請(qǐng)求到apache,然后apache把請(qǐng)求扔給php,php處理,最多連個(gè)數(shù)據(jù)庫(kù),比較簡(jiǎn)單,但是nodejs就不一樣了,因?yàn)閚odejs是集web服務(wù)器和應(yīng)用服務(wù)器與一身的,你用nodejs可以直接監(jiān)聽(tīng)一個(gè)端口,這就是一個(gè)服務(wù)器,不需要像apache和nginx那樣配置什么,然后你前端的請(qǐng)求到nodejs后,nodejs會(huì)執(zhí)行相應(yīng)的回調(diào),這里有很多東西需要自己去做,而且nodejs的框架如express基本沒(méi)什么功能,需要安裝一大堆中間件去幫你干活。不像php那些框架,什么都幫你做好了,像在php里上傳一個(gè)文件,用$_FILES就可以拿到文件的內(nèi)容和信息,在nodejs里你首先要百度一波,然后找一個(gè)希望沒(méi)把你折磨死的中間件,才能完成這個(gè)功能,再比如前端傳的查詢(xún)字符串,在php里可以直接根據(jù)鍵值拿到相應(yīng)的值,在nodejs甚至express里,你得到的只是一個(gè)字符串,然后你自己去解析,或者你用中間件去解析,在express的早期版本里,前端post一個(gè)嵌套了兩層的對(duì)象過(guò)去,在express里第二層的對(duì)象直接變成數(shù)組了,差點(diǎn)沒(méi)嚇?biāo)牢?,原因就是早期的中間件功能還不夠牛逼,所以對(duì)于這種框架,除非你牛逼,自己寫(xiě)中間件,要不然你就是等著那些大牛寫(xiě)中間件給你用的處境,這只是說(shuō)一下nodejs的學(xué)習(xí)成本并不低,現(xiàn)在nodejs和相關(guān)的框架已經(jīng)進(jìn)化得很好,是比較熱門(mén)的一個(gè)技術(shù),因?yàn)閚odejs的語(yǔ)法是基于js的,所以也比較適合前端程序員學(xué)習(xí),只是我們?cè)趎odejs里,有時(shí)候我們可能需要比在php里做的更多。這差不多就是nodejs給我們最原始的玩意,讓我們自己隨心所欲去做我們想做的事情。不像python,能用一句代碼解決的事情,就不要用兩句。nodejs的相關(guān)知識(shí)點(diǎn)很多,畢竟功能比較強(qiáng)大,常用的大概包括進(jìn)程相關(guān)的,文件系統(tǒng)相關(guān)的,處理網(wǎng)絡(luò)請(qǐng)求相關(guān)的,字符串處理相關(guān)的。nodejs的運(yùn)行原理和架構(gòu)也需要去學(xué)習(xí),因?yàn)檫@樣才能用好用對(duì)nodejs。如果使用express的話可以去瞅瞅他的源碼,源碼不是很多,因?yàn)樗强看罅康闹虚g件來(lái)工作的,其中路由的源碼是比較核心的,路由也是express里很重要的功能,這里的路由不是前端請(qǐng)求最后怎么分發(fā)到具體的業(yè)務(wù)代碼里,指的是對(duì)于每一個(gè)前端請(qǐng)求,在express里是怎么被處理的。express的路由思想看起來(lái)不是很難,不過(guò)很有意思,類(lèi)似于nginx的請(qǐng)求被處理的過(guò)程,對(duì)于每一個(gè)進(jìn)來(lái)的請(qǐng)求,被串行地傳到一個(gè)個(gè)函數(shù)或者模塊里進(jìn)行處理,然后決定要傳給下一個(gè)還是結(jié)束這個(gè)請(qǐng)求,nginx里也是這么一個(gè)過(guò)程,有點(diǎn)像設(shè)計(jì)模式里的責(zé)任鏈模式。最后express里的很多package源碼非常值得研究,在npm官網(wǎng)的里很多源碼包也值得研究,對(duì)js的功力提升有很多的幫助。
數(shù)據(jù)庫(kù),前端其實(shí)很少會(huì)接觸到數(shù)據(jù)庫(kù),基本上就是設(shè)計(jì)一些簡(jiǎn)單的表,然后進(jìn)行增刪改查,需要了解一些sql, 對(duì)于數(shù)據(jù)庫(kù),其實(shí)也是基于一個(gè)客戶(hù)端,服務(wù)器模型,我們的cgi程序去連接遠(yuǎn)端的數(shù)據(jù)庫(kù),也就是進(jìn)程間的通信,只是有時(shí)候是不同的主機(jī)間的,連接數(shù)據(jù)庫(kù),基本的參數(shù)是數(shù)據(jù)庫(kù)進(jìn)程所在的主機(jī)ip,端口(默認(rèn)是3306),數(shù)據(jù)庫(kù)名,用戶(hù)名,密碼。數(shù)據(jù)庫(kù)也是一個(gè)復(fù)雜的玩意,比如sql的解析和優(yōu)化,連接管理,對(duì)數(shù)據(jù)的緩存和管理。
安全也是web程序員很重要的一部分知識(shí),和前端相關(guān)的大概有xss,csrf,點(diǎn)擊劫持,和后端相關(guān)的大概有sql注入,http頭注入,會(huì)話劫持,文件上傳。xss主要是提交和顯示的時(shí)候需要對(duì)內(nèi)容進(jìn)行轉(zhuǎn)義,csrf主要是利用csrfCode來(lái)確認(rèn)某個(gè)請(qǐng)求是否是用戶(hù)自愿發(fā)出的,點(diǎn)擊劫持遇到不是很多,有一個(gè)方法是引用iframe嵌入第三方網(wǎng)站的界面,然后隱藏自己的界面進(jìn)行劫持用戶(hù)的輸入和點(diǎn)擊。這種可以使用http頭禁止自己的網(wǎng)頁(yè)被嵌到別的網(wǎng)頁(yè)里,或者在前端代碼里進(jìn)行判斷跳轉(zhuǎn),后端的安全主要是需要對(duì)用戶(hù)的輸入進(jìn)行過(guò)濾,會(huì)話劫持可以修改php配置文件解決,文件上傳沒(méi)有接觸過(guò)。前端安全中最重要的應(yīng)該屬于同源策略了,現(xiàn)在增加了很多新特性來(lái)防止各種安全問(wèn)題,包括一些http頭來(lái)定義返回的html文檔里資源訪問(wèn)的限制,和對(duì)iframe標(biāo)簽里的文檔進(jìn)行權(quán)限控制。還有一個(gè)是之前比較出名的a標(biāo)簽的target='_blank'的安全問(wèn)題。
設(shè)計(jì)模式也是一個(gè)重要的部分,寫(xiě)代碼一開(kāi)始是先隨便寫(xiě),實(shí)現(xiàn)功能第一,漸漸地,就需要開(kāi)始考慮怎樣寫(xiě)好代碼,怎么寫(xiě)比較好,怎么組織比較好,有時(shí)候,有些設(shè)計(jì)模式的確會(huì)很優(yōu)雅地解決一些問(wèn)題,代碼的重用和可維護(hù)性對(duì)于自己和別人都是很重要的。提高寫(xiě)代碼的方法,不僅需要看書(shū)學(xué)習(xí),也需要不斷琢磨,思考,還需要不斷地看別人的代碼和一些寫(xiě)得比較好的源碼。因?yàn)槿缃竦那岸瞬辉偈且郧暗膸讉€(gè)交互,現(xiàn)在的前端已經(jīng)變得越來(lái)越復(fù)雜,所以寫(xiě)可持續(xù)發(fā)展的代碼變得很重要。
函數(shù)庫(kù),框架和軟件是用來(lái)提高工作和學(xué)習(xí)效率的,web程序員的常用軟件大概有fiddler神器,wireshark神器,whistle還有瀏覽器f12出來(lái)的各種tab,隨著瀏覽器原生地支持很多功能,有些庫(kù)的功能可能會(huì)漸漸變得沒(méi)必要,現(xiàn)在各大公司已經(jīng)漸漸放棄作死的ie,不再支持8以下的版本,很多兼容性問(wèn)題漸漸也會(huì)消失。剩下的一堆打包壓縮工具就沒(méi)啥好說(shuō)的了。
工作其實(shí)最主要的技能是學(xué)習(xí)能力和解決問(wèn)題能力,學(xué)習(xí)能力是必不可少的,學(xué)習(xí)一個(gè)新東西時(shí),首先看他的文檔先使用它,然后再看他的源碼,因?yàn)檫@樣才能真正了解他的本質(zhì),這是有必要的,即使像nodejs的源碼,雖然不可能完全看懂,但是大致的脈搏還是需要摸清一下的,至少我覺(jué)得這樣用起來(lái)才會(huì)安心,舒服。當(dāng)擁有一定的知識(shí)后,遇到問(wèn)題怎么解決,這也是一個(gè)重要的部分,控球好的人不一定就會(huì)過(guò)人,做一個(gè)需求,有時(shí)候可能只是改一點(diǎn)點(diǎn)東西,但是在哪改,改完會(huì)不會(huì)對(duì)之前的功能有影響。所以改一點(diǎn)點(diǎn)東西的需求,首先你要去看別人的一堆代碼,而這堆代碼還不一定是在同一個(gè)地方,甚至不在一個(gè)系統(tǒng)里, 但是這種問(wèn)題始終需要解決,但這種困難就像看英文文檔一樣,一開(kāi)始是會(huì)很痛苦,但是慢慢地,你就會(huì)習(xí)慣這種情況,并且潛移默化中提升了自己的能力。還有一種需求是開(kāi)發(fā)一個(gè)完全新的功能,這時(shí)候你不需要看別人的代碼了,所以這時(shí)候你就可以考慮,琢磨你該如何組織和編寫(xiě)自己的代碼,使得看起來(lái)很舒服,維護(hù)和擴(kuò)展也很容易,這無(wú)形中又提高了自己,工作的勞累和壓力其實(shí)不僅僅來(lái)源于工作本身的內(nèi)容,更來(lái)源于你無(wú)時(shí)無(wú)刻都需要思考,很多事情,只有細(xì)細(xì)考慮,慢慢琢磨才能做好,做對(duì),這會(huì)大量消耗你的腦力,所以做技術(shù)費(fèi)腦,有時(shí)候并不一定是說(shuō)你要多聰明,而是你要一直保持你大腦的效率和清晰。
最后想說(shuō)的是,其實(shí)做技術(shù)還是需要找到自己的興趣和方向,抓住重點(diǎn)去 學(xué)習(xí),現(xiàn)在技術(shù)更新太快,枝繁葉茂,難免會(huì)有點(diǎn)方,但是還是需要按照自己的計(jì)劃慢慢去學(xué),不需要太急躁!多去學(xué)一些本質(zhì)的知識(shí),因?yàn)橹挥羞@些是不會(huì)變的,比如es6的class,其實(shí)只是一種語(yǔ)法糖,他的背后也還是以前的原型鏈知識(shí)?,F(xiàn)在的開(kāi)源社區(qū)非常活躍,你在上面其實(shí)會(huì)發(fā)現(xiàn)很多很好的代碼和很厲害的人,但是我覺(jué)得應(yīng)該學(xué)習(xí)他們,而不是給自己不好的心理壓力。蘇軾說(shuō)的對(duì),早年讀書(shū)無(wú)甚解,晚年省事有奇功,學(xué)習(xí)的時(shí)候,很多東西你覺(jué)得沒(méi)用的,其實(shí)在一定程度上他已經(jīng)提高了你。