12. 项目 [vuejs-10]:插件 [dao],异步请求 HTTP
项目 [vuejs-10] 的目录结构如下:

[vuejs-10] 项目展示了一个向远程服务器发起 HTTP 请求的组件。所采用的架构如下:

[Vue.js] 组件利用 [dao] 层与税费计算服务器进行交互。
12.1. 依赖项安装
应用程序 [vuejs-10] 使用库 [axios] 向税费计算服务器发送异步请求。我们需要安装此依赖项:

- 在 [4-5] 中,安装 [axios] 库后,在 [package.json] 文件中添加的行 [1-3];
12.2. 类 [Dao]
类 [Dao] 即在段落 [La classe Dao] 中开发的类。为便于查阅,在此再次列出:
'use strict';
// 导入
import qs from 'qs'
// 类 [Dao]
class Dao {
// 构造函数
constructor(axios) {
this.axios = axios;
// 会话cookie
this.sessionCookieName = "PHPSESSID";
this.sessionCookie = '';
}
// 初始化会话
async initSession() {
// 请求选项 HHTP [get /main.php?action=init-session&type=json]
const options = {
method: "GET",
// URL的参数
params: {
action: 'init-session',
type: 'json'
}
};
// 执行查询 HTTP
return await this.getRemoteData(options);
}
async authentifierUtilisateur(user, password) {
// 请求选项 HHTP [post /main.php?action=authentifier-utilisateur]
const options = {
method: "POST",
headers: {
'Content-type': 'application/x-www-form-urlencoded',
},
// POST 的正文
data: qs.stringify({
user: user,
password: password
}),
// URL的参数
params: {
action: 'authentifier-utilisateur'
}
};
// HTTP 请求的执行
return await this.getRemoteData(options);
}
async getAdminData() {
// HHTP 查询选项 [get /main.php?action=get-admindata]
const options = {
method: "GET",
// URL 的参数
params: {
action: 'get-admindata'
}
};
// 执行请求 HTTP
const data = await this.getRemoteData(options);
// 结果
return data;
}
async getRemoteData(options) {
// 用于会话cookie
if (!options.headers) {
options.headers = {};
}
options.headers.Cookie = this.sessionCookie;
// 执行请求 HTTP
let response;
try {
// 异步请求
response = await this.axios.request('main.php', options);
} catch (error) {
// 参数 [error] 是一个异常实例——它可能有各种形式
if (error.response) {
// 服务器响应位于 [error.response]
response = error.response;
} else {
// 重新抛出该错误
throw error;
}
}
// response 包含服务器响应的全部内容 HTTP(HTTP 头部 + 响应正文)
// 若存在,则获取会话cookie
const setCookie = response.headers['set-cookie'];
if (setCookie) {
// setCookie 是一个数组
// 在此数组中查找会话cookie
let trouvé = false;
let i = 0;
while (!trouvé && i < setCookie.length) {
// 查找会话cookie
const results = RegExp('^(' + this.sessionCookieName + '.+?);').exec(setCookie[i]);
if (results) {
// 保存会话cookie
// eslint-disable-next-line require-atomic-updates
this.sessionCookie = results[1];
// 已找到
trouvé = true;
} else {
// 下一个元素
i++;
}
}
}
// 服务器响应位于 [response.data]
return response.data;
}
}
// 导出类
export default Dao;
项目 [vuejs-10] 仅使用第 18-30 行中的异步方法 [initSession]。 需要说明的是,类 [Dao] 在第 10 行通过参数 [axios] 进行实例化,该参数由调用代码初始化。此处的调用代码即为脚本 [./main.js]。
12.3. 插件 [pluginDao]
插件 [pluginDao] 如下所示:
export default {
install(Vue, dao) {
// 向 Vue 类添加属性 [$dao]
Object.defineProperty(Vue.prototype, '$dao', {
// 当引用 Vue.$dao 时,返回第二个参数 [dao]
get: () => dao,
})
}
}
若回顾针对插件 [event-bus] 的说明,可知插件 [pluginDao] 在类/函数 [Vue] 中创建了一个名为 [$dao] 的新属性。 该属性的值(尚待验证)将是脚本 [./Dao] 导出的对象,即前面的类 [Dao]。
12.4. 主脚本 [main.js]
主脚本 [main.js] 的代码如下:
// 导入
import Vue from 'vue'
import App from './App.vue'
import axios from 'axios';
// 插件
import BootstrapVue from 'bootstrap-vue'
Vue.use(BootstrapVue);
// bootstrap
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'
// 层[dao]
import Dao from './Dao';
// axios 配置
axios.defaults.timeout = 2000;
axios.defaults.baseURL = 'http://localhost/php7/scripts-web/impots/version-14';
axios.defaults.withCredentials = true;
// [dao] 层实例化
const dao = new Dao(axios);
// 插件 [dao]
import pluginDao from './plugins/dao'
Vue.use(pluginDao, dao)
// 配置
Vue.config.productionTip = false
// 项目实例化 [App]
new Vue({
render: h => h(App),
}).$mount('#app')
脚本 [main.js]:
- 在第 14-21 行实例化 [dao] 层;
- 在第24-25行集成插件[pluginDao];
- 第 15 行:导入类 [Dao];
- 第17-18行:配置对象[axios],该对象负责执行HTTP请求。该对象在第4行被导入;
- 第17行:定义一个2秒的[timeout];
- 第18行:税款计算服务器的URL;
- 第19行:用于与服务器交换Cookie;
- 第24-25行:使用[pluginDao]插件
- 第24行:导入插件;
- 第25行:集成插件。可以看到,方法[Vue.use]的第二个参数是第21行定义的[dao]层的引用。 正因如此,在 [Vue] 类/函数的所有实例中(即所有 [Vue.js] 组件中),属性 [Vue.$dao] 将指向图层 [dao];
12.5. 主视图 [App.vue]
主视图 [App] 的代码如下:
<template>
<div class="container">
<b-card>
<!-- 消息 -->
<b-alert show variant="success" align="center">
<h4>[vuejs-10] : plugin [dao], requêtes HTTP asynchrones</h4>
</b-alert>
<!-- 向税费计算服务器发起异步请求的组件-->
<Component1 @error="doSomethingWithError" @endWaiting="endWaiting" @beginWaiting="beginWaiting" />
<!-- 显示可能出现的错误 -->
<b-alert show
variant="danger"
v-if="showError">Evénement [error] intercepté par [App]. Valeur reçue = {{error}}</b-alert>
<!-- 带加载图标的等待提示 -->
<b-alert show v-if="showWaiting" variant="light">
<strong>Requête au serveur de calcul d'impôt en cours...</strong>
<b-spinner variant="primary" label="Spinning"></b-spinner>
</b-alert>
</b-card>
</div>
</template>
<script>
import Component1 from "./components/Component1";
export default {
name: "app",
// 组件状态
data() {
return {
// 控制加载图标
showWaiting: false,
// 控制错误显示
showError: false,
// 拦截的错误
error: {}
};
},
// 使用的组件
components: {
Component1
},
// 事件处理方法
methods: {
// 开始等待
beginWaiting() {
// 显示等待状态
this.showWaiting = true;
// 隐藏错误消息
this.showError = false;
},
// 结束等待
endWaiting() {
// 隐藏等待
this.showWaiting = false;
},
// 错误处理
doSomethingWithError(error) {
// 记录已发生错误
this.error = error;
// 显示错误信息
this.showError = true;
}
}
};
</script>
注释
- 第 9 行:[Component1] 是执行异步请求 HTTP 的组件。它可能触发三个事件:
- [beginWaiting]:请求即将发出。需向用户显示等待提示;
- [endWaiting]:请求已完成。需停止等待;
- [error]:请求失败。需显示错误提示;
- 第 10-13 行:显示可能出现的错误信息的提示框。该提示框由第 33 行的布尔值 [showError] 控制。它显示第 35 行的错误信息;
- 第14-18行:显示带加载图标的等待提示的警报。该警报由第47行的布尔变量[showWaiting]控制;
- 第 45-50 行:[beginWaiting] 是接收到 [beginWaiting] 事件时执行的方法。 该方法显示等待提示(第47行),并隐藏错误提示(第49行),以防该提示因先前操作而可见;
- 第 52-55 行:[endWaiting] 是接收到事件 [endWaiting] 时执行的方法。它隐藏了等待消息(第 54 行);
- 第 57-62 行:[doSomethingWithError] 是接收到事件 [error] 时执行的方法。它记录接收到的错误(第 59 行)并显示错误消息(第 61 行);
12.6. 组件 [Component1]
[Component1] 组件的代码如下:
<template>
<b-row>
<b-col>
<b-alert show
variant="warning"
v-if="showMsg">Valeur reçue du serveur = {{data}}</b-alert>
</b-col>
</b-row>
</template>
<script>
export default {
name: "component1",
// 组件状态
data() {
return {
showMsg: false
};
},
// 事件处理方法
methods: {
// 处理从服务器接收到的数据
doSomethingWithData(data) {
// 记录接收到的数据
this.data = data;
// 显示数据
this.showMsg = true;
}
},
// 组件已创建
created() {
// 与服务器初始化会话 - 异步请求
// 使用 QZXW2HTML 层方法返回的 PromiseBW2Rhb10ZQX
// 报告操作开始
this.$emit("beginWaiting");
// 启动异步操作
this.$dao
// 这是与税务计算服务器初始化一个 jSON 会话
.initSession()
// 成功时处理接收到的数据的方法
.then(data => {
// 处理接收到的数据
this.doSomethingWithData(data);
})
// 处理错误的函数(若发生错误)
.catch(error => {
// 将错误上报给父组件
this.$emit("error", error.message);
}).finally(() => {
// 等待结束
this.$emit("endWaiting");
})
}
};
</script>
注释
- 第 4-6 行:该组件仅包含一个警报,用于显示税务计算服务器返回的值,但仅在 HTTP 请求成功时才会显示。 该提示由第 17 行的布尔值 [showMsg] 控制;
- 第31-53行:组件创建完成后立即执行HTTP请求。因此,其代码被放置在第31行的[created]方法中;
- 第 35 行:通知父组件异步请求即将启动;
- 第37-39行:执行方法[this.$dao.initSession]。该方法通过税务计算服务器初始化一个jSON会话。该方法的直接结果是一个[Promise];
- 第 41-44 行:当服务器无错误地返回结果时,此代码将执行。服务器的结果存储在 [data] 中。第 43 行,调用 [doSomethingWithData] 方法处理该结果;
- 第 46-49 行:当请求执行过程中发生错误时,此代码将被执行。第 48 行,通知父组件已发生错误,并向其传递错误消息 [error.message];
- 第49-52行:该代码在任何情况下都会执行。向父组件报告请求HTTP已完成;
- 第23-28行:方法[doSomethingWithData]负责处理服务器发送的数据[data]。第25行,将该数据保存;第27行,将其显示;
12.7. 项目运行

如果启动项目时税费计算服务器未运行,则会得到以下结果:

启动服务器 [Laragon](参见 https://tahe.developpez.com/tutoriels-cours/php7),并重新加载上述页面。此时结果如下:

注:此处使用的是 https://tahe.developpez.com/tutoriels-cours/php7 中定义的第 14 版税费计算服务器。