Vue 3 + Vite 2 + ElementPlus 1.1.0-beta(按需引入及自定義主題)


更新于: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 又又又修改了笙僚!


不過這回貌似更簡單了,按照以下步驟操作即可困食。

  1. 創(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)
  1. 但我們需要在按需導(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'垛贤。
  1. 接下來也不需要在使用時(shí)手動導(dǎo)入組件焰坪,而是配置一個(gè)自動導(dǎo)入插件 unplugin-vue-components

更新于:2021.09.23


element-plus 的官方文檔更新了,組件的按需引入可以用 unplugin-vue-components 十分便捷地實(shí)現(xiàn)南吮,步驟如下:

  1. 安裝
npm install unplugin-vue-components -D
  1. 配置文件?? 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 只是貼在這里記錄方法??。

  1. 按需引入 element-plus 后使用消息組件MessageMessageBox渡紫,就需要這樣調(diào)用:
    ElMessage.error('xxx')
    ElMessageBox.confirm()

  2. 至于 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)容開始:

  1. ??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')
  1. 創(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底瓣、ElDropdownItemElDropdownMenu扭弧、ElFormItem阎姥、ElMenuItemElOption鸽捻、ElRadioGroup呼巴、ElTableColumnElTabPane...

  1. 引入自定義主題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痰滋。

  1. 以及國際化(設(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 顏色樣式輕松多了有木有~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市多艇,隨后出現(xiàn)的幾起案子逻恐,更是在濱河造成了極大的恐慌,老刑警劉巖峻黍,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件复隆,死亡現(xiàn)場離奇詭異,居然都是意外死亡姆涩,警方通過查閱死者的電腦和手機(jī)挽拂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來骨饿,“玉大人亏栈,你說我怎么就攤上這事台腥。” “怎么了绒北?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵黎侈,是天一觀的道長。 經(jīng)常有香客問我闷游,道長峻汉,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任脐往,我火速辦了婚禮休吠,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘钙勃。我一直安慰自己蛛碌,他們只是感情好聂喇,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布辖源。 她就那樣靜靜地躺著,像睡著了一般希太。 火紅的嫁衣襯著肌膚如雪克饶。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天誊辉,我揣著相機(jī)與錄音矾湃,去河邊找鬼。 笑死堕澄,一個(gè)胖子當(dāng)著我的面吹牛邀跃,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蛙紫,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼拍屑,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了坑傅?” 一聲冷哼從身側(cè)響起僵驰,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎唁毒,沒想到半個(gè)月后蒜茴,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡浆西,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年粉私,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片近零。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡毡鉴,死狀恐怖崔泵,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情猪瞬,我是刑警寧澤憎瘸,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站陈瘦,受9級特大地震影響幌甘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜痊项,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一锅风、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧鞍泉,春花似錦皱埠、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至托修,卻和暖如春忘巧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背睦刃。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工砚嘴, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人涩拙。 一個(gè)月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓际长,卻偏偏與公主長得像,于是被迫代替她去往敵國和親兴泥。 傳聞我的和親對象是個(gè)殘疾皇子工育,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容