网站客户体验,wordpress 医院模板,中国建设网站红黑榜名单,河北建投商务网代码获取
07-⾃定义指令插槽商品列表案例
⼀、⾃定义指令
1. 基本使⽤
1.1 指令介绍 内置指令#xff1a;v-model、v-for、v-bind、v-on… 这都是Vue给咱们内置的⼀些指令#xff0c;可以直接使⽤ ⾃定义指令#xff1a;同时Vue也⽀持让开发者#xff0c;⾃⼰注册⼀些…代码获取
07-⾃定义指令插槽商品列表案例
⼀、⾃定义指令
1. 基本使⽤
1.1 指令介绍 内置指令v-model、v-for、v-bind、v-on… 这都是Vue给咱们内置的⼀些指令可以直接使⽤ ⾃定义指令同时Vue也⽀持让开发者⾃⼰注册⼀些指令。这些指令被称为⾃定义指令
1.2 作⽤
封装⼀段 公共的DOM操作 代码便于复⽤
1.3 语法
注册
// main.js 中
app.directive(指令名, {// 元素挂载后(成为真实DOM) ⾃动触发⼀次⾃动执⾏mounted(el) {// el: 指令所在的DOM元素}
})使⽤
p v-指令名/p1.4 代码⽰例
需求当⻚⾯加载时, 让元素获取焦点
// main.js
// 注册全局指令app.directive(focus, {mounted(el) {console.log(el)// input 元素// 聚焦el.focus()}
})App.vue
script setup/script
templatediv classappinput typetext v-focus //div
/template1.5 总结
⾃定义指令的作⽤是什么
答封装⼀段公共的 DOM操作 的代码
使⽤⾃定义指令的步骤是哪两步
答 先注册、后使⽤
指令配置选项中的 mounted 钩⼦何时执⾏
答元素 挂载后 (成为DOM树的⼀部分时) ⾃动执⾏
2. 绑定数据
2.1 需求
实现⼀个 color 指令 传⼊不同的颜⾊, 给标签设置⽂字颜⾊
2.2 语法
1.在绑定指令时可以通过“等号”的形式为指令 绑定 具体的参数值
div v-colorcolorStrSome Text/div2.通过 binding.value 可以拿到指令值指令值修改会 触发 updated 钩⼦
app.directive(指令名, {// 挂载后⾃动触发⼀次mounted(el, binding) { },// 数据更新, 每次都会执⾏updated(el, binding) { }
})2.3 代码⽰例
// main.js
app.directive(color, {mounted(el, binding) {el.style.color binding.value},updated(el, binding) {el.style.color binding.value}
})App.vue
script setup
import { ref } from vue
// 颜⾊
const colorStr ref(red)
/scripttemplatep v-colorcolorStr/p
/template2.4 简化形式
对于⾃定义指令来说⼀个很常⻅的情况是仅仅需要在 mounted 和 updated 上实现相同的⾏为。这种情况下我们可以直接⽤⼀个函数来定义指令如下所⽰
app.directive(color, (el, binding) {// 这会在 mounted 和 updated 时都调⽤el.style.color binding.value
})3. 封装v-loading指令
3.1 场景
实际开发过程中发送请求需要时间在请求的数据未回来时⻚⾯会处于空⽩状态 , ⽤⼾体验不好
3.2 解决⽅案
封装⼀个 v-loading 指令实现加载中的效果
3.3 分析 本质 loading效果就是⼀个蒙层盖在了盒⼦上 数据请求中开启loading状态添加蒙层 数据请求完毕关闭loading状态移除蒙层
3.4 实现 准备⼀个 loading类通过伪元素定位设置宽⾼实现蒙层 开启关闭 loading状态添加移除蒙层本质只需要添加移除类即可 结合⾃定义指令的语法进⾏封装复⽤
3.5 代码实现
App.vue
script setup
import axios from axios
import { ref } from vue// 新闻列表
const newsList ref([])
const isFinish ref(false)getNewsData()// 获取新闻列表
async function getNewsData() {isFinish.value false// 请求新闻数据const resp await axios.get(http://localhost:4000/api/news)// 保存数据newsList.value resp.data.data// 睡眠1秒await new Promise((resolve) {setTimeout(() {resolve()}, 1000)})isFinish.value true
}
/scripttemplatediv classboxulli v-foritem in newsList :keyitem.id classnews v-loading!isFinishdiv classleftdiv classtitle{{ item.title }}/divdiv classinfospan{{ item.source }}/spanspan{{ item.time }}/span/div/divdiv classrightimg :srcitem.img altimg //div/li/ul/div
/templatestyle
.loading:before {z-index: 999;content: ;position: absolute;left: 0;top: 0;width: 100%;height: 100%;background: #fff url(./assets/loading.gif) no-repeat center;background-size: auto;
}.box {position: relative;width: 800px;min-height: 600px;margin: 10px auto;border-radius: 5px;
}.news {display: flex;margin: 0 auto;padding: 20px 0;cursor: pointer;
}.news .left {flex: 1;display: flex;flex-direction: column;justify-content: space-between;padding-right: 10px;
}.news .left .title {font-size: 20px;
}.news .left .info {color: #999999;
}.news .left .info span {margin-right: 20px;
}.news .right {width: 160px;height: 120px;
}.news .right img {width: 100%;height: 100%;object-fit: cover;
}
/style
main.js
import App from ./App.vue;
import { createApp } from vue;const app createApp(App);// main.js
app.directive(loading, (el, binding) {if (binding.value) {el.classList.add(loading)} else {el.classList.remove(loading)}
})app.mount(#app);⼆、插槽
插槽分类 默认插槽 具名插槽 作⽤域插槽
1. 默认插槽
1.1 作⽤
让组件内部的⼀些 结构 ⽀持 ⾃定义 1.2 需求
将需要多次显⽰的对话框,封装成⼀个组件
1.3 问题
组件的内容部分不希望写死希望能使⽤的时候⾃定义。怎么办
1.4 插槽的基本语法 组件内需要定制的结构部分改⽤ slot/slot 占位 使⽤组件时, MyDialog/MyDialog写成双标签 , 包裹结构, 传⼊替换slot
1.5 代码⽰例
MyDialog.vue
script setup
/script
templatediv classdialogdiv classdialog-headerh3友情提⽰/h3span classclose✖️/span/divdiv classdialog-contentslot/slot/divdiv classdialog-footerbutton取消/buttonbutton确认/button/div/div
/templatestyle scoped
* {margin: 0;padding: 0;
}.dialog {width: 470px;height: 230px;padding: 0 25px;background-color: #ffffff;margin: 40px auto;border-radius: 5px;
}.dialog-header {height: 70px;line-height: 70px;font-size: 20px;border-bottom: 1px solid #ccc;position: relative;
}.dialog-header .close {position: absolute;right: 0px;top: 0px;cursor: pointer;
}.dialog-content {height: 80px;font-size: 18px;padding: 15px 0;
}.dialog-footer {display: flex;justify-content: flex-end;
}.dialog-footer button {width: 65px;height: 35px;background-color: #ffffff;border: 1px solid #e1e3e9;cursor: pointer;outline: none;margin-left: 10px;border-radius: 3px;
}.dialog-footer button:last-child {background-color: #007acc;color: #fff;
}
/style
App.vue
script setup
import MyDialog from /components/MyDialog.vue;
/scripttemplatedivMyDialog你确定要删除吗/MyDialogMyDialog你确定要退出吗?/MyDialog/div
/templatestyle scoped/style1.6 总结
组件内某⼀部分结构不确定想要⾃定义怎么办
答使⽤ 插槽 (技术)
插槽的步骤分为哪⼏步
答两步; 先占位、后传⼊
2. 插槽默认值
2.1 问题
通过插槽完成了内容的定制传什么显⽰什么, 但是如果不传则是空⽩ 能否给插槽设置 默认显⽰内容 呢
2.2 解决⽅案
封装组件时可以为 slot/slot 提供默认内容
2.3 语法
在 slot/slot 标签内放置内容, 作为默认内容
2.4 效果 使⽤组件时不传则会显⽰slot的默认内容 使⽤组件时传了则slot整体会被换掉, 从⽽显⽰传⼊的
2.5 总结
插槽默认内容有什么⽤
答使⽤组件不传内容时, 防⽌出现空⽩
默认内容何时显⽰何时不显⽰
答使⽤组件时, 不传内容,则显⽰默认内容; 否则显⽰传递的内容
3. 具名插槽
3.1 需求
⼀个组件内有多处结构需要外部传⼊标签进⾏定制 上⾯的弹框中有三处不同但是默认插槽只能定制⼀处内容这时怎么办?
3.2 具名插槽语法 多个slot使⽤name属性区分名字 template配合v-slot:名字 来分发对应标签
3.3 v-slot的简写
v-slot写起来太⻓vue给我们提供⼀个简单写法 v-slot: 直接简写为 #
3.4 代码实现
MyDialog.vue
script setup
/script
templatediv classdialogdiv classdialog-headerslot nameheaderh3友情提⽰/h3/slotspan classclose✖️/span/divdiv classdialog-contentslot namemain默认值/slot/divdiv classdialog-footerslot namefooterbutton取消/buttonbutton确定/button/slot/div/div
/templatestyle
* {margin: 0;padding: 0;
}.dialog {width: 470px;height: 230px;padding: 0 25px;background-color: #ffffff;margin: 40px auto;border-radius: 5px;
}.dialog-header {height: 70px;line-height: 70px;font-size: 20px;border-bottom: 1px solid #ccc;position: relative;
}.dialog-header .close {position: absolute;right: 0px;top: 0px;cursor: pointer;
}.dialog-content {height: 80px;font-size: 18px;padding: 15px 0;
}.dialog-footer {display: flex;justify-content: flex-end;
}.dialog-footer button {width: 65px;height: 35px;background-color: #ffffff;border: 1px solid #e1e3e9;cursor: pointer;outline: none;margin-left: 10px;border-radius: 3px;
}.dialog-footer button:last-child {background-color: #007acc;color: #fff;
}
/style
App.vue
script setup
import MyDialog from /components/MyDialog.vue;
/scripttemplateMyDialogtemplate #headerh3友情提示/h3/templatetemplate #mainp请输入正确手机号/p/templatetemplate #footerbutton取消/buttonbutton确定/button/template/MyDialogMyDialogtemplate #headerh3警告/h3/templatetemplate #mainp你确定要退出吗/p/templatetemplate #footerbutton取消/buttonbutton确定/button/template/MyDialog
/templatestyle scoped/style3.5 总结
组件内有多处不确定的结构 怎么办?
答具名插槽
具名插槽的使⽤语法是什么
答1、 slot name“名字” 默认内容 /slot
2、 template #名字 要展⽰的内容 /template
4. 作⽤域插槽
4.1 作⽤
带数据的插槽, 可以让组件功能更强⼤、更灵活、复⽤性更⾼; ⽤ slot 占位的同时, 还可以给 slot 绑定数据,将来使⽤组件时, 不仅可以传内容, 还能使⽤ slot 带来的数据
4.2 场景
封装表格组件 4.3 使⽤步骤
给 slot 标签, 以添加属性的⽅式传值
slot ahello :b666/slot所有添加的属性, 都会被收集到⼀个对象中
{ a: hello, b: 666 }在template中, 通过 #插槽名 “obj” 接收默认插槽名为 default
!-- obj会收集 slot 上绑定的所有⾃定义属性 --
template #defaultobj
{{ obj }}
/template4.4 静态代码
components/MyTable.vue
script setup/scripttemplatetable classmy-tabletheadtrth序号/thth姓名/thth年纪/thth操作/th/tr/theadtbodytrtd1/tdtd赵⼩云/tdtd19/tdtdbutton查看/button/td/trtrtd1/tdtd张⼩花/tdtd19/tdtdbutton查看/button/td/trtrtd1/tdtd孙⼤明/tdtd19/tdtdbutton查看/button/td/tr/tbody/table
/templatestyle
.my-table {width: 450px;text-align: center;border: 1px solid #ccc;font-size: 24px;margin: 30px auto;
}.my-table thead {background-color: #1f74ff;color: #fff;
}.my-table thead th {font-weight: normal;
}.my-table thead tr {line-height: 40px;
}.my-table th,
.my-table td {border-bottom: 1px solid #ccc;border-right: 1px solid #ccc;
}.my-table td:last-child {border-right: none;
}.my-table tr:last-child td {border-bottom: none;
}.my-table button {width: 65px;height: 35px;font-size: 18px;border: 1px solid #ccc;outline: none;border-radius: 3px;cursor: pointer;background-color: #ffffff;margin-left: 5px;
}
/styleApp.vue
script setup
import { ref } from vueimport MyTable from ./components/MyTable.vueconst tableData1 ref([{ id: 11, name: 狗蛋, age: 18 },{ id: 22, name: ⼤锤, age: 19 },{ id: 33, name: 铁棍, age: 17 }
])const tableData2 ref([{ id: 21, name: Jack, age: 18 },{ id: 32, name: Rose, age: 19 },{ id: 43, name: Henry, age: 17 }
])
/script
templateMyTable /MyTable /
/templatestyle
body {background-color: #fff;
}
/style4.5 代码实现
MyTable
script setup
const proper defineProps({data: {type: Array,default: () []}
})
/scripttemplatetable classmy-tabletheadtrth序号/thth姓名/thth年纪/thth操作/th/tr/theadtbody v-for(item, index) in data :keyitem.idtrtd{{index 1}}/tdtd{{item.name}}/tdtd{{item.age}}/tdtdslot :indexindexbuttonnone/button/slot/td/tr/tbody/table
/templatestyle
.my-table {width: 450px;text-align: center;border: 1px solid #ccc;font-size: 24px;margin: 30px auto;
}.my-table thead {background-color: #1f74ff;color: #fff;
}.my-table thead th {font-weight: normal;
}.my-table thead tr {line-height: 40px;
}.my-table th,
.my-table td {border-bottom: 1px solid #ccc;border-right: 1px solid #ccc;
}.my-table td:last-child {border-right: none;
}.my-table tr:last-child td {border-bottom: none;
}.my-table button {width: 65px;height: 35px;font-size: 18px;border: 1px solid #ccc;outline: none;border-radius: 3px;cursor: pointer;background-color: #ffffff;margin-left: 5px;
}
/styleApp.vue
script setup
import { ref } from vueimport MyTable from ./components/MyTable.vueconst tableData1 ref([{ id: 11, name: 狗蛋, age: 18 },{ id: 22, name: ⼤锤, age: 19 },{ id: 33, name: 铁棍, age: 17 }
])const tableData2 ref([{ id: 21, name: Jack, age: 18 },{ id: 32, name: Rose, age: 19 },{ id: 43, name: Henry, age: 17 }
])const showDetail (index) {alert(查看详情: JSON.stringify(tableData2.value[index]))
}/script
templateMyTable :datatableData1 template #default{ index }button clicktableData1.splice(index, 1)删除/button/template/MyTableMyTable :datatableData2template #default{ index }button clickshowDetail(index)查看/button/template/MyTable/templatestyle
body {background-color: #fff;
}
/style4.6 总结
作⽤域插槽的作⽤是什么
答让插槽带数据, 使得组件功能更强⼤、更灵活 作⽤域插槽的使⽤步骤是什么 答1、 slot上绑定数据
2、 template #名字“{ 数据 }” /template
三、综合案例
1. 整体效果和分析
1.1 整体效果 1.2 结构分析 1.3 需求说明
1、my-table 表格组件封装 动态传递表格数据渲染 表头⽀持⽤⼾⾃定义 主体⽀持⽤⼾⾃定义
2、my-tag 标签组件封装 双击显⽰输⼊框输⼊框获取焦点 失去焦点隐藏输⼊框 回显标签信息 内容修改, 回⻋修改标签信息
2. 封装 MyTable 并渲染
2.1 静态代码
components/MyTable.vue
script setup
/scripttemplatetable classmy-tabletheadtrth编号/thth名称/thth图⽚/thth width100px标签/th/tr/theadtbodytrtd1/tdtd⽑茸茸⼩熊出没⼉童⽺羔绒背⼼73-90cm/tdtdimg srchttps://yanxuanitem.nosdn.127.net/cd4b840751ef4f7505c85004f0bebcb5.png //tdtd标签内容1/td/trtrtd2/tdtd⽑茸茸⼩熊出没⼉童⽺羔绒背⼼73-90cm/tdtdimg srchttps://yanxuanitem.nosdn.127.net/cd4b840751ef4f7505c85004f0bebcb5.png //tdtd标签内容2/td/tr/tbody/table
/templatestyle langscss scoped
.my-table {width: 100%;border-spacing: 0;img {width: 100px;height: 100px;object-fit: contain;vertical-align: middle;}th {background: #f5f5f5;border-bottom: 2px solid #069;}td {border-bottom: 1px dashed #ccc;}td,th {text-align: center;padding: 10px;transition: all .5s;.red {color: red;}}.none {height: 100px;line-height: 100px;color: #999;}
}
/styleApp.vue
script setup
import { ref } from vue
import MyTable from ./components/MyTable.vue// 商品列表
const goodsList ref([{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
templateMyTable /
/templatestyle langscss
#app {width: 1000px;margin: 50px auto;img {width: 100px;height: 100px;object-fit: contain;vertical-align: middle;}
}
/style3. MyTable插槽⾃定义
MyTable.vue
script setup
// 接受父组件传递的商品列表
const props defineProps({goodsList: {type: Array,required: true}
})
/scripttemplatetable classmy-tabletheadslot nametheadSlot/slot/theadtbody v-for(goods, index) in goodsList :keygoods.idslot nametbodySlot :goodsgoods :indexindex/slot/tbody/table
/templatestyle langscss .my-table {width: 100%;border-spacing: 0;img {width: 100px;height: 100px;object-fit: contain;vertical-align: middle;}th {background: #f5f5f5;border-bottom: 2px solid #069;}td {border-bottom: 1px dashed #ccc;}td,th {text-align: center;padding: 10px;transition: all .5s;.red {color: red;}}.none {height: 100px;line-height: 100px;color: #999;}
}
/styleApp.vue
script setup
import { ref } from vue
import MyTable from ./components/MyTable.vue// 商品列表
const goodsList ref([{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
templateMyTable :goodsListgoodsList template #theadSlottrth编号/thth名称/thth图⽚/thth width100px标签/th/tr/templatetemplate #tbodySlot{ goods, index }trtd{{ index 1 }}/tdtd{{ goods.name }}/tdtdimg :srcgoods.picture //tdtd{{ goods.tag }}/td/tr/template/MyTable/templatestyle langscss
#app {width: 1000px;margin: 50px auto;img {width: 100px;height: 100px;object-fit: contain;vertical-align: middle;}
}
/style4. 封装MyTag组件
MyTag.vue
!-- format --script setupimport { ref } from vueconst model defineModel()// 是否处于编辑状态const isEdit ref(false)// 双击const onDblClick () {isEdit.value true}// 在输入框上敲击了回车const onEnter (e) {// 获取输入框的值并去除首尾空格const tagName e.target.value.trim()if (tagName) {// 如果有值需要把这个同步到父组件中直接修改 model model.value tagName}// 敲完回车不管输入框有没有值都要让输入框消失isEdit.value false}
/scripttemplatediv classmy-taginputv-focusv-ifisEditclassinputtypetext:valuemodelblurisEdit falseplaceholder输入标签keydown.enteronEnter /divclasstextv-elsedblclickonDblClick{{ model }}/div/div
/templatestyle langscss 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总体代码获取
e lang“scss” #app { width: 1000px; margin: 50px auto;
img {width: 100px;height: 100px;object-fit: contain;vertical-align: middle;
}}
### 4. 封装MyTag组件MyTag.vuevue
!-- format --script setupimport { ref } from vueconst model defineModel()// 是否处于编辑状态const isEdit ref(false)// 双击const onDblClick () {isEdit.value true}// 在输入框上敲击了回车const onEnter (e) {// 获取输入框的值并去除首尾空格const tagName e.target.value.trim()if (tagName) {// 如果有值需要把这个同步到父组件中直接修改 model model.value tagName}// 敲完回车不管输入框有没有值都要让输入框消失isEdit.value false}
/scripttemplatediv classmy-taginputv-focusv-ifisEditclassinputtypetext:valuemodelblurisEdit falseplaceholder输入标签keydown.enteronEnter /divclasstextv-elsedblclickonDblClick{{ model }}/div/div
/templatestyle langscss 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总体代码获取