Skip to content

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

[vuejs-13] 项目在 [vuejs-12] 项目的基础上进行了如下改动:HTML 表格显示的数组被定义在一个所有组件均可访问的 [session] 对象中。因此,这成为组件之间共享信息的一种方式。这一概念直接借鉴了 Web 会话的概念。我们通过插件方法,将该共享对象作为 [Vue.$session] 属性提供给各组件使用。

项目目录结构如下:

Image

15.1. [session] 对象

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


const session = {
  // liste des simulations
  get lignes() {
    return this._lignes;
  },
  // génération de la liste des simulations
  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 }
      ]
  },
  // suppression ligne n° index
  deleteLigne(index) {
    this._lignes.splice(index, 1);
  }
}
// export de l'objet [session]
export default session;

15.2. [./plugins/pluginSession] 插件

[pluginSession] 脚本如下:


export default {
  install(Vue, session) {
    // ajoute une propriété [$session] à la classe vue
    Object.defineProperty(Vue.prototype, '$session', {
      // lorsque Vue.$session est référencé, on rend le 2ième paramètre [session] de la méthode [install]
      get: () => session,
    })
  }
}
  • 第 4 行:共享对象 [session] 将作为 [$session] 属性在所有组件中可用;

15.3. 主脚本 [main.js]

主脚本 [main.js] 如下所示:


// imports
import Vue from 'vue'
import App from './App.vue'

// plugins
import BootstrapVue from 'bootstrap-vue'
Vue.use(BootstrapVue);
 
// bootstrap
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'
 
// session
import session from './session';
import pluginSession from './plugins/pluginSession'
Vue.use(pluginSession, session)
 
// configuration
Vue.config.productionTip = false
 
// instanciation projet [App]
new Vue({
  name: "app",
  render: h => h(App),
}).$mount('#app')
  • 第 14–16 行:[pluginSession] 插件已集成到 [Vue.js] 框架中;
  • 第 16 行之后,所有组件均可使用 [$session] 属性;

15.4. 主视图 [App]

[App] 视图现在如下所示:


<template>
  <div class="container">
    <b-card>
      <!-- message -->
      <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>
      <!-- table HTML -->
      <Table @updateTable="updateTable" :key="versionTable"/>
    </b-card>
  </div>
</template>
 
<script>
import Table from "./components/Table";
export default {
  // nom
  name: "app",
  // composants
  components: {
    Table
  },
  // état interne
  data() {
    return {
      // version table
      versionTable: 1
    };
  },
 
  // méthodes
  methods: {
    updateTable() {
      // eslint-disable-next-line
      console.log("App updateTable");
      // incrément version table
      this.versionTable++;
    }
  }
};
</script>

评论

  • 第 9 行中的 [App] 视图不再管理由 [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>
    <!-- empty list -->
    <template v-if="lignes.length==0">
      <b-alert show variant="warning">
        <h4>Votre liste de simulations est vide</h4>
      </b-alert>
      <!-- reload button-->
      <b-button variant="primary" @click="rechargerListe">Recharger la liste</b-button>
    </template>
    <!-- non-empty list-->
    <template v-if="lignes.length!=0">
      <b-alert show variant="primary" v-if="lignes.length==0">
        <h4>Liste de vos simulations</h4>
      </b-alert>
      <!-- simulation table -->
      <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 {
  // état calculé
  computed: {
    lignes() {
      return this.$session.lignes;
    }
  },
  // état interne
  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" }
      ]
    };
  },
  // méthodes
  methods: {
    supprimerLigne(index) {
      // eslint-disable-next-line
      console.log("Table supprimerLigne", index);
      // on supprime la ligne
      this.$session.deleteLigne(index);
      // on demande au composant parent de mettre à jour la vue
      this.$emit("updateTable");
    },
    // rechargement de la liste affichée
    rechargerListe() {
      // eslint-disable-next-line
      console.log("Table rechargerListe");
      // on régènère la liste des simulations
      this.$session.generateLignes();
      // on demande au composant parent de mettre à jour la vue
      this.$emit("updateTable");
    }
  }
};
</script>

注释:

  • [rows] 属性(第 4、12、17 行)不再是由父组件设置的参数,而是 [Table] 组件的计算属性(第 30–32 行)。因此,[rows] 即为数组 [$session.rows](第 31 行);
  • 第 49–56 行:[deleteRow] 方法会从 [$session.rows] 数组中移除一行。默认情况下,此删除操作不会改变 HTML 表格的显示。这是因为 [$session] 的元素不具备响应式特性:对其所做的更改不会反映在使用它们的组件中。 因此,[Table] 组件通过 [updateTable] 事件(第 55 行)请求其父组件重新生成它。我们看到,父组件随后会递增 [Table] 组件的 [:key] 属性以强制其重新生成;
  • 第 58–65 行:[reloadList] 方法请求 [$session] 对象重新生成 [$session.rows] 数组。与之前的原因相同,默认情况下,对 [$session.list] 的此修改也不会改变 HTML 表格的显示。 因此,[Table] 组件通过 [updateTable] 事件(第 64 行)请求其父组件重新生成它。

15.6. 运行项目

Image

我们得到了与 [vuejs-12] 项目中相同的结果。