Vue?3項(xiàng)目中Element?Plus組件庫實(shí)戰(zhàn)技巧與踩坑記錄
分享我在Vue 3項(xiàng)目中使用Element Plus的經(jīng)驗(yàn)技巧和踩坑記錄
前言
Element Plus是Vue 3生態(tài)中最流行的UI組件庫之一,提供了豐富的組件和良好的設(shè)計(jì)。在開發(fā)博客項(xiàng)目的過程中,我積累了很多使用Element Plus的經(jīng)驗(yàn)和技巧,也踩過一些坑。本文將分享這些實(shí)戰(zhàn)經(jīng)驗(yàn)。
快速上手
1. 安裝與配置
# 安裝Element Plus npm install element-plus # 安裝圖標(biāo)庫 npm install @element-plus/icons-vue
// main.ts
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import App from './App.vue'
const app = createApp(App)
// 注冊所有組件
app.use(ElementPlus)
// 注冊所有圖標(biāo)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
app.mount('#app')
2. 按需引入(推薦)
為了減小包體積,建議按需引入組件:
# 安裝按需引入插件 npm install -D unplugin-vue-components unplugin-auto-import
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
export default defineConfig({
plugins: [
vue(),
AutoImport({
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
],
})
這樣配置后,使用組件時(shí)會(huì)自動(dòng)按需引入,無需手動(dòng)import。
常用組件技巧
1. 表單組件
el-form深度驗(yàn)證
<template>
<el-form
ref="formRef"
:model="formData"
:rules="rules"
label-width="120px"
>
<el-form-item label="標(biāo)題" prop="title">
<el-input v-model="formData.title" />
</el-form-item>
<el-form-item label="郵箱" prop="email">
<el-input v-model="formData.email" />
</el-form-item>
<el-form-item label="密碼" prop="password">
<el-input
v-model="formData.password"
type="password"
show-password
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSubmit">
提交
</el-button>
<el-button @click="handleReset">
重置
</el-button>
</el-form-item>
</el-form>
</template>
<script setup lang="ts">
import type { FormInstance, FormRules } from 'element-plus'
const formRef = ref<FormInstance>()
const formData = reactive({
title: '',
email: '',
password: ''
})
const rules = reactive<FormRules>({
title: [
{ required: true, message: '請輸入標(biāo)題', trigger: 'blur' },
{ min: 2, max: 50, message: '長度在 2 到 50 個(gè)字符', trigger: 'blur' }
],
email: [
{ required: true, message: '請輸入郵箱地址', trigger: 'blur' },
{ type: 'email', message: '請輸入正確的郵箱地址', trigger: ['blur', 'change'] }
],
password: [
{ required: true, message: '請輸入密碼', trigger: 'blur' },
{ min: 6, message: '密碼長度不能少于 6 位', trigger: 'blur' }
]
})
const handleSubmit = async () => {
if (!formRef.value) return
await formRef.value.validate((valid, fields) => {
if (valid) {
// 驗(yàn)證通過,提交表單
console.log('提交:', formData)
} else {
console.log('驗(yàn)證失敗:', fields)
}
})
}
const handleReset = () => {
formRef.value?.resetFields()
}
</script>
動(dòng)態(tài)表單
<template>
<el-form :model="formData">
<el-form-item
v-for="(item, index) in formData.items"
:key="index"
:label="'項(xiàng)目 ' + (index + 1)"
>
<el-input v-model="item.value" />
<el-button
@click="removeItem(index)"
icon="Delete"
type="danger"
>
刪除
</el-button>
</el-form-item>
<el-button @click="addItem" icon="Plus">
添加項(xiàng)目
</el-button>
</el-form>
</template>
<script setup lang="ts">
const formData = reactive({
items: [{ value: '' }]
})
const addItem = () => {
formData.items.push({ value: '' })
}
const removeItem = (index: number) => {
formData.items.splice(index, 1)
}
</script>
2. 表格組件
表格排序和篩選
<template>
<el-table
:data="filteredData"
:default-sort="{ prop: 'date', order: 'descending' }"
@sort-change="handleSortChange"
>
<el-table-column prop="title" label="標(biāo)題" sortable />
<el-table-column
prop="category"
label="分類"
:filters="categoryFilters"
:filter-method="filterCategory"
/>
<el-table-column prop="views" label="瀏覽量" sortable />
<el-table-column prop="date" label="日期" sortable />
</el-table>
</template>
<script setup lang="ts">
const articles = ref<Article[]>([])
const filteredData = computed(() => {
return articles.value
})
const categoryFilters = [
{ text: 'Vue', value: 'Vue' },
{ text: 'React', value: 'React' },
{ text: 'TypeScript', value: 'TypeScript' }
]
const filterCategory = (value: string, row: Article) => {
return row.category === value
}
const handleSortChange = (sort: any) => {
console.log('排序改變:', sort)
}
</script>
表格分頁
<template>
<el-table :data="paginatedData">
<!-- 列定義 -->
</el-table>
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:total="total"
:page-sizes="[10, 20, 50, 100]"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</template>
<script setup lang="ts">
const currentPage = ref(1)
const pageSize = ref(10)
const total = ref(0)
const paginatedData = computed(() => {
const start = (currentPage.value - 1) * pageSize.value
const end = start + pageSize.value
return articles.value.slice(start, end)
})
const handleSizeChange = (size: number) => {
pageSize.value = size
}
const handleCurrentChange = (page: number) => {
currentPage.value = page
}
</script>
3. 彈窗組件
對話框嵌套
<template>
<el-button @click="showDialog = true">打開對話框</el-button>
<el-dialog v-model="showDialog" title="父對話框">
<p>這是父對話框的內(nèi)容</p>
<el-button @click="showChildDialog = true">
打開子對話框
</el-button>
<el-dialog
v-model="showChildDialog"
title="子對話框"
append-to-body
>
<p>這是子對話框的內(nèi)容</p>
</el-dialog>
</el-dialog>
</template>
<script setup lang="ts">
const showDialog = ref(false)
const showChildDialog = ref(false)
</script>
注意:嵌套對話框時(shí),子對話框需要添加append-to-body屬性。
4. 樹形組件
異步加載樹
<template>
<el-tree
:props="defaultProps"
:load="loadNode"
lazy
show-checkbox
/>
</template>
<script setup lang="ts">
const defaultProps = {
label: 'name',
children: 'children',
isLeaf: 'leaf'
}
const loadNode = async (node: Node, resolve: (data: TreeData[]) => void) => {
if (node.level === 0) {
// 加載根節(jié)點(diǎn)
const data = await loadRootNodes()
resolve(data)
} else {
// 加載子節(jié)點(diǎn)
const data = await loadChildNodes(node.data.id)
resolve(data)
}
}
const loadRootNodes = async () => {
// 異步加載數(shù)據(jù)
return [
{ name: '節(jié)點(diǎn)1', id: 1 },
{ name: '節(jié)點(diǎn)2', id: 2 }
]
}
</script>
主題定制
1. 使用CSS變量
// styles/theme.scss
:root {
--el-color-primary: #409eff;
--el-color-success: #67c23a;
--el-color-warning: #e6a23c;
--el-color-danger: #f56c6c;
--el-color-info: #909399;
}
// 使用自定義主題
$--color-primary: var(--el-color-primary);
2. SCSS變量覆蓋
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ElementPlus from 'unplugin-element-plus/vite'
export default defineConfig({
plugins: [
vue(),
ElementPlus({
// 使用scss樣式
useSource: true
})
]
})
// styles/element-variables.scss /* 改變主題色變量 */ $--color-primary: #1890ff; $--color-success: #52c41a; $--color-warning: #faad14; $--color-danger: #f5222d; $--color-info: #909399; /* 改變icon字體路徑變量,必需 */ $--font-path: '~element-plus/lib/theme-chalk/fonts'; @import "~element-plus/packages/theme-chalk/src/index";
3. 暗黑模式
<template>
<el-switch
v-model="isDark"
@change="toggleDark"
inline-prompt
active-text="暗"
inactive-text="亮"
/>
</template>
<script setup lang="ts">
const isDark = ref(false)
const toggleDark = (value: boolean) => {
if (value) {
document.documentElement.classList.add('dark')
} else {
document.documentElement.classList.remove('dark')
}
}
</script>
<style>
/* 暗黑模式樣式 */
html.dark {
--el-bg-color: #141414;
--el-text-color-primary: #e5eaf3;
--el-border-color: #4c4d4f;
--el-border-color-light: #414243;
}
</style>
性能優(yōu)化
1. 圖標(biāo)按需加載
// utils/icons.ts
import { registerIcons } from 'element-plus/es/components/icon'
// 只注冊需要的圖標(biāo)
export function lazyRegisterIcons() {
const icons = [
'Edit',
'Delete',
'View',
'Download',
'Share',
'Star',
'Plus',
'Search',
'Home'
]
// 使用requestIdleCallback在空閑時(shí)注冊
const idleCallback = window.requestIdleCallback || window.setTimeout
idleCallback(() => {
registerIcons(icons)
})
}
// main.ts
import { lazyRegisterIcons } from './utils/icons'
lazyRegisterIcons()
2. 虛擬滾動(dòng)
<template>
<el-virtual-list
:data="items"
:height="400"
:item-size="50"
>
<template #default="{ item, index }">
<div class="item">
{{ index }} - {{ item.name }}
</div>
</template>
</el-virtual-list>
</template>
<script setup lang="ts">
import { ElVirtualList } from 'element-plus'
// 生成大量數(shù)據(jù)
const items = Array.from({ length: 10000 }, (_, i) => ({
id: i,
name: `Item ${i}`
}))
</script>
踩坑記錄
1. Dialog關(guān)閉不觸發(fā)事件
問題:點(diǎn)擊遮罩層關(guān)閉Dialog時(shí),沒有觸發(fā)關(guān)閉事件。
解決:使用before-close屬性:
<el-dialog
v-model="visible"
:before-close="handleClose"
>
<template #header>
<span>標(biāo)題</span>
</template>
</el-dialog>
<script setup lang="ts">
const handleClose = (done: () => void) => {
// 執(zhí)行關(guān)閉前的邏輯
done()
}
</script>
2. Table固定列錯(cuò)位
問題:表格固定列在滾動(dòng)時(shí)出現(xiàn)錯(cuò)位。
解決:監(jiān)聽窗口大小變化,調(diào)用doLayout方法:
<template>
<el-table
ref="tableRef"
:data="tableData"
>
<el-table-column prop="date" label="日期" fixed />
<el-table-column prop="name" label="姓名" />
</el-table>
</template>
<script setup lang="ts">
const tableRef = ref()
onMounted(() => {
window.addEventListener('resize', () => {
tableRef.value?.doLayout()
})
})
</script>
3. Select下拉框顯示位置錯(cuò)誤
問題:Select組件的下拉框在頁面滾動(dòng)后顯示位置錯(cuò)誤。
解決:使用popper-options配置:
<el-select
v-model="value"
:popper-options="{
modifiers: [
{
name: 'flip',
options: {
fallbackPlacements: ['bottom-start', 'top-start']
}
}
]
}"
>
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
4. DatePicker時(shí)間格式問題
問題:DatePicker返回的日期格式不符合預(yù)期。
解決:使用value-format屬性:
<el-date-picker v-model="date" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" placeholder="選擇日期時(shí)間" />
5. Upload組件上傳失敗
問題:Upload組件在某些情況下上傳失敗。
解決:正確處理on-success和on-error回調(diào):
<el-upload
action="/api/upload"
:on-success="handleSuccess"
:on-error="handleError"
:before-upload="beforeUpload"
>
<el-button type="primary">上傳文件</el-button>
</el-upload>
<script setup lang="ts">
const handleSuccess = (response: any, file: any) => {
if (response.code === 200) {
ElMessage.success('上傳成功')
} else {
ElMessage.error(response.message)
}
}
const handleError = (error: any) => {
ElMessage.error('上傳失?。? + error.message)
}
const beforeUpload = (file: File) => {
const isJPG = file.type === 'image/jpeg' || file.type === 'image/png'
const isLt2M = file.size / 1024 / 1024 < 2
if (!isJPG) {
ElMessage.error('只能上傳JPG/PNG圖片!')
}
if (!isLt2M) {
ElMessage.error('圖片大小不能超過2MB!')
}
return isJPG && isLt2M
}
</script>
最佳實(shí)踐
1. 統(tǒng)一配置
// config/element-plus.ts
import { ElConfigProvider } from 'element-plus'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
export default {
locale: zhCn,
size: 'default',
zIndex: 3000
}
<!-- App.vue -->
<template>
<el-config-provider :locale="locale">
<router-view />
</el-config-provider>
</template>
<script setup lang="ts">
import zhCn from 'element-plus/es/locale/lang/zh-cn'
const locale = zhCn
</script>
2. 封裝常用組件
<!-- components/SearchInput.vue -->
<template>
<el-input
v-model="searchText"
:placeholder="placeholder"
clearable
@clear="handleClear"
@input="handleInput"
>
<template #prefix>
<el-icon><Search /></el-icon>
</template>
<template #suffix>
<el-button
v-if="searchText"
link
icon="Close"
@click="handleClear"
/>
</template>
</el-input>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue'
interface Props {
modelValue: string
placeholder?: string
}
const props = withDefaults(defineProps<Props>(), {
placeholder: '請輸入搜索內(nèi)容'
})
const emit = defineEmits<{
(e: 'update:modelValue', value: string): void
(e: 'search', value: string): void
}>()
const searchText = ref(props.modelValue)
watch(() => props.modelValue, (val) => {
searchText.value = val
})
watch(searchText, (val) => {
emit('update:modelValue', val)
})
const handleClear = () => {
searchText.value = ''
emit('search', '')
}
const handleInput = debounce((value: string) => {
emit('search', value)
}, 300)
</script>
3. 全局樣式覆蓋
// styles/element-overrides.scss
// 全局修改el-button樣式
.el-button {
border-radius: 4px;
font-weight: 500;
&--primary {
background-color: #1890ff;
border-color: #1890ff;
&:hover {
background-color: #40a9ff;
border-color: #40a9ff;
}
}
}
// 修改el-dialog樣式
.el-dialog {
border-radius: 8px;
overflow: hidden;
.el-dialog__header {
padding: 20px 20px 10px;
border-bottom: 1px solid #f0f0f0;
}
.el-dialog__body {
padding: 20px;
}
}
總結(jié)
Element Plus是一個(gè)功能強(qiáng)大、設(shè)計(jì)優(yōu)秀的UI組件庫,掌握以下要點(diǎn)可以更好地使用它:
- 按需引入 - 減小包體積
- 主題定制 - 符合項(xiàng)目風(fēng)格
- 性能優(yōu)化 - 圖標(biāo)懶加載、虛擬滾動(dòng)
- 踩坑經(jīng)驗(yàn) - 了解常見問題和解決方案
- 最佳實(shí)踐 - 封裝常用組件、統(tǒng)一配置
希望這些經(jīng)驗(yàn)?zāi)軒椭阍赩ue 3項(xiàng)目中更好地使用Element Plus!
到此這篇關(guān)于Vue 3項(xiàng)目中Element Plus組件庫實(shí)戰(zhàn)技巧與踩坑記錄的文章就介紹到這了,更多相關(guān)Element Plus組件庫技巧與踩坑內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue 屬性攔截實(shí)現(xiàn)雙向綁定的實(shí)例代碼
這篇文章主要介紹了vue 屬性攔截實(shí)現(xiàn)雙向綁定的實(shí)例代碼,代碼簡答易懂,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-10-10
vant使用datetime-picker組件設(shè)置maxDate和minDate的坑及解決
這篇文章主要介紹了vant使用datetime-picker組件設(shè)置maxDate和minDate的坑及解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12
Vue3中關(guān)于路由規(guī)則的props配置方式
這篇文章主要介紹了Vue3中關(guān)于路由規(guī)則的props配置方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03
vue 項(xiàng)目中實(shí)現(xiàn)按鈕防抖方法
這篇文章主要介紹了vue 項(xiàng)目中實(shí)現(xiàn)按鈕防抖方法,首先需要新建 .js文件存放防抖方法,引入防抖文件,methods中添加方法,本文結(jié)合示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-12-12
Vue Element前端應(yīng)用開發(fā)之組織機(jī)構(gòu)和角色管理
本篇文章繼續(xù)深化Vue Element權(quán)限管理模塊管理的內(nèi)容,介紹組織機(jī)構(gòu)和角色管理模塊的處理,使得我們了解界面組件化模塊的開發(fā)思路和做法,提高我們界面設(shè)計(jì)的技巧,并減少代碼的復(fù)雜性,提高界面代碼的可讀性,同時(shí)也是利用組件的復(fù)用管理。2021-05-05
keep-alive緩存某一指定路由實(shí)現(xiàn)方式
使用路由元信息配合keep-alive實(shí)現(xiàn)條件緩存,通過兩個(gè)router-view和meta變量判斷是否緩存特定路由,注意keep-alive優(yōu)先使用組件的name屬性而非路由name,避免常見錯(cuò)誤2025-07-07
解決在vue項(xiàng)目中,發(fā)版之后,背景圖片報(bào)錯(cuò),路徑不對的問題
下面小編就為大家分享一篇解決在vue項(xiàng)目中,發(fā)版之后,背景圖片報(bào)錯(cuò),路徑不對的問題,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-03-03

