Vue动态添加WordPress菜单API的路线,可能的重新攻击问题
我在前端有一个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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论