1. 程式人生 > 實用技巧 >Vue - 與後端互動

Vue - 與後端互動

零:與後端互動 - ajax

版本1 - 出現了跨域問題

前端:index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vue與後端互動 - 出現了跨域問題</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js"></script>
</head>
<body>
<div id="box">
    <button @click="handleClick">載入資料</button>
</div>
</body>
<script>
    let vm = new Vue({
        el: '#box',
        data: {},
        methods: {
            handleClick() {
                $.ajax({
                    url: 'http://127.0.0.1:5000/',	// 傳送請求的url,本地的5000埠,是flask的預設埠
                    method: 'get',
                    success: (data) => {
                        console.log(data)
                    }
                })
            }
        }
    })
</script>
</html>

後端:main.py

from flask import Flask	# 這裡用輕量級的Flask框架來測試

app = Flask(__name__)


@app.route('/')
def index():
    print('請求來了')
    return 'Hello World'


if __name__ == '__main__':
    app.run()

這裡可以看出:前端向後端成功傳送了請求,後端也成功響應了,但是前端卻報錯了

這是因為:跨域問題的存在,瀏覽器檢測到前端和後端不是來自同一個,所以認為這是不安全的,所以就攔截了該資源的傳遞

想要解決這個問題,就要實現:CORS

,也就是 跨域資源共享

版本2 - 解決了跨域問題

前端:index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vue與後端互動 - 解決了跨域問題</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js"></script>
</head>
<body>
<div id="box">
    <button @click="handleClick">載入資料</button>
    <p>載入的資料:{{myText}}</p>
</div>
</body>
<script>
    let vm = new Vue({
        el: '#box',
        data: {
            myText: ''
        },
        methods: {
            handleClick() {
                $.ajax({
                    url: 'http://127.0.0.1:5000/',
                    method: 'get',
                    success: (data) => {
                        console.log(data)
                        this.myText = data
                    }
                })
            }
        }
    })
</script>
</html>

後端:main.py

from flask import Flask, make_response

app = Flask(__name__)


@app.route('/')
def index():
    print('請求來了')
    res = make_response('Hello World')
    res.headers['Access-Control-Allow-Origin'] = '*'	# 訪問控制允許的源 設定為全部
    return res


if __name__ == '__main__':
    app.run()

版本3 - 後端讀取json檔案傳到前端

json檔案:file.json

{
  "name": "Darker",
  "age": "18",
  "gender": "male"
}

前端:index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vue與後端互動 - json</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js"></script>
</head>
<body>
<div id="box">
    <button @click="handleClick">載入資料</button>
    <p>載入的資料:{{myText}}</p>
</div>
</body>
<script>
    let vm = new Vue({
        el: '#box',
        data: {
            myText: ''
        },
        methods: {
            handleClick() {
                $.ajax({
                    url: 'http://127.0.0.1:5000/',
                    method: 'get',
                    success: (data) => {
                        console.log(data)
                        this.myText = data
                    }
                })
            }
        }
    })
</script>
</html>

後端:main.py

import json

from flask import Flask, jsonify

app = Flask(__name__)


@app.route('/')
def index():
    print('請求來了')
    with open('file.json', mode='rt', encoding='utf-8') as f:
        dic = json.load(f)
    res = jsonify(dic)
    res.headers['Access-Control-Allow-Origin'] = '*'
    return res


if __name__ == '__main__':
    app.run()

一:fetch

1.簡介

① fetch介紹

提供了一個 JavaScript 介面,用於訪問和操縱 HTTP 管道的一些具體部分,例如請求和響應

它還提供了一個全域性 fetch() 方法,該方法提供了一種簡單,合理的方式來跨網路非同步獲取資源

② fetch基本格式

fetch('http://example.com/movies.json')
  .then(function(response) {
    return response.json();
  })
  .then(function(myJson) {
    console.log(myJson);
  });

2.例項

json檔案:file.json

{
  "name": "Darker",
  "age": "18",
  "gender": "male"
}

後端:main.py

import json

from flask import Flask, jsonify

app = Flask(__name__)


@app.route('/')
def index():
    print('請求來了')
    with open('file.json', mode='rt', encoding='utf-8') as f:
        dic = json.load(f)
    res = jsonify(dic)
    res.headers['Access-Control-Allow-Origin'] = '*'
    return res


if __name__ == '__main__':
    app.run()

前端:index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vue與後端互動 - fetch</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js"></script>
</head>
<body>
<div id="box">
    <button @click="handleClick">載入資料</button>
    <p>載入的資料:</p>
    <ul>
        <li >姓名:{{name}}</li>
        <li >年齡:{{age}}</li>
        <li >性別:{{gender}}</li>
    </ul>
</div>
</body>
<script>
    let vm = new Vue({
        el: '#box',
        data: {
            name:'',
            age: '',
            gender: ''
        },
        methods: {
            handleClick() {
                fetch('http://127.0.0.1:5000/').then(response => {
                    return response.json()
                }).then(json => {
                    console.log('從後端獲取的json資料', json)   // success 獲取的資料
                    this.name = json.name
                    this.age = json.age
                    this.gender = json.gender
                }).catch(ex => {
                    console.log('出現了異常', ex)    // 丟擲異常
                })
            }
        }
    })
</script>
</html>

二:Axios

1.簡介

① Axios 是一個基於 promise 的 HTTP 庫,可以用在瀏覽器和 node.js 中

② axios官網:http://www.axios-js.com/

2.例項

json檔案:film.json(這裡只是一部分,原始碼太多了...)

{
    "data":{
        "films":[
            {
                "actors":[
                    {
                        "avatarAddress":"https://pic.maizuo.com/usr/movie/45deca0f886633f1a8902f7eece4248a.jpg",
                        "name":"林超賢",
                        "role":"導演"
                    },
                    {
                        "avatarAddress":"https://pic.maizuo.com/usr/movie/ff8f5bfd455e660298452c0546bb23d5.jpg",
                        "name":"辛芷蕾",
                        "role":"方宇凌"
                    },
                    {
                        "avatarAddress":"https://pic.maizuo.com/usr/movie/f27b94579d10d3131b476ec6c63a2722.jpg",
                        "name":"藍盈瑩",
                        "role":"演員"
                    },
                    {
                        "avatarAddress":"https://pic.maizuo.com/usr/movie/0a30de65731f3ddc5ce5d5915c8d9d49.jpg",
                        "name":"彭于晏",
                        "role":"高謙"
                    },
                    {
                        "avatarAddress":"https://pic.maizuo.com/usr/movie/728944e292dc6e8161acd7d5344d361c.jpg",
                        "name":"王彥霖",
                        "role":"趙呈"
                    }
                ],
                "category":"劇情|災難",
                "director":"林超賢",
                "filmId":4836,
                "filmType":{
                    "name":"2D",
                    "value":1
                },
                "grade":"7.8",
                "isPresale":true,
                "isSale":false,
                "item":{
                    "name":"2D",
                    "type":1
                },
                "language":"漢語普通話",
                "name":"緊急救援",
                "nation":"中國大陸",
                "poster":"https://pic.maizuo.com/usr/movie/c14a70858e2bda9c2bc8eaa7bfa0e2aa.jpg",
                "premiereAt":1579910400,
                "runtime":100,
                "synopsis":"《緊急救援》是由林超賢導演,彭于晏、王彥霖、辛芷蕾、藍盈瑩出演的中國首部海上救援打撈題材的作品。籌備過程中,導演林超賢帶領團隊實地勘察選址,力求打造更勁爆,更震撼的場景設定,本次與奧斯卡最佳攝影鮑德熹,《泰坦尼克號》美術指導Martin Laing等國際頂級製作團隊的強強聯合,預示著電影將成為首部開啟國際市場的華語救援題材作品,也勢必會成為又一部華語電影的標杆之作。",
                "timeType":3,
                "videoId":"XNDIyODM2NjE4OA=="
            },
            {
                "actors":[
                    {
                        "avatarAddress":"https://pic.maizuo.com/usr/movie/c8722df42d08942db01c2a262f576950.jpg",
                        "name":"易小星",
                        "role":"導演"
                    },
                    {
                        "avatarAddress":"https://pic.maizuo.com/usr/movie/9627d1ebac2b4b12a06fd1623a62af99.jpg",
                        "name":"彭昱暢",
                        "role":"肖翔"
                    },
                    {
                        "avatarAddress":"https://pic.maizuo.com/usr/movie/4b6da2deabe5dc7883321bb17372c25b.jpg",
                        "name":"喬杉",
                        "role":"周東海"
                    },
                    {
                        "avatarAddress":"https://pic.maizuo.com/usr/movie/7efe813dd79280f236b8d1286cdc313c.jpg",
                        "name":"卜冠今",
                        "role":"周希希"
                    },
                    {
                        "avatarAddress":"https://pic.maizuo.com/usr/movie/f244a95cdcd64625a50069bd64db49c2.jpg",
                        "name":"葦青",
                        "role":"王素芬"
                    }
                ],
                "category":"喜劇|動作",
                "director":"易小星",
                "filmId":5266,
                "filmType":{
                    "name":"2D",
                    "value":1
                },
                "grade":"7.2",
                "isPresale":true,
                "isSale":false,
                "item":{
                    "name":"2D",
                    "type":1
                },
                "language":"",
                "name":"沐浴之王",
                "nation":"中國大陸",
                "poster":"https://pic.maizuo.com/usr/movie/e6aabffd040bda24f483bcc95ed79e62.jpg",
                "premiereAt":1607644800,
                "runtime":102,
                "synopsis":"在一次搓澡服務中,富二代肖翔(彭昱暢 飾)和搓澡工周東海(喬杉 飾)發生矛盾,讓周東海面臨生活困境。肖翔因跳傘事故被送到醫院記憶全失,周東海恰巧撞見,心生一計,騙肖翔是自己的弟弟並騙回周家澡堂當搓澡工,於是一個富二代開始了一段終身難忘的搓澡生涯……",
                "timeType":3,
                "videoId":""
            },
            {
                "actors":[
                    {
                        "avatarAddress":"https://pic.maizuo.com/usr/movie/168e82b3c4ec676ca577cb99e5a9ee06.jpg",
                        "name":"派蒂·傑金斯",
                        "role":"導演"
                    },
                    {
                        "avatarAddress":"https://pic.maizuo.com/usr/movie/c4cfadaaab8f81ebaf3bdc7aa30fac49.jpg",
                        "name":"蓋爾·加朵",
                        "role":"戴安娜·普林斯 / 神奇女俠 Diana Prince / Wonder Woman"
                    },
                    {
                        "avatarAddress":"https://pic.maizuo.com/usr/movie/6e0a82c6efac00bfb6b7c1b4bb46eaa9.jpg",
                        "name":"克里斯·派恩",
                        "role":"史蒂夫·特雷弗 Steve Trevor"
                    },
                    {
                        "avatarAddress":"https://pic.maizuo.com/usr/movie/79a97235ee2f33741b63eafc2f34658e.jpg",
                        "name":"克里斯汀·韋格",
                        "role":"芭芭拉·密涅瓦 Cheetah"
                    },
                    {
                        "avatarAddress":"https://pic.maizuo.com/usr/movie/13c53574fc0c4b13b99e966af5af7e9d.jpg",
                        "name":"佩德羅·帕斯卡",
                        "role":"馬克斯維爾·勞德 Max Lord"
                    }
                ],
                "category":"動作|奇幻|冒險",
                "director":"派蒂·傑金斯",
                "filmId":5287,
                "filmType":{
                    "name":"3D",
                    "value":2
                },
                "isPresale":true,
                "isSale":false,
                "item":{
                    "name":"3D",
                    "type":2
                },
                "language":"",
                "name":"神奇女俠1984",
                "nation":"美國",
                "poster":"https://pic.maizuo.com/usr/movie/4f84f5a48560f5b14b0fac7768da4e05.jpg",
                "premiereAt":1608249600,
                "runtime":151,
                "synopsis":"神奇女俠的全新大銀幕冒險來到了1980年代。神奇女俠戴安娜在華盛頓的自然歷史博物館過著與普通人無異的生活,然而在阻止了一場看似平常的劫案後,身邊的一切都發生了變化。在強大的神力誘惑下,兩位全新勁敵悄然出現——與神奇女俠“相愛相殺”的頂級掠食者豹女,以及掌控著能改變世界力量的麥克斯·洛德,一場驚天大戰在所難免。另外一邊,舊愛史蒂夫突然“死而復生”,與戴安娜再續前緣,然而浪漫感動之餘,史蒂夫的迴歸也疑竇叢生。",
                "timeType":3,
                "videoId":""
            },
            {
                "actors":[
                    {
                        "avatarAddress":"https://pic.maizuo.com/usr/movie/6735332cb677645542c2f136eb37e561.jpg",
                        "name":"邱禮濤",
                        "role":"導演"
                    },
                    {
                        "avatarAddress":"https://pic.maizuo.com/usr/movie/ca47cd961fe0be0c91149f6bcca2f13d.jpg",
                        "name":"劉德華",
                        "role":"潘乘風"
                    },
                    {
                        "avatarAddress":"https://pic.maizuo.com/usr/movie/82a5d97e318c447133cd6c8262cee846.jpg",
                        "name":"劉青雲",
                        "role":"董卓文"
                    },
                    {
                        "avatarAddress":"https://pic.maizuo.com/usr/movie/e776871fe75f0367aa60957457a6f96c.jpg",
                        "name":"倪妮",
                        "role":"龐玲"
                    },
                    {
                        "avatarAddress":"https://pic.maizuo.com/usr/movie/dd120eb1f6bda9f3b9c9e4c49785d0ce.jpg",
                        "name":"謝君豪",
                        "role":"演員"
                    }
                ],
                "category":"動作|犯罪",
                "director":"邱禮濤",
                "filmId":5257,
                "filmType":{
                    "name":"2D",
                    "value":1
                },
                "isPresale":true,
                "isSale":false,
                "item":{
                    "name":"2D",
                    "type":1
                },
                "language":"",
                "name":"拆彈專家2",
                "nation":"中國大陸,中國香港",
                "poster":"https://pic.maizuo.com/usr/movie/4a2bec5290b94f7eb597913727b21df6.jpg",
                "premiereAt":1608768000,
                "runtime":0,
                "synopsis":"香港某處發生爆炸案,前拆彈專家潘乘風(劉德華 飾)因昏迷於現場,被警方懷疑牽涉其中。甦醒後的潘乘風只能一邊逃亡一邊查明真相,然而,他的好友董卓文(劉青雲 飾)和他的前女友龐玲(倪妮 飾)卻給他講述了兩段截然不同的經歷。有計劃的爆炸案接二連三發生,真相卻越來越撲朔迷離……",
                "timeType":3,
                "videoId":""
            }
        ],
        "total":47
    },
    "msg":"ok",
    "status":0
}

後端:main.py

import json

from flask import Flask, jsonify

app = Flask(__name__)


@app.route('/film')
def index():
    print('請求來了')
    with open('film.json', mode='rt', encoding='utf-8') as f:
        dic = json.load(f)
    res = jsonify(dic)
    res.headers['Access-Control-Allow-Origin'] = '*'
    return res


if __name__ == '__main__':
    app.run()

前端:index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vue與後端互動</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/axios/0.21.0/axios.min.js"></script>
</head>
<body>

<div id="box">
    <button @click="handleClick">載入電影資料</button>
    <p>載入的資料:</p>
    <ul>
        <li v-for="item in dataList">
            <p>電影:{{item.name}}</p>
            <p>導演:{{item.director}}</p>
            <img :src="item.poster" alt="">
        </li>

    </ul>
</div>

</body>
<script>
    let vm = new Vue({
        el: '#box',
        data: {
            dataList: []
        },
        methods: {
            handleClick() {
                axios.get("http://127.0.0.1:5000/film/").then(res => {
                    console.log(res.data.data.films) // axios 自動包裝data屬性 res.data
                    this.dataList = res.data.data.films
                }).catch(err => {
                    console.log(err);
                })
            }
        }
    })
</script>
</html>

三:計算屬性

計算屬性是基於它們的依賴進行快取的

計算屬性只有在它的相關依賴發生改變時才會重新求值

計算屬性就像Python中的property,可以把方法/函式偽裝成屬性

1.通過計算屬性實現名字首字母大寫

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首字母大寫</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js"></script>
</head>
<body>
<div id="box">
    <!--大段的程式碼寫在這裡不好,使用計算屬性-->
    模板插值:
    {{myText.substring(0,1).toUpperCase()+myText.substring(1)}}
    <p>普通方法:{{getNameMethod()}}</p>
    <!--區別是在同一個頁面中使用多次計算屬性,不會多次執行-->
    <p>計算屬性:{{getName}}</p>
    <!--普通方法要加括號-->

</div>
</body>
<script>
    var vm = new Vue({
        el: '#box',
        data: {
            myText: 'darker',
        },
        computed: {
            getName() { // 依賴的狀態改變了,會重新計算
                return this.myText.substring(0, 1).toUpperCase() + this.myText.substring(1)
            }
        },
        methods: {
            getNameMethod() {
                return this.myText.substring(0, 1).toUpperCase() + this.myText.substring(1)
            }
        }
    })
</script>
</html>

2.通過計算屬性重寫過濾案例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>通過計算屬性重寫過濾案例</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js"></script>
</head>
<body>
<div id="box">
    <p><input type="text" v-model="myText" placeholder="請輸入要篩選的內容:"></p>
    <ul>
        <li v-for="data in newList">{{data}}</li>
    </ul>
</div>
</body>
<script>
    var vm = new Vue({
        el: '#box',
        data: {
            myText: '',
            dataList: ['a', 'at', 'atom', 'be', 'beyond', 'cs', 'csrf'],
        },
        computed: {
            newList() {
                return this.dataList.filter(item => {
                    return item.indexOf(this.myText) > -1   // 返回索引大於1的元素:>-1 就表示包含在其中
                })
            }
        }
    })
</script>
</html>

四:虛擬DOM 與diff演算法 key的作用

1.Vue2.0 v-for 中 :key 有什麼用呢?

其實呢不只是vue,react中在執行列表渲染時也會要求給每個元件新增key這個屬性

key簡單點來說就是唯一標識,就像ID一樣唯一性

要知道,vue和react都實現了一套虛擬DOM,使我們可以不直接操作DOM元素只操作資料便可以重新渲染頁面

而隱藏在背後的原理便是其高效的Diff演算法

  • 分層級比較:只做同層級的對比
  • 通過key值比較:出現新的key就插入同組(迴圈中,儘量加key,讓key值唯一)
  • 通過元件/標籤進行比較:然後進行替換

2.虛擬DOM的diff演算法

虛擬DOM資料渲染圖示

3.具體實現

① 把樹按照層級分解

② 按照key值比較

③ 通過元件進行比較

<div id="box">
    <div v-if="isShow">111</div>
    <p v-else>222</p>
    <!--    
    {tag:div,value:111}
    {tag:p,value:222}
    直接不比較,直接刪除div,新增p
    -->

    <div v-if="isShow">111</div>
    <div v-else>222</div>
    <!--    
    {tag:div,value:111}
    {tag:div,value:222}
    比較都是div,只替換文字內容
    -->
</div>

思考:什麼是跨域問題?如何解決?