Vue动态添加WordPress菜单API的路线,可能的重新攻击问题

发布于 2025-01-22 12:53:58 字数 6477 浏览 0 评论 0原文

我在前端有一个VUE应用程序,后端有一个WordPress API。我正在击中菜单API,并在运行时动态地将路线添加到前端。

这很棒。直到我重置一个动态路线之一上的页面。该组件不加载,已安装()从未调用。在这一点上,我可以按预期单击“导航栏”中的路由器链接,页面组件呈现。

例如。在WordPress管理员中,我创建一个名为Hello-World的页面,然后将其添加到主菜单中。 Vue将击中API并创建具有相同名称的路线。然后,我加载页面,然后加载正常。我单击Hello World在NAV栏中的链接,它的呈现得很漂亮。

现在,我坐在http://website.com/hello-world上,然后重置页面。该应用程序安装和导航栏渲染。但是,page组件不会呈现。如果我再次单击nav栏中的链接,则可以很好地呈现。

我认为这是一个反应性问题,但我找不到。有什么建议吗?

编辑。一直在思考这个。加载了路由器组件,并异步获取菜单项。现在,我已经坐在一个动态路线上,/hello-world。该应用现在已加载,并且还不存在Hello-World路由,因为API请求仍在待处理。由于没有匹配的路由,因此VUE应用程序不知道要安装哪个组件...也许有一种方法可以使路由器组件本身自动反应?

相关路由器代码...

store.dispatch("getPrimaryMenu").then(() => {
    store.state.menu.items.forEach((item) => {
        if (item.object === "post") {
            router.addRoute({
                path: `/${item.slug}`,
                name: item.slug,
                component: () => import("@/views/Post.vue"),
            });
        }
        if (item.object === "page") {
            router.addRoute({
                path: `/${item.slug}`,
                name: item.slug,
                component: () => import("@/views/Page.vue"),
            });
        }
    });
});

和我的商店...

export default createStore({
    state: {
        menu: {
            items: [],
        },
        page: {
            title: {},
            content: {},
        },
        post: {
            title: {},
            content: {},
        },
    },
    mutations: {
        SET_MENU(state, data) {
            state.menu = data
        },
        SET_PAGE(state, data) {
            state.page = data
        },
        SET_POST(state, data) {
            state.post = data
        },
    },
    actions: {
        getPrimaryMenu({ commit, state }) {
            console.log('get menus')
            return new Promise(async (resolve, reject) => {
                try {
                    const { data } = await axios.get(
                        `http://sslchkr.com/wp-json/menus/v1/menus/primary`, {
                        headers: {
                            'Content-Type': 'application/json'
                        }
                    }
                    )
                    commit('SET_MENU', data)
                    resolve(data)
                } catch (e) {
                    reject(e)
                }
            })
        },
        getPage({ commit, state }, payload) {
            console.log('get page')
            return new Promise(async (resolve, reject) => {
                try {
                    const { data } = await axios.get(
                        `http://sslchkr.com/wp-json/wp/v2/pages/${payload.id}`, {
                        headers: {
                            'Content-Type': 'application/json'
                        }
                    }
                    )
                    commit('SET_PAGE', data)
                    resolve(data)
                } catch (e) {
                    reject(e)
                }
            })
        },
        getPost({ commit, state }, payload) {
            console.log('get post')
            return new Promise(async (resolve, reject) => {
                try {
                    const { data } = await axios.get(
                        `http://sslchkr.com/wp-json/wp/v2/posts/${payload.id}`, {
                        headers: {
                            'Content-Type': 'application/json'
                        }
                    }
                    )
                    commit('SET_POST', data)
                    resolve(data)
                } catch (e) {
                    reject(e)
                }
            })
        },
    },
}

页面组件... 我将路由名称与菜单对象的项目slug匹配,并使用该项目object_id获取页面对象。

<template>
  <div class="page">
    <div>
      <h1>{{ page.title.rendered }}</h1>
    </div>
    <div v-html="page.content.rendered"></div>
  </div>
</template>

<script>
export default {
  name: "Page",
  computed: {
    menuItem() {
      return this.$store.state.menu.items.find(
        (item) => item.slug === this.$route.name
      );
    },
    page() {
      return this.$store.state.page;
    },
  },
  mounted() {
    this.$store.dispatch("getPage", { id: this.menuItem.object_id });
  },
};
</script>

和NAV组件的完整性...

<template>
<ul id="menu-primary list-inline">
    <li
        v-for="item in menu.items"
        :key="item.ID"
        class="nav-item list-inline-item"
    >
        <router-link :to="slash(item.slug)" class="nav-link">{{
            item.title
        }}</router-link>
    </li>
</ul>
</template>

<script>
export default {
  name: "Nav",
  computed: {
    menu() {
      return this.$store.state.menu;
    },
  },
  methods: {
    slash(s) {
      return `/${s}`;
    },
  },
};
</script>

编辑以包括main.js和app.vue

import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap/dist/js/bootstrap.js'
import 'vue-toastification/dist/index.css'

import { createApp } from 'vue'
import Toast, { POSITION } from 'vue-toastification'

import App from './App.vue'
import router from './router'
import store from './store'

let app = createApp(App)
app.use(store)
app.use(router)
app.use(Toast, { position: POSITION.TOP_CENTER })
app.mount('#app')

<template>
  <link rel="stylesheet" :href="theme" />
  <Nav />
  <div class="container-fluid">
    <div class="row padding-top">
      <div class="col-md-2"></div>
      <div class="col-md-8">
        <router-view :key="$route.path" />
      </div>
      <div class="col-md-2"></div>
    </div>
  </div>
</template>

<script>
import Nav from "@/components/Nav.vue";

export default {
  components: {
    Nav,
  },
  computed: {
    theme() {
      return this.$store.state.theme;
    },
  },
  mounted() {
    this.$store.dispatch("getTheme");
  },
};
</script>

I have a vue application on the frontend and a wordpress api on the backend. I am hitting the menus api and dynamically adding routes to the frontend at run time.

This works great. Until I reset the page on one of the dynamic routes. The component does not load and mounted() is never called. At this point, I can click the router link in the nav bar and the page component renders as expected.

For example. In the wordpress admin, I create a page called hello-world and add it to the primary menu. Vue will hit the api and create a route with the same name. I then load up the page and it loads fine. I click the hello world link in the nav bar, and it renders beautifully.

Now, I'm sitting at http://website.com/hello-world, and I reset the page. The app mounts and the nav bar renders. However, the page component does not render. If I click the link in the nav bar again, then it renders fine.

I am thinking this is a reactivity problem, but I can't find it. Any suggestions?

Edit. Been pondering this. The router component is loaded, and fetches the menu items asynchronously. Now, Im already sitting on one of the dynamic routes, /hello-world. The app is now loaded and there doesn't exist yet a hello-world route, since the api request is still pending. Since there is no matching route, the vue application doesn't know which component to mount... Perhaps there is a way to make the router component itself reactive?

relevant router code...

store.dispatch("getPrimaryMenu").then(() => {
    store.state.menu.items.forEach((item) => {
        if (item.object === "post") {
            router.addRoute({
                path: `/${item.slug}`,
                name: item.slug,
                component: () => import("@/views/Post.vue"),
            });
        }
        if (item.object === "page") {
            router.addRoute({
                path: `/${item.slug}`,
                name: item.slug,
                component: () => import("@/views/Page.vue"),
            });
        }
    });
});

and my store...

export default createStore({
    state: {
        menu: {
            items: [],
        },
        page: {
            title: {},
            content: {},
        },
        post: {
            title: {},
            content: {},
        },
    },
    mutations: {
        SET_MENU(state, data) {
            state.menu = data
        },
        SET_PAGE(state, data) {
            state.page = data
        },
        SET_POST(state, data) {
            state.post = data
        },
    },
    actions: {
        getPrimaryMenu({ commit, state }) {
            console.log('get menus')
            return new Promise(async (resolve, reject) => {
                try {
                    const { data } = await axios.get(
                        `http://sslchkr.com/wp-json/menus/v1/menus/primary`, {
                        headers: {
                            'Content-Type': 'application/json'
                        }
                    }
                    )
                    commit('SET_MENU', data)
                    resolve(data)
                } catch (e) {
                    reject(e)
                }
            })
        },
        getPage({ commit, state }, payload) {
            console.log('get page')
            return new Promise(async (resolve, reject) => {
                try {
                    const { data } = await axios.get(
                        `http://sslchkr.com/wp-json/wp/v2/pages/${payload.id}`, {
                        headers: {
                            'Content-Type': 'application/json'
                        }
                    }
                    )
                    commit('SET_PAGE', data)
                    resolve(data)
                } catch (e) {
                    reject(e)
                }
            })
        },
        getPost({ commit, state }, payload) {
            console.log('get post')
            return new Promise(async (resolve, reject) => {
                try {
                    const { data } = await axios.get(
                        `http://sslchkr.com/wp-json/wp/v2/posts/${payload.id}`, {
                        headers: {
                            'Content-Type': 'application/json'
                        }
                    }
                    )
                    commit('SET_POST', data)
                    resolve(data)
                } catch (e) {
                    reject(e)
                }
            })
        },
    },
}

a page component...
I am matching the route name to an item slug from the menu object, and using that item object_id to fetch the page object.

<template>
  <div class="page">
    <div>
      <h1>{{ page.title.rendered }}</h1>
    </div>
    <div v-html="page.content.rendered"></div>
  </div>
</template>

<script>
export default {
  name: "Page",
  computed: {
    menuItem() {
      return this.$store.state.menu.items.find(
        (item) => item.slug === this.$route.name
      );
    },
    page() {
      return this.$store.state.page;
    },
  },
  mounted() {
    this.$store.dispatch("getPage", { id: this.menuItem.object_id });
  },
};
</script>

and the nav component for completeness...

<template>
<ul id="menu-primary list-inline">
    <li
        v-for="item in menu.items"
        :key="item.ID"
        class="nav-item list-inline-item"
    >
        <router-link :to="slash(item.slug)" class="nav-link">{{
            item.title
        }}</router-link>
    </li>
</ul>
</template>

<script>
export default {
  name: "Nav",
  computed: {
    menu() {
      return this.$store.state.menu;
    },
  },
  methods: {
    slash(s) {
      return `/${s}`;
    },
  },
};
</script>

Edit to include main.js and App.vue

import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap/dist/js/bootstrap.js'
import 'vue-toastification/dist/index.css'

import { createApp } from 'vue'
import Toast, { POSITION } from 'vue-toastification'

import App from './App.vue'
import router from './router'
import store from './store'

let app = createApp(App)
app.use(store)
app.use(router)
app.use(Toast, { position: POSITION.TOP_CENTER })
app.mount('#app')

<template>
  <link rel="stylesheet" :href="theme" />
  <Nav />
  <div class="container-fluid">
    <div class="row padding-top">
      <div class="col-md-2"></div>
      <div class="col-md-8">
        <router-view :key="$route.path" />
      </div>
      <div class="col-md-2"></div>
    </div>
  </div>
</template>

<script>
import Nav from "@/components/Nav.vue";

export default {
  components: {
    Nav,
  },
  computed: {
    theme() {
      return this.$store.state.theme;
    },
  },
  mounted() {
    this.$store.dispatch("getTheme");
  },
};
</script>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文