javascript - Vue.js 如何在路由器 View 组件之间发出事件

标签 javascript vue.js vue-router

我正在使用 vue.js 和 api 作为后端构建电子商务网站,
我有一个名为 Main.vue 的根组件
导航为 router-linkrouter-view 的正文.

在一条名为 Cart.vue 的 route 当用户更新我需要的某些产品的数量时 Cart.vue$emit一个 event到根组件Main.vue触发一个名为 getCartTotal() 的函数

提示 Cart.vue不是 child componentMain.vueMain.vue :

<template>
  <div>
    <div>
      <div v-on:getCartAgain="getCartTotal()" id="top-header" class="has-background-ishtari">
        <div class="container" style="padding:5px 0">
          <div class="is-hidden-mobile" style="font-size: 13px">
            <div
              class="has-text-white has-text-right"
              style="display: flex;align-items: center;justify-content: flex-end"
            >
              <i class="icon material-icons" style="margin-right: 0px;">attach_money</i>
              <span style="margin-right:15px">Best Deals</span>

              <i class="icon material-icons" style="margin-right: 5px;">low_priority</i>
              <span style="margin-right: 15px">Free & Easy Returns</span>

              <i class="icon material-icons" style="margin-right: 5px;">local_shipping</i>
              <span>Free Delivery (OVER $100)</span>
            </div>
          </div>
        </div>

        <div class="container" style="padding:10px 0">
          <div style="display: flex;justify-content: space-between;align-items: center;">
            <div id="header-logo" @click="openHomePage()">
              <img src="../assets/images/logo-ishtari.png" class width="140" />
            </div>
            <div style="flex-grow: 2;margin:0 40px">
              <p class="control is-expanded">
                <input
                  id="header-search"
                  class="input is-radiusless"
                  style="height: 35px;"
                  type="text"
                  placeholder="What are you looking for?"
                />
              </p>
            </div>
            <div
              class="has-text-white"
              style="display: flex;align-items: center;justify-content: space-between"
            >
              <div style="display: flex;align-items: center;padding-right:10px">
                <span>Login Or SignUp</span>
                <i class="icon material-icons">arrow_drop_down</i>
              </div>

              <div
                id="cart-container"
                style="display: flex;align-items: center;padding-left: 15px;border-left:1px solid rgba(255,255,255,0.5)"
                @click="openCartPage()"
              >
                <span style="margin-right:5px">Cart</span>
                <span>
                  <i class="icon material-icons">shopping_cart</i>
                  <span
                    class="has-background-ishtari-blue is-paddingless"
                    :class="this.cartCount.length == 0 ? 'button is-loading' : ''"
                    id="cart-total"
                  >{{this.cartCount}}</span>
                </span>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>

    <vue-page-transition name="fade-in-right">
      <router-view></router-view>
    </vue-page-transition>
  </div>
</template>
<script>
import VueCookies from "vue-cookies";

export default {

  data() {
    return {
      showNav: false,
      cartCount: "",
      readyToken: false
    };
  },
  created() {
    this.checkToken();
  },
  mounted() {
    this.getCartTotal();
  },

  methods: {
    openCartPage() {
      this.$router.push({ name: "cart" }).catch(err => {
        return err;
      });
    },
    openHomePage() {
      this.$router.push({ name: "home" }).catch(err => {
        return err;
      });
    },

    checkToken() {
      if (!VueCookies.isKey("token")) {
        let requestBody = {
          client_id: "shopping_oauth_client",
          client_secret: "shopping_oauth_secret"
        };
        let requestHeader = {
          "Content-Type": "application/x-www-form-urlencoded",
          Authorization:
            "Basic c2hvcHBpbmdfb2F1dGhfY2xpZW50OnNob3BwaW5nX29hdXRoX3NlY3JldA",
          "Access-Control-Allow-Origin": "*",
          "Cache-Control": null,
          "X-Requested-With": null
        };

        window.axios
          .post(window.main_urls["get-token"], requestBody, {
            headers: requestHeader
          })
          .then(response => {
            VueCookies.set("token", response.data.access_token);
          });
      } else {
        console.log(VueCookies.get("token"));
      }
    },

    getCartTotal() {
      console.log("here");
      let requestHeader = {
        Authorization: "Bearer " + VueCookies.get("token"),
        "Access-Control-Allow-Origin": "*",
        "Cache-Control": null,
        "X-Requested-With": null
      };
      window.axios
        .get(window.main_urls["get-cart"], { headers: requestHeader })
        .then(response => {
          if (response.data.error === "Cart is empty") {
            console.log(response.data);
            this.cartCount = 0;
          } else {
            this.cartCount = response.data.data.products.length.toString();
          }
        });
    }
  }
};
</script>
<style>
</style>


Cart.vue :

<template>
  <div class="has-background-ishtari-grey">
    <div class="container">
      <div>
        <section v-if="this.loading || this.emptyCart" class="hero is-fullheight-with-navbar">
          <div
            v-if="this.loading"
            class="button is-loading hero is-fullheight-with-navbar has-background-ishtari-grey"
            style="border: none"
          >Please Wait</div>
          <div v-if="! this.loading && this.emptyCart" class="hero-body">
            <div class="container has-text-centered">
              <i class="material-icons has-text-grey" style="font-size: 80px">shopping_cart</i>
              <h1 class="title has-text-grey has-text-weight-bold is-4">Your Shopping Cart Is Empty</h1>
              <h2 class="subtitle title has-text-grey is-6">what are you waiting for</h2>
              <br />
              <button
                @click="goHome()"
                class="button is-ishtari-blue is-outlined has-text-weight-bold is-7"
              >START SHOPPING</button>
            </div>
          </div>
        </section>
      </div>

      <div class="section" v-if="! this.loading && !this.emptyCart">
        <div class="columns is-bordered" id="cart-products-container">
          <div class="column is-9" style="margin-right:20px">
            <h1
              class="subtitle has-text-weight-bold is-4"
            >My Cart ({{this.cartData.products.length}} items)</h1>
            <img
              src="https://storage.googleapis.com/noon-cdn-res/rn/banners/en_disclaimer-cart-desktop.gif"
              style="margin-bottom:15px"
            />

            <div
              class="cart-product-row has-background-white"
              style="padding:10px 15px"
              v-for="product in cartData.products"
              :key="product.product_id"
            >
              <div
                class="columns padding-top:20px"
                :class="product.stock ? '' : 'has-background-danger'"
              >
                <div class="image is-128x128 column is-narrow">
                  <img :src="product.thumb" />
                </div>

                <div
                  class="column"
                  style="display:flex;flex-direction:column;justify-content:space-between"
                >
                  <p
                    class="has-text-grey subtitle is-7 is-marginless"
                    style="margin-bottom:10px !important"
                  >{{product.model}}</p>
                  <p class="has-text-weight-bold" style="font-size:14px">{{product.name}}</p>
                  <p>
                    <i @click="product.quantity = 0;updateCartQuantity(product.key,0)" class="material-icons has-text-grey" id="cart-delete">delete</i>
                  </p>
                </div>
                <div
                  class="column is-narrow"
                  style="padding-left:15px;display:flex;flex-direction:column;justify-content:center"
                >
                  <p class="has-text-weight-bold">{{product.price}}</p>
                </div>

                <div
                  class="column is-narrow"
                  style="display:flex;flex-direction:column;justify-content:center"
                >
                  <div class="field has-addons">
                    <p class="control">
                      <a
                        @click="product.quantity = Number(product.quantity) - 1;updateCartQuantity(product.key,product.quantity)"
                        class="button"
                      >-</a>
                    </p>
                    <p class="control">
                      <input
                        class="input has-text-centered"
                        type="text"
                        placeholder="0"
                        style="width:80px"
                        readonly
                        :value="product.quantity"
                        :ref="product.product_id"
                      />
                    </p>
                    <p class="control">
                      <a
                        class="button"
                        @click="product.quantity = Number(product.quantity) + 1;updateCartQuantity(product.key,product.quantity)"
                      >+</a>
                    </p>
                  </div>
                </div>
              </div>
            </div>
          </div>
          <div class="column is-narrow" style=" align-self:flex-start;margin-top:10px">
            <div style="border:1px solid #eee; background:#f7f9fe; padding:20px 15px">
              <div class="field has-addons">
                <p class="control">
                  <input class="input is-ishtari-green" type="text" placeholder="Coupon Code" />
                </p>
                <p class="control">
                  <a class="button is-ishtari-green">Apply</a>
                </p>
              </div>

              <div class="columns">
                <div class="column">
                  <p class="has-text-weight-bold">Order Summary</p>
                </div>
              </div>
              <div class="columns"></div>
              <div v-for="total in cartData.totals" :key="total.code">
                <div class="columns">
                  <div class="column">
                    <p>{{total.title}}</p>
                  </div>
                  <div class="column is-narrow">
                    <p>{{total.text}}</p>
                  </div>
                </div>
              </div>
            </div>
            <button
              class="button is-ishtari-blue has-text-weight-bold"
              style="display:block;width:100%;margin-top:10px"
            >CHECKOUT NOW</button>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import VueCookies from "vue-cookies";

export default {
  data() {
    return {
      cartData: [],
      emptyCart: false,
      loading: true
    };
  },

  created() {
    this.getCartContent();
  },
  methods: {
    goHome() {
      this.$router.push({ name: "home" }).catch(err => {
        return err;
      });
    },

    getCartContent() {
      let requestHeader = {
        Authorization: "Bearer " + VueCookies.get("token")
      };
      window.axios
        .get(window.main_urls["get-cart"], { headers: requestHeader })
        .then(response => {
          if (response.data.error === "Cart is empty") {
            this.emptyCart = true;
            this.loading = false;
          } else {
            this.loading = false;
            this.cartData = response.data.data;
          }
        });
    },

    updateCartQuantity(pkey, pquan) {
      this.loading = true;
      let requestHeader = {
        Authorization: "Bearer " + VueCookies.get("token")
      };
      let requestBody = {
        key: pkey.toString(),
        quantity: pquan.toString()
      };

      window.axios
        .put(window.main_urls["get-cart"], requestBody, {
          headers: requestHeader
        })
        .then(response => {
          if (response.data.success == "1") {
            this.getCartContent();
            this.$emit("getCartAgain");
          }

        });
    },
  }
};
</script>
<style scoped la>
.cart-product-row {
  border-bottom: 1px solid #eee;
  padding: 10px 0;
}
.cart-product-row:last-of-type {
  border-bottom: none;
}
#cart-delete {
  font-size: 20px;
  cursor: pointer;
}
#cart-delete:hover{
  transform: scale(1.05);
}
</style>



我需要名为 updateCartQuantity() 的函数在 Cart.vue触发函数 getCartTotal()Main.vue

最佳答案

Vuex 可能比你需要的多一点。您可以使用 pub/sub通过创建一个导出新 vue 实例的简单文件,您可以监听该 Vue 实例上发出的事件:

//bus.js

import Vue from 'vue'

export default new Vue()

然后在你的Main.vue你可以导入:
import EventBus from '@/path/to/bus'

在您的 created钩子(Hook)你可以设置一个监听器:
created() {
    EventBus.$on('refresh_cart_total', this.getCartTotal)
}

你可以$emit来自您的 Cart.vue 的那个事件当你需要的时候。再次导入总线:
import EventBus from '@/path/to/bus'

然后在你的Cart.vue需要时调用它:
EventBus.$emit('refresh_cart_total')

现在你有了一个双向的发布/订阅系统,你不需要为像这样的简单任务引入 Vuex。

奖金

为了保持干燥,您还可以在 bus.js 中实现常量。 , 如:
export const REFRESH_CART_TOTAL = 'refresh_cart_total'

现在您可以使用 import * as CART_CONSTANTS from '/path/to/bus'并使用此方法:
EventBus.$on(CART_CONSTANTS.REFRESH_CART_TOTAL, this.getCartTotal)

和:
EventBus.$emit(CART_CONSTANTS.REFRESH_CART_TOTAL)

关于javascript - Vue.js 如何在路由器 View 组件之间发出事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61034661/

相关文章:

javascript - 使用 MomentJS 将时区特定时间转换为 UTC

vue.js - 使用 Vue.js 的 JWT 身份验证

vue.js - Nuxt 3 路由 : "Simple" dynamic route return 404 error

routes - 使用 nuxtJS 处理子域

php - 是否可以检测并避免用户在在线测试项目中打开新选项卡?

javascript - 混淆代码的迹象

javascript - 顺时针或逆时针旋转网页

vue.js - 在不更改路由的情况下重新评估 Nuxt.js 中间件

vue.js - VueJS Grandchild 组件调用Great grandparent 组件中的函数

vue.js - 如何在 Vue.js 中实现 n 级嵌套动态路由?