当前位置: 首页 > news >正文

商品案例-组件封装(vue)

MyTable.vue

<template><div class="my-tag"><inputv-if="isEdit"v-focusref="inp"class="input"type="text"placeholder="输入标签":value="value"@blur="isEdit = false"@keyup.enter="handleEnter"/><div v-else@dblclick="handleClick"class="text">{{ value }}</div></div>
</template><script>
export default {props: {value: String},data () {return {isEdit: false}},methods: {handleClick () {// 双击后,切换到显示状态 (Vue是异步dom更新)this.isEdit = true// // 等dom更新完了,再获取焦点// this.$nextTick(() => {//   // 立刻获取焦点//   this.$refs.inp.focus()// })},handleEnter (e) {// 非空处理if (e.target.value.trim() === '') return alert('标签内容不能为空')// 子传父,将回车时,[输入框的内容] 提交给父组件更新// 由于父组件是v-model,触发事件,需要触发 input 事件this.$emit('input', e.target.value)// 提交完成,关闭输入状态this.isEdit = false}}
}
</script><style lang="less" scoped>
.my-tag {cursor: pointer;.input {appearance: none;outline: none;border: 1px solid #ccc;width: 100px;height: 40px;box-sizing: border-box;padding: 10px;color: #666;&::placeholder {color: #666;}}
}
</style>

// my-table 表格组件的封装

// 1. 数据不能写死,动态传递表格渲染的数据  props

// 2. 结构不能写死 - 多处结构自定义 【具名插槽】

//    (1) 表头支持自定义

//    (2) 主体支持自定义

MyTag.vue

<template><div class="my-tag"><inputv-if="isEdit"v-focusref="inp"class="input"type="text"placeholder="输入标签":value="value"@blur="isEdit = false"@keyup.enter="handleEnter"/><div v-else@dblclick="handleClick"class="text">{{ value }}</div></div>
</template><script>
export default {props: {value: String},data () {return {isEdit: false}},methods: {handleClick () {// 双击后,切换到显示状态 (Vue是异步dom更新)this.isEdit = true// // 等dom更新完了,再获取焦点// this.$nextTick(() => {//   // 立刻获取焦点//   this.$refs.inp.focus()// })},handleEnter (e) {// 非空处理if (e.target.value.trim() === '') return alert('标签内容不能为空')// 子传父,将回车时,[输入框的内容] 提交给父组件更新// 由于父组件是v-model,触发事件,需要触发 input 事件this.$emit('input', e.target.value)// 提交完成,关闭输入状态this.isEdit = false}}
}
</script><style lang="less" scoped>
.my-tag {cursor: pointer;.input {appearance: none;outline: none;border: 1px solid #ccc;width: 100px;height: 40px;box-sizing: border-box;padding: 10px;color: #666;&::placeholder {color: #666;}}
}
</style>

// my-tag 标签组件的封装

// 1. 创建组件 - 初始化

// 2. 实现功能

//    (1) 双击显示,并且自动聚焦

//        v-if v-else @dbclick 操作 isEdit

//        自动聚焦:

//        1. $nextTick => $refs 获取到dom,进行focus获取焦点

//        2. 封装v-focus指令

//    (2) 失去焦点,隐藏输入框

//        @blur 操作 isEdit 即可

//    (3) 回显标签信息

//        回显的标签信息是父组件传递过来的

//        v-model实现功能 (简化代码)  v-model => :value 和 @input

//        组件内部通过props接收, :value设置给输入框

//    (4) 内容修改了,回车 => 修改标签信息

//        @keyup.enter, 触发事件 $emit('input', e.target.value)

App.vue

<template><div class="table-case"><MyTable :data="goods"><template #head><th>编号</th><th>名称</th><th>图片</th><th width="100px">标签</th></template><template #body="{ item, index }"><td>{{ index + 1 }}</td><td>{{ item.name }}</td><td><img:src="item.picture"/></td><td><MyTag v-model="item.tag"></MyTag></td></template></MyTable></div>
</template><script>import MyTag from './components/MyTag.vue'
import MyTable from './components/MyTable.vue'
export default {name: 'TableCase',components: { MyTag,MyTable},data () {return {// 测试组件功能的临时数据tempText: '水杯',tempText2: '钢笔',goods: [{ id: 101, picture: 'https://yanxuan-item.nosdn.127.net/f8c37ffa41ab1eb84bff499e1f6acfc7.jpg', name: '梨皮朱泥三绝清代小品壶经典款紫砂壶', tag: '茶具' },{ id: 102, picture: 'https://yanxuan-item.nosdn.127.net/221317c85274a188174352474b859d7b.jpg', name: '全防水HABU旋钮牛皮户外徒步鞋山宁泰抗菌', tag: '男鞋' },{ id: 103, picture: 'https://yanxuan-item.nosdn.127.net/cd4b840751ef4f7505c85004f0bebcb5.png', name: '毛茸茸小熊出没,儿童羊羔绒背心73-90cm', tag: '儿童服饰' },{ id: 104, picture: 'https://yanxuan-item.nosdn.127.net/56eb25a38d7a630e76a608a9360eec6b.jpg', name: '基础百搭,儿童套头针织毛衣1-9岁', tag: '儿童服饰' },]}}
}
</script><style lang="less" scoped>
.table-case {width: 1000px;margin: 50px auto;img {width: 100px;height: 100px;object-fit: contain;vertical-align: middle;}
}</style>

一、各组件作用分析

1. MyTable 组件(通用表格组件)

核心作用:提供一个结构灵活的表格容器,支持表头和表格内容的自定义,数据由父组件动态传入。

  • 模板部分

    • 定义了表格的基础结构(tabletheadtbody)。
    • 通过具名插槽实现结构自定义:
      • slot name="head":留给父组件定义表头(th)内容。
      • slot name="body":留给父组件定义表格行(td)内容,同时通过插槽 Props(itemindex)传递当前行数据和索引。
    • v-for="(item, index) in data":根据父组件传入的data数组循环渲染表格行。
  • 脚本部分

    • 通过props接收data(必填数组),作为表格的渲染数据源。
  • 样式部分

    • 定义表格的基础样式(宽度、边框、对齐方式等),包括表头(th)、单元格(td)、图片的默认样式,以及状态类(如.red.none)。

2. MyTag 组件(可编辑标签组件)

核心作用:实现一个支持双击编辑、回车 / 失焦保存的标签组件,通过v-model与父组件同步数据。

  • 模板部分: 

    • 条件渲染:isEdittrue时显示输入框(input),否则显示文本(div)。
    • 输入框(input):
      • v-focus:自定义指令(用于自动聚焦,需额外定义)。
      • :value="value":绑定父组件传入的标签值。
      • @blur:失焦时退出编辑模式(isEdit = false)。
      • @keyup.enter:回车时触发保存逻辑(handleEnter)。
    • 文本区(div):
      • @dblclick:双击时进入编辑模式(isEdit = true)。
      • 显示value(父组件传入的标签值)。
  • 脚本部分

    • props: { value }:接收父组件通过v-model传入的标签文本。
    • data() { isEdit }:控制编辑 / 显示状态的开关。
    • 方法:
      • handleClick:双击时切换到编辑模式(需配合$nextTick确保输入框渲染后聚焦,原代码注释了该逻辑,实际需补充)。
      • handleEnter:回车时验证非空,通过$emit('input', 新值)通知父组件更新(配合v-model实现双向绑定),然后退出编辑模式。
  • 样式部分

    • 定义输入框和文本区的基础样式(边框、尺寸、光标等)。

3. App 组件(页面级组件)

核心作用:整合MyTableMyTag组件,实现一个带可编辑标签的商品表格页面。

  • 模板部分

    • 使用MyTable组件,通过:data="goods"传入商品数据。
    • 自定义MyTable的表头(#head插槽):定义 “编号、名称、图片、标签” 四列。
    • 自定义MyTable的行内容(#body插槽):
      • 接收MyTable传递的item(当前商品)和index(索引)。
      • 依次渲染:编号(index + 1)、商品名称(item.name)、商品图片(img :src="item.picture")、可编辑标签(MyTag v-model="item.tag")。
  • 脚本部分

    • 导入并注册MyTableMyTag组件。
    • data()中定义goods数组:存储商品数据(包含idpicturenametag等字段),作为表格的数据源。
  • 样式部分

    • 定义页面容器样式(宽度、居中)和图片的补充样式。

二、运行流程

  1. 初始化渲染

    • TableCase组件挂载,渲染MyTable组件,并将goods数组传给MyTabledata props。
    • MyTable渲染表头:使用#head插槽的内容(四个th)。
    • MyTable循环goods数组,为每个商品渲染一行(tr),并通过#body插槽渲染行内容:
      • 编号、名称、图片按商品数据直接展示。
      • 标签列渲染MyTag组件,通过v-model="item.tag"绑定当前商品的tag值,初始显示item.tag的文本。
  2. 标签编辑交互:

    • 双击MyTag的文本区(div):触发handleClickisEdit变为true,切换到输入框(input)显示。
    • 输入框自动聚焦(需v-focus指令或$nextTick逻辑支持),用户修改内容后:
      • 按回车:触发handleEnter,验证非空后,通过$emit('input', 新值)通知Appitem.tag被更新为新值,随后isEdit变为false,回到文本显示模式。
      • 失焦(点击其他区域):触发@blurisEdit变为false,退出编辑模式(未保存则保留原内容)。
  3. 数据同步

    • MyTag通过v-modelAppitem.tag双向绑定,编辑后的标签值会实时更新到goods数组中,表格显示随之刷新。

 

补充

一. MyTag 组件中,value 的值是如何传来的

1. 父组件中通过 v-model 绑定数据

在使用 MyTag 组件的父组件(比如你之前案例中的 TableCase)中,通常会用 v-model 绑定一个数据,例如:

<!-- 父组件 TableCase 中使用 MyTag -->
<MyTag v-model="item.tag"></MyTag>

这里的 item.tag 是父组件中的数据(比如商品的标签值,如 “茶具”“男鞋” 等)。

2. v-model 的本质:value props + input 事件

Vue 中 v-model 是一个语法糖,它等价于两个操作:

  • 给子组件传递一个名为 value 的 props,值为绑定的数据(即 item.tag);
  • 监听子组件触发的 input 事件,当事件触发时,更新绑定的数据(item.tag)。

所以上面的 v-model 写法等价于:

<!-- 等价于 v-model 的手动写法 -->
<MyTag :value="item.tag"  <!-- 父传子:将 item.tag 作为 value 传给子组件 -->@input="newValue => item.tag = newValue"  <!-- 子传父:监听 input 事件更新数据 -->
></MyTag>
3. 子组件 MyTag 通过 props 接收 value

在 MyTag 组件中,通过 props 声明接收 value,就可以拿到父组件传递过来的值:

// MyTag 组件的 props 定义
export default {props: {value: {  // 接收父组件通过 v-model 传递的 valuetype: String,default: ''  // 默认值为空字符串}},// ...
}

此时,MyTag 组件内部就可以通过 this.value 访问到父组件传递过来的标签值(比如 “茶具”)。

4. MyTag 组件中使用 value

在 MyTag 的模板中,value 被用于两种场景:

  • 文本显示:当不处于编辑模式时(isEdit=false),div 中显示 {{ value }},即父组件传递的标签值;
  • 输入框回显:当处于编辑模式时(isEdit=true),input 通过 :value="value" 绑定,显示当前标签值(方便用户在原有值基础上编辑)。

二、自动获取焦点功能实现

1.局部实现
     this.$nextTick(() => {// 立刻获取焦点this.$refs.inp.focus()

在 Vue 中,this.$nextTick() 的作用是等待下一次 DOM 更新循环结束后执行回调函数。在你的场景中,获取输入框焦点必须用 $nextTick,核心原因是:Vue 对 DOM 的更新是异步的

当你双击标签触发 handleClick 时,执行了 this.isEdit = true,这个操作会导致模板中的 v-if="isEdit" 条件变化(从 false 变为 true),进而触发输入框(input)的渲染(从隐藏到显示)。

但是,Vue 并不会在你修改 isEdit 后立即更新 DOM(比如立刻创建 input 元素)。Vue 会将数据变化缓存起来,等当前同步代码执行完后,再批量更新 DOM(这是 Vue 的性能优化策略,避免频繁操作 DOM 导致的性能损耗)。

也就是说:

  • 执行 this.isEdit = true 后,input 还未被实际渲染到 DOM 中;
  • 此时直接调用 this.$refs.inp.focus(),会因为 input 还不存在(this.$refs.inp 为 undefined)而报错,或者无法成功聚焦。

this.$nextTick(() => { ... }) 会把回调函数推迟到下一次 DOM 更新循环结束后执行。此时:

  • Vue 已经完成了 isEdit = true 导致的 DOM 更新(input 已被成功渲染到页面中);
  • this.$refs.inp 能够正确获取到渲染后的 input 元素,调用 focus() 就能正常生效。

因为修改 isEdit 后,input 的渲染是异步的,$nextTick 确保了我们在 DOM 真正更新完成(input 已存在)之后 再执行聚焦操作,否则会因为元素不存在而失败。

2.全局实现
Vue.directive('focus',{inserted(el){el.focus()}
})

在main.js文件中实现,不改变其他代码,添加如上代码

注意:需要在组件中的input标签追加 v-focus

  <inputv-if="isEdit" v-focusref="inp"class="input"type="text":value="value"placeholder="输入标签"@blur="isEdit=false"@keyup.enter="shuru"/>

三、handleEnter函数说明

1. 为什么需要传参数 e

handleEnter 是绑定在 input 元素的 @keyup.enter 事件上的处理函数(@keyup.enter="handleEnter")。在 Vue 中,当原生 DOM 事件(如 keyupclick 等)触发时,Vue 会自动将 原生事件对象(Event) 作为参数传递给事件处理函数。

这个事件对象 e 包含了当前事件的所有信息,比如:

  • e.target:触发事件的 DOM 元素(这里就是输入框 input);
  • e.type:事件类型(这里是 keyup);
  • e.key:触发事件的按键(这里是 Enter)。

通过 e,我们可以直接获取到触发事件的元素及其属性(如输入框的值),而不需要额外通过 ref 手动查找元素,这是事件处理的自然逻辑。

​​​​​​​
2. 为什么用 e.target.value 而不是 this.$refs.inp.value

两者在当前场景下理论上都能获取到输入框的值,但 e.target.value 更符合事件处理的逻辑,且更稳健,原因如下:

  • 直接性:e.target 就是当前触发事件的元素@keyup.enter 事件是绑定在 input 上的,所以 e.target 必然指向这个 input 元素(事件的源头)。通过 e.target.value 可以直接拿到该元素的当前值,无需额外依赖其他标识(如 ref)。

  • 减少对 ref 的依赖,降低耦合this.$refs.inp 依赖于模板中 ref="inp" 的定义。如果未来修改了 ref 的名称(比如改成 ref="input"),this.$refs.inp 就会失效,需要同步修改代码;而 e.target 完全不依赖 ref,只要事件绑定在 input 上,就始终能正确获取元素。

  • 符合原生事件处理的逻辑在原生 JavaScript 中,处理输入框事件时,通常也是通过事件对象 e 获取目标元素的值(e.target.value),这是更通用的做法,符合开发者的直觉。

总结

  • 传参数 e 是因为 Vue 会自动传递原生事件对象,通过 e 可以便捷地获取事件相关信息(包括触发事件的元素)。
  • 使用 e.target.value 比 this.$refs.inp.value 更直接、更稳健,减少了对 ref 的依赖,符合事件驱动的编程逻辑。

两种方式在当前场景下效果一致,但 e.target.value 是更推荐的写法。

http://www.dtcms.com/a/601362.html

相关文章:

  • 新视角看 js 的数据类型
  • PySide6 + QML - QSerialPort01 - 扫描操作系统上有效的串口
  • 【前端面试】HTML篇
  • Next.js第四章(路由导航)
  • 从jsp打开一个html,怎么把jsp的某些参数传递给html
  • 谷歌google官方网站网站开发 书籍
  • 北京网站备案公司安徽观元建设有限公司网站
  • 若依plus请求加解密
  • PHP Filter:深入了解其功能与实现
  • Linux基础指令(简易版)
  • 农田灌区监测设备:赋能现代农业的精准感知与智能调控
  • 中山 灯饰 骏域网站建设专家百度关键词推广帝搜软件
  • 自己怎么做 优惠券网站西京一师一优课建设网站
  • CST电动车EMC仿真(二)——电机控制器MCU的EMC仿真
  • WPP Media(群邑)DOOH 解决方案 重构数字户外广告价值
  • 基于SpringBoot+Vue的美容美发在线预约系统的设计与实现【附源码】
  • 数字化转型改变了什么?从技术底层到业务本质的深度重构
  • 从 “被动抢修” 到 “主动防控”,安科瑞 mini 智能微断,重构末端配电安全新逻辑
  • 从经验到算法:智能获客系统如何重构ToB销售效率
  • Oracle 19C 数据字典 DBA_HIST_SEG_STAT 详细说明
  • tsfile.raw提示
  • JAVA中六种策略模式的实现
  • 【ZeroRange WebRTC】TLS 底层原理与工作机制(深入解析)
  • 【论文阅读16】-LLM-TSFD:一种基于大型语言模型的工业时间序列人机回路故障诊断方法
  • 联想键盘失灵处理方法
  • 网站建设scyiyouhtml5模板之家
  • 做网站网络公司泉州住房建设局网站
  • 电子绕核运动为何不辐射能量、不坠入原子核?
  • RK3588核心板/开发板RT-Linux系统实时性及硬件中断延迟测试
  • 11. 函数极限