首页 / 资讯中心 / 文章详情

Vue前端小例子实战:列表渲染、表单绑定与组件通信核心机制解析

Vue前端小例子实战:列表渲染、表单绑定与组件通信核心机制解析 ★ FEATURED ARTICLE
简介这是一份面向Vue.js初学者与前端进阶者的实战型小例子资源围绕一个资产管理应用展开帮助读者在动手实践中理解Vue的核心概念与工程化配置。内容涵盖Vue实例与数据绑定、计算属性与侦听器、组件化拆分、模板语法中的条件与循环渲染以及路由管理、状态管理、响应式布局和错误处理等常见知识点适合用来对照学习或作为课程练习参考。资源包共39个文件以15个js脚本、9个vue单文件组件为主另含7张png示意图、2个json配置、1个html入口及若干工程配置文件压缩包约45KB体量轻巧、结构完整。目前已有4211人学习下载读者可从中获得可直接运行的示例代码、清晰的目录组织方式与组件划分思路便于快速上手Vue项目开发并迁移到更复杂的应用场景中。1. 从一个真实场景说起为什么我又回头翻出了这个 VUE 前端小例子前阵子帮一个做后台管理系统的朋友救火需求很简单——把一张表格里的数据按状态筛选、分页、点行展开详情。他用了某个组件库的 ProTable配置项写了三百多行结果筛选条件一改就整页重渲染控制台里 watch 和 computed 打成一团。我让他把业务逻辑抽出来用最朴素的 VUE 前端小例子重写一遍两个文件、不到一百五十行跑通了。这件事让我重新意识到VUE 例子的价值不在于炫技而在于它把响应式、组件通信、生命周期这些核心机制压缩到最小可运行单元里。这份VUE 前端小例子资源就是干这个的——它不教你从零搭脚手架而是给你一批能直接npm run serve跑起来的最小实现覆盖列表渲染、表单双向绑定、父子传参、条件渲染这些日常开发里绕不开的场景。适合谁刚学完模板语法但不知道怎么写完整页面的新手以及想快速验证某个交互逻辑、不想被工程配置拖住的老手。VUE 实现资源里这种小而全的样本反而最难找。2. 项目结构与环境搭建从零把第一个例子跑起来2.1 目录结构长什么样拿到这份资源解压后你会看到一个典型的 VUE CLI 风格目录。它没有用 Vite也没有上 TypeScript就是最朴素的 webpack 版本好处是依赖少、报错信息直白新手不会被一堆插件配置劝退。vue-examples/ ├── public/ │ └── index.html # 挂载入口只有一个 #app 节点 ├── src/ │ ├── main.js # 全局入口挂载 Vue 实例 │ ├── App.vue # 根组件负责路由或示例切换 │ ├── components/ # 通用子组件 │ │ ├── TodoItem.vue │ │ └── UserCard.vue │ └── views/ # 每个例子一个页面级组件 │ ├── ListDemo.vue │ ├── FormDemo.vue │ └── CommunicateDemo.vue ├── package.json └── vue.config.js # 可选改端口或配代理用这个结构的关键在于views和components的分离views里放的是一个完整页面components里放的是可复用零件。很多新手把两者混在一起写结果组件越写越大最后变成一坨。我一般会强制自己只要一个.vue文件超过 200 行就拆出至少一个子组件。2.2 依赖安装与启动命令环境要求很宽松Node.js 12 以上就能跑。进入项目根目录后执行# 安装依赖国内网络建议加 registry 参数 npm install # 启动开发服务器默认 8080 端口 npm run serve # 打包生产版本产物在 dist 目录 npm run buildnpm run serve背后是vue-cli-service serve它会启动 webpack-dev-server 并开启热更新。改完代码保存浏览器自动刷新不用手动重启。如果 8080 被占用在vue.config.js里加一段// vue.config.js module.exports { devServer: { port: 8081, // 换成空闲端口 open: true // 启动后自动打开浏览器 } }这里有个血泪经验open: true在 CI 环境或远程服务器上会报错因为找不到图形界面。本地开发开着方便提交代码前记得关掉。2.3 入口文件做了什么main.js是整个应用的起点它只干三件事引入 Vue、引入根组件、挂载到 DOM。// src/main.js import Vue from vue import App from ./App.vue Vue.config.productionTip false // 关掉生产环境提示 new Vue({ render: h h(App) // 用 render 函数替代 template体积更小 }).$mount(#app) // 挂载到 public/index.html 里的 #apprender: h h(App)这种写法等价于template: App/但前者不需要运行时编译器打包体积能小一圈。$mount(#app)把 Vue 实例和页面上那个空 div 绑定之后所有组件都渲染在这个 div 内部。理解这三行你就理解了 VUE 应用的启动链路——后面所有例子都是在这个根实例上长出来的。3. 核心功能拆解列表、表单、组件通信三个例子逐段讲3.1 列表渲染与条件筛选ListDemo.vue是最基础也最常用的例子一个数组渲染成列表支持按关键字过滤空结果时显示提示。template div classlist-demo !-- 搜索框v-model 双向绑定 keyword -- input v-modelkeyword placeholder输入关键字筛选 / !-- 有数据时渲染列表 -- ul v-iffilteredList.length li v-foritem in filteredList :keyitem.id {{ item.name }} - {{ item.status }} /li /ul !-- 无数据时显示空状态 -- p v-else classempty没有匹配的结果/p /div /template script export default { data() { return { keyword: , list: [ { id: 1, name: 任务A, status: 进行中 }, { id: 2, name: 任务B, status: 已完成 }, { id: 3, name: 任务C, status: 进行中 } ] } }, computed: { // 依赖 keyword 和 list任一变化自动重算 filteredList() { const kw this.keyword.trim().toLowerCase() if (!kw) return this.list return this.list.filter(item item.name.toLowerCase().includes(kw) ) } } } /script这段代码的核心是computed而不是methods。filteredList依赖keyword和listVUE 会缓存计算结果只有依赖变化时才重新执行。如果你把它写成methods里的函数每次重渲染都会重新过滤一遍数据量大时就是性能杀手。:keyitem.id也不能省——用 index 做 key 在列表顺序变化时会导致 DOM 复用错乱这是新手最常见的翻车点之一。3.2 表单双向绑定与校验FormDemo.vue演示了v-model在输入框、下拉框、复选框上的不同表现以及一个轻量的提交前校验。template form submit.preventhandleSubmit !-- 文本输入 -- label用户名/label input v-model.trimform.username / !-- 下拉选择 -- label角色/label select v-modelform.role option valueadmin管理员/option option valueuser普通用户/option /select !-- 复选框绑定布尔值 -- label input typecheckbox v-modelform.agree / 同意条款 /label button typesubmit提交/button p v-iferrorMsg classerror{{ errorMsg }}/p /form /template script export default { data() { return { form: { username: , role: user, agree: false }, errorMsg: } }, methods: { handleSubmit() { // 提交前做最小校验 if (!this.form.username) { this.errorMsg 用户名不能为空 return } if (!this.form.agree) { this.errorMsg 请先同意条款 return } this.errorMsg console.log(提交数据, { ...this.form }) } } } /scriptv-model.trim是修饰符自动去掉首尾空格比在提交时手动trim()更省事。submit.prevent阻止表单默认提交行为否则页面会刷新Vue 的状态全丢。注意form对象里的字段必须先在data里声明VUE 2 无法检测对象新增属性的变化——如果你在提交时动态加一个form.extra视图不会更新。这是响应式系统的边界不是 bug。3.3 父子组件通信props 下行、emit 上行CommunicateDemo.vue和UserCard.vue配合演示了最经典的父子通信模式。!-- 父组件 CommunicateDemo.vue -- template div UserCard v-foruser in users :keyuser.id :user-infouser deletehandleDelete / /div /template script import UserCard from /components/UserCard.vue export default { components: { UserCard }, data() { return { users: [ { id: 1, name: 张三 }, { id: 2, name: 李四 } ] } }, methods: { handleDelete(id) { // 子组件传来 id父组件负责真正删除 this.users this.users.filter(u u.id ! id) } } } /script!-- 子组件 UserCard.vue -- template div classuser-card span{{ userInfo.name }}/span button click$emit(delete, userInfo.id)删除/button /div /template script export default { // props 声明类型和必填便于排查传参错误 props: { userInfo: { type: Object, required: true } } } /script父组件通过:user-infouser把数据传下去子组件用props接收子组件通过$emit(delete, id)把事件抛上去父组件用deletehandleDelete监听。数据单向流动谁的数据谁修改这样出问题时排查路径非常清晰。我见过有人直接在子组件里改this.userInfo.name xxx短期能跑但父组件和其他兄弟组件的数据源就乱了后期加个撤销功能都无从下手。4. 避坑与排查这几个错误我几乎每次都能遇到4.1 数据改了但页面没更新现象this.list[0] newItem或this.obj.newKey value之后视图纹丝不动。原因VUE 2 的响应式基于Object.defineProperty无法检测数组索引赋值和对象新增属性。解决数组用this.$set(this.list, 0, newItem)或splice对象用this.$set(this.obj, newKey, value)。更彻底的做法是初始化时就把字段声明全。4.2 组件样式互相污染现象A 组件里写的.title { color: red }把 B 组件的标题也染红了。原因style默认是全局作用域所有组件共享。解决给每个组件的 style 加scoped即style scoped。如果还需要改子组件内部样式用::v-deepVUE 2穿透但别滥用穿透多了说明组件边界没设计好。4.3 路由跳转后组件状态没重置现象从详情页返回列表页搜索框里还留着上次的关键字。原因keep-alive缓存了组件实例或者路由复用了同一个组件。解决在activated钩子里重置数据或者给router-view加:key$route.fullPath强制重建。前者性能好后者省心看场景选。4.4 npm install 卡住或报错现象npm install停在某个包上不动或者报node-sass编译失败。原因网络问题或 Node 版本与依赖不匹配。解决先换 registry再检查 Node 版本。node-sass这类包对 Node 版本极其敏感Node 16 以上建议换成sassdart-sass。如果还不行删掉node_modules和package-lock.json重来这是最有效的后悔药。4.5 控制台报 Cannot read property of undefined现象页面白屏控制台指向模板里某个{{ user.name }}。原因异步数据还没回来user是undefined模板已经渲染了。解决用v-ifuser包一层或者初始化时给user一个空对象{}。更规范的做法是用可选链{{ user?.name }}但需要模板编译器支持老项目里还是v-if最稳。5. 二次开发与改造把这批例子变成你自己的项目底座这批例子的定位是最小可运行单元所以二次开发的第一步不是加功能而是定规范。我一般会先做三件事统一组件命名大驼峰文件名、模板里用短横线、统一事件命名动词开头如handleSubmit、onDelete、统一目录职责views只做页面组装components只做展示和局部交互。这三条定下来后面加多少页面都不会乱。第二步是抽公共逻辑。比如列表筛选、分页、表单校验几乎每个页面都要写一遍。可以抽成 mixin// src/mixins/listMixin.js export default { data() { return { keyword: , page: 1, pageSize: 10 } }, computed: { pagedList() { const start (this.page - 1) * this.pageSize return this.filteredList.slice(start, start this.pageSize) } }, methods: { handlePageChange(newPage) { this.page newPage } } }组件里mixins: [listMixin]引入再补一个filteredList计算属性即可。mixin 的缺点是来源不清晰变量冲突时不好排查所以只抽真正通用的逻辑业务相关的别往里塞。第三步是加一层 API 封装。例子里的数据都是写死的真实项目要调接口。建议在src/api/下按模块建文件统一处理请求拦截、错误提示和 loading 状态// src/api/request.js import axios from axios const request axios.create({ baseURL: process.env.VUE_APP_BASE_API, // 从环境变量读别硬编码 timeout: 10000 }) request.interceptors.response.use( res res.data, err { console.error(请求失败, err.message) return Promise.reject(err) } ) export default requestbaseURL走环境变量本地开发、测试、生产各一份.env文件切换环境不用改代码。这是我在多个项目里踩过坑之后固定下来的习惯——硬编码的接口地址上线前一定会有人忘记改。改造完怎么验证我的做法是先跑通一个完整闭环列表页调接口拿数据、筛选、分页、点进详情、返回列表保持筛选状态。这一个闭环跑通说明路由、状态、请求、组件通信都没问题剩下的页面就是复制粘贴加微调。如果闭环里某一步卡住问题一定出在那一步对应的模块排查范围小很多。从那以后我每次拿到一批示例代码都强制自己先跑通一个最小闭环再动手改而不是一上来就加功能。这个习惯帮我省下了大量改到一半发现底层不通的时间。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站