## 前後端分離開發入門 在傳統的Web應用開發中,大多數的程序員會將瀏覽器作爲前後端的分界線。將瀏覽器中爲用戶進行頁面展示的部分稱之爲前端,而將運行在服務器,爲前端提供業務邏輯和數據準備的所有代碼統稱爲後端。所謂前後端分離的開發,就是前後端工程師約定好數據交互接口,並行的進行開發和測試,後端只提供數據,不負責將數據渲染到頁面上,前端通過HTTP請求獲取數據並負責將數據渲染到頁面上,這個工作是交給瀏覽器中的JavaScript代碼來完成。 使用前後端分離開發有諸多的好處,下面我們簡要的說下這些好處: 1. **提升開發效率**。前後端分離以後,可以實現前後端代碼的解耦,只要前後端溝通約定好應用所需接口以及接口參數,便可以開始並行開發,無需等待對方的開發工作結束。在這種情況下,前後端工程師都可以只專注於自己的開發工作,有助於打造出更好的團隊。除此之外,在前後端分離的開發模式下,即使需求發生變更,只要接口與數據格式不變,後端開發人員就不需要修改代碼,只要前端進行變動即可。 2. **增強代碼的可維護性**。前後端分離後,應用的代碼不再是前後端混合,只有在運行期纔會有調用依賴關係,這樣的話維護代碼的工作將變得輕鬆愉快很多,再不會牽一髮而動全身。當你的代碼變得簡明且整潔時,代碼的可讀性和可維護性都會有質的提升。 3. **支持多終端和服務化架構**。前後端分離後,同一套數據接口可以爲不同的終端提供服務,更有助於打造多終端應用;此外,由於後端提供的接口之間可以通過HTTP(S)進行調用,有助於打造服務化架構(包括微服務)。 接下來我們就用前後端分離的方式來改寫之前的投票應用。 ### 返回JSON格式的數據 剛纔說過,在前後端分離的開發模式下,後端需要爲前端提供數據接口,這些接口通常返回JSON格式的數據。在Django項目中,我們可以先將對象處理成字典,然後就可以利用Django封裝的`JsonResponse`向瀏覽器返回JSON格式的數據,具體的做法如下所示。 ```Python def show_subjects(request): queryset = Subject.objects.all() subjects = [] for subject in queryset: subjects.append({ 'no': subject.no, 'name': subject.name, 'intro': subject.intro, 'isHot': subject.is_hot }) return JsonResponse(subjects, safe=False) ``` 上面的代碼中,我們通過循環遍歷查詢學科得到的`QuerySet`對象,將每個學科的數據處理成一個字典,在將字典保存在名爲`subjects`的列表容器中,最後利用`JsonResponse`完成對列表的序列化,向瀏覽器返回JSON格式的數據。由於`JsonResponse`序列化的是一個列表而不是字典,所以需要指定`safe`參數的值爲`False`才能完成對`subjects`的序列化,否則會產生`TypeError`異常。 可能大家已經發現了,自己寫代碼將一個對象轉成字典是比較麻煩的,如果對象的屬性很多而且某些屬性又關聯到一個比較複雜的對象時,情況會變得更加糟糕。爲此我們可以使用一個名爲bpmappers的三方庫來簡化將對象轉成字典的操作,這個三方庫本身也提供了對Django框架的支持。 安裝三方庫bpmappers。 ```Shell pip install bpmappers ``` 編寫映射器(實現對象到字典轉換)。 ```Python from bpmappers.djangomodel import ModelMapper from poll2.models import Subject class SubjectMapper(ModelMapper): class Meta: model = Subject ``` 修改視圖函數。 ```Python def show_subjects(request): queryset = Subject.objects.all() subjects = [] for subject in queryset: subjects.append(SubjectMapper(subject).as_dict()) return JsonResponse(subjects, safe=False) ``` 配置URL映射,然後訪問該接口,可以得到如下所示的JSON格式數據。 ```JSON [ { "no": 101, "name": "Python全棧+人工智能", "intro": "Python是一種計算機程序設計語言。是一種面向對象的動態類型語言,最初被設計用於編寫自動化腳本(shell),隨着版本的不斷更新和語言新功能的添加,越來越多被用於獨立的、大型項目的開發。", "create_date": "2017-08-01", "is_hot": true }, // 此處省略下面的內容 ] ``` 如果不希望在JSON數據中顯示學科的成立時間,我們可以在映射器中排除`create_date`屬性;如果希望將是否爲熱門學科對應的鍵取名爲`isHot`(默認的名字是`is_hot`),也可以通過修改映射器來做到。具體的做法如下所示: ```Python from bpmappers import RawField from bpmappers.djangomodel import ModelMapper from poll2.models import Subject class SubjectMapper(ModelMapper): isHot = RawField('is_hot') class Meta: model = Subject exclude = ('create_date', 'is_hot') ``` 再次查看學科接口返回的JSON數據。 ```JSON [ { "no": 101, "name": "Python全棧+人工智能", "intro": "Python是一種計算機程序設計語言。是一種面向對象的動態類型語言,最初被設計用於編寫自動化腳本(shell),隨着版本的不斷更新和語言新功能的添加,越來越多被用於獨立的、大型項目的開發。", "isHot": true }, // 此處省略下面的內容 ] ``` 關於bpmappers詳細的使用指南,請參考它的[官方文檔](<https://bpmappers.readthedocs.io/en/stable/>),這個官方文檔是用日語書寫的,可以使用瀏覽器的翻譯功能將它翻譯成你熟悉的語言即可。 ### 使用Vue.js渲染頁面 關於Vue.js的知識,我們在第21天到第30天的內容中已經介紹過了,這裏我們不再進行贅述。如果希望全面的瞭解和學習Vue.js,建議閱讀它的[官方教程](<https://cn.vuejs.org/v2/guide/>)或者在[YouTube](<https://www.youtube.com/>)上搜索Vue.js的新手教程(Crash Course)進行學習。 重新改寫subjects.html頁面,使用Vue.js來渲染頁面。 ```HTML <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>學科</title> </head> <body> <h1>所有學科</h1> <hr> <div id="app"> <div v-for="subject in subjects"> <h3> <a :href="getTeachersHref(subject.no)">{{ subject.name }}</a> <img v-if="subject.isHot" src="/static/images/hot.png" width="32"> </h3> <p>{{ subject.intro }}</p> </div> </div> <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script> <script> const app = new Vue({ el: '#app', data: { subjects: [] }, created() { fetch('/subjects/') .then(resp => resp.json()) .then(json => this.subjects = json) }, methods: { getTeachersHref(sno) { return `/static/teachers.html/?sno=${sno}` } } }) </script> </body> </html> ``` 前後端分離的開發需要將前端頁面作爲靜態資源進行部署,項目實際上線的時候,我們會對整個Web應用進行動靜分離,靜態資源通過Nginx或Apache服務器進行部署,生成動態內容的Python程序部署在uWSGI或者Gunicorn服務器上,對動態內容的請求由Nginx或Apache路由到uWSGI或Gunicorn服務器上。 在開發階段,我們通常會使用Django自帶的測試服務器,如果要嘗試前後端分離,可以先將靜態頁面放在之前創建的放靜態資源的目錄下,具體的做法可以參考[項目完整代碼](<https://github.com/jackfrued/django1902_vue/>)。