更新于:2021.11.16
關(guān)于 element-plus 自定義主題部分, 最新版的 element-plus 1.1.0-beta.1x 官網(wǎng)文檔 ?? https://element-plus.gitee.io/zh-CN/guide/theming.html 又又又修改了笙僚!
不過這回貌似更簡單了,按照以下步驟操作即可困食。
- 創(chuàng)建一個(gè)新的樣式文件,例如 ??
styles/element/index.scss
翎承,直接覆蓋 Element Plus 樣式變量:
// styles/element/index.scss
@forward "element-plus/theme-chalk/src/common/var.scss" with (
$colors: (
'primary': (
'base': #4FC08D,
),
'success': (
'base': #8BC34A,
),
'warning': (
'base': #FFE787,
),
'danger': (
'base': #7C77B9,
),
'error': (
'base': #E65D6E,
),
'info': (
'base': #606266,
),
),
$text-color: (
'primary': #3FB984,
'regular': #606266,
'secondary': #909399
)
);
如果是完整導(dǎo)入 element-plus硕盹,則在入口?? main.js/main.ts
:
import Vue from 'vue'
import './styles/element/index.scss'
import ElementPlus from 'element-plus'
import App from './App.vue'
const app = createApp(App)
app.use(ElementPlus)
- 但我們需要在按需導(dǎo)入時(shí)自定義主題,并且使用 vite叨咖。就可以安裝用于按需導(dǎo)入 element-plus 樣式的 unplugin-element-plus 插件并進(jìn)行配置瘩例。
??vite.config.js
:
// vite.config.ts
import path from 'path'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
// 自動按需導(dǎo)入 element-plus 樣式
import ElementPlus from 'unplugin-element-plus/vite'
export default defineConfig({
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
},
},
css: {
preprocessorOptions: {
scss: {
additionalData: `@use "@/styles/element/index.scss" as *;`,
},
},
},
plugins: [
vue(),
ElementPlus({
useSource: true,
}),
],
})
可選配置 useSource: boolean
啊胶,默認(rèn)是 false。
// useSource: false
import { ElButton } from 'element-plus'
↓ ↓ ↓ ↓ ↓ ↓
import { ElButton } from 'element-plus'
import 'element-plus/es/components/button/style/css'
// useSource: true
import { ElButton } from 'element-plus'
↓ ↓ ↓ ↓ ↓ ↓
import { ElButton } from 'element-plus'
import 'element-plus/es/components/button/style/index'垛贤。
- 接下來也不需要在使用時(shí)手動導(dǎo)入組件焰坪,而是配置一個(gè)自動導(dǎo)入插件 unplugin-vue-components :
更新于:2021.09.23
element-plus 的官方文檔更新了,組件的按需引入可以用 unplugin-vue-components 十分便捷地實(shí)現(xiàn)南吮,步驟如下:
- 安裝
npm install unplugin-vue-components -D
- 配置文件??
vite.config.js
// vite.config.ts/vite.config.js
import { defineConfig } from 'vite'
// vue 按需自動導(dǎo)入組件插件,直接使用即可誊酌,公共組件無需再手動 import
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' // ElementPlus 組件專用的內(nèi)置解析器
import ElementPlus from 'unplugin-element-plus/vite' // 按需導(dǎo)入 element-plus 樣式插件
export default defineConfig({
plugins: [
// ...
Components({
resolvers: [ElementPlusResolver()], // 啟用 ElementPlus 專用的 UI 組件解析器
}),
ElementPlus({ useSource: true }), // import 組件后會自動引入組件對應(yīng)的樣式
],
css: {
preprocessorOptions: {
scss: {
// 自定義 element 主題樣式
additionalData: `@use "@styles/element/index.scss" as *;`,
},
},
},
})
這樣不僅 element-plus 組件部凑,所有src/components
下的公用自定義組件都不需要再做諸如以下這類導(dǎo)入和配置,而是直接在template
中使用即可碧浊。
import DesignForm from '@/components/DesignForm.vue'
export default defineComponent({
name: 'DesignIndex',
components: { DesignForm },
...
}
以下是這個(gè)插件的默認(rèn)配置涂邀,你可以在vite.config
的插件配置中根據(jù)需要自定義
Components({
// 用于查找自動處理的組件目錄的相對路徑
dirs: ['src/components'],
// 組件的有效文件擴(kuò)展名
extensions: ['vue'],
// 是否查找子目錄
deep: true,
// resolvers for custom components
resolvers: [],
// example of importing Vant,不是默認(rèn)值
/* resolvers: [
(name) => {
// where `name` is always CapitalCase
if (name.startsWith('Van'))
return { importName: name.slice(3), path: 'vant' }
},
],*/
// 是否生成 `components.d.ts` 全局聲明箱锐,
// also accepts a path for custom filename
dts: false,
// 不允許子目錄作為組件的命名空間前綴
directoryAsNamespace: false,
// 忽略的命名空間前綴的子目錄路徑比勉,當(dāng) `directoryAsNamespace: true` 時(shí)才起作用
globalNamespaces: [],
// 是否自動導(dǎo)入指令
// 默認(rèn): `true` for Vue 3, `false` for Vue 2
// Vue2 需要 Babel 來進(jìn)行轉(zhuǎn)換,出于性能考慮驹止,默認(rèn)禁用浩聋。
// To install Babel, run: `npm install -D @babel/parser @babel/traverse`
directives: true,
// 轉(zhuǎn)換目標(biāo)的過濾器
include: [/\.vue$/, /\.vue\?vue/],
exclude: [/[\\/]node_modules[\\/]/, /[\\/]\.git[\\/]/, /[\\/]\.nuxt[\\/]/],
})
有沒有很省事!但是臊恋!當(dāng)你需要用到自定義主題的時(shí)候衣洁,這個(gè)路子就不好使了。我們還是要像之前那樣手動導(dǎo)入(然后全局注冊)抖仅。 可以放心大膽滴使用~
跟著往下看:
以下(有部分)更新于2021.08.27
今天把 ElementPlus 更新到了最新坊夫,然后一跑項(xiàng)目馬上就報(bào)錯了。特記錄下1.1.0
版后使用的區(qū)別:
??package.json
:
{
"dependencies": {
"element-plus": "^1.1.0-beta.9",
"vue": "^3.2.6",
"vue-router": "^4.0.11",
"vuex": "^4.0.2"
},
"devDependencies": {
"@vitejs/plugin-vue": "^1.6.0",
"@vitejs/plugin-vue-jsx": "^1.1.7",
"@vue/compiler-sfc": "^3.0.5",
"sass": "^1.38.1",
"vite": "^2.5.1",
"unplugin-element-plus": "0.1.0",
}
}
用 unplugin-element-plus 插件來實(shí)現(xiàn)按需加載樣式撤卢,別忘了先安裝npm i unplugin-element-plus -D
环凿。
但是官方是強(qiáng)烈建議全局引入樣式,沒必要為此特地用插件增加負(fù)擔(dān)放吩。此外像這樣配置按需引入樣式也無法使自定義主題生效智听。so 只是貼在這里記錄方法??。
按需引入 element-plus 后使用消息組件
Message
和MessageBox
渡紫,就需要這樣調(diào)用:
ElMessage.error('xxx')
ElMessageBox.confirm()
至于
v-loading
指令按需后也不好使了瞭稼,我封裝了個(gè)組合函數(shù)useLoading
,參考下:
// src/composables/useLoading.js
import { ref, watch, nextTick } from 'vue'
import { ElLoading } from 'element-plus'
/**
@target:Loading 動畫需要覆蓋的 DOM 對象(ref 對象)
@isLoading:表示數(shù)據(jù)是否正在加載的 ref 對象
**/
export default function useLoading(target, isLoading) {
const loadingInstance = ref(null)
watch(
[isLoading, target],
vals => {
if (vals[0] && vals[1]) {
nextTick(() => {
loadingInstance.value = ElLoading.service({
target: vals[1].$el, // 需要獲取DOM 節(jié)點(diǎn)
fullscreen: false,
text: ' 數(shù)據(jù)加載中',
})
})
} else {
nextTick(() => {
if (loadingInstance.value) loadingInstance.value.close()
})
}
},
{ immediate: true }
)
}
使用簡單示例:
<template>
<el-table
ref="loadingRef"
:data="list">
</el-table>
</template>
<script>
import { computed, defineComponent, watch, toRefs, reactive, ref } from 'vue'
import useList from '@composables/useList' // 封裝好的分頁列表查詢函數(shù)
import useLoading from '@composables/useLoading'
import { getDesignList } from '@api/design' // 請求 api
export default defineComponent({
name: 'Banner',
components: {},
setup() {
const loadingRef = ref(null)
const listQuery = reactive({
query: '',
page: 1,
pageSize: 10,
})
const { list, total, listLoading, refreshList } = useList(
listQuery, // 列表查詢參數(shù)
getDesignList // 列表請求 Api
)
useLoading(loadingRef, listLoading)
return {
isLoading
loadingRef,
list,
total
}
}
})
</script>
??:下面的部分都不用看了D寤荨;分狻!
正式內(nèi)容開始:
- ??
src/main.js
:
// import 'element-plus/dist/index.css' // 官方建議全局引入樣式
import { createApp } from 'vue'
import App from './App.vue'
// 把按需引入 ElementPlus 組件 的代碼單獨(dú)拎到一個(gè) js
import installElementPlus from './plugins/element'
import router from './router'
import store from './store'
import './styles/index.scss' // 我們自己的樣式
const app = createApp(App).use(store).use(router)
app.provide('apiUrl', import.meta.env.VITE_APP_BASE_API)
installElementPlus(app)
app.mount('#app')
- 創(chuàng)建一個(gè)用于覆蓋 Element Plus 樣式變量的文件
??src/styles/element-variables.scss
:
/* 改變主題色變量 */
$--colors: (
'primary': (
'base': #388E3C,
),
'success': (
'base': #67c23a,
),
'warning': (
'base': #C7D66D,
),
'danger': (
'base': #7C77B9,
),
'error': (
'base': #F6828C,
),
'info': (
'base': #909399,
),
);
/* 必須指明字體圖標(biāo)路徑集灌,不然會報(bào)錯 */
$--font-path: 'element-plus/theme-chalk/fonts';
/* 在主題變量后再導(dǎo)入 element-plus的 scss悔雹,避免 sass 混合變量的問題 */
@import 'element-plus/packages/theme-chalk/src/index';
注意 ??:ElementPlus 1.1.0 版之后有一些破壞性改動:
按需引入的情況下复哆,那些必須嵌套使用的子組件,比如el-select
組件內(nèi)部的el-option
組件腌零、菜單組件的el-menu-item
隙赁,都不用再導(dǎo)入了好渠,因?yàn)閮?nèi)容已經(jīng)被集成到父組件內(nèi)了;
像submenu
不僅目錄名改了,只剩下一個(gè)style
子文件夾里面的內(nèi)容也都空了(index.js
等 4 個(gè)文件都沒有內(nèi)容)田绑。 要記得把模版中原本的<el-submenu>
替換成<el-sub-menu>
。
大致列一下避雷:
ElBreadcrumbItem
竭望、ElDropdownItem
底瓣、ElDropdownItem
、ElDropdownMenu
扭弧、ElFormItem
阎姥、ElMenuItem
、ElOption
鸽捻、ElRadioGroup
呼巴、ElTableColumn
、ElTabPane
...
- 引入自定義主題
scss
并全局注冊需要的組件
??src/plugins/element.js
:
import {
ElBreadcrumb,
ElButton,
...
ElMessage,
ElMessageBox,
}
const components = [
ElBreadcrumb,
ElButton,
...
ElMessage,
ElMessageBox,
]
const option = { size: 'small', zIndex: 3000 }
import '../styles/element-variables.scss'
export default app => {
// 按需引入時(shí)做 element-plus 的全局配置
app.config.globalProperties.$ELEMENT = option
// 注冊需要的 element-plus 組件
components.forEach(component => {
app.use(component)
})
}
新版的 ElementPlus 組件全都有install
方法御蒲,而1.0.2
的時(shí)候只有幾個(gè)組件有install
衣赶,其余要用app.component()
注冊全局組件。現(xiàn)在方便了厚满,全部遍歷然后直接app.use()
即可屑埋。
而且用use()
等于安裝插件,那些消息組件就會自動像這樣注冊全局方法:app.config.globalProperties.$message = _Message
不用擔(dān)心按需導(dǎo)入就無this.$message
痰滋。
- 以及國際化(設(shè)置默認(rèn)語言為中文)的實(shí)現(xiàn):
??src/App.vue
:
<template>
<el-config-provider :locale="locale">
<router-link to="/">Home</router-link> |
<router-link to="/user">User</router-link> |
<router-link to="/about">About</router-link>
<router-view> </router-view>
</el-config-provider>
</template>
<script>
import { defineComponent } from 'vue'
import { ElConfigProvider } from 'element-plus'
import zhCn from 'element-plus/lib/locale/lang/zh-cn'
export default defineComponent({
components: {
ElConfigProvider,
},
setup() {
return {
locale: zhCn, // 國際化摘能,將組件設(shè)置為中文
}
},
})
</script>
??src/router/index.js
:
import { createRouter, createWebHashHistory } from 'vue-router'
const routes = [
{
path: '/',
name: 'Home',
component: () => import('@views/Home.vue'),
},
{
path: '/user',
name: 'User',
component: () =>
import('@views/User.vue'),
},
{
path: '/404',
component: () => import("page-404" */ '@views/404'),
hidden: true,
},
{
path: '/:pathMatch(.*)*',
name: 'NotFound',
redirect: '/404',
hidden: true,
},
]
const router = createRouter({
scrollBehavior: () => ({
top: 0,
}),
history: createWebHashHistory(),
routes,
})
export default router
??src/views/Home.vue
:
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png" />
<el-button type="primary" @click="this.$message('哈哈')"
>調(diào)用全局 $message 方式一</el-button
>
<el-button type="success" @click="showMsg">調(diào)用全局 $message 方式二、三</el-button>
<el-date-picker v-model="value1" type="date" placeholder="選擇日期">
</el-date-picker>
</div>
</template>
<script>
import { ref, getCurrentInstance } from 'vue'
import { ElMessage } from 'element-plus'; // 方式三
export default {
name: 'Home',
inject: ['apiUrl'], // 接收全局 provide 的變量
setup() {
const value1 = ref('')
const internalInstance = getCurrentInstance() // 方式二敲街,不推薦
const showMsg = () => {
internalInstance.appContext.config.globalProperties.$message('啊啊') // 方式二团搞,不推薦
ElMessage.success('好吧') // 方式三
}
return {
value1,
showMsg,
}
},
}
</script>
比自己覆蓋 UI 顏色樣式輕松多了有木有~