前後端分離,不分離簡介
前言
前後端分離已成為網際網路專案開發的業界標準使用方式,通過nginx+tomcat的方式(也可以中間加一個nodejs)有效的進行解耦,並且前後端分離會為以後的大型分散式架構、彈性計算架構、微服務架構、多端化服務(多種客戶端,例如:瀏覽器,車載終端,安卓,IOS等等)打下堅實的基礎。這個步驟是系統架構從猿進化成人的必經之路。
核心思想是前端html頁面通過ajax呼叫後端的restuful api介面並使用json資料進行互動。
名詞解釋
Web伺服器
/*
一般指像Nginx,apache這類的伺服器,他們一般只能解析靜態資源.
*/
應用伺服器
/*
一般指像tomcat,jetty,resin這類的伺服器可以解析動態資源也可以解析靜態資源,但解析靜態資源能力沒有web伺服器好.
*/
未分離時代(各種耦合)
早期主要使用MVC框架,Jsp+Servlet的結構圖如下
大致就是所有的請求都被髮送給作為控制器的Servlet,它接受請求,並根據請求資訊將它們分發給適當的JSP來響應。同時,Servlet還根據JSP的需求生成JavaBeans的例項並輸出給JSP環境。JSP可以通過直接呼叫方法或使用UseBean的自定義標籤得到JavaBeans中的資料。需要說明的是,這個View還可以採用 Velocity、Freemaker 等模板引擎。使用了這些模板引擎,可以使得開發過程中的人員分工更加明確,還能提高開發效率。
那麼,在這個時期,開發方式有如下兩種:
方式一
方式二
方式二已經逐漸淘汰。主要原因有兩點:
/*
1)前端在開發過程中嚴重依賴後端,在後端沒有完成的情況下,前端根本無法幹活;
2)由於趨勢問題,會JSP,懂velocity,freemarker等模板引擎的前端越來越少;
因此,方式二逐漸不被採用。然而,不得不說一點,方式一,其實很多小型傳統軟體公司至今還在使用。那麼,方式一和方式二具有哪些共同的缺點呢?
1、前端無法單獨除錯,開發效率低;
2、 前端不可避免會遇到後臺程式碼,例如:
*/
<body>
<%
request.setCharacterEncoding("utf-8")
String name=request.getParameter("username");
out.print(name);
%>
</body>
這種方式耦合性太強。那麼,就算你用了freemarker等模板引擎,不能寫Java程式碼。那前端也不可避免的要去重新學習該模板引擎的模板語法,無謂增加了前端的學習成本。正如我們後端開發不想寫前端一樣,你想想如果你的後臺程式碼裡嵌入前端程式碼,你是什麼感受?因此,這種方式十分不妥。
3、JSP本身所導致的一些其他問題 比如,JSP第一次執行的時候比較緩慢,因為裡頭包含一個將JSP翻譯為Servlet的步驟。再比如因為同步載入的原因,在JSP中有很多內容的情況下,頁面響應會很慢。
前後端未分離
在前後端不分離架構中,所有的靜態資源和業務程式碼統一部署在同一臺伺服器上。伺服器接收到瀏覽器的請求後,進行處理得到資料,然後將資料填充到靜態頁面中,最終返回給瀏覽器。
半分離時代
前後端半分離,前端負責開發頁面,通過介面(Ajax)獲取資料,採用Dom操作對頁面進行資料繫結,最終是由前端把頁面渲染出來。這也就是Ajax與SPA應用(單頁應用)結合的方式,其結構圖如下:
步驟如下
/*
(1)瀏覽器請求,CDN返回HTML頁面;
(2)HTML中的JS程式碼以Ajax方式請求後臺的Restful介面;
(3)介面返回Json資料,頁面解析Json資料,通過Dom操作渲染頁面;
*/
後端提供的都是以JSON為資料格式的API介面供Native端使用,同樣提供給WEB的也是JSON格式的API介面。
那麼意味著Web工作流程是
/*
1、開啟web,載入基本資源,如CSS,JS等;
2、發起一個Ajax請求再到服務端請求資料,同時展示loading;
3、得到json格式的資料後再根據邏輯選擇模板渲染出DOM字串;
4、將DOM字串插入頁面中web view渲染出DOM結構;
*/
這些步驟都由使用者所使用的裝置中逐步執行,也就是說使用者的裝置效能與APP的執行速度聯絡的更緊換句話說就是如果使用者的裝置很低端,那麼APP開啟頁面的速度會越慢。
為什麼說是半分離的?因為不是所有頁面都是單頁面應用,在多頁面應用的情況下,前端因為沒有掌握controller層,前端需要跟後端討論,我們這個頁面是要同步輸出呢,還是非同步Json渲染呢?而且,即使在這一時期,通常也是一個工程師搞定前後端所有工作。因此,在這一階段,只能算半分離。
首先,這種方式的優點是很明顯的。前端不會嵌入任何後臺程式碼,前端專注於HTML、CSS、JS的開發,不依賴於後端。自己還能夠模擬Json資料來渲染頁面。發現Bug,也能迅速定位出是誰的問題。
然而,在這種架構下,還是存在明顯的弊端的。最明顯的有如下幾點:
/*
1)JS存在大量冗餘,在業務複雜的情況下,頁面的渲染部分的程式碼,非常複雜;
2)在Json返回的資料量比較大的情況下,渲染的十分緩慢,會出現頁面卡頓的情況;
3)SEO( Search Engine Optimization,即搜尋引擎優化)非常不方便,由於搜尋引擎的爬蟲無法爬下JS非同步渲染的資料,導致這樣的頁面,SEO會存在一定的問題;
4)資源消耗嚴重,在業務複雜的情況下,一個頁面可能要發起多次HTTP請求才能將頁面渲染完畢。可能有人不服,覺得PC端建立多次HTTP請求也沒啥。那你考慮過移動端麼,知道移動端建立一次HTTP請求需要消耗多少資源麼?
*/
前後端分離
大家一致認同的前後端分離的例子就是SPA(Single-page application),所有用到的展現資料都是後端通過非同步介面(AJAX/JSONP)的方式提供的,前端只管展現。從某種意義上來說,SPA確實做到了前後端分離,但這種方式存在兩個問題:
- WEB服務中,SPA類佔的比例很少。很多場景下還有同步/同步+非同步混合的模式,SPA不能作為一種通用的解決方案;
- 現階段的SPA開發模式,介面通常是按照展現邏輯來提供的,而且為了提高效率我們也需要後端幫我們處理一些展現邏輯,這就意味著後端還是涉足了view層的工作,不是真正的前後端分離。
SPA式的前後端分離,從物理層做區分(認為只要是客戶端的就是前端,伺服器端就是後端)這種分法已經無法滿足前後端分離的需求,我們認為從職責上劃分才能滿足目前的使用場景:
/*
前端負責view和controller層
後端只負責model層,業務處理與資料持久化等
*/
controller層與view層對於目前的後端開發來說,只是很邊緣的一層,目前的java更適合做持久層、model層的業務。
在前後端徹底分離這一時期,前端的範圍被擴充套件,controller層也被認為屬於前端的一部分。在這一時期:
/*
前端:負責View和Controller層。
後端:只負責Model層,業務/資料處理等。
*/
可是服務端人員對前端HTML結構不熟悉,前端也不懂後臺程式碼呀,controller層如何實現呢?這就是node.js的妙用了,node.js適合運用在高併發、I/O密集、少量業務邏輯的場景。最重要的一點是,前端不用再學一門其他的語言了,對前端來說,上手度大大提高。
可以就把Nodejs當成跟前端互動的api。總得來說,NodeJs的作用在MVC中相當於C(控制器)。Nodejs路由的實現邏輯是把前端靜態頁面程式碼當成字串傳送到客戶端(例如瀏覽器),簡單理解可以理解為路由是提供給客戶端的一組api介面,只不過返回的資料是頁面程式碼的字串而已。
用NodeJs來作為橋樑架接伺服器端API輸出的JSON。後端出於效能和別的原因,提供的介面所返回的資料格式也許不太適合前端直接使用,前端所需的排序功能、篩選功能,以及到了檢視層的頁面展現,也許都需要對介面所提供的資料進行二次處理。這些處理雖可以放在前端來進行,但也許資料量一大便會浪費瀏覽器效能。因而現今,增加Node中間層便是一種良好的解決方案。
瀏覽器(webview)不再直接請求jsp的api,而是
/*
1)瀏覽器請求伺服器端的NodeJS;
2)NodeJS再發起HTTP去請求JSP;
3)JSP依然原樣API輸出JSON給NodeJS;
4)NodeJS收到JSON後再渲染出HTML頁面;
5)NodeJS直接將HTML頁面flush到瀏覽器;
這樣,瀏覽器得到的就是普通的HTML頁面,而不用再發Ajax去請求伺服器了。
*/
淘寶的前端團隊提出的中途島(Midway Framework)的架構如下圖所示:
/*
1. 伺服器一分為二,前後端分別部署,靜態資源放在前端伺服器,業務程式碼放在後端伺服器.
2. 前端伺服器需要接受http請求 (一般使用node.js)
3. 前端伺服器需要進行檢視解析 (一般使用vue.js,angular.js)
4. 前端伺服器需要處理路由 (也就是頁面之間的跳轉邏輯)
5. 後端伺服器只需要返回資料.
*/
增加node.js中間層好處
1.適配性提升;我們其實在開發過程中,經常會給PC端、mobile、app端各自研發一套前端。其實對於這三端來說,大部分端業務邏輯是一樣的。唯一區別就是互動展現邏輯不同。如果controller層在後端手裡,後端為了這些不同端頁面展示邏輯,自己維護這些controller,模版無法重用,徒增和前端溝通端成本。 如果增加了node.js層,此時架構圖如下:
在該結構下,每種前端的介面展示邏輯由node層自己維護。如果產品經理中途想要改動介面什麼的,可以由前端自己專職維護,後端無需操心。前後端各司其職,後端專注自己的業務邏輯開發,前端專注產品效果開發。
2.響應速度提升: 我們有時候,會遇到後端返回給前端的資料太簡單了,前端需要對這些資料進行邏輯運算。那麼在資料量比較小的時候,對其做運算分組等操作,並無影響。但是當資料量大的時候,會有明顯的卡頓效果。這時候,node中間層其實可以將很多這樣的程式碼放入node層處理、也可以替後端分擔一些簡單的邏輯、又可以用模板引擎自己掌握前臺的輸出。這樣做靈活度、響應度都大大提升。
舉個例子,即使做了頁面靜態化之後,前端依然還是有不少需要實時從後端獲取的資訊,這些資訊都在不同的業務系統中,所以需要前端傳送5、6個非同步請求來。有了NodeJs之後,前端可以在NodeJs中去代理這5個非同步請求。還能很容易的做bigpipe,這塊的優化能讓整個渲染效率提升很多。在PC上你覺得發5、6個非同步請求也沒什麼,但是在無線端,在客戶手機上建立一個http請求開銷很大。有了這個優化,效能一下提升好幾倍。
3.效能得到提升: 大家應該都知道單一職責原則。從該角度來看,我們,請求一個頁面,可能要響應很多個後端介面,請求變多了,自然速度就變慢了,這種現象在mobile端更加嚴重。採用node作為中間層,將頁面所需要的多個後端資料,直接在內網階段就拼裝好,再統一返回給前端,會得到更好的效能。
4.非同步與模板統一;淘寶首頁就是被幾十個HTML片段(每個片段一個檔案)拼裝成,之前PHP同步include這幾十個片段,一定是序列的,Node可以非同步,讀檔案可以並行,一旦這些片段中也包含業務邏輯,非同步的優勢就很明顯了,真正做到哪個檔案先渲染完就先輸出顯示。前端機的檔案系統越複雜,頁面的組成片段越多,這種非同步的提速效果就越明顯。前後端模板統一在無線領域很有用,PC頁面和WIFI場景下的頁面適合前端渲染(後端資料Ajax到前端),2G、3G弱網路環境適合後端渲染(資料隨頁面吐給前端),所以同樣的模板,在不同的條件下走不同的渲染渠道,模板只需一次開發。
增加nodejs中間層的前後端職責劃分
前後端分離技術分工
以前的JavaWeb專案大多數都是java程式設計師又當爹又當媽,又搞前端,又搞後端。
隨著時代的發展,漸漸的許多大中小公司開始把前後端的界限分的越來越明確,前端工程師只管前端的事情,後端工程師只管後端的事情。正所謂術業有專攻,一個人如果什麼都會,那麼他畢竟什麼都不精。大中型公司需要專業人才,小公司需要全才,但是對於個人職業發展來說,我建議是分開。
對於後端java工程師
負責Model層, 業務處理/資料等
把精力放在java基礎,設計模式,jvm原理,spring+springmvc原理及原始碼,linux,mysql事務隔離與鎖機制,mongodb,http/tcp,多執行緒,分散式架構,彈性計算架構,微服務架構,java效能優化,以及相關的專案管理等等。
後端追求的是:三高(高併發,高可用,高效能),安全,儲存,業務等等。
對於前段工程師
負責view和controller層
把精力放在html5,css3,jquery,angularjs,bootstrap,reactjs,vuejs,webpack,less/sass,gulp,nodejs,Google V8引擎,javascript多執行緒,模組化,面向切面程式設計,設計模式,瀏覽器相容性,效能優化等等。
前段追求的是: 頁面表現,速度流暢,相容性,使用者體驗等等。
術業有專攻,這樣你的核心競爭力才會越來越高,正所謂你往生活中投入什麼,生活就會反饋給你什麼。並且兩端的發展都越來越高深,你想什麼都會,那你畢竟什麼都不精。
通過將team分成前後端team,讓兩邊的工程師更加專注各自的領域,獨立治理,然後構建出一個全棧式的精益求精的team。
開發模式
不分離方式
產品經歷/領導/客戶提出需求=》UI做出設計圖 =》前端工程師做html頁面=》後端工程師將html頁面套成jsp頁面(前後端強依賴,後端必須要等前端的html做好才能套jsp。如果html發生變更,就更痛了,開發效率低)=》整合出現問題 ===》前端返工 =》後端返工=》二次整合 ===》整合成功 ==》交付
分離方式
產品經歷/領導/客戶提出需===》UI做出設計圖 ===》前後端約定介面&資料&引數 =》前後端並行開發(無強依賴,可前後端並行開發,如果需求變更,只要介面&引數不變,就不用兩邊都修改程式碼,開發效率高)=》前後端整合 ===》前端頁面調整 ===》整合成功 ===》交付.
請求方式
不分離方式
/*
1. 客戶端請求
2. 服務端的servlet或controller接受請求 (後端控制路由與渲染頁面,整個專案開發的權重大部分在後端)
3. 呼叫service,dao程式碼完成程式碼邏輯
4. 返回jsp
5. jsp展現一些動態的程式碼
*/
分離方式
/*
1. 瀏覽器傳送請求
2. 直接到達html頁面(前端控制路由與渲染頁面,整個專案開發的權重前移)
3. html頁面負責呼叫服務端介面產生資料(通過ajax等等, 後臺返回json格式資料,json資料格式因為簡介高效取代xml).
4. 填充html,展現動態效果,在頁面進行解析並操作DOM.
*/
大量併發瀏覽器請求 ---> web伺服器叢集(nginx) ---> 應用伺服器叢集(tomcat),檔案/資料庫/快取/訊息佇列伺服器叢集同時又可以玩分模組,還可以按業務拆成一個個的小叢集,為後面的架構升級做準備
前後端分離優勢
1.可以實現真正的前後端解耦
前端伺服器使用nginx。前端/WEB伺服器放的是css,js,圖片等等一系列靜態資源(甚至你還可以css,js,圖片等資源放到特定的檔案伺服器,例如阿里雲的oss,並使用cdn加速),前端伺服器負責控制頁面引用&跳轉&路由,前端頁面非同步呼叫後端的介面,後端/應用伺服器使用tomcat(把tomcat想象成一個資料提供者),加快整體響應速度。(這裡需要使用一些前端工程化的框架比如nodejs,react,router,react,redux,webpack)
2.發現bug
發現bug,可以快速定位是誰的問題,不會出現互相踢皮球的現象。頁面邏輯,跳轉錯誤,瀏覽器相容性問題,指令碼錯誤,頁面樣式等問題,全部由前端工程師來負責。介面資料出錯,資料沒有提交成功,應答超時等問題,全部由後端工程師來解決。雙方互不干擾,前端與後端是相親相愛的一家人。
3.大併發情況可以水平擴充套件前後端伺服器
在大併發情況下,我可以同時水平擴充套件前後端伺服器,比如淘寶的一個首頁就需要2000+臺前端伺服器做叢集來抗住日均多少億+的日均pv。(去參加阿里的技術峰會,聽他們說他們的web容器都是自己寫的,就算他單例項抗10萬http併發,2000臺是2億http併發,並且他們還可以根據預知洪峰來無限擴充,很恐怖,就一個首頁。。。)
4.減少後端伺服器的併發/負載壓力
減少後端伺服器的併發/負載壓力。除了介面以外的其他所有http請求全部轉移到前端nginx上,介面的請求呼叫tomcat,參考nginx反向代理tomcat。且除了第一次頁面請求外,瀏覽器會大量呼叫本地快取。
5.及時後端伺服器暫時超時或者當機,前端頁面也會正常訪問,只不過資料刷不出來而已
6.多端應用
也許你也需要有微信相關的輕應用,那樣你的介面完全可以共用,如果也有app相關的服務,那麼只要通過一些程式碼重構,也可以大量複用介面,提升效率。(多端應用)
7.頁面顯示的東西再多也不怕,因為是非同步載入.
8.nginx支援頁面熱部署,不用重啟伺服器,前端升級更無縫
9.增加程式碼的維護性&易讀性
前後端耦在一起的程式碼讀起來相當費勁
10.提升開發效率
因為可以前後端並行開發,而不是像以前的強依賴
11.在nginx中部署證照,外網https訪問,只開放443和80,其他埠一律關閉(防止黑客埠掃描),內網使用http,效能和安全都有保障
12.前端大量的元件程式碼得以複用,元件化,提升開發效率,抽出來
前後端分離注意事項
1.在開需求會議的時候,前後端工程師必須全部參加,並且需要制定好介面文件,後端工程師要寫好測試用例(2個維度),不要讓前端工程師充當你的專職測試,推薦使用chrome的外掛postman或soapui或jmeter,service層的測試用例拿junit寫。ps:前端也可以玩單元測試嗎?
2.上述的介面並不是java,go裡的interface,說白了呼叫介面就是呼叫你controler裡的方法。
3.加重了前端團隊的工作量,減輕了後端團隊的工作量,提高了效能和可擴充套件性。
4.我們需要一些前端的框架來解決類似於頁面巢狀,分頁,頁面跳轉控制等功能。(上面提到的那些前端框架).
5.如果你的專案很小,或者是一個單純的內網專案,那你大可放心,不用任何架構而言,但是如果你的專案是外網專案...
6.以前還有人在使用類似於velocity/freemarker等模板框架來生成靜態頁面,仁者見仁智者見智。
7.這篇文章主要的目的是說jsp在大型外網java web專案中被淘汰掉,可沒說jsp可以完全不學,對於一些學生朋友來說,jsp/servlet等相關的java web基礎還是要掌握牢的,不然你以為springmvc這種框架是基於什麼來寫的?
8.如果頁面上有一些許可權等等相關的校驗,那麼這些相關的資料也可以通過ajax從介面裡拿。
9.對於既可以前端做也可以後端做的邏輯,我建議是放到前端,為什麼?因為你的邏輯需要計算資源進行計算,如果放到後端去run邏輯,則會消耗頻寬&記憶體&cpu等等計算資源,你要記住一點就是服務端的計算資源是有限的,而如果放到前端,使用的是客戶端的計算資源,這樣你的服務端負載就會下降(高併發場景)。類似於資料校驗這種,前後端都需要做!
10.前端需要有機制應對後端請求超時以及後端服務當機的情況,友好的展示給使用者
前後端介面聯調
前言
以JC同事為例,他公司為前後端分離架構,前端vue全家桶;前後端人員開會協商資料介面後(主要是定義傳輸的資料和API介面),前後端並行開發;因為後臺此時無法提供後端資料,所以前端需要用mock模擬假資料,管理API介面,獲取資料,到時介面聯調時連線後端伺服器,訪問後端資料即可。
但是JC同事的ajax的藉口寫的是與後端叔叔商量好的絕對路徑(域名+請求路徑+請求引數,跨域問題已解決),因為這是以後真正的請求路徑,所以JC同事又不像先寫本地相對路徑,後期再來修改(萬一後臺叔叔開發的慢了,鬼知道有多少介面要修改呀)。於是他就迷茫了。。。
仔細看看這其實就是前後端分離中的mock資料和聯調的問題,就現在來說能解決的方式有很多種。先說mock資料,gulp,webpack, fekit (去哪兒網的一款前端自動化構建工具,據說歷史比webpack和gulp都要久遠)等等自動化構建工具都有mock資料的功能,這不是問題;再說絕對路徑的問題,其實只需要做一個host的對映就行了。
什麼是前後端介面聯調
之前開發寫程式碼的時候,所有的ajax資料都不是後端返回的真實資料,而是我們自己通過介面mock模擬的假資料,當前端的程式碼編寫完畢,後端的介面也已經寫好之後,我們就需要把mock資料幹掉,嘗試使用後端提供的資料,進行前後端的一個除錯,這個過程我們就把它稱之為前後端的介面聯調。
為什麼要聯調 本地的mock資料是JC同事自己寫的,肯定符合前端需求,但是後端介面首先需要測試通不通,還需要測試資料格式對不對,還有後端有沒有填寫足夠的資料,比如寫列表頁,前端想分頁,如果後端就寫了兩條測試資料,你咋整? 所以,Jack需要根據後端對介面的調整,不斷地來回切換url,這樣豈不是還在受後端的影響,還談什麼毛線的前後端分離,名存實亡嘛!
如何實現前後端介面聯調
首先,我們已經知道,目前的前後端分離的架構應用分為兩種情況:
前後端完全分離,前後端分別擁有自己的域名和伺服器
前後端開發分離,但是部署時是一個域名和一臺伺服器
雖然架構可以採用前後端分離,但是部署有可能就不一樣了,這和專案的大小,公司的情況等等都有關係了,一個百八十人用的小系統,還得兩臺伺服器兩個域名,你不覺著浪費嗎?兩種不同的部署情況直接導致了前期在設計聯調方案的時候就不同了.
如果你們公司的專案在部署時是兩臺伺服器對應兩個域名,恭喜你,這是最nice的方案,也是聯調最舒服的方式。第二種情況,也就是開發時前後端分離,部署時是一個域名和一臺伺服器。知道這個之後,他就明白接下來該怎麼操作了。JC同事之前在專案根目錄static資料夾下新建了一個mock資料夾,裡面寫了一些json檔案,當我們做聯調的時候,這些mock資料就沒用了,我們要把mock資料切換成後端提供給我們的真實的資料。
當我的朋友Jack把static資料夾下的mock資料刪除之後,在執行專案,發現報錯了,瀏覽器告訴他,你訪問的mock下面的index.json檔案找不到404。
我們平時本地前端開發環境dev地址大多是 localhost:8080,而後臺伺服器的訪問地址就有很多種情況了,比如 後端程式猿本地IP(127.0.0.1:8889),或者外網域名,當前端與後臺進行資料互動時,自然就出現跨域問題(後臺服務沒做處理情況下)。axios不支援jsonp, 所以我們就要使用http-proxy-middleware中介軟體做代理。
現在通過在前端修改 vue-cli 的配置可解決: vue-cli中的 config/index.js 下配置 dev選項的 {proxyTable}:
proxyTable: {
'/api': {
target: '127.0.0.1:8889', // 真實請求的地址
changeOrigin: true, // 是否跨域
}
}
如果你想在公司的vue專案中實現前後端聯調,不需要再使用類似於fiddler charles的抓包代理工具了,你只需要使用proxyTable這個配置項,把你需要請求的後端的伺服器地址寫在target值裡就OK了。
解決完跨域問題後,接下來Jack該想想怎麼在一臺伺服器一個域名下進行聯調的問題了。比較常見的做法是前端在本地修改,本地檢視,測試好了以後上傳到伺服器,看看線上環境可不可以,OK的話一切都好;不行就本地接著改,然後在上傳。
聯調完之後,如何將前端打包的專案檔案發給後端,這裡也需要注意兩點:
1.css,js和圖片等靜態檔案
這時候的靜態檔案在開發階段不需要任何考慮,按照你喜歡的相對路徑或者相對於專案的根路徑的形式寫就行了,因為早晚還得交給後端。但是,需要注意:
如果你採用 相對專案根路徑的書寫方式來寫你的靜態檔案路徑 時,一定要先和後端商量好,將來專案部署的時候他會把你的前端整個專案放在哪裡?如果不是根目錄下,你就掛了。比如:你的reset.css的路徑是 /exports/styles/common/reset.css ,後端把你前端專案放在了根目錄下的 frontEnd 資料夾下, reset.css 檔案就報404了。
如果後端採用的java,你需要特別注意的是, tomcat的根目錄 並不是 webapps 檔案,而後端專案預設是部署在 webapps/ROOT 檔案下的,所以你如果使用了相對專案根路徑的書寫方式來寫你的靜態檔案路徑時,對不起又是404了。
2.ajax後端資料
因為現在唯一的一臺伺服器還是在後端程式猿那裡,所以此時你還是可以寫絕對路徑(域名+請求路徑),利用hosts檔案來改變域名對映實現聯調。
介面問題排錯
1.檢視介面日誌,檢視是否有任何異常資訊,還有請求引數
2.讓前端呼叫介面地址改為我本地伺服器介面地址,進行測試,如果本地沒問題而且遠端程式碼和原生程式碼一樣,就可以排除程式碼同步問題
3.檢視介面程式碼,看哪個地方有可能出現異常,並且異常被捕獲沒被處理,很有執行過程出錯了,但是異常被吃掉,導致儲存附件失敗並且沒有任何異常資訊
4.登入測試伺服器,檢視該伺服器是否能訪問儲存附件的雲端伺服器地址,如果不能則測試伺服器網路問題,有可能是許可權問題,被限制訪問
5.登入測試伺服器檢視日誌檔案,看是否有異常資訊
介面規範
後端需給出固定的路徑
比如 /xxapi
後端返回的基本資料結構
data: null,
success: true,
message: "請求成功",
預設的資料結構應該至少有這3個屬性,如果沒有資料則
data
為null
;success
屬性是方便前端判斷響應結果是否為成功的狀態,比如登入頁需要響應給前端的錯誤資訊有很多種,而前端首先需要知道是成功還是失敗來進行邏輯編碼;如果失敗,前端可直接將message
顯示給使用者。
後端返回的列表頁資料結構
data: {
list: [
{id:1, title: "1"},
{id:2, title: "2"},
...
],
pagination: {
total: 300,
current: 1,
}
},
success: true,
message: "請求成功",
total
為記錄總數,current
為當前第幾頁,預設顯示數前後端應商定好,比如20條,寫到各自工程的配置引數。
後端返回的詳情頁資料結構
data: {
title: "標題",
content: "內容",
...
},
success: true,
message: "請求成功",
前端提交的新建 / 修改頁資料結構
title: "標題",
content: "內容",
...
後端返回的基礎(單個)柱圖、餅圖、折線圖的圖表資料結構
data:[
{"x":"廣東省","y":76011802.16},
{"x":"江蘇省","y":40717628.60},
...
],
success: true,
message: "請求成功",
後端返回的多條折線圖的圖示資料結構
data:[
{"x":"廣東省","y":76011802.16, type:"阿迪達斯"},
{"x":"江蘇省","y":40717628.60, type:"耐克"},
...
],
success: true,
message: "請求成功",
常見介面報錯及解決方案
在前端頁面訪問後端介面的時候,如果後端或伺服器端未做一些設定,會造成頁面訪問介面失敗,在瀏覽器的控制檯會顯示報錯資訊。下面針對一些常見的錯誤,列出瞭解決方案。
不通埠或不同域名產生的跨域問題
Failed to load ... : No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://...' is therefore not allowed access. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
出現這個錯誤是因為前端頁面跟後端介面在不同的埠或IP或域名下面,也就是跨域。關於跨域你可能需要詳細瞭解 跨域資源共享 CORS。跨域產生的原因是瀏覽器遵循的基本安全策略 - [MDN web docs瀏覽器的同源策略]。
解決方案
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET,PUT,POST,DELETE,OPTIONS
如果後端是Java專案,一般是在開發環境伺服器的nginx配置檔案中新增上面幾項來統一配置,而不是在後端程式碼裡配置。新增完後重啟nginx,然後在瀏覽器的響應頭裡應該可以看到剛才配置的幾項,說明配置成功。這裡
Access-Control-Allow-Origin
設定為*
表示允許所有的域,也可以根據具體的環境來設定具體的IP或域名。
請求頭型別的錯誤
Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.
這個錯誤當前請求Content-Type的值不被支援。可能前端傳送的請求頭型別有
application/json
,這個請求涉及到預檢請求,可參看 HTTP訪問控制(CORS)。
解決方案
Access-Control-Allow-Headers: Content-Type,*
nginx配置檔案中新增這項配置,表示接收前端的
Content-Type
。
攜帶cookie設定問題
Failed to load ... : Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'. Origin 'http://...' is therefore not allowed access.
出現這個錯誤是因為前端請求介面的時候在請求頭裡加了
credentials: 'include'
引數攜帶cookie資訊,而後端未做接收cookie的設定。
解決方案
nginx配置檔案中新增這項配置,表示接收前端攜帶的cookie資訊。只有前後端都設定,跨域攜帶cookie請求才能成功。詳細的瞭解可參看 [MDN web docs Access-Control-Allow-Credentials]
自定義響應頭解決方案
在獨立的前端工程中,使用者訪問到的雖然是靜態頁面,而實際上現在的前端頁面已經有了自己的路由、許可權控制等等。
在獨立的前端工程工程中同樣會碰到一個問題:前端頁面如何比較好的獲取使用者超時狀態來退出登入?本文介紹使用自定義響應頭欄位來解決這個問題。
解決方案
1.後端在響應頭裡增加屬性, 例如
x-session-expired: 1
後端程式碼通過增加
x-session-expired
為1
來告訴前端當前使用者超時了。然後在瀏覽器的響應裡應該能看到上面這個配置。但是前端在跨域請求情況下預設只能獲取到響應頭裡的Cache-Control
、Content-Language
、Content-Type
、Expires
、Last-Modified
、Pragma
6個基本欄位,如果要獲取到剛增加的x-session-expired
欄位,需要進行下一步。
2.後端增加以下配置
Access-Control-Expose-Headers: x-session-expired
該配置的作用是指定哪些首部可以作為響應的一部分暴露給外部,這樣前端才能獲取到上面一步新增的
x-session-expired
欄位及其值。
3.前端在fetch請求時候獲取自定義響應頭欄位
return fetch(url, newOptions)
.then(checkStatus)
.then(response => {
const { dispatch } = store;
const { headers } = response;
if (headers && headers.has('x-session-expired')) {
// 如果有響應頭裡有x-session-expired則退出
dispatch({
type: 'login/logout',
});
return;
}
return response.json();
})
.catch((e) => {
...
});
可以看到上面程式碼的
headers.has('x-session-expired')
,前端通過has方法來獲取響應頭裡是否有x-session-expired
欄位,來判斷是退出還是解析資料。至此,後端設定自定義響應頭欄位且前端獲取該欄位的問題解決了。該方法也同樣適用於新增其他的響應頭欄位,解決無許可權或其他問題。
前端效能優化
內容優化
/*
(1)減少HTTP請求數
(3)避免重定向
(4)使用Ajax快取
(5)延遲載入元件,預載入元件
(6)減少DOM元素數量
(7)避免404
*/
伺服器優化
/*
(1)使用內容分發網路(CDN):把網站內容分散到多個、處於不同地域位置的伺服器上可以加快下載速度。
(2)GZIP壓縮
(3)設定ETag:ETags(Entity tags,實體標籤)是web伺服器和瀏覽器用於判斷瀏覽器快取中的內容和伺服器中的原始內容是否匹配的一種機制。
(4)提前重新整理緩衝區
(5)對Ajax請求使用GET方法
(6)避免空的影像src
*/
css優化
/*
1)將CSS程式碼放在HTML頁面的頂部
2)避免使用CSS表示式
(3)使用<link>來代替@import
(4)避免使用Filters
*/
javascript優化
/*
(1)將JavaScript指令碼放在頁面的底部。
(2)將JavaScript和CSS作為外部檔案來引用:在實際應用中使用外部檔案可以提高頁面速度,因為JavaScript和CSS檔案都能在瀏覽器中產生快取。
(3)縮小JavaScript和CSS
(4)刪除重複的指令碼
(5)最小化DOM的訪問:使用JavaScript訪問DOM元素比較慢。
(6)開發智慧的事件處理程式
(7)javascript程式碼注意:謹慎使用with,避免使用eval Function函式,減少作用域鏈查詢。
*/
影像優化
/*
(1)優化圖片大小
(2)通過CSS Sprites優化圖片
(3)不要在HTML中使用縮放圖片
(4)favicon.ico要小而且可快取
*/
前端安全問題
/*
1.演算法加密:
(1) RSA加密
(2) MD5加密
(3) SHA256加密
*/
本文部分內容來自