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


更新于:2021.11.16


关于 element-plus 自定义主题部分, 最新版的 element-plus 1.1.0-beta.1x 官网文档 ➡️ https://element-plus.gitee.io/zh-CN/guide/theming.html 又又又修改了!


不过这回貌似更简单了,按照以下步骤操作即可。

  1. 创建一个新的样式文件,例如 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
  )
);

如果是完整导入 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. 但我们需要在按需导入时自定义主题,并且使用 vite。就可以安装用于按需导入 element-plus 样式的 unplugin-element-plus 插件并进行配置。

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'
// 自动按需导入 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,默认是 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. 接下来也不需要在使用时手动导入组件,而是配置一个自动导入插件 unplugin-vue-components :

更新于:2021.09.23


element-plus 的官方文档更新了,组件的按需引入可以用 unplugin-vue-components 十分便捷地实现,步骤如下:

  1. 安装
npm install unplugin-vue-components -D
  1. 配置文件 vite.config.js
// vite.config.ts/vite.config.js
import { defineConfig } from 'vite'
// vue 按需自动导入组件插件,直接使用即可,公共组件无需再手动 import
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' //  ElementPlus 组件专用的内置解析器
import ElementPlus from 'unplugin-element-plus/vite' //  按需导入 element-plus 样式插件

export default defineConfig({
  plugins: [
    // ...
    Components({
      resolvers: [ElementPlusResolver()], // 启用 ElementPlus 专用的 UI 组件解析器
    }),
    ElementPlus({ useSource: true }), // import 组件后会自动引入组件对应的样式
  ],
  css: {
    preprocessorOptions: {
      scss: {
        // 自定义 element 主题样式
        additionalData: `@use "@styles/element/index.scss" as *;`,
      },
    },
  },
})

这样不仅 element-plus 组件,所有src/components下的公用自定义组件都不需要再做诸如以下这类导入和配置,而是直接在template中使用即可。

import DesignForm from '@/components/DesignForm.vue'

export default defineComponent({
  name: 'DesignIndex',
  components: { DesignForm },
  ...
}

以下是这个插件的默认配置,你可以在vite.config的插件配置中根据需要自定义

Components({
  // 用于查找自动处理的组件目录的相对路径
  dirs: ['src/components'],

  // 组件的有效文件扩展名
  extensions: ['vue'],
  // 是否查找子目录
  deep: true,
  // resolvers for custom components
  resolvers: [],
  // example of importing Vant,不是默认值
  /* 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,
  // 忽略的命名空间前缀的子目录路径,当 `directoryAsNamespace: true` 时才起作用
  globalNamespaces: [],

  // 是否自动导入指令
  // 默认: `true` for Vue 3, `false` for Vue 2
  // Vue2 需要 Babel 来进行转换,出于性能考虑,默认禁用。
  // To install Babel, run: `npm install -D @babel/parser @babel/traverse`
  directives: true,

  // 转换目标的过滤器
  include: [/\.vue$/, /\.vue\?vue/],
  exclude: [/[\\/]node_modules[\\/]/, /[\\/]\.git[\\/]/, /[\\/]\.nuxt[\\/]/],
})

有没有很省事!但是!当你需要用到自定义主题的时候,这个路子就不好使了。我们还是要像之前那样手动导入(然后全局注册)。 可以放心大胆滴使用~
跟着往下看:


以下(有部分)更新于2021.08.27


今天把 ElementPlus 更新到了最新,然后一跑项目马上就报错了。特记录下1.1.0版后使用的区别:
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 插件来实现按需加载样式,别忘了先安装npm i unplugin-element-plus -D
但是官方是强烈建议全局引入样式,没必要为此特地用插件增加负担。此外像这样配置按需引入样式也无法使自定义主题生效。so 只是贴在这里记录方法。

  1. 按需引入 element-plus 后使用消息组件MessageMessageBox,就需要这样调用:
    ElMessage.error('xxx')
    ElMessageBox.confirm()

  2. 至于 v-loading 指令按需后也不好使了,我封装了个组合函数useLoading,参考下:

// src/composables/useLoading.js
import { ref, watch, nextTick } from 'vue'
import { ElLoading } from 'element-plus'

/**
@target:Loading 动画需要覆盖的 DOM 对象(ref 对象)
@isLoading:表示数据是否正在加载的 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 节点
            fullscreen: false,
            text: ' 数据加载中',
          })
        })
      } else {
        nextTick(() => {
          if (loadingInstance.value) loadingInstance.value.close()
        })
      }
    },
    { immediate: true }
  )
}

使用简单示例:



下面的部分都不用看了!!!

正式内容开始:

  1. src/main.js
// import 'element-plus/dist/index.css' // 官方建议全局引入样式
import { createApp } from 'vue'
import App from './App.vue'
// 把按需引入 ElementPlus 组件 的代码单独拎到一个 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. 创建一个用于覆盖 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,
  ),
);

/* 必须指明字体图标路径,不然会报错 */ 
$--font-path: 'element-plus/theme-chalk/fonts';
/* 在主题变量后再导入 element-plus的 scss,避免 sass 混合变量的问题 */ 
@import 'element-plus/packages/theme-chalk/src/index';

注意 ⚠️:ElementPlus 1.1.0 版之后有一些破坏性改动:
按需引入的情况下,那些必须嵌套使用的子组件,比如el-select组件内部的el-option组件、菜单组件的el-menu-item,都不用再导入了,因为内容已经被集成到父组件内了;

submenu不仅目录名改了,只剩下一个style子文件夹里面的内容也都空了(index.js等 4 个文件都没有内容)。 要记得把模版中原本的替换成

大致列一下避雷:
ElBreadcrumbItemElDropdownItemElDropdownItemElDropdownMenuElFormItemElMenuItemElOptionElRadioGroupElTableColumnElTabPane...

  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 => {
  // 按需引入时做 element-plus 的全局配置
  app.config.globalProperties.$ELEMENT = option 
  // 注册需要的 element-plus 组件
  components.forEach(component => {
    app.use(component)
  })
}

新版的 ElementPlus 组件全都有install方法,而1.0.2的时候只有几个组件有install,其余要用app.component()注册全局组件。现在方便了,全部遍历然后直接app.use()即可。

而且用use()等于安装插件,那些消息组件就会自动像这样注册全局方法:app.config.globalProperties.$message = _Message
不用担心按需导入就无this.$message

  1. 以及国际化(设置默认语言为中文)的实现:
    src/App.vue



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




效果

比自己覆盖 UI 颜色样式轻松多了有木有~

你可能感兴趣的:(Vue 3 + Vite 2 + ElementPlus 1.1.0-beta(按需引入及自定义主题))