Table of Contents
需求
最近要做一個project,打算前後端分離,但服務器端口號有限,對nginx 這種端口複用的手段也不是很熟悉(公司的服務器都是windows的,相關資料少的一批……_(:з」∠)_),所以只能大開腦洞了。
後端我們使用的是django。那麼,如果能夠將react 打出來的包塞到django裏頭,當做django項目的一部分,不就可以只用一個端口了麼??
(以下只是我個人解決問題的一個總結,肯定有很多很笨的地方,如果有人看到還望指正。)
思路&原理
從前後端分離的角度,react打出來的包通常會被當做一個獨立的項目看待。但實際上,react的包就是由各種靜態資源(HTML,css,js...)組成的。因此可以將其當做單純的靜態資源,放置到django中對應的位置中去。
這裏有兩個思路:
1 Inside Django App: 將react project拆散,分別塞入到django項目某App的各個位置(templates,statics等等)
2 Outside Django App: 保持react project的完整性,把它放置在與django項目的App同層級的位置
這裏我使用了後一種方案
如何做
這裏借鑑了這個帖子: https://www.cnblogs.com/ranyihang/p/10694635.html 這裏說的是VUE,但道理都一樣。
step 1, 將react 文件放置到與App同級的位置:
這裏爲了清晰起見,在build外又包了一層:
step 2, 修改django templates 查找路徑:
被塗死的就是我在外層包裹的文件名。經過測試,不影響其他App的工作。
step3,設置static路徑(注意此處有坑!!在本地運行沒問題,但發佈到IIS上後還需要進一步處理。後面再講)
在STATICFILE_DIRS中:
塗死處爲外層包裹的文件夾名。正常這裏應該是App的名字。
step4,在django中添加導向react project的url:注意此處有坑!!無論本地還是發佈到IIS後,都會出現刷新頁面404的情況)
from django.views.generic.base import TemplateView # try integrate react into django
urlpatterns = [
path('XXX', TemplateView.as_view(template_name='index.html')),]
如此就可以了。
有坑咋辦
這個過程中遇到兩個坑:
1 發佈到IIS上後,靜態資源無法加載
各種404.
這是因爲IIS 發佈後,頁面上的靜態資源是ip/static/XXX 這種形式。
理論上只要用虛擬路徑指向react project的靜態資源就好。但這裏有兩個問題,第一如果項目中有vendor,創建虛擬路徑貌似不好用。第二,如果django是多App的,那麼會有一個虛擬路徑文件夾static下有多個文件夾,分別指向多個App的情況。此時如果直接將react project的靜態文件夾(js,css等)掛在虛擬路徑文件夾static下就太亂了,不方便維護。
那怎麼辦?簡單,把整個react project都塞到static下面就行!
解決方案:
第一步,在IIS上創建虛擬路徑,在static下指向build:
第二步,將react project 中的index.html的所有“/static/XXX和”/vendor/XXX都改成"/static/build/static和"/static/build/vendor
done~
2 頁面刷新404
如果將react project單獨發佈,只能設置路由重寫來解決這個問題。比如nginx:
location / {
root C:/nginx-1.5.9/html/build;
index index.html index.htm;
try_files $uri $uri/ /index.html; # look at here
}
至於IIS……咱別提IIS了好嗎_(:з」∠)_
氮素!如果將react project視作django的一部分,這個問題就很簡單了。
爲什麼?看問題出現的原理:
刷新出現404多因爲使用了react-router。按照它的邏輯,要先加載頁面,然後再進行路由。這個時候url會發生變化,跟初始進入頁面時不同。
由於url的變化,刷新時使用的url無法被找到,因此還沒等加載頁面就被攔截住了。
以上是我總結的。具體可以看網上的帖子,說的更準確些。
但無論如何,可以確定這個節點的路由,是由後端完成的,也就是django。
那把路由註冊到django裏不就完事了嘛╮(╯▽╰)╭
依然如下。只不過這裏的XXX要與react-router中的一致。
path('XXX', TemplateView.as_view(template_name='index.html')), # try to solve the issue that when refresh the web page, page is 404
好了,問題解決。