第十三期 · 「正統」前端開發(下):上手Vue.js編程與重構前端頁面
來自專欄簡明教程5 人贊了文章
本篇概述
好久不見!
在上一期中,我們一起學習了如何搭建一個前端開發環境,以及如何使用我們的老朋友PyCharm進行前端開發。在本期中,我們將一起利用上期所學來重構我們學習項目的前端頁面,並在此過程中學習到如何使用Vue.js進行前端開發。
Vue項目結構初探
在上一期中,我們已經成功初始化了一個Vue項目,這個項目的文件就位於 fe/src 這個目錄下。
現在 src 目錄下的文件是Vue默認提供的演示項目的相關程序文件,這些文件的作用就是能展示一個我們在上一期看到的Vue歡迎頁面。
說起來,Vue初始化出來的這個項目文件夾,其組織方式和我們更為熟悉的Flask有異曲同工之妙,下面就來分別說明一下。
main.js:這是Vue的主模塊文件。這個文件主要是定義程序需要導入的依賴包以及依賴的初始化配置等內容。非常類似於我們當前Flask里run.py的那一堆from ……import,然後各種初始化的那一堆代碼的作用。我們還沒有學習如何組織我們的Flask應用代碼(很快會學習到),如果經過一定組織,會在應用根目錄下有一個_ _init_ _.py的文件,這個文件和Vue.js里的main.js的作用非常接近,甚至連代碼結構都十分相似。
App.vue:這是Vue的主框架(總組件)。其實在Flask提供的模版引擎里有類似的功能,就是組件化頁面的一些部分,比如網頁的底部版權部分在很多頁面里都是一樣的內容,因此我們可以把這部分單獨寫成一個.html文件,然後按需引入,這樣後期維護將十分方便。此時我們需要一個頁面去包含和承載這些組件,這樣這個頁面就能擁有這些組件的展示效果了,比如會展示一個網站底部了。對於App.vue而言,其實就是承載各個前端組件的主框架頁面。在Vue這種前端項目里稱之為「組件」的東西,可以簡單的理解為就是一個頁面,或者是前面說的類似網站底部這種頁面的一部分。
components:剛才說App.vue是承載各組件的主頁面,那麼各組件放哪呢?就放在這個文件夾里。現在這個文件夾下有一個HelloWorld.vue,我們查看其源代碼,可以看得出其內容就是上一期我們看到的歡迎頁面。
assets:就像Flask為我們專門準備了一個static文件夾,用於存放圖片等各種靜態資源一樣,Vue也準備了這樣一個文件夾用以同樣的目的。現在我們在assets下可以看到一個名為logo.png的圖片文件,這剛好就是歡迎頁面里唯一的靜態資源了。
這裡還有一點需要額外說明的,就是和/src同級的一個目錄/static。說起來這個static和/src/assets很讓初次接觸Vue的人困惑。因為這兩個文件夾的作用都是存放靜態資源的,而且他們都參與項目的編譯構建過程,並且看起來static似乎更「名正言順」些,這就讓人十分不解為什麼同一個功能弄出兩個文件夾了。
這裡我簡單的說一下兩者的重要區別和使用原則,大家一看就明白了。
兩者的區別在於,如果靜態文件放到assets這個文件夾下,編譯出生產文件時,assets里的圖片文件等靜態資源會被打包壓縮,結果就是你在打包出的文件中,找不到之前的圖片資源。比如大家可以試一下在上一期我們編譯構建的Vue歡迎頁文件中,看能不能在/dist下找到logo.png(上一期中我們最開始的一次編譯構建是在這個文件夾下的,後面才配置生成到Flask項目里的指定位置),結果當然是找不到,哪去了呢?答案是藏在某個.js文件里了。
這種打包壓縮的過程,伴隨著一個有趣的工作就是hash文件名。hash文件名就是大家可以看見,打包出來的在/dist下的文件的文件名都有類似「30790115300ab27614ce176899523b62」這樣一串數字字母組合,這就是hash文件名。弄這種看起來花里胡哨的文件名,其最大的作用就是刷緩存。現在我們來說下瀏覽器緩存。瀏覽器有一個機制,假設一個網站上有一張名為a.jpg的圖片,在多個網頁1、2、3……都有顯示,當用戶第一次訪問這個網站的網頁1時,會從伺服器下載a.jpg到本地並作緩存,當用戶再次訪問網頁1,或者去訪問2、3……時,瀏覽器發現這些頁面都需要展示a.jpg,此時會先試圖看下自己的緩存里有沒有下載過這個文件,如果有,則直接顯示了,這樣就不用重複從伺服器上下載了。
這種機制會有一個問題,就是如果我們在伺服器上更新了a.jpg這張圖片,雖然文件名還是一樣,但內容已經是別的內容,此時訪問過我們網站的用戶會因為上述的緩存機制,並不能立刻看到更新後的a.jpg,這顯然不是我們想要的結果,那麼一個方式就是更換一個新的名字,這樣瀏覽器會當成新的圖片而再次下載緩存。我們又知道,文件換了一個名字,我們的網頁中對應的代碼也要修改,因為需要更新其圖片路徑,如果是相對於1、2、3……很多個頁面,那將是巨大的工作量而且枯燥乏味。所以類似Vue這樣的現代前端技術,都幫我們自動的去完成上面的這些工作(修改文件名+更新圖片路徑),後續我們重構前端頁面時會體會這點。
回到前面說的,hash文件名就是Vue幫我們自動更新文件名,以應對緩存更新需要的一種手段。
安裝和引入前端依賴
作為重構前的頭等大事,當然是根據之前的代碼重新引入所需的依賴。即像Python一樣,先安裝好所需要的依賴包。
我們先確認一下之前的前端頁面用到了哪些依賴。
首先,我們要明確Vue不能算是一個,這是框架,地位和Flask一樣,而我們剛才已經裝好它了。
其次,我們之前用到了element這個餓了么出品的前端模板框架。這算是一個依賴,現在我們來安裝這個依賴包。
早前,我們使用了直接引入其CSS和JS資源的方式去使用這個element。也就是所謂CDN方式。現在我們來使用更加「正統」的NPM安裝。
定位到項目下的 /fe 根目錄下,打開命令行程序,直接複製粘貼進上面的安裝命令按回車鍵安裝。另外大家注意,以後安裝Vue的依賴,都要在這個路徑下面安裝,別安裝錯了地方。
這樣我們就安裝好了element這個依賴了。現在是要引入它。剛才我們講過,main.js就是引入依賴的地方。我們在Flask里有過一些經驗,就是使用依賴大體分為三步:安裝、引入、初始化。那麼Vue這樣的前端項目是否也是這樣呢?我們還是一起邊做邊體會。
先引入element,相關的辦法在element文檔的「快速上手」章節里有詳細的說明,我們照搬即可。
這樣我們就算引入element到Vue之中了。
最後,我們應該還記得在(第十期)的教程中,我們因為解決跨域問題而引入了一個jsonp(包)工具,也就是在index.html中,我們引入了這個文件。
因此,我們才能在處理請求時使用jsonp()方法。
當時我們已經提供了一個鏈接,是axios推薦處理jsonp()的方法的說明頁面:[axios/COOKBOOK.md at master · axios/axios · GitHub](axios/axios),我們注意到頁面上有下圖的這麼一段。
嘿,這不是剛剛才用過的安裝命令么。但有個麻煩事了,因為在jsonp的演示例子里,並沒有告訴我們怎麼在Vue里使用它。
當然,遇到這樣的問題,並不是Vue不能使用jsonp,而是直接使用很不方便。這很類似於我們Flask碰到的一個問題,比如在Flask里,也可以直接使用SQLAlchemy,但不如使用flask-SQLAlchemy來得方便。所以,我們順著這個思路,會想到在Vue中有沒有類似的vue-jsonp呢?答案是肯定的,而且你還別說,連名字都還真叫vue-jsonp:LancerComet/vue-jsonp
安裝過程我們已經熟悉了(下圖)。雖然說明裡沒有明確指明安裝命令,但我們應該學會舉一反三了,知道npm install 後的包名都是和引入包的名字是一致的,更一般的,和程序代碼的倉庫項目名是一致的。
查看vue-jsonp的官方說明,看到有一個例子(下圖部分)。
這個例子經過前面的知識儲備,我們理解起來並不困難了。有用的部分我已經用紅框框出來。不過既然是簡明教程,為了說清楚還是要啰嗦幾句。
例子的第一部分是如何引入vue-jsonp和初始化,這個我們在用element時已經見識過了,沒什麼好說的。只不過例子里給了一個帶參數的初始化方式,來定義如何處理響應超時,例子設定的是5000毫秒則請求超時,我們呢,用不到,所以不去管它。
第二部分其實是vue-jsonp兩種不同的用法,用哪種和Vue是怎麼用的有關,這點我們下面馬上會學習到是什麼意思。這裡,我們先確認用第一種,就是紅框部分的。代碼如何修改我們馬上在重構工作中再詳細講。
至此,安裝和引入前端依賴的工作就告一段落。
開始重構
我們先打開index.html(Flask下的index.html)、HelloWorld.vue、App.vue這三個文件。
得益於我們之前的靜態頁面就已經是基於Vue和element構建的,所以在index.html中的大部分代碼我們都可以復用。首先能復用的當然是頁面的主體部分,也就是下圖的這段。
怎麼復用呢?
很簡單,把這段複製出來,直接去替換HelloWorld.vue里的<template></template>下第一個<div></div>之間的內容。也就是下圖的這個位置。
為什麼不能直接複製到<template></template>里呢?這是因為Vue特殊的構建機制導致的,我們現在還不能去理解這麼深層面的東西,但簡而言之,你會發現我們之前也是要把代碼寫在<div id=「app」></div>之間的,所以這裡也要等同的放到<div></div>裡面。當然,我們不必再去說明id=「app」這件事了(因為在App.vue里已經說明了)。
這樣,到這裡我們已經復用了之前的頁面主體部分代碼,但我們還沒有復用之前的數據模型以及方法,也就是之前在index.html中<script></script>有大段代碼的部分(下圖)。
但是這部分我們就不能無腦的複製粘貼了。究其原因,還不是因為我們用了新的方式使用Vue,不過這種方式的變化照邏輯也應該只是影響Vue自己的部分代碼,並不應該影響我們編寫的部分。那麼真實情況是否是這樣呢?我們還是一起邊動手邊感受。
我們注意到在HelloWorld.vue中,也是在<script></script>下,有一段代碼神似剛才提到的在index.html中的那部分。但細節上又有不少差異。
這裡,我們只簡單作以說明,先讓大家有一個初步的理解。
簡單來說,我們之前使用Vue的方式是在頁面里直接引入,沒有額外的main.js這樣的文件去說明依賴關係和做初始化,所以在這種場景下使用的Vue,需要使用new Vue()去構建一個Vue的實例。說白了,就是類似我們寫Python時,寫好了一個類(class),但在要使用這個類時,則必須先生成一個類的實例。這都是類似的思路。
而我們現在採用的方式是直接構建了一個Vue的項目工程,上面說的初始化等工作,我們知道已經在main.js里就做了許多,所以,我們在HelloWorld.vue中看不到new Vue()這樣的語法,取而代之是 export default{}了。順便說句,如果你有接觸過iOS開發的話,其實 export default{ }這樣的語法你也能猜到在Vue里的作用。
言歸正傳,既然不能直接複製粘貼了,那我們就部分復用,同時按照之前的思路,我們能復用的一定是我們自己寫的部分。
我們回到index.html,按照代碼的上下先後順序,首先需要處理的就是data部分。
在HelloWorld.vue里,我們看到已經有一個data了,但是與index.html不同。雖然我們還不是十分了解JS的語法,但結合Python語法的經驗,可以看出來HelloWorld.vue里的data是一個方法;而在index.html里的並不是。
按照前面的思路,我們先不去管這種不同,因為這些不同都是Vue的,我們先嘗試僅複製出index.html中data里的內容,去替換掉HelloWorld.vue中data里的內容。完成這步後,HelloWorld.vue會是下圖這樣。需要注意,替換HelloWorld.vue里的部分內容的起止位置是return {}的花括弧里的頭尾。因為return {}我們結合Python的方法語法經驗,能夠明白這是方法的返回定義,是屬於函數的一部分,也即data()函數的一部分,也是Vue的內容,因此必須在這個裡面去定義我們自己的內容。正如原來Vue的示例項目自己也是寫在return {}里的一樣。
接著,我們用同樣的思路去處理methods部分。但是問題來了,原來的HelloWorld.vue沒有methods怎麼寫的例子,不過既然這樣的話,我們先試試直接拷貝原來的methods整體部分過來。
然後我們已經知道在處理前端依賴時,還有一個遺留問題是vue-jsonp。因為我們換成vue-jsonp了,所以得根據它的新用法去重寫我們之前的老的jsonp()方法邏輯。這其實也不難,照葫蘆畫瓢,我們的新代碼如下如所示。
為了方便大家學習,這裡我僅僅注釋掉了老代碼部分,這樣大家可以對比感受一下區別,但在提交到代碼倉庫時,會刪除這段已經沒用的老代碼。
到這裡,我們基本上把index.html里的主要邏輯代碼都遷移到HelloWorld.vue里了。
如果觀察細緻,我們發現在HelloWorld.vue、App.vue里的<style></style>中有一些樣式代碼(下圖)。毫無疑問,這是之前的樣式代碼,對我們沒啥作用,於是要清空這部分,否則可能會影響我們自己程序的樣式。
需要注意的是,<style></style>不要刪除,只刪除<style></style>里的內容,同時<style></style>上的 scoped 這個單詞也能刪除,我們也刪除掉。最後只剩下空的<style></style>即可。
至此,我們的重構工作就告一段落。
解決異常和優化細節
我們跑一下前端試試(上一期我們已經學習過怎麼跑一個前端開發調試環境了)。
我們發現IDE有一些報錯(下圖),但是報錯級別是個警告(WARNING),所以前端頁面應該會顯示出來,但可能不能正常使用。
前端頁面如下圖所示。
嗯哼,好大個Logo。
這裡我們先處理這個logo顯示異常的問題。當然,有兩種辦法,一種換成我們自己的,另一種就是直接去掉。
我們還是先直接去掉。我們發現在打開的App.vue中有相關的這段代碼,直接刪掉即可。
然後我們刷新下頁面,不出所料,正常了。
然後,我們要解決的當然就是這個警告的問題了。
對於警告信息我們大致能理解是針對v-for這點的,並且還貼心的給出了一個鏈接。我們查看鏈接的頁面,英文?!沒關係,我們已經知道Vue的文檔有中文版的,切換版本的功能就在右上角。
根據警告錯誤信息,我們大致能理解其說明的問題就是關於v-for中key的問題。有關於這個問題,Vue的說法如下圖所示。
看不懂Vue的說明沒關係。這個問題簡單點說,就是如果要無中生有一些數據,並利用到了v-for渲染這些數據出現在頁面上,最好加一個唯一標識,方式是多寫一個 :key 的屬性,並且對應數據中的具備唯一特徵的欄位。比較巧合的是,豆瓣給我們的數據中,恰好有一個id欄位是電影的編號,每一部都不一樣,所以我們甚至可以直接複製Vue說明裡的代碼到我們的文件中(下圖)。
嘿,報錯果然沒了(下圖)。
我們再次看一下前端開發調試環境中的頁面,看起來各項功能都正常了(下圖)。
於是我們編譯生成生產環境的文件。至於方法前面幾期我們已經學習過了,而且還做了一些設置使得生成的文件會直接覆蓋掉Flask里的老文件。
接著,我們在Flask的開發調試環境中測試一下新頁面的效果(下圖)。
哇,搞定!
至此,我們的小項目在前後端分離上又更精進了一步。
最後,再和大家說幾句題外話。
很感謝一直支持《簡明教程》並不離不棄的讀者們。這次的更新的確慢了許多。這次讓大家等待許久,是因為我太忙了。因為除了忙於工作生活,我打算在學習計算機這條路上走得更遠,於是我報考了一個交大的計算機及應用專業的自學考試。四月份我考了高等數學,此前都在備考。我想和大家說的是,如果有夢想,就大膽前行,如果有了興趣想去嘗試和學習新的東西,任何時候都不晚。加油,各位!
推薦閱讀:
※使用Flask-SQLAlchemy
※Pipenv:新一代Python項目環境與依賴管理工具
※Flask 發布 1.0 穩定版
※WSGI及gunicorn指北(二)