Skip to content

15. 项目 [vuejs-13]:组件更新,使用会话

项目 [vuejs-13] 基于项目 [vuejs-12],并进行了以下修改:由表 HTML 显示的表格被定义在一个名为 [session] 的对象中,该对象为所有组件所共享。 因此,这是一种在组件之间共享信息的方式。该概念直接借鉴了 Web 会话的思路。我们采用插件方法,将该共享对象作为 [Vue.$session] 属性提供。

项目结构如下:

Image

15.1. 对象 [session]

由所有组件共享的 [session] 对象在脚本 [./session.js] 中定义:


const session = {
  // 模拟列表
  get lignes() {
    return this._lignes;
  },
  // 生成模拟列表
  generateLignes() {
    this._lignes =
      [
        { id: 3, marié: "oui", enfants: 2, salaire: 35000, impôt: 1200 },
        { id: 5, marié: "non", enfants: 2, salaire: 35000, impôt: 1900 },
        { id: 7, marié: "non", enfants: 0, salaire: 30000, impôt: 2000 }
      ]
  },
  // 删除索引号为 n 的行
  deleteLigne(index) {
    this._lignes.splice(index, 1);
  }
}
// 导出对象 [session]
export default session;

15.2. 插件 [./plugins/pluginSession]

脚本 [pluginSession] 如下:


export default {
  install(Vue, session) {
    // 向视图类添加属性 [$session]
    Object.defineProperty(Vue.prototype, '$session', {
      // 当引用 Vue.$session 被引用时,将方法 [install] 的第二个参数 [session] 渲染出来
      get: () => session,
    })
  }
}
  • 第 4 行:共享对象 [session] 将作为所有组件的 [$session] 属性提供;

15.3. 主脚本 [main.js]

主脚本 [main.js] 如下:


// 导入
import Vue from 'vue'
import App from './App.vue'

// 插件
import BootstrapVue from 'bootstrap-vue'
Vue.use(BootstrapVue);

// bootstrap
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'

// 会话
import session from './session';
import pluginSession from './plugins/pluginSession'
Vue.use(pluginSession, session)

// 配置
Vue.config.productionTip = false

// 项目实例化[App]
new Vue({
  name: "app",
  render: h => h(App),
}).$mount('#应用程序')
  • 第 14-16 行:插件 [pluginSession] 已集成到框架 [Vue.js] 中;
  • 第16行之后,[$session]属性对所有组件可用;

15.4. 主视图 [App]

视图 [App] 现如下所示:


<template>
  <div class="container">
    <b-card>
      <!-- 消息 -->
      <b-alert show variant="success" align="center">
        <h4>[vuejs-13] : mise à jour d'un composant, partage des données avec une session</h4>
      </b-alert>
      <!-- 表HTML -->
      <Table @updateTable="updateTable" :key="versionTable"/>
    </b-card>
  </div>
</template>

<script>
import Table from "./components/Table";
export default {
  // 名称
  name: "app",
  // 组件
  components: {
    Table
  },
  // 内部状态
  data() {
    return {
      // 表版本
      versionTable: 1
    };
  },

  // 方法
  methods: {
    updateTable() {
      // eslint-disable-next-line
      console.log("App updateTable");
      // 版本表递增
      this.versionTable++;
    }
  }
};
</script>

注释

  • 视图 [App] 不再管理第 9 行中组件 [Table] 显示的数组;
  • 第 9 行:组件 [Table] 触发事件 [updateTable],该事件要求重新生成组件 [Table]。 实现此功能的一种方法是使用属性 [:key]。将该属性设为可修改值。每次修改该值时,组件 [Table] 都会被重新生成;
  • 第 9 行:[:key] 属性的值即为第 27 行中的 [versionTable] 属性。 方法 [updateTable](第 33-38 行)负责重新生成第 9 行中的组件 [Table]。 为此,该方法会递增第37行组件[Table]属性[:key]的值。随后,组件[Table]将自动重新生成;

15.5. 组件 [Table]

组件 [Table] 的变化如下:


<template>
  <div>
    <!-- 空列表 -->
    <template v-if="lignes.length==0">
      <b-alert show variant="warning">
        <h4>Votre liste de simulations est vide</h4>
      </b-alert>
      <!-- 刷新按钮-->
      <b-button variant="primary" @click="rechargerListe">Recharger la liste</b-button>
    </template>
    <!-- 非空列表-->
    <template v-if="lignes.length!=0">
      <b-alert show variant="primary" v-if="lignes.length==0">
        <h4>Liste de vos simulations</h4>
      </b-alert>
      <!-- 模拟表 -->
      <b-table striped hover responsive :items="lignes" :fields="fields">
        <template v-slot:cell(action)="row">
          <b-button variant="link" @click="supprimerLigne(row.index)">Supprimer</b-button>
        </template>
      </b-table>
    </template>
  </div>
</template>

<script>
export default {
  // 计算结果
  computed: {
    lignes() {
      return this.$session.lignes;
    }
  },
  // 内部状态
  data() {
    return {
      fields: [
        { label: "#", key: "id" },
        { label: "Marié", key: "marié" },
        { label: "Nombre d'enfants", key: "enfants" },
        { label: "Salaire", key: "salaire" },
        { label: "Impôt", key: "impôt" },
        { label: "", key: "action" }
      ]
    };
  },
  // 方法
  methods: {
    supprimerLigne(index) {
      // eslint-disable-next-line
      console.log("Table supprimerLigne", index);
      // 删除该行
      this.$session.deleteLigne(index);
      // 请求父组件更新视图
      this.$emit("updateTable");
    },
    // 重新加载显示的列表
    rechargerListe() {
      // eslint-disable-next-line
      console.log("Table rechargerListe");
      // 重新生成模拟列表
      this.$session.generateLignes();
      // 请求父组件更新视图
      this.$emit("updateTable");
    }
  }
};
</script>

注释:

  • 属性 [lignes](第 4、12、17 行)不再是父组件设定的参数,而是组件 [Table](第 30-32 行)的计算属性。 因此,[lignes] 即为数组 [$session.lignes](第 31 行);
  • 第49-56行:方法[supprimerLigne]会从表[$session.lignes]中删除一行。默认情况下,此删除操作不会改变表HTML的显示。实际上, [$session]中的元素不具备响应性:对其的修改不会反映到使用它们的组件上。因此,[Table]组件会通过[updateTable]事件 (第 55 行)请求其父级重新生成它。我们看到,父组件随后会递增 [Table] 组件的 [:key] 属性,以强制其重新生成;
  • 第 58-65 行:方法 [rechargerListe] 要求对象 [$session] 重新生成数组 [$session.lignes]。 与前文所述原因相同,默认情况下,对 [$session.liste] 的此项修改不会改变表 HTML 的显示。 因此,组件 [Table] 通过事件 [updateTable](第 64 行)请求其父级对其进行重新生成。

15.6. 项目执行

Image

结果与项目 [vuejs-12] 相同。