欢迎光临
我们一直在努力

【Web全栈进阶】Vue3起步:组件、模板语法、响应式数据

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */
#content_views .toc,
/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */
#content_views.markdown_views > p:empty:has(+ .toc),
#content_views.markdown_views > .toc + p:empty,
/* 富文本旧版目录标记 */
#content_views.htmledit_views #main-toc,
#content_views.htmledit_views #hr-toc,
#content_views.htmledit_views p[id*=\”-toc\”] {
display: none !important;
}
/* 目录去掉后,紧跟的首个标题不再多出一块上边距 */
#content_views.markdown_views > .toc + h1,
#content_views.markdown_views > .toc + h2,
#content_views.markdown_views > .toc + h3,
#content_views.markdown_views > .toc + h4,
#content_views.markdown_views > .toc + p:empty + h1,
#content_views.markdown_views > .toc + p:empty + h2,
#content_views.markdown_views > .toc + p:empty + h3,
#content_views.markdown_views > .toc + p:empty + h4 {
margin-top: 0 !important;
}

今天正式写Vue。目标是早报站首页的第一个真实组件:文章卡片——标题、摘要、链接、点赞按钮,全站复用。

🎯 本篇学会四件事:单文件组件(SFC)怎么写、ref响应式怎么用、props怎么传参、v-for怎么渲染列表。含组件代码约60行。


📌 太长不看版(给想快速上手的你)

项目信息一句话说明
本篇目标 写出早报站第一个Vue组件:ArticleCard
代码行数 ~60行(含注释)
依赖 无新增(脚手架已就位)
核心功能 组件 + props传参 + ref响应式 + v-for列表
跑起来的命令 npm run dev → http://localhost:5173
核心知识点 SFC、ref、defineProps、computed、v-for
做完你能得到 一个可复用、可交互、有隔离样式的文章卡片组件

一、概念复习:三句话把Vue装进脑子

在写代码前,把前端世界观翻译成Vue的三个具体机制:

#机制说明
① 组件 = 自包含的积木 一个.vue文件 = HTML(长得怎样)+ JS(怎么反应)+ CSS(怎么打扮)住一个房间——拆组件就是在做你之前做过的事:函数拆分,只不过拆的是 “界面函数”
② 模板 = 视图的声明 {{ title }}就是 “这里显示title”,v-for就是 “这个列表重复渲染多少次”——你声明,Vue执行
③ ref = 会通知视图的变量 普通let count = 0改了视图不知道;ref(0)包装后,count.value一变,用到它的视图自动更新——这就是 “数据是账本,视图是投影”的落地

二、第0步:清理脚手架

在上篇的daily-front项目里:

# 删掉默认的HelloWorld组件
rm src/components/HelloWorld.vue

# 清空App.vue

💡 从零开始写早报站的壳——脚手架给的默认内容都是演示,真正写项目时第一步永远是清空它们。


三、第1步:第一个组件ArticleCard

src/components/ArticleCard.vue:

<script setup>
import { computed, ref } from 'vue'

// ① props:父组件传入的参数(≈ 函数的参数,只读)
const props = defineProps({
title: String,
summary: String,
url: String,
})

// ② ref:响应式状态——liked / likeCount 一变,视图自动更新
const liked = ref(false)
const likeCount = ref(0)

// ③ 事件处理:模板里的 @click 会调用它
function toggleLike() {
liked.value = !liked.value
likeCount.value += liked.value ? 1 : -1
}

// ④ computed:由已有数据 “派生”的新值,依赖变化自动重算
const preview = computed(() =>
(props.summary || '').slice(0, 40) + '…')
</script>

<template>
<article class="card">
<h2><a :href="url" target="_blank">{{ title }}</a></h2>
<p>{{ preview }}</p>
<button @click="toggleLike">
{{ liked ? '❤️ 已赞' : '🤍 点赞' }} · {{ likeCount }}
</button>
</article>
</template>

<style scoped>
.card { border: 1px solid #d8dee4; border-radius: 8px;
padding: 14px 18px; margin: 12px 0; }
.card h2 { margin: 0 0 6px; font-size: 17px; }
button { cursor: pointer; border: 1px solid #1d6f9e;
background: none; color: #1d6f9e;
padding: 4px 12px; border-radius: 999px; }
</style>

📖 逐段拆解这个60行的文件——它同时演示了Vue的全部基础

#概念说明
① <script setup> 组合式API的语法糖——这里写的变量和函数,模板里直接用(setup = “准备”),不用再写一坨return
② defineProps 声明这个组件接收哪些参数。title: String意思是 “调用方必须按字符串传title”。props ≈ 函数参数,而且是只读的——组件不能改自己的参数
③ ref(false) 把布尔值包装成 “响应式变量”。JS里读写要.value,模板里不用——Vue自动解包
④ computed 基于props派生的摘要预览——数据变了它自动重算,不用手动维护
⑤ @click 事件绑定(v-on的简写),按钮点了调用toggleLike
⑥ <style scoped> scoped意味着这套样式只作用于本组件——组件的自我隔离,外面同名类名不影响你

四、第2步:在App.vue里使用组件

src/App.vue:

<script setup>
import ArticleCard from './components/ArticleCard.vue'

// 模拟数据:第13篇联调时换成从API拉
const articles = [
{ id: 1, title: '重构教会我的事', summary: '把歪代码拆开重装,功能不变结构变好……', url: '#' },
{ id: 2, title: '第一个爬虫抓到了什么', summary: '250部电影入库之后,我看到了榜单背后的规律……', url: '#' },
{ id: 3, title: '给项目装上AI', summary: '一次HTTP调用 + 一段好Prompt……', url: '#' },
]
</script>

<template>
<h1>Python 早报</h1>
<p class="subtitle">每天5分钟,掌握Python社区动态</p>
<!– v-for:列表渲染;:key是每个条目的身份证 –>
<ArticleCard
v-for="a in articles"
:key="a.id"
:title="a.title"
:summary="a.summary"
:url="a.url"
/>
</template>

📌 三个语法点

#语法点说明
① v-for="a in articles" 循环渲染——articles有几条,就生成几个ArticleCard
② :key="a.id" 每个列表项的身份证,Vue用它追踪 “谁是谁”——永远用稳定的id,别用下标index(第六节③)
③ :title是v-bind:title的简写 把变量值绑给prop。:title="a.title"与title="a.title"完全不同——后者传的是字符串字面量"a.title"

✅ 验收时刻

浏览器打开localhost:5173: 请添加图片描述

🎉 三张卡片渲染,点点赞按钮,数字+1,❤️/🤍切换——没有整页刷新,没有任何后端请求,这就是声明式渲染。


五、验收清单

1. 三张文章卡片渲染成功,样式隔离生效(scoped无污染)
2. 点击点赞 → 数字+1、图标切换,无页面刷新
3. 连点两次 → 数字回到原值(toggle逻辑正确)
4. 在articles里加第4条 → 保存即出现第四张卡(热重载 + v-for生效)
5. 故意把title拼错 → 浏览器控制台出现清晰警告(练读报错)
6. 全程无报错后提交Git

git add .
git commit -m "Vue3起步:ArticleCard组件 + 响应式点赞"


六、常见报错:这6个,Vue新手的标配(重点!)

① 页面空白,什么都没有

🔍 原因:八成是组件没挂上——App.vue忘了import,或template里没写<ArticleCard />。

✅ 解法:三步自查——

  • import了吗?
  • 模板里用了吗?
  • 组件文件路径对吗?
  • 📌 组件不会自己出现在页面上。


    ② [Vue warn]: Property "articles" was accessed during render but is not defined

    🔍 原因:模板里用了articles,但script setup里没定义(或拼写不一致)。

    ✅ 解法:模板引用的每个名字,都必须在script setup里存在。

    📌 模板与脚本是同一份变量的两面。


    ③ v-for控制台警告缺key

    🔍 原因:列表渲染没写:key,或用了index当下标。

    ✅ 解法:用数据的稳定id。index当key在 “列表中间插入/删除”时会张冠李戴——后端读者可以理解为 “索引变化导致对错行”。


    ④ Cannot read properties of undefined (reading 'value') 或点击无反应

    🔍 原因:JS里忘了.value——liked = !liked改的是包装对象本身,不是里面的值。

    ✅ 解法:script里凡是ref变量,读写一律.value;模板里不需要。

    📌 记住这条分界线:JS里.value,模板里光着。


    ⑤ 样式写了一大堆,页面纹丝不动

    🔍 原因:<style scoped>只作用于当前组件——你写在了A组件,但类名用在B组件。

    ✅ 解法:

    • 全局样式(字体、背景)放App.vue的非scoped style块;
    • 组件样式各管各家。

    ⑥ 改完代码,页面上的点赞数归零了

    🔍 原因:不是bug——热重载/刷新后内存状态自然重置,ref是内存变量不是数据库。

    ✅ 解法:现阶段接受 “刷新即失忆”;登录态和数据的持久化之后的(Pinia + localStorage)和(联调API)处理。


    七、课后练习

    #练习难度提示
    1 标签属性:给ArticleCard加一个tag prop(如"AI"/“爬虫”),卡片左上角渲染一个小标签 ⭐ 复习props全流程
    2 卡片跳转:把整张卡片包进链接,点击卡片跳原文 ⭐⭐ 卡片h2的a已就位,扩展到整卡
    3 空摘要兜底:summary为空时显示 “(暂无摘要)” ⭐⭐ v-if / 或computed返回兜底文案——两种思路都试
    4(选做) 拆SiteHeader:把标题+副标题拆成独立组件,在App里用一次 ⭐⭐ 体会 “组件即函数”的复用感

    📦 配套代码

    完整组件已上传Git(python_daily/,node_modules不入仓):【gitee仓库地址】

    赞(0)
    未经允许不得转载:171主机测评 » 【Web全栈进阶】Vue3起步:组件、模板语法、响应式数据
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址