目录
[2、 引用src\assets目录中的静态资源](#2、 引用src\assets目录中的静态资源)
通过上一节的学习,相信读者对组件的基础知识已经有了一定的了解。接下来,本章将继续对组件的基础知识进行讲解,主要包括动态组件、插槽、自定义指令和引用静态资源,学习这些内容,可以帮助读者更灵活地运用组件来开发Vue项目。
一、动态组件
1、概念

2、代码及演示
创建src\components\MyLeft.vue文件。
<template>MyLeft组件</template>
创建src\components\MyRight.vue文件
<template>MyRight组件</template>
创建src\components\DynamicComponent.vue文件,在该文件中导入并使用MyLeft和MyRight组件,实现单击按钮时动态切换组件的效果。
<template>
<button @click="showComponent = MyLeft">展示MyLeft组件</button>
<button @click="showComponent = MyRight">展示MyRight组件</button>
<div><component :is="showComponent"></component></div>
</template>
<script setup >
import MyLeft from './MyLeft.vue'
import MyRight from './MyRight.vue'
import { shallowRef } from 'vue'
const showComponent = shallowRef(MyLeft)
</script>
修改src\main.js文件,切换页面中显示的组件。
import DynamicComponent from './components/DynamicComponent.vue'
createApp(DynamicComponent).mount('#app')

二、插槽
1、概念



2、代码及演示
创建src\components\SlotSubComponent.vue文件,展示子组件的内容
<template>
<div>测试插槽的组件</div>
<slot></slot>
</template>
创建src\components\MySlot.vue文件,用于展示插槽的相关内容。
<template>
父组件-----{{ message }}
<hr>
<!-- 子组件需要写成双标签形式 -->
<SlotSubComponent>
<!-- 这是传给子组件插槽的内容 -->
<span style="color: blue;">下面是测试插槽组件,并传递数据给插槽</span>
<p>{{ message }}</p>
</SlotSubComponent>
</template>
<script setup>
import SlotSubComponent from './SlotSubComponent.vue'
const message = '这是组件的使用者自定义的内容'
</script>

具名插槽


代码及演示效果
创建src\components\ArticleInfo.vue文件,用于展示文章内容模板。
<template>
<div class="article-container">
<div class="header-box"><slot name="header"></slot></div>
<div class="content-box"><slot name="content"></slot></div>
<div class="footer-box"><slot name="footer"></slot></div>
</div>
</template>
<style>
.article-container > div { border: 1px solid black; }
</style>
创建src\components\MyArticle.vue文件,用于提供文章数据,在MyArticle组件中导入并使用ArticleInfo组件,并在<ArticleInfo>标签中为不同插槽添加不同的信息。
<template>
<ArticleInfo>
<template v-slot:header><p>这是文章的头部区域</p></template>
<template v-slot:content><p>这是文章的内容区域</p></template>
<template #footer><p>这是文章的尾部区域</p></template>
</ArticleInfo>
</template>
<script setup>import ArticleInfo from './ArticleInfo.vue' </script>

三、引用静态资源
1、引用public目录中的静态资源



创建src\components\Image.vue文件
<template>
<img src="/vite.svg" >
</template>
2、 引用src\assets目录中的静态资源

创建src\components\Icon.vue文件。
<template>
<img :src="icon">
</template>
<script setup>
import icon from '../assets/vue.svg'
</script>
在引用src\assets目录中的图片时,首先将图片保存到本地,然后使用import语法将图片导入需要的组件,最后通过img元素的src属性添加图片的路径。

四:阶段案例-商品管理






1、安装并全局引入Element Plus 组件
npm install element-plus
修改main.js
// 👇 必须同时引入这两行
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
createApp(shangpin).use(ElementPlus).mount('#app')
2、全部代码
<!-- 商品管理完整可运行代码 -->
<template>
<el-table :data="goodsList" border style="width: 100%">
<!-- 编号列 -->
<el-table-column type="index" label="编号" />
<!-- 商品名称 -->
<el-table-column prop="goods_name" label="商品名称" />
<!-- 商品价格 -->
<el-table-column prop="goods_price" label="商品价格" />
<!-- 标签列(作用域插槽) -->
<el-table-column label="标签">
<template v-slot="{ row }">
<div class="flex gap-2 items-center">
<!-- 标签循环 -->
<el-tag
v-for="(tag, index) in row.tags"
:key="index"
closable
disable-transitions
@close="() => handleClose(row, tag)"
>
{{ tag }}
</el-tag>
<!-- 新增标签输入框 -->
<el-input
v-if="row.inputVisible"
v-model="row.inputValue"
size="small"
class="w-20"
@keyup.enter="() => handleInputConfirm(row)"
@blur="() => handleInputConfirm(row)"
/>
<!-- +Tag 按钮 -->
<el-button
v-else
class="button-new-tag"
size="small"
@click="() => showInput(row)"
>
+ Tag
</el-button>
</div>
</template>
</el-table-column>
<!-- 操作列 -->
<el-table-column label="操作">
<template v-slot="{ row }">
<el-button type="danger" plain @click="onRemove(row.id)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
</template>
<script setup>
import { ref, nextTick } from 'vue'
// 商品列表数据
const goodsList = ref([
{
id: 1,
goods_name: '夏季专柜同款女鞋',
goods_price: 298,
tags: ['舒适', '透气'],
inputVisible: false,
inputValue: ''
},
{
id: 2,
goods_name: '冬季保暖女士休闲雪地靴 舒适加绒防水短靴 防滑棉鞋',
goods_price: 89,
tags: ['保暖', '防滑'],
inputVisible: false,
inputValue: ''
},
{
id: 3,
goods_name: '秋冬新款女士毛衣 套头宽松针织衫 简约上衣',
goods_price: 199,
tags: ['秋冬', '毛衣'],
inputVisible: false,
inputValue: ''
},
{
id: 4,
goods_name: '2023春秋装新款大码女装 衬衫 上衣',
goods_price: 19,
tags: ['雪纺衫', '打底'],
inputVisible: false,
inputValue: ''
},
{
id: 5,
goods_name: '长款长袖圆领女士毛衣 2022秋装新款假两件连衣裙',
goods_price: 178,
tags: ['圆领', '连衣裙'],
inputVisible: false,
inputValue: ''
}
])
// 关闭标签
const handleClose = (row, tag) => {
const i = row.tags.indexOf(tag)
if (i >= 0) row.tags.splice(i, 1)
}
// 显示输入框
const showInput = async (row) => {
row.inputVisible = true
await nextTick()
// 让输入框自动聚焦
const input = document.querySelector('.el-input__inner:focus')
input?.focus()
}
// 确认添加标签
const handleInputConfirm = (row) => {
if (row.inputValue.trim()) {
row.tags.push(row.inputValue.trim())
}
row.inputValue = ''
row.inputVisible = false
}
// 删除整行
const onRemove = (id) => {
goodsList.value = goodsList.value.filter(item => item.id !== id)
}
</script>
<style scoped>
.flex {
display: flex;
}
.gap-2 {
gap: 8px;
}
.items-center {
align-items: center;
}
.w-20 {
width: 80px;
}
.button-new-tag {
height: 24px;
line-height: 24px;
padding: 0 8px;
}
</style>