本文详解如何用Vue.js作为前端、WordPress作为后端(Headless架构),实现前后端完全分离。
架构概览
[Vue.js SPA] ←→ [WordPress REST API] ←→ [MySQL]
↑
└─────────── 用户浏览器 ──────────────────────┘
优势:
- 前端用Vue.js(极速交互)
- 后端用WordPress(内容管理成熟)
- 完全解耦,各自独立部署
第一步:准备WordPress后端
启用REST API(默认已启用)
// functions.php(确认REST API已启用)
add_action('init', function() {
// 确保REST API不被禁用
if (defined('REST_API_ENABLED') && !REST_API_ENABLED) {
define('REST_API_ENABLED', true);
}
});
// 允许跨域请求(开发环境)
add_action('rest_api_init', function() {
remove_filter('rest_pre_serve_request', 'rest_send_cors_headers');
add_filter('rest_pre_serve_request', function($value) {
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
header('Access-Control-Allow-Credentials: true');
return $value;
});
});
创建Vue.js专用API端点
// 注册自定义API端点(返回精简数据)
add_action('rest_api_init', 'register_vue_endpoint');
function register_vue_endpoint() {
register_rest_route('vue/v1', '/posts', [
'methods' => 'GET',
'callback' => 'get_posts_for_vue',
'permission_callback' => '__return_true',
]);
}
function get_posts_for_vue($request) {
$per_page = $request->get_param('per_page') ?: 10;
$query = new WP_Query([
'post_type' => 'post',
'posts_per_page' => $per_page,
'post_status' => 'publish',
]);
$posts = [];
foreach ($query->posts as $post) {
$posts[] = [
'id' => $post->ID,
'title' => get_the_title($post),
'excerpt' => get_the_excerpt($post),
'content' => apply_filters('the_content', $post->post_content),
'featured_image' => get_the_post_thumbnail_url($post, 'full'),
'date' => get_the_date('c', $post),
'author' => get_the_author_meta('display_name', $post->post_author),
'categories' => wp_get_post_categories($post->ID, ['fields' => 'names']),
];
}
return rest_ensure_response($posts);
}
第二步:创建Vue.js前端
初始化Vue项目
# 使用Vite + Vue 3
npm create vite@latest wp-vue-frontend -- --template vue
cd wp-vue-frontend
npm install
# 安装必要依赖
npm install axios vue-router@4 pinia # Pinia = Vue 3状态管理
配置环境变量
# .env.development
VITE_WP_API_URL=http://localhost:8000/wp-json
VITE_WP_API_NAMESPACE=vue/v1
# .env.production
VITE_WP_API_URL=https://www.shenma98.com/wp-json
VITE_WP_API_NAMESPACE=vue/v1
第三步:在Vue中获取WordPress数据
创建API服务层
// src/services/wpApi.js
import axios from 'axios';
const apiClient = axios.create({
baseURL: import.meta.env.VITE_WP_API_URL,
timeout: 10000,
});
export default {
// 获取文章列表
async getPosts(perPage = 10, page = 1) {
const res = await apiClient.get(`/wp/v2/posts`, {
params: { per_page: perPage, page },
});
return {
posts: res.data,
totalPages: parseInt(res.headers['x-wp-totalpages']),
total: parseInt(res.headers['x-wp-total']),
};
},
// 获取单个文章
async getPost(slug) {
const res = await apiClient.get('/wp/v2/posts', {
params: { slug },
});
return res.data[0] || null;
},
// 获取页面
async getPage(slug) {
const res = await apiClient.get('/wp/v2/pages', {
params: { slug },
});
return res.data[0] || null;
},
// 获取媒体文件
async getMedia(mediaId) {
const res = await apiClient.get(`/wp/v2/media/${mediaId}`);
return res.data;
},
};
创建文章列表页面
<!-- src/views/PostList.vue -->
<template>
<div class="post-list">
<h1>最新文章</h1>
<div v-if="loading" class="loading">加载中...</div>
<div v-else>
<article v-for="post in posts" :key="post.id" class="post-card">
<img
v-if="post._embedded?.['wp:featuredmedia']"
:src="post._embedded['wp:featuredmedia'][0].source_url"
/>
<h2>
<router-link :to="{ name: 'post', params: { slug: post.slug } }">
{{ post.title.rendered }}
</router-link>
</h2>
<div v-html="post.excerpt.rendered"></div>
</article>
<!-- 分页 -->
<div class="pagination">
<button @click="prevPage" :disabled="currentPage <= 1">上一页</button>
<span>第 {{ currentPage }} / {{ totalPages }} 页</span>
<button @click="nextPage" :disabled="currentPage >= totalPages">下一页</button>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import wpApi from '@/services/wpApi';
const posts = ref([]);
const loading = ref(true);
const currentPage = ref(1);
const totalPages = ref(1);
const loadPosts = async () => {
loading.value = true;
try {
const res = await wpApi.getPosts(10, currentPage.value);
posts.value = res.posts;
totalPages.value = res.totalPages;
} catch (err) {
console.error('Failed to load posts:', err);
} finally {
loading.value = false;
}
};
const prevPage = () => { if (currentPage.value > 1) { currentPage.value--; loadPosts(); } };
const nextPage = () => { if (currentPage.value < totalPages.value) { currentPage.value++; loadPosts(); } };
onMounted(loadPosts);
</script>
第四步:配置Vue Router
路由配置
// src/router/index.js
import { createRouter, createWebHistory } from 'vue-router';
import PostList from '@/views/PostList.vue';
import PostDetail from '@/views/PostDetail.vue';
import PageView from '@/views/PageView.vue';
const routes = [
{ path: '/', name: 'home', component: PostList },
{ path: '/post/:slug', name: 'post', component: PostDetail, props: true },
{ path: '/:slug', name: 'page', component: PageView, props: true },
];
const router = createRouter({
history: createWebHistory(),
routes,
});
export default router;
文章详情页
<!-- src/views/PostDetail.vue -->
<template>
<div class="post-detail" v-if="post">
<h1>{{ post.title.rendered }}</h1>
<div class="post-meta">
<span>作者:{{ post._embedded?.author?.[0]?.name }}</span>
<span>日期:{{ formatDate(post.date) }}</span>
</div>
<div class="post-content" v-html="post.content.rendered"></div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { useRoute } from 'vue-router';
import wpApi from '@/services/wpApi';
const route = useRoute();
const post = ref(null);
const formatDate = (dateStr) => new Date(dateStr).toLocaleDateString('zh-CN');
onMounted(async () => {
post.value = await wpApi.getPost(route.params.slug);
});
</script>
第五步:处理WordPress认证
方式一:Application Password(推荐)
// 在Vue中获取用户信息(需要认证)
const authClient = axios.create({
baseURL: import.meta.env.VITE_WP_API_URL,
auth: {
username: 'your_username',
password: 'xxxx xxxx xxxx xxxx', // Application Password
},
});
// 创建文章(需要认证)
async function createPost(title, content) {
return authClient.post('/wp/v2/posts', {
title,
content,
status: 'publish',
});
}
方式二:JWT认证
# 在WordPress中安装 JWT Authentication for WP REST API 插件
// Vue中登录获取JWT Token
async function login(username, password) {
const res = await axios.post(`${WP_URL}/jwt-auth/v1/token`, {
username,
password,
});
localStorage.setItem('jwt_token', res.data.token);
return res.data.token;
}
// 在请求头中添加Token
apiClient.defaults.headers.common['Authorization'] = `Bearer ${localStorage.getItem('jwt_token')}`;
第六步:部署前后端
部署WordPress后端
# 正常部署WordPress(LAMP/LNMP)
# 确保REST API可访问:
curl https://www.shenma98.com/wp-json/wp/v2/posts
部署Vue.js前端
# 构建生产版本
npm run build # 输出到 dist/
# 部署到静态托管(Vercel/Netlify/CDN)
vercel --prod
# 或者部署到自己的服务器
scp -r dist/* user@server:/var/www/html/vue-app/
配置Nginx反向代理(可选)
# 将 /api/ 请求代理到WordPress
location /api/ {
proxy_pass https://www.shenma98.com/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
性能优化
1. 启用REST API缓存
// 在WordPress中启用Transient缓存
function get_cached_posts() {
$cached = get_transient('cached_posts');
if (false === $cached) {
$query = new WP_Query(['post_type' => 'post', 'posts_per_page' => 10]);
$cached = $query->posts;
set_transient('cached_posts', $cached, 12 * HOUR_IN_SECONDS);
}
return $cached;
}
2. Vue.js侧缓存
// 使用Pinia持久化缓存
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate';
const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);
// 现在store数据会自动保存到localStorage
3. 预获取(Prefetch)数据
<!-- 在文章列表页预获取文章内容 -->
<router-link
:to="{ name: 'post', params: { slug: post.slug } }"
@mouseover="prefetchPost(post.id)"
>
{{ post.title.rendered }}
</router-link>
<script setup>
function prefetchPost(postId) {
// 预获取文章数据并缓存
queryClient.prefetchQuery(['post', postId], () => wpApi.getPostById(postId));
}
</script>
2026年Headless WordPress趋势
趋势一:WordPress作为统一内容源
架构:
WordPress(内容管理)
↓ REST API / GraphQL
Vue.js / React / Next.js(前端,可多个)
↓
Web / iOS / Android / 小程序(多端消费)
趋势二:使用WPGraphQL替代REST API
// 使用GraphQL查询(更灵活)
import { request } from 'graphql-request';
const query = `
query GetPosts {
posts(first: 10) {
nodes {
title
excerpt
featuredImage {
sourceUrl
}
}
}
}
`;
const data = await request('https://example.com/graphql', query);
趋势三:ISR(增量静态再生)
// Next.js ISR:静态页面但定期更新
export async function getStaticProps() {
const posts = await wpApi.getPosts();
return {
props: { posts },
revalidate: 60, // 60秒后重新生成
};
}
决策建议
- 新项目 + 高交互 → Vue.js + WordPress REST API(Headless)
- 传统企业站 → 传统WordPress主题(更适合SEO)
- 多端应用 → Headless WordPress + GraphQL
- 快速原型 → Vue.js + WordPress REST API(开发快)
总结
WordPress REST API + Vue.js是2026年前后端分离的经典架构。WordPress负责内容管理,Vue.js负责交互体验,两者通过REST API通信。
立即行动:尝试用Vue.js创建一个简单的Headless WordPress前端,体验前后端分离的开发模式。
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

评论(0)