Skip to content

12. 项目 [vuejs-10]:插件 [dao],异步请求 HTTP

项目 [vuejs-10] 的目录结构如下:

Image

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

Image

[Vue.js] 组件利用 [dao] 层与税费计算服务器进行交互。

12.1. 依赖项安装

应用程序 [vuejs-10] 使用库 [axios] 向税费计算服务器发送异步请求。我们需要安装此依赖项:

Image

  • [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. 项目运行

Image

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

Image

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

Image

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