wordpress幻燈片名seo搜索引擎優(yōu)化實訓
代碼獲取
07-?定義指令+插槽+商品列表案例
?、?定義指令
1. 基本使?
1.1 指令介紹
-
內置指令:v-model、v-for、v-bind、v-on… 這都是Vue給咱們內置的?些指令,可以直接使?
-
?定義指令:同時Vue也?持讓開發(fā)者,??注冊?些指令。這些指令被稱為?定義指令
1.2 作?
封裝?段 公共的DOM操作 代碼,便于復?
1.3 語法
- 注冊
// main.js 中
app.directive('指令名', {// 元素掛載后(成為真實DOM) ?動觸發(fā)?次?動執(zhí)?mounted(el) {// el: 指令所在的DOM元素}
})
- 使?
<p v-指令名></p>
1.4 代碼?例
需求:當??加載時, 讓元素獲取焦點
// main.js
// 注冊全局指令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 總結
- ?定義指令的作?是什么?
? 答:封裝?段公共的 DOM操作 的代碼
- 使??定義指令的步驟是哪兩步?
? 答: 先注冊、后使?
- 指令配置選項中的 mounted 鉤?何時執(zhí)??
? 答:元素 掛載后 (成為DOM樹的?部分時) ?動執(zhí)?
2. 綁定數據
2.1 需求
實現?個 color 指令 :傳?不同的顏?, 給標簽設置?字顏?
2.2 語法
1.在綁定指令時,可以通過“等號”的形式為指令 綁定 具體的參數值
<div v-color="colorStr">Some Text</div>
2.通過 binding.value 可以拿到指令值,指令值修改會 觸發(fā) updated 鉤?
app.directive('指令名', {// 掛載后?動觸發(fā)?次mounted(el, binding) { },// 數據更新, 每次都會執(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 簡化形式
對于?定義指令來說,?個很常?的情況是僅僅需要在 mounted 和 updated 上實現相同的?為。這種情況下我們可以直接??個函數來定義指令,如下所?:
app.directive('color', (el, binding) => {// 這會在 `mounted` 和 `updated` 時都調?el.style.color = binding.value
})
3. 封裝v-loading指令
3.1 場景
實際開發(fā)過程中,發(fā)送請求需要時間,在請求的數據未回來時,??會處于空?狀態(tài) , ??體驗不好
3.2 解決?案
封裝?個 v-loading 指令,實現加載中的效果
3.3 分析
-
本質 loading效果就是?個蒙層,蓋在了盒?上
-
數據請求中,開啟loading狀態(tài),添加蒙層
-
數據請求完畢,關閉loading狀態(tài),移除蒙層
3.4 實現
-
準備?個 loading類,通過偽元素定位,設置寬?,實現蒙層
-
開啟關閉 loading狀態(tài)(添加移除蒙層),本質只需要添加移除類即可
-
結合?定義指令的語法進?封裝復?
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
}
</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");
?、插槽
插槽分類
-
默認插槽
-
具名插槽
-
作?域插槽
1. 默認插槽
1.1 作?
讓組件內部的?些 結構 ?持 ?定義
1.2 需求
將需要多次顯?的對話框,封裝成?個組件
1.3 問題
組件的內容部分,不希望寫死,希望能使?的時候?定義。怎么辦
1.4 插槽的基本語法
-
組件內需要定制的結構部分,改? <slot></slot> 占位
-
使?組件時, <MyDialog></MyDialog>寫成雙標簽 , 包裹結構, 傳?替換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>確認</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>你確定要刪除嗎?</MyDialog><MyDialog>你確定要退出嗎?</MyDialog></div>
</template><style scoped></style>
1.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屬性區(qū)分名字
-
template配合v-slot:名字 來分發(fā)對應標簽
3.3 v-slot的簡寫
v-slot寫起來太?,vue給我們提供?個簡單寫法 v-slot: 直接簡寫為 #
3.4 代碼實現
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">默認值</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>請輸入正確手機號</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 總結
- 組件內有多處不確定的結構 怎么辦?
? 答:具名插槽
- 具名插槽的使?語法是什么?
? 答:1、 <slot name=“名字”> 默認內容 </slot>
? 2、 <template #名字> 要展?的內容 </template>
4. 作?域插槽
4.1 作?
帶數據的插槽, 可以讓組件功能更強?、更靈活、復?性更?; ? slot 占位的同時, 還可以給 slot 綁定數據,將來使?組件時, 不僅可以傳內容, 還能使? slot 帶來的數據
4.2 場景
封裝表格組件
4.3 使?步驟
- 給 slot 標簽, 以添加屬性的?式傳值
<slot a="hello" :b="666"></slot>
- 所有添加的屬性, 都會被收集到?個對象中
{ a: 'hello', b: 666 }
- 在template中, 通過 #插槽名= “obj” 接收,默認插槽名為 default
<!-- obj會收集 slot 上綁定的所有?定義屬性 -->
<template #default="obj">
{{ obj }}
</template>
4.4 靜態(tài)代碼
components/MyTable.vue
<script setup></script><template><table class="my-table"><thead><tr><th>序號</th><th>姓名</th><th>年紀</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 代碼實現
MyTable
<script setup>
const proper = defineProps({data: {type: Array,default: () => []}
})
</script><template><table class="my-table"><thead><tr><th>序號</th><th>姓名</th><th>年紀</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 總結
- 作?域插槽的作?是什么?
? 答:讓插槽帶數據, 使得組件功能更強?、更靈活
-
作?域插槽的使?步驟是什么?
答:1、 slot上綁定數據
? 2、 <template #名字=“{ 數據 }”> </template>
三、綜合案例
1. 整體效果和分析
1.1 整體效果

1.2 結構分析

1.3 需求說明
1、my-table 表格組件封裝
-
動態(tài)傳遞表格數據渲染
-
表頭?持???定義
-
主體?持???定義
2、my-tag 標簽組件封裝
-
雙擊顯?輸?框,輸?框獲取焦點
-
失去焦點,隱藏輸?框
-
回顯標簽信息
-
內容修改, 回?修改標簽信息
2. 封裝 MyTable 并渲染
2.1 靜態(tài)代碼
components/MyTable.vue
<script setup>
</script><template><table class="my-table"><thead><tr><th>編號</th><th>名稱</th><th>圖?</th><th width="100px">標簽</th></tr></thead><tbody><tr><td>1</td><td>?茸茸?熊出沒,?童?羔絨背?73-90cm</td><td><img src="https://yanxuanitem.nosdn.127.net/cd4b840751ef4f7505c85004f0bebcb5.png" /></td><td>標簽內容1</td></tr><tr><td>2</td><td>?茸茸?熊出沒,?童?羔絨背?73-90cm</td><td><img src="https://yanxuanitem.nosdn.127.net/cd4b840751ef4f7505c85004f0bebcb5.png" /></td><td>標簽內容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: '梨?朱泥三絕清代?品壺經典款紫砂壺',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>
<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: '梨?朱泥三絕清代?品壺經典款紫砂壺',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>
<template><MyTable :goodsList="goodsList" ><template #theadSlot><tr><th>編號</th><th>名稱</th><th>圖?</th><th width="100px">標簽</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) {// 如果有值,需要把這個同步到父組件中(直接修改 model )model.value = tagName}// 敲完回車,不管輸入框有沒有值,都要讓輸入框消失isEdit.value = false}
</script><template><div class="my-tag"><inputv-focusv-if="isEdit"class="input"type="text":value="model"@blur="isEdit = false"placeholder="輸入標簽"@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) {// 如果有值,需要把這個同步到父組件中(直接修改 model )model.value = tagName}// 敲完回車,不管輸入框有沒有值,都要讓輸入框消失isEdit.value = false}
</script><template><div class="my-tag"><inputv-focusv-if="isEdit"class="input"type="text":value="model"@blur="isEdit = false"placeholder="輸入標簽"@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>
總體代碼獲取