【VUE】Vue3.0实现文章详情页功能

📅 2026/7/8 16:07:56
【VUE】Vue3.0实现文章详情页功能
【VUE】Vue3.0实现文章详情页功能项目目录结构核心代码实现1. API模块 (src/api/article.js)2. 路由配置 (src/router/index.js)3. 文章列表页面 (src/views/ArticleListView.vue)4. 文章卡片组件 (src/components/ArticleCard.vue)5. 文章详情页面 (src/views/ArticleView.vue)6. App.vue (src/App.vue)7. main.js (src/main.js)实现说明运行项目步骤注意事项下面是一个完整的Vue3项目实现包括目录结构、路由配置、API调用和页面渲染。项目目录结构src/ ├── api/ │ └── article.js # API请求模块 ├── components/ │ ├── ArticleCard.vue # 文章卡片组件 │ └── ArticleDetail.vue # 文章详情组件 ├── router/ │ └── index.js # 路由配置 ├── views/ │ ├── ArticleListView.vue # 文章列表页面 │ └── ArticleView.vue # 文章详情页面 ├── App.vue └── main.js核心代码实现1. API模块 (src/api/article.js)importaxiosfromaxios;constapiClientaxios.create({baseURL:http://your-flask-api.com/api,// 替换为你的Flask API地址timeout:5000});exportdefault{getArticleList(){returnapiClient.get(/articles);},getArticleDetail(id){returnapiClient.get(/articles/${id});}}2. 路由配置 (src/router/index.js)import{createRouter,createWebHistory}fromvue-router;importArticleListViewfrom/views/ArticleListView.vue;importArticleViewfrom/views/ArticleView.vue;constroutes[{path:/,name:ArticleList,component:ArticleListView},{path:/article/:id,name:ArticleDetail,component:ArticleView,props:true}];constroutercreateRouter({history:createWebHistory(),routes});exportdefaultrouter;3. 文章列表页面 (src/views/ArticleListView.vue)template div classarticle-list h1文章列表/h1 div v-ifloading加载中.../div div v-else ArticleCard v-forarticle in articles :keyarticle.id :articlearticle clickgoToArticle(article.id) / /div /div /template script import { ref, onMounted } from vue; import { useRouter } from vue-router; import ArticleCard from /components/ArticleCard.vue; import articleApi from /api/article; export default { components: { ArticleCard }, setup() { const router useRouter(); const articles ref([]); const loading ref(true); const fetchArticles async () { try { const response await articleApi.getArticleList(); articles.value response.data; } catch (error) { console.error(获取文章列表失败:, error); } finally { loading.value false; } }; const goToArticle (id) { router.push({ name: ArticleDetail, params: { id } }); }; onMounted(() { fetchArticles(); }); return { articles, loading, goToArticle }; } } /script style .article-list { max-width: 800px; margin: 0 auto; padding: 20px; } /style4. 文章卡片组件 (src/components/ArticleCard.vue)template div classarticle-card div classcontent h3{{ article.title }}/h3 p{{ article.excerpt }}/p small作者: {{ article.author }} | 发布日期: {{ formatDate(article.publishDate) }}/small /div /div /template script export default { props: { article: { type: Object, required: true } }, methods: { formatDate(dateString) { return new Date(dateString).toLocaleDateString(); } } } /script style scoped .article-card { border: 1px solid #eee; border-radius: 8px; padding: 15px; margin-bottom: 15px; transition: box-shadow 0.3s; cursor: pointer; } .article-card:hover { box-shadow: 0 2px 8px rgba(0,0,0,0.1); } .content h3 { margin-top: 0; } /style5. 文章详情页面 (src/views/ArticleView.vue)template div classarticle-detail div v-ifloading classloading加载中.../div div v-else-ifarticle button clickgoBack classback-button返回列表/button div classdetail-content h1{{ article.title }}/h1 div classmeta span作者: {{ article.author }}/span span发布日期: {{ formatDate(article.publishDate) }}/span span阅读量: {{ article.readCount }}/span /div div v-ifarticle.coverImage classcover-image img :srcarticle.coverImage alt封面图片 /div div classcontent v-htmlarticle.content/div /div /div div v-else classerror p无法加载文章内容/p button clickgoBack返回列表/button /div /div /template script import { ref, onMounted, watch } from vue; import { useRoute, useRouter } from vue-router; import articleApi from /api/article; export default { props: { id: { type: String, required: true } }, setup(props) { const route useRoute(); const router useRouter(); const article ref(null); const loading ref(true); const fetchArticle async () { try { loading.value true; const response await articleApi.getArticleDetail(props.id); article.value response.data; } catch (error) { console.error(获取文章详情失败:, error); article.value null; } finally { loading.value false; } }; const formatDate (dateString) { return new Date(dateString).toLocaleDateString(); }; const goBack () { router.push({ name: ArticleList }); }; onMounted(() { fetchArticle(); }); // 监听props变化当在同一组件中切换文章时重新获取数据 watch(() props.id, (newId) { if (newId) fetchArticle(); }); return { article, loading, formatDate, goBack }; } } /script style scoped .article-detail { max-width: 800px; margin: 0 auto; padding: 20px; } .back-button { padding: 8px 16px; margin-bottom: 20px; background-color: #f5f5f5; border: 1px solid #ddd; border-radius: 4px; cursor: pointer; } .back-button:hover { background-color: #e9e9e9; } .detail-content h1 { margin-top: 0; } .meta { display: flex; justify-content: space-between; margin: 10px 0 20px; padding-bottom: 10px; border-bottom: 1px solid #eee; font-size: 0.9em; color: #666; } .cover-image { margin: 20px 0; } .cover-image img { width: 100%; border-radius: 4px; } .content { line-height: 1.6; } .loading { text-align: center; padding: 40px; } .error { text-align: center; padding: 40px; color: #f56c6c; } /style6. App.vue (src/App.vue)template router-view / /template script export default { name: App } /script style #app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; color: #2c3e50; } /style7. main.js (src/main.js)import{createApp}fromvue;importAppfrom./App.vue;importrouterfrom./router;importaxiosfromaxios;constappcreateApp(App);// 全局配置axiosaxios.defaults.baseURLprocess.env.VUE_APP_API_BASE_URL||http://your-flask-api.com/api;app.use(router);app.mount(#app);实现说明路由设置使用Vue Router实现路由跳转文章详情页使用动态路由参数来传递文章IDAPI封装将API请求封装在单独的模块中便于维护异步请求使用axios进行HTTP请求使用async/await处理异步操作响应式设计使用Vue3的reactive和ref实现响应式数据代码组织将组件分为视图组件和基础组件便于复用用户体验添加加载状态显示和错误处理运行项目步骤创建Vue项目vue create article-app安装依赖npm install axios vue-routernext创建上述目录结构并添加文件配置环境变量可选在项目根目录创建.env文件VUE_APP_API_BASE_URLhttp://your-flask-api.com/api注意事项后端API应返回以下结构的数据文章列表文章ID、标题、摘要、作者、发布日期等文章详情标题、作者、内容、封面图片、发布日期等如果使用v-html渲染内容需要确保内容安全防止XSS攻击根据实际需求调整样式在实际项目中添加错误处理和加载状态管理确保跨域问题已经解决在Flask中配置CORS