Vue.js 學習筆記之三:與伺服器的數據交互
顯而易見的,之前的02_toDoList
存在著一個很致命的缺陷。那就是它的數據只存在於瀏覽器端,一但用戶關閉或重新載入頁面,他之前加入到程式中的數據就會全部丟失,一切又恢復到程式的初始狀態。要想解決這個問題,就需要 Web 應用的前端在適當的時間將獲得的輸入數據存儲到後端伺服器上,然後在需要時再從伺服器上獲取這些數據。這部分筆記將記錄如何利用 Vue.js 框架來完成 Web 應用程式的前端與後端之間的交互。這一次,我同樣會通過構建一個”留言本”應用來貫穿整個學習過程。
首先需要在code
目錄下依次執行npm install express body-parser knex
和npm install sqlite3@<指定的版本>
命令,安裝接下來創建 Web 服務所需要的後端組件(需要注意的是,這裡安裝的sqlite3
要根據knex
安裝後的提示選擇對應的版本)。接下來,在code
目錄下創建一個名為03_Message
的目錄,並在該目錄下執行npm init -y
命令,將其初始化成一個 Node.js 項目。在這裡,之所以將服務端所需要的組件安裝在項目目錄的上一級目錄中,是因為我接下來還需要在項目目錄中安裝前端組件,並將其開放給瀏覽器端訪問,所以前後端所需要的組件最好分開存放。
現在,我要基於 Express 框架來創建一個 Web 服務了。具體做法就是在code/03_Message
目錄下創建一個名為index.js
的伺服器端腳本文件,並在其中輸入如下程式碼:
const path = require('path');
const express = require('express')
const bodyParser = require('body-parser');
const knex = require('knex');
const port = 8080;
// 創建伺服器實例
const app = express();
// 配置 public 目錄,將其開放給瀏覽器端
app.use('/', express.static(path.join(__dirname, 'public')));
// 配置 node_modules 目錄,將其開放給瀏覽器端
app.use('/node_modules', express.static(path.join(__dirname, 'node_modules')));
//配置 body-parser 中間件,以便獲取 POST 請求數據。
app.use(bodyParser.urlencoded({ extended : false}));
app.use(bodyParser.json());
// 創建資料庫連接對象:
const appDB = knex({
client: 'sqlite3', // 設置要連接的數據類型
connection: { // 設置資料庫的鏈接參數
filename: path.join(__dirname, 'data/database.sqlite')
},
debug: true, // 設置是否開啟 debug 模式,true 表示開啟
pool: { // 設置資料庫連接池的大小,默認為{min: 2, max: 10}
min: 2,
max: 7
},
useNullAsDefault: true
});
appDB.schema.hasTable('notes') // 查看資料庫中是否已經存在 notes 表
.then(function(exists) {
if(exists == false) { // 如果 notes 表不存在就創建它
appDB.schema.createTable('notes', function(table) {
// 創建 notes 表:
table.increments('uid').primary();// 將 uid 設置為自動增長的欄位,並將其設為主鍵。
table.string('userName'); // 將 userName 設置為字元串類型的欄位。
table.string('noteMessage'); // 將 notes 設置為字元串類型的欄位。
});
}
})
.then(function() {
// 請求路由
// 設置網站首頁
app.get('/', function(req, res) {
res.redirect('/index.htm');
});
// 響應前端獲取數據的 GET 請求
app.get('/data/get', function(req, res) {
appDB('notes').select('*')
.then(function(data) {
console.log(data);
res.status(200).send(data);
}).catch(function() {
res.status(404).send('找不到相關數據');
});
});
// 響應前端刪除數據的 POST 請求
app.post('/data/delete', function(req, res) {
appDB('notes').delete()
.where('uid', '=', req.body['uid'])
.catch(function() {
res.status(404).send('刪除數據失敗');
});
res.send(200);
});
// 響應前端添加數據的 POST 請求
app.post('/data/add', function(req, res) {
console.log('post data');
appDB('notes').insert(
{
userName : req.body['userName'],
noteMessage : req.body['noteMessage']
}
).catch(function() {
res.status(404).send('添加數據失敗');
});
res.send(200);
});
// 監聽 8080 埠
app.listen(port, function(){
console.log(`訪問 //localhost:${port}/,按 Ctrl+C 終止服務!`);
});
})
.catch(function() {
// 斷開資料庫連接,並銷毀 appDB 對象
appDB.destroy();
});
由於 Vue.js 框架的特點,前端需要後端提供的服務除了獲取指定的 HTML 和 JavaScript 文件之外,主要就是對資料庫的增刪改查操作了,所以在上面這個服務中,除了將public
、node_modules
目錄整體開放給瀏覽器端訪問之外,主要提供了一個基於 GET 請求的數據查詢服務,和兩個基於 POST 請求的數據添加與刪除操作。
接下來,我可以開始前端部分的構建了。首先需要在code/03_Message
目錄下執行npm install vue axios
命令,安裝接下來所要用到的前端組件(該命令會自動生成一個node_modules
目錄,正如上面所說,該目錄會被服務端腳本整體開放給瀏覽器端)。然後,繼續在同一目錄下創建public
目錄,並在其中創建一個名為index.htm
的文件,其程式碼如下:
<!DOCTYPE html>
<html lang="zh-cn">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<script defer="defer" src="/node_modules/vue/dist/vue.js"></script>
<script defer="defer" src="/node_modules/axios/dist/axios.js"></script>
<script defer="defer" src="/js/main.js"></script>
<title>留言本</title>
</head>
<body>
<div id="app">
<h1>留言本</h1>
<div id="showNote" v-for="note in notes">
<span>{{ note.userName }} 說:{{ note.noteMessage }} </span>
<input type="button" value="刪除" @click="remove(note.uid)">
</div>
<div id="addMessage">
<h2>請留言:</h2>
<label :for="userName">用戶名:</label>
<input type="text" v-model="userName">
<br>
<label :for="Message">寫留言:</label>
<input type="text" v-model="Message"></input>
<input type="button" value="添加留言" @click="addNew">
</div>
</div>
</body>
</html>
這個頁面主要被分為了兩個部分,第一部分會根據notes
中的數據使用v-for
指令迭代顯示已被添加到資料庫中的留言,並提供了一個刪除
按鈕以便刪除指定的留言(使用v-on
指令綁定單擊事件處理函數)。第二部分則是一個用於添加留言
的輸入介面,這裡使用了v-model
指令來獲取需要用戶輸入的userName
和Message
數據。現在,我需要來創建相應的 Vue 對象實例了,為此,我會在剛才創建的public
目錄下再創建一個js
目錄,並在其中創建名為main.js
的自定義前端腳本文件,其程式碼如下:
// 程式名稱: Message
// 實現目標:
// 1. 學習 axios 庫的使用
// 2. 掌握如何與伺服器進行數據交互
const app = new Vue({
el: '#app',
data:{
userName: '',
Message: '',
notes: []
},
created: function() {
that = this;
axios.get('/data/get')
.then(function(res) {
that.notes = res.data;
})
.catch(function(err) {
console.error(err);
});
},
methods:{
addNew: function() {
if(this.userName !== '' && this.Message !== '') {
that = this;
axios.post('/data/add', {
userName: that.userName,
noteMessage: that.Message
}).catch(function(err) {
console.error(err);
});
this.Message = '';
this.userName = '';
axios.get('/data/get')
.then(function(res) {
that.notes = res.data;
})
.catch(function(err) {
console.error(err);
});
}
},
remove: function(id) {
if(uid > 0) {
that = this;
axios.post('/data/delete', {
uid : id
}).catch(function(err) {
console.error(err);
});
axios.get('/data/get')
.then(function(res) {
that.notes = res.data;
})
.catch(function(err) {
console.error(err);
});
}
}
}
});
這個 Vue 實例與我們之前創建的大同小異,主要由以下四個成員組成:
-
el
成員:用於以 CSS 選擇器的方式指定 Vue 實例所對應的元素容器,在這裡,我指定的是<div id="app">
元素。 -
data
成員:用於設置頁面中綁定的數據,這裡設置了以下三個數據變數:notes
:這是一個數組變數,用於存放已被添加的留言記錄。userName
:這是一個字元串變數,用於獲取”用戶名”數據。Message
:這是一個字元串變數,用於獲取”留言”數據。
-
created
成員:用於在程式載入時做初始化操作,在這裡,我從服務端讀取了已被添加的留言記錄,並將其載入到notes
變數中。 -
methods
成員:用於定義頁面中綁定的事件處理函數,這裡定義了以下兩個事件處理函數:addNew
:用於添加新的留言記錄,並同步更新notes
中的數據。remove
:用於刪除指定的留言記錄,並同步更新notes
中的數據。
通常情況下,我們在 Vue.js 框架中會選擇使用 axios 這樣的第三方組件來處理髮送請求和接收響應數據的工作,引入該組件的方式與引入 Vue.js 框架的方式是一樣的,可以像上面一樣先下載到本地,然後使用<script>
標籤引入,也可以使用 CDN 的方式直接使用<script>
標籤引入,像這樣:
<!-- 開發環境版本,包含了有幫助的命令行警告 -->
<script src="//unpkg.com/axios/dist/axios.js"></script>
<!-- 或者 -->
<!-- 生產環境版本,優化了文件大小和載入速度 -->
<script src="//unpkg.com/axios/dist/axios.min.js"></script>
需要注意的是,該引用標籤在 HTML 頁面中的位置必須要在自定義 JavaScript 腳本文件(即main.js
)的引用標籤之前。當然,我在上述程式碼中只展示了axios.get
和axios.post
這兩個最常用方法的基本用法,由於該組件支援返回 Promise 對象,所以我們可以採用then
方法調用鏈來處理響應數據和異常狀況。關於 axios 組件更多的使用方法,可以參考相關文檔(//www.axios-js.com/zh-cn/docs/)。