wordpress幻燈片名seo搜索引擎優(yōu)化實(shí)訓(xùn)
代碼獲取
07-?定義指令+插槽+商品列表案例
?、?定義指令
1. 基本使?
1.1 指令介紹
-
內(nèi)置指令:v-model、v-for、v-bind、v-on… 這都是Vue給咱們內(nèi)置的?些指令,可以直接使?
-
?定義指令:同時(shí)Vue也?持讓開(kāi)發(fā)者,??注冊(cè)?些指令。這些指令被稱為?定義指令
1.2 作?
封裝?段 公共的DOM操作 代碼,便于復(fù)?
1.3 語(yǔ)法
- 注冊(cè)
// main.js 中
app.directive('指令名', {// 元素掛載后(成為真實(shí)DOM) ?動(dòng)觸發(fā)?次?動(dòng)執(zhí)?mounted(el) {// el: 指令所在的DOM元素}
})
- 使?
<p v-指令名></p>
1.4 代碼?例
需求:當(dāng)??加載時(shí), 讓元素獲取焦點(diǎn)
// main.js
// 注冊(cè)全局指令app.directive('focus', {mounted(el) {console.log(el)// input 元素// 聚焦el.focus()}
})
App.vue
<script setup></script>
<template><div class="app"><input type="text" v-focus /></div>
</template>
1.5 總結(jié)
- ?定義指令的作?是什么?
? 答:封裝?段公共的 DOM操作 的代碼
- 使??定義指令的步驟是哪兩步?
? 答: 先注冊(cè)、后使?
- 指令配置選項(xiàng)中的 mounted 鉤?何時(shí)執(zhí)??
? 答:元素 掛載后 (成為DOM樹(shù)的?部分時(shí)) ?動(dòng)執(zhí)?
2. 綁定數(shù)據(jù)
2.1 需求
實(shí)現(xiàn)?個(gè) color 指令 :傳?不同的顏?, 給標(biāo)簽設(shè)置?字顏?
2.2 語(yǔ)法
1.在綁定指令時(shí),可以通過(guò)“等號(hào)”的形式為指令 綁定 具體的參數(shù)值
<div v-color="colorStr">Some Text</div>
2.通過(guò) binding.value 可以拿到指令值,指令值修改會(huì) 觸發(fā) updated 鉤?
app.directive('指令名', {// 掛載后?動(dòng)觸發(fā)?次mounted(el, binding) { },// 數(shù)據(jù)更新, 每次都會(huì)執(zhí)?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')
</script><template><p v-color="colorStr"></p>
</template>
2.4 簡(jiǎn)化形式
對(duì)于?定義指令來(lái)說(shuō),?個(gè)很常?的情況是僅僅需要在 mounted 和 updated 上實(shí)現(xiàn)相同的?為。這種情況下我們可以直接??個(gè)函數(shù)來(lái)定義指令,如下所?:
app.directive('color', (el, binding) => {// 這會(huì)在 `mounted` 和 `updated` 時(shí)都調(diào)?el.style.color = binding.value
})
3. 封裝v-loading指令
3.1 場(chǎng)景
實(shí)際開(kāi)發(fā)過(guò)程中,發(fā)送請(qǐng)求需要時(shí)間,在請(qǐng)求的數(shù)據(jù)未回來(lái)時(shí),??會(huì)處于空?狀態(tài) , ??體驗(yàn)不好
3.2 解決?案
封裝?個(gè) v-loading 指令,實(shí)現(xiàn)加載中的效果
3.3 分析
-
本質(zhì) loading效果就是?個(gè)蒙層,蓋在了盒?上
-
數(shù)據(jù)請(qǐng)求中,開(kāi)啟loading狀態(tài),添加蒙層
-
數(shù)據(jù)請(qǐng)求完畢,關(guān)閉loading狀態(tài),移除蒙層
3.4 實(shí)現(xiàn)
-
準(zhǔn)備?個(gè) loading類,通過(guò)偽元素定位,設(shè)置寬?,實(shí)現(xiàn)蒙層
-
開(kāi)啟關(guān)閉 loading狀態(tài)(添加移除蒙層),本質(zhì)只需要添加移除類即可
-
結(jié)合?定義指令的語(yǔ)法進(jìn)?封裝復(fù)?
3.5 代碼實(shí)現(xiàn)
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// 請(qǐng)求新聞數(shù)據(jù)const resp = await axios.get('http://localhost:4000/api/news')// 保存數(shù)據(jù)newsList.value = resp.data.data// 睡眠1秒await new Promise((resolve) => {setTimeout(() => {resolve()}, 1000)})isFinish.value = true
}
</script><template><div class="box"><ul><li v-for="item in newsList" :key="item.id" class="news" v-loading="!isFinish"><div class="left"><div class="title">{{ item.title }}</div><div class="info"><span>{{ item.source }}</span><span>{{ item.time }}</span></div></div><div class="right"><img :src="item.img" alt="img" /></div></li></ul></div>
</template><style>
.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");
?、插槽
插槽分類
-
默認(rèn)插槽
-
具名插槽
-
作?域插槽
1. 默認(rèn)插槽
1.1 作?
讓組件內(nèi)部的?些 結(jié)構(gòu) ?持 ?定義
1.2 需求
將需要多次顯?的對(duì)話框,封裝成?個(gè)組件
1.3 問(wèn)題
組件的內(nèi)容部分,不希望寫死,希望能使?的時(shí)候?定義。怎么辦
1.4 插槽的基本語(yǔ)法
-
組件內(nèi)需要定制的結(jié)構(gòu)部分,改? <slot></slot> 占位
-
使?組件時(shí), <MyDialog></MyDialog>寫成雙標(biāo)簽 , 包裹結(jié)構(gòu), 傳?替換slot
1.5 代碼?例
MyDialog.vue
<script setup>
</script>
<template><div class="dialog"><div class="dialog-header"><h3>友情提?</h3><span class="close">??</span></div><div class="dialog-content"><slot></slot></div><div class="dialog-footer"><button>取消</button><button>確認(rèn)</button></div></div>
</template><style 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';
</script><template><div><MyDialog>你確定要?jiǎng)h除嗎?</MyDialog><MyDialog>你確定要退出嗎?</MyDialog></div>
</template><style scoped></style>
1.6 總結(jié)
- 組件內(nèi)某?部分結(jié)構(gòu)不確定,想要?定義怎么辦?
? 答:使? 插槽 (技術(shù))
- 插槽的步驟分為哪?步?
? 答:兩步; 先占位、后傳?
2. 插槽默認(rèn)值
2.1 問(wèn)題
通過(guò)插槽完成了內(nèi)容的定制,傳什么顯?什么, 但是如果不傳,則是空?
能否給插槽設(shè)置 默認(rèn)顯?內(nèi)容 呢?
2.2 解決?案
封裝組件時(shí),可以為 <slot></slot> 提供默認(rèn)內(nèi)容
2.3 語(yǔ)法
在 <slot></slot> 標(biāo)簽內(nèi),放置內(nèi)容, 作為默認(rèn)內(nèi)容
2.4 效果
-
使?組件時(shí),不傳,則會(huì)顯?slot的默認(rèn)內(nèi)容
-
使?組件時(shí),傳了,則slot整體會(huì)被換掉, 從?顯?傳?的
2.5 總結(jié)
- 插槽默認(rèn)內(nèi)容有什么??
? 答:使?組件不傳內(nèi)容時(shí), 防?出現(xiàn)空?
- 默認(rèn)內(nèi)容何時(shí)顯??何時(shí)不顯??
? 答:使?組件時(shí), 不傳內(nèi)容,則顯?默認(rèn)內(nèi)容; 否則顯?傳遞的內(nèi)容
3. 具名插槽
3.1 需求
?個(gè)組件內(nèi)有多處結(jié)構(gòu),需要外部傳?標(biāo)簽,進(jìn)?定制
上?的彈框中有三處不同,但是默認(rèn)插槽只能定制?處內(nèi)容,這時(shí)怎么辦?
3.2 具名插槽語(yǔ)法
-
多個(gè)slot使?name屬性區(qū)分名字
-
template配合v-slot:名字 來(lái)分發(fā)對(duì)應(yīng)標(biāo)簽
3.3 v-slot的簡(jiǎn)寫
v-slot寫起來(lái)太?,vue給我們提供?個(gè)簡(jiǎn)單寫法 v-slot: 直接簡(jiǎn)寫為 #
3.4 代碼實(shí)現(xiàn)
MyDialog.vue
<script setup>
</script>
<template><div class="dialog"><div class="dialog-header"><slot name="header"><h3>友情提?</h3></slot><span class="close">??</span></div><div class="dialog-content"><slot name="main">默認(rèn)值</slot></div><div class="dialog-footer"><slot name="footer"><button>取消</button><button>確定</button></slot></div></div>
</template><style >
* {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';
</script><template><MyDialog><template #header><h3>友情提示</h3></template><template #main><p>請(qǐng)輸入正確手機(jī)號(hào)</p></template><template #footer><button>取消</button><button>確定</button></template></MyDialog><MyDialog><template #header><h3>警告</h3></template><template #main><p>你確定要退出嗎</p></template><template #footer><button>取消</button><button>確定</button></template></MyDialog>
</template><style scoped></style>
3.5 總結(jié)
- 組件內(nèi)有多處不確定的結(jié)構(gòu) 怎么辦?
? 答:具名插槽
- 具名插槽的使?語(yǔ)法是什么?
? 答:1、 <slot name=“名字”> 默認(rèn)內(nèi)容 </slot>
? 2、 <template #名字> 要展?的內(nèi)容 </template>
4. 作?域插槽
4.1 作?
帶數(shù)據(jù)的插槽, 可以讓組件功能更強(qiáng)?、更靈活、復(fù)?性更?; ? slot 占位的同時(shí), 還可以給 slot 綁定數(shù)據(jù),將來(lái)使?組件時(shí), 不僅可以傳內(nèi)容, 還能使? slot 帶來(lái)的數(shù)據(jù)
4.2 場(chǎng)景
封裝表格組件
4.3 使?步驟
- 給 slot 標(biāo)簽, 以添加屬性的?式傳值
<slot a="hello" :b="666"></slot>
- 所有添加的屬性, 都會(huì)被收集到?個(gè)對(duì)象中
{ a: 'hello', b: 666 }
- 在template中, 通過(guò) #插槽名= “obj” 接收,默認(rèn)插槽名為 default
<!-- obj會(huì)收集 slot 上綁定的所有?定義屬性 -->
<template #default="obj">
{{ obj }}
</template>
4.4 靜態(tài)代碼
components/MyTable.vue
<script setup></script><template><table class="my-table"><thead><tr><th>序號(hào)</th><th>姓名</th><th>年紀(jì)</th><th>操作</th></tr></thead><tbody><tr><td>1</td><td>趙?云</td><td>19</td><td><button>查看</button></td></tr><tr><td>1</td><td>張?花</td><td>19</td><td><button>查看</button></td></tr><tr><td>1</td><td>孫?明</td><td>19</td><td><button>查看</button></td></tr></tbody></table>
</template><style>
.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;
}
</style>
App.vue
<script setup>
import { ref } from 'vue'import MyTable from './components/MyTable.vue'const 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>
<template><MyTable /><MyTable />
</template><style>
body {background-color: #fff;
}
</style>
4.5 代碼實(shí)現(xiàn)
MyTable
<script setup>
const proper = defineProps({data: {type: Array,default: () => []}
})
</script><template><table class="my-table"><thead><tr><th>序號(hào)</th><th>姓名</th><th>年紀(jì)</th><th>操作</th></tr></thead><tbody v-for="(item, index) in data" :key="item.id"><tr><td>{{index + 1}}</td><td>{{item.name}}</td><td>{{item.age}}</td><td><slot :index="index"><button>none</button></slot></td></tr></tbody></table>
</template><style>
.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;
}
</style>
App.vue
<script setup>
import { ref } from 'vue'import MyTable from './components/MyTable.vue'const 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>
<template><MyTable :data="tableData1" ><template #default="{ index }"><button @click="tableData1.splice(index, 1)">刪除</button></template></MyTable><MyTable :data="tableData2"><template #default="{ index }"><button @click="showDetail(index)">查看</button></template></MyTable></template><style>
body {background-color: #fff;
}
</style>
4.6 總結(jié)
- 作?域插槽的作?是什么?
? 答:讓插槽帶數(shù)據(jù), 使得組件功能更強(qiáng)?、更靈活
-
作?域插槽的使?步驟是什么?
答:1、 slot上綁定數(shù)據(jù)
? 2、 <template #名字=“{ 數(shù)據(jù) }”> </template>
三、綜合案例
1. 整體效果和分析
1.1 整體效果

1.2 結(jié)構(gòu)分析

1.3 需求說(shuō)明
1、my-table 表格組件封裝
-
動(dòng)態(tài)傳遞表格數(shù)據(jù)渲染
-
表頭?持???定義
-
主體?持???定義
2、my-tag 標(biāo)簽組件封裝
-
雙擊顯?輸?框,輸?框獲取焦點(diǎn)
-
失去焦點(diǎn),隱藏輸?框
-
回顯標(biāo)簽信息
-
內(nèi)容修改, 回?修改標(biāo)簽信息
2. 封裝 MyTable 并渲染
2.1 靜態(tài)代碼
components/MyTable.vue
<script setup>
</script><template><table class="my-table"><thead><tr><th>編號(hào)</th><th>名稱</th><th>圖?</th><th width="100px">標(biāo)簽</th></tr></thead><tbody><tr><td>1</td><td>?茸茸?熊出沒(méi),?童?羔絨背?73-90cm</td><td><img src="https://yanxuanitem.nosdn.127.net/cd4b840751ef4f7505c85004f0bebcb5.png" /></td><td>標(biāo)簽內(nèi)容1</td></tr><tr><td>2</td><td>?茸茸?熊出沒(méi),?童?羔絨背?73-90cm</td><td><img src="https://yanxuanitem.nosdn.127.net/cd4b840751ef4f7505c85004f0bebcb5.png" /></td><td>標(biāo)簽內(nèi)容2</td></tr></tbody></table>
</template><style lang="scss" 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;}
}
</style>
App.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: '梨?朱泥三絕清代?品壺經(jīng)典款紫砂壺',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: '?茸茸?熊出沒(méi),?童?羔絨背?73-90cm',tag: '?童服飾'},{id: 104,picture:'https://yanxuan-item.nosdn.127.net/56eb25a38d7a630e76a608a9360eec6b.jpg',name: '基礎(chǔ)百搭,?童套頭針織??1-9歲',tag: '?童服飾'}
])
</script>
<template><MyTable />
</template><style lang="scss">
#app {width: 1000px;margin: 50px auto;img {width: 100px;height: 100px;object-fit: contain;vertical-align: middle;}
}
</style>
3. MyTable插槽?定義
MyTable.vue
<script setup>
// 接受父組件傳遞的商品列表
const props = defineProps({goodsList: {type: Array,required: true}
})
</script><template><table class="my-table"><thead><slot name="theadSlot"></slot></thead><tbody v-for="(goods, index) in goodsList" :key="goods.id"><slot name="tbodySlot" :goods="goods" :index="index"></slot></tbody></table>
</template><style lang="scss" >.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;}
}
</style>
App.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: '梨?朱泥三絕清代?品壺經(jīng)典款紫砂壺',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: '?茸茸?熊出沒(méi),?童?羔絨背?73-90cm',tag: '?童服飾'},{id: 104,picture:'https://yanxuan-item.nosdn.127.net/56eb25a38d7a630e76a608a9360eec6b.jpg',name: '基礎(chǔ)百搭,?童套頭針織??1-9歲',tag: '?童服飾'}
])
</script>
<template><MyTable :goodsList="goodsList" ><template #theadSlot><tr><th>編號(hào)</th><th>名稱</th><th>圖?</th><th width="100px">標(biāo)簽</th></tr></template><template #tbodySlot="{ goods, index }"><tr><td>{{ index + 1 }}</td><td>{{ goods.name }}</td><td><img :src="goods.picture" /></td><td>{{ goods.tag }}</td></tr></template></MyTable></template><style lang="scss">
#app {width: 1000px;margin: 50px auto;img {width: 100px;height: 100px;object-fit: contain;vertical-align: middle;}
}
</style>
4. 封裝MyTag組件
MyTag.vue
<!-- @format --><script setup>import { ref } from 'vue'const model = defineModel()// 是否處于編輯狀態(tài)const isEdit = ref(false)// 雙擊const onDblClick = () => {isEdit.value = true}// 在輸入框上敲擊了回車const onEnter = (e) => {// 獲取輸入框的值,并去除首尾空格const tagName = e.target.value.trim()if (tagName) {// 如果有值,需要把這個(gè)同步到父組件中(直接修改 model )model.value = tagName}// 敲完回車,不管輸入框有沒(méi)有值,都要讓輸入框消失isEdit.value = false}
</script><template><div class="my-tag"><inputv-focusv-if="isEdit"class="input"type="text":value="model"@blur="isEdit = false"placeholder="輸入標(biāo)簽"@keydown.enter="onEnter" /><divclass="text"v-else@dblclick="onDblClick">{{ model }}</div></div>
</template><style lang="scss" 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.vue````vue
<!-- @format --><script setup>import { ref } from 'vue'const model = defineModel()// 是否處于編輯狀態(tài)const isEdit = ref(false)// 雙擊const onDblClick = () => {isEdit.value = true}// 在輸入框上敲擊了回車const onEnter = (e) => {// 獲取輸入框的值,并去除首尾空格const tagName = e.target.value.trim()if (tagName) {// 如果有值,需要把這個(gè)同步到父組件中(直接修改 model )model.value = tagName}// 敲完回車,不管輸入框有沒(méi)有值,都要讓輸入框消失isEdit.value = false}
</script><template><div class="my-tag"><inputv-focusv-if="isEdit"class="input"type="text":value="model"@blur="isEdit = false"placeholder="輸入標(biāo)簽"@keydown.enter="onEnter" /><divclass="text"v-else@dblclick="onDblClick">{{ model }}</div></div>
</template><style lang="scss" 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>
總體代碼獲取