小復(fù)盤(pán):Vue+Flask實(shí)戰(zhàn)Web應(yīng)用

本文主題

對(duì)于還不是很明白前后端分離的Web應(yīng)用開(kāi)發(fā)的朋友引有,可以通過(guò)本文,了解一下通過(guò)前端訪問(wèn)的URL路徑倦逐,是如何一步步找到后端去執(zhí)行具體函數(shù)的譬正。

在這個(gè)過(guò)程中,主要聊到如下主題檬姥,您感興趣就隨便看看:

  • 初遇路由
  • 藍(lán)圖的注冊(cè)
  • Vue單頁(yè)面應(yīng)用
  • Vue-cli配置文件中的跨域和路由
  • 路由和視圖函數(shù)

〇曾我、前提

項(xiàng)目相關(guān)環(huán)境
前端:http://127.0.0.1:8010
后端:http://127.0.0.1:8089

一、路由的秘密

很多地方都會(huì)用到路由的概念健民,也很難一句話解釋清楚抒巢,大家應(yīng)該知道有個(gè)叫“路由器”的東西,數(shù)據(jù)從一個(gè)網(wǎng)絡(luò)到另一個(gè)網(wǎng)絡(luò)就是靠路由來(lái)完成的秉犹。

引用一張圖來(lái)解釋路由(出處:http://www.reibang.com/p/b55cf53e633a):

image.png

  1. 先說(shuō)一個(gè)現(xiàn)象

當(dāng)我們?cè)谇岸隧?yè)面
http://127.0.0.1:8010/#/manage/flowerManage
進(jìn)行某個(gè)操作時(shí)蛉谜,在調(diào)試窗口看到它訪問(wèn)了這樣一個(gè)接口并且得到了響應(yīng):

http://127.0.0.1:8010/api/flower/proGather/list

試試直接在Postman這種接口測(cè)試工具里輸入該接口,也得到了對(duì)應(yīng)的Response崇堵。

不過(guò)型诚,在這個(gè)項(xiàng)目中,是在后端寫(xiě)了/proGather/list路徑對(duì)應(yīng)的視圖函數(shù)鸳劳,執(zhí)行后返回處理結(jié)果俺驶。

疑問(wèn)產(chǎn)生

后端是在8089端口啟的服務(wù),為什么這里使用8010端口也能訪問(wèn)呢棍辕?如果直接改成8089端口,可以訪問(wèn)到嘛还绘?那我們?cè)賮?lái)試試在Postman中訪問(wèn)這個(gè)接口:

http://127.0.0.1:8089/api/flower/proGather/list

哈哈楚昭,出錯(cuò)啦。不過(guò)注意到?jīng)]有拍顷,報(bào)了這樣的錯(cuò)誤:
raise NotFound()\nwerkzeug.exceptions.NotFound: 404 Not Found: The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.

2.為什么呢抚太?

簡(jiǎn)單解釋就是:前端服務(wù)通過(guò)路由,找到后端對(duì)應(yīng)的視圖函數(shù)昔案,進(jìn)行處理尿贫。這里又有一個(gè)視圖函數(shù)的概念哎!本文后面會(huì)解釋一下這個(gè)踏揣,你就先理解成是一個(gè)函數(shù)庆亡,運(yùn)行之后給出了返回值作為Response給到前端就好了。

接下來(lái)看看后端為啥沒(méi)有訪問(wèn)成功捞稿,這個(gè)是和我們視圖函數(shù)的寫(xiě)法又谋,以及配置息息相關(guān)拼缝。因此會(huì)放到后面“Vue-cli配置文件”的部分詳細(xì)解釋清楚,先來(lái)說(shuō)下針對(duì)這個(gè)項(xiàng)目彰亥,如何可以訪問(wèn)成功:

http://127.0.0.1:8089/flower/proGather/list

去掉路徑里面的api咧七,就可以了。

二任斋、藍(lán)圖

概念大家都懂了哇继阻?說(shuō)一下怎么用。一個(gè)WebApp里面可以注冊(cè)一個(gè)藍(lán)圖废酷,當(dāng)然也可以注冊(cè)多個(gè)藍(lán)圖瘟檩,下面的例子就是注冊(cè)多個(gè)藍(lán)圖的情況。
create_app的定義里面注冊(cè)就好了锦积,然后可以指定對(duì)應(yīng)的url_prefix芒帕。

__init__.py文件的create_app()中,寫(xiě)了相關(guān)代碼如下:

def create_app():
    app = Flask(__name__)
    app.config.from_object(BaseConfig)
    app.logger.addHandler(config_log())  # 初始化日志
    BaseConfig.init_app(app)
    moment.init_app(app)

    db.app = app
    db.create_all()

    login_manager.init_app(app)
    scheduler.start()  # 定時(shí)任務(wù)啟動(dòng)

    # 注冊(cè)多個(gè)藍(lán)圖模塊
    from app.admin import admin_module
    from app.flower import flower_module
    app.register_blueprint(admin_module, url_prefix='/admin')
    app.register_blueprint(flower_module, url_prefix='/flower')

    return app

那么再來(lái)看看藍(lán)圖模塊是怎么寫(xiě)的呢丰介?
目錄結(jié)構(gòu)是這個(gè)樣子的:

app
    -admin
        __init__.py
        admin_manage.py
    -flower
        __init__.py
        flower_manage.py
    __init__.py

看下admin目錄下的兩個(gè)文件背蟆;flower目錄下也是類(lèi)似的

app/admin/__init__.py

from flask import Blueprint
admin_module = Blueprint('admin_module', __name__)
from . import admin_manage
app/admin/admin_manage.py

from . import admin_module

@admin_module.route('/admin/add', methods=['POST'])
def add_user():
    pass

好啦,照葫蘆畫(huà)瓢哮幢,你也會(huì)注冊(cè)藍(lán)圖模塊了带膀!

三、理解Vue的單頁(yè)面應(yīng)用

很久以前用html做過(guò)網(wǎng)站橙垢,所以一直有一個(gè)慣性思維垛叨,覺(jué)得點(diǎn)擊網(wǎng)頁(yè)上的超鏈接跳來(lái)跳去的,就一定是在不同的頁(yè)面間切換柜某。

所以一開(kāi)始也按照這個(gè)思路來(lái)學(xué)習(xí)Vue嗽元,看到別人案例中,有一個(gè)個(gè)的Vue文件喂击,想當(dāng)然的就理解成對(duì)應(yīng)的頁(yè)面了剂癌,后來(lái)才發(fā)現(xiàn)并非如此。

組件

Vue中有一個(gè)很重要的概念就是組件翰绊!官網(wǎng)都是這么介紹組件的:

// 定義一個(gè)名為 button-counter 的新組件
Vue.component('button-counter', {
  data: function () {
    return {
      count: 0
    }
  },
  template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})
<div id="components-demo">
  <button-counter></button-counter>
</div>
new Vue({ el: '#components-demo' })

組件是可復(fù)用的 Vue 實(shí)例佩谷,所以它們與 new Vue 接收相同的選項(xiàng),例如 data监嗜、computed谐檀、watch、methods 以及生命周期鉤子等裁奇。僅有的例外是像 el 這樣根實(shí)例特有的選項(xiàng)桐猬。

你可以將組件進(jìn)行任意次數(shù)的復(fù)用。

對(duì)了單頁(yè)面應(yīng)用簡(jiǎn)稱(chēng)SPA刽肠。羅里吧嗦一大堆课幕,那么它和單頁(yè)面應(yīng)用之間有什么聯(lián)系厦坛?

首先,我們定義組件乍惊;
然后杜秸,我們定義路由規(guī)則;
接著润绎,我們?cè)L問(wèn)路徑撬碟,就會(huì)跳轉(zhuǎn)到相應(yīng)的組件上去。

所以莉撇,我們并不是在頁(yè)面間跳轉(zhuǎn)呢蛤,而是通過(guò)路由規(guī)則,在組件之間進(jìn)行跳轉(zhuǎn)棍郎!

四其障、Vue-cli配置文件

在這個(gè)項(xiàng)目里,前端使用了Vue-cli腳手架涂佃。
如果你也直接用Vue-cli創(chuàng)建一個(gè)vue項(xiàng)目的話励翼,可以在config目錄下看到好幾個(gè).js文件,就是配置文件啦辜荠。
如果你是網(wǎng)上找來(lái)的項(xiàng)目汽抚,可能會(huì)略有不同,不過(guò)內(nèi)容上都是類(lèi)似的伯病。

這次我們就來(lái)看看index.js文件里的部分內(nèi)容吧造烁,由于把配置詳解一遍很花時(shí)間,所以我們就聚焦一點(diǎn)午笛,來(lái)揭開(kāi)第一部分接口調(diào)用路徑的謎底惭蟋!
當(dāng)然,有些項(xiàng)目的配置不一定保持了初始的名字药磺,總之是含有module.exports = {這行代碼的配置文件就對(duì)了敞葛!

我這里是這樣配置的:

module.exports = {
    lintOnSave: true,
    productionSourceMap: false,
    chainWebpack: (config) => {
        config.resolve.alias
            .set('~', resolve('src'))
    },
    devServer: {
        host: '127.0.0.1',
        port: 8010,
        proxy: {
            '/api/': {
                target: 'http://127.0.0.1:8089',
                changeOrigin: true,
                pathRewrite: {
                    '^/api': ''
                }
            }
        }
    },
    css: {
        loaderOptions: {
            css: {
                localIdentName: process.env.node_env == 'production' ? "[hash]" : '[path]_[name]_[local]_[hash:base64:5]',
                camelCase: 'only'
            },
            sass: {}
        },
    }
};

首先看下這個(gè)devServer.proxy.changeOrigin,它是為了解決跨域問(wèn)題的一個(gè)設(shè)置与涡。
changeOrigin: true, //開(kāi)啟代理:在本地會(huì)創(chuàng)建一個(gè)虛擬服務(wù)端,然后發(fā)送請(qǐng)求的數(shù)據(jù)持偏,并同時(shí)接收請(qǐng)求的數(shù)據(jù)驼卖,這樣服務(wù)端和服務(wù)端進(jìn)行數(shù)據(jù)的交互就不會(huì)有跨域問(wèn)題

那什么是跨域贴谎?

瀏覽器從一個(gè)域名的網(wǎng)頁(yè)去請(qǐng)求另一個(gè)域名的資源時(shí)阵翎,域名咬腋、端口窃植、協(xié)議任一不同,都是跨域榄审,如下例子:
域名
主域名不同 http://www.baidu.com/index.html –>http://www.sina.com/test.js
子域名不同 http://www.666.baidu.com/index.html –>http://www.555.baidu.com/test.js
域名和域名ip http://www.baidu.com/index.html –>http://180.149.132.47/test.js
端口
 http://www.baidu.com:8080/index.html–> http://www.baidu.com:8081/test.js
協(xié)議
 http://www.baidu.com:8080/index.html–> https://www.baidu.com:8080/test.js
備注
 1、端口和協(xié)議的不同伊群,只能通過(guò)后臺(tái)來(lái)解決
 2策精、localhost和127.0.0.1雖然都指向本機(jī)咽袜,但也屬于跨域

綜上询刹,我目前的設(shè)置前后端的端口不一樣范抓,也算是跨域了匕垫,因此就要設(shè)置成true啦!

接著看下這段:

 proxy: {
            '/api/': {
                target: 'http://127.0.0.1:8089',
                changeOrigin: true,
                pathRewrite: {
                    '^/api': ''
                }
            }
      }

vue-cli的這個(gè)設(shè)置來(lái)自于其使用的插件http-proxy-middleware寞秃,github地址如下:
https://github.com/chimurai/http-proxy-middleware
官網(wǎng)文檔中有這么一句:

proxy('/api', {...}) - matches paths starting with /api

這就說(shuō)明春寿,當(dāng)我們的路徑中含有/api的時(shí)候忽孽,就會(huì)匹配到這里對(duì)應(yīng)的配置啦。

那么pathRewrite是干啥的呢厘线?文檔解釋如下:
option.pathRewrite: object/function, rewrite target's url path. Object-keys will be used as RegExp to match paths.
并且給出示例:

// rewrite path
pathRewrite: {'^/old/api' : '/new/api'}

// remove path
pathRewrite: {'^/remove/api' : ''}

// add base path
pathRewrite: {'^/' : '/basepath/'}

// custom rewriting
pathRewrite: function (path, req) { return path.replace('/api', '/base/api') }

而我們的配置中有寫(xiě)pathRewrite: { '^/api': '' }出革,因此proxy時(shí)要把路徑中的/api去掉骂束。
OK,那么整體來(lái)解釋一下devServer這段配置的意思蹬昌!

devServer: {
        host: '127.0.0.1',
        port: 8010,
        proxy: {
            '/api/': {
                target: 'http://127.0.0.1:8089',
                changeOrigin: true,
                pathRewrite: {
                    '^/api': ''
                }
            }
        }
    }

首先,127.0.0.1:8010過(guò)來(lái)的請(qǐng)求先紫,如果路徑中含有/api遮精,就會(huì)被路由到127.0.0.1:8089上去本冲,并且把路徑中的/api去掉劫扒。
也就是說(shuō),當(dāng)我們?cè)谇岸嗽L問(wèn)了一個(gè)這樣的接口:

http://127.0.0.1:8010/api/flower/proGather/list

實(shí)際上對(duì)應(yīng)的是后端這個(gè)接口:

http://127.0.0.1:8089/flower/proGather/list

再來(lái)回到開(kāi)頭的問(wèn)題上來(lái)沟饥,為什么使用postman訪問(wèn)http://127.0.0.1:8089/api/flower/proGather/list會(huì)報(bào)404的錯(cuò)誤添怔?這下你就清楚了吧贤旷!

那么再來(lái)問(wèn)一個(gè)問(wèn)題,后端的接口路徑為什么是這樣的呢幼驶,那就要來(lái)聊聊視圖函數(shù)啦艾杏。

五盅藻、路由與視圖函數(shù)

在《Flask Web開(kāi)發(fā)》一書(shū)中购桑,對(duì)于URL如何映射到Python函數(shù),有這樣的一段解釋?zhuān)?/p>

客戶端(例如Web瀏覽器)把請(qǐng)求發(fā)送給Web服務(wù)器氏淑,Web服務(wù)器再把請(qǐng)求發(fā)送給Flask程序?qū)嵗3绦驅(qū)嵗枰缹?duì)每個(gè)URL請(qǐng)求運(yùn)行哪些代碼,所以保存了一個(gè)URL到Python函數(shù)的映射關(guān)系守问。處理URL和函數(shù)之間關(guān)系的程序稱(chēng)為路由耗帕。

在Flask程序中定義路由的最簡(jiǎn)便方式仿便,是使用程序?qū)嵗峁┑腶pp.route修飾器体啰,把修飾的函數(shù)注冊(cè)為路由。

再來(lái)看我們的項(xiàng)目代碼闻坚。還記得剛才說(shuō)的目錄結(jié)構(gòu)么沽翔?

app
    -admin
        __init__.py
        admin_manage.py
    -flower
        __init__.py
        flower_manage.py
    __init__.py

看一下flower_manage.py這個(gè)文件:

from . import flower_module

@flower_module.route('/proGather/list')
def get_list():
    pass

看來(lái)真正訪問(wèn)的是get_list()這個(gè)函數(shù)呢!可是我們?cè)谛揎椘骼镏粚?xiě)了相對(duì)路徑/proGather/list呀窿凤!
再來(lái)復(fù)習(xí)一下我們?cè)?code>create_app()函數(shù)里面注冊(cè)藍(lán)圖的時(shí)候是怎么寫(xiě)的:

   from app.flower import flower_module
   app.register_blueprint(flower_module, url_prefix='/flower')

所以flower_module對(duì)應(yīng)著url_prefix='/flower'仅偎,在拼上后端服務(wù)啟動(dòng)時(shí)的端口號(hào),可不就是http://127.0.0.1:8089/flower/proGather/list了么雳殊!

六橘沥、小結(jié)

本文差不多就是這樣了,如果還覺(jué)得看得不夠清楚夯秃,下次我寫(xiě)一個(gè)示例項(xiàng)目傳到github上再解說(shuō)吧座咆。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市寝并,隨后出現(xiàn)的幾起案子箫措,更是在濱河造成了極大的恐慌,老刑警劉巖衬潦,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件斤蔓,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡镀岛,警方通過(guò)查閱死者的電腦和手機(jī)弦牡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)漂羊,“玉大人驾锰,你說(shuō)我怎么就攤上這事∽咴剑” “怎么了椭豫?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我赏酥,道長(zhǎng)喳整,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任裸扶,我火速辦了婚禮框都,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘呵晨。我一直安慰自己魏保,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布摸屠。 她就那樣靜靜地躺著谓罗,像睡著了一般。 火紅的嫁衣襯著肌膚如雪餐塘。 梳的紋絲不亂的頭發(fā)上妥衣,一...
    開(kāi)封第一講書(shū)人閱讀 51,718評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音戒傻,去河邊找鬼税手。 笑死,一個(gè)胖子當(dāng)著我的面吹牛需纳,可吹牛的內(nèi)容都是我干的芦倒。 我是一名探鬼主播,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼不翩,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼兵扬!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起口蝠,我...
    開(kāi)封第一講書(shū)人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤器钟,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后妙蔗,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體傲霸,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年眉反,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了昙啄。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡寸五,死狀恐怖梳凛,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情梳杏,我是刑警寧澤韧拒,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布淹接,位于F島的核電站,受9級(jí)特大地震影響叛溢,放射性物質(zhì)發(fā)生泄漏蹈集。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一雇初、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧减响,春花似錦靖诗、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至颂鸿,卻和暖如春嘴纺,著一層夾襖步出監(jiān)牢的瞬間栽渴,已是汗流浹背尖坤。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留纯路,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像宵溅,于是被迫代替她去往敵國(guó)和親恃逻。 傳聞我的和親對(duì)象是個(gè)殘疾皇子凸郑,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355