Laravel 10/Vue3/Vite4/Vuetify3项目冗余组件加载优化咨询
问题:Laravel+Vue+Vite+Inertia项目中Vuetify组件按需加载配置
我正在用Laravel、Vue、Vite和Inertia开发项目,UI框架用的是Vuetify。页面加载时,网络面板显示有500+请求,大部分都是加载我完全不需要的Vuetify组件。请问怎么配置才能只加载项目里实际用到的组件?
提供的文件内容
main.js
/* eslint-disable import/order */ import layoutsPlugin from '@/plugins/layouts' import vuetify from '@/plugins/vuetify' import VueToast from 'vue-toast-notification' import 'vue-toast-notification/dist/theme-default.css' import { createInertiaApp, Link } from '@inertiajs/vue3' import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers' import { loadFonts } from '@/plugins/webfontloader' import './../styles/@core/template/index.scss' import '@styles/styles.scss' import { createPinia } from 'pinia' import { createApp } from 'vue' import { ZiggyVue } from '../../vendor/tightenco/ziggy/dist/vue.m' import 'sweetalert2/dist/sweetalert2.min.css' loadFonts() createInertiaApp({ title: title => `${title}`, resolve: name => resolvePageComponent(`./pages/${name}.vue`, import.meta.glob('./pages/**/*.vue')), setup({ el, App, props, plugin }) { // eslint-disable-next-line vue/component-api-style const vueApp = createApp({ render: () => h(App, props) }) vueApp.use(plugin) .use(ZiggyVue, Ziggy) .use(VueToast, { position: 'top', pauseOnHover: true, }).provide('toast', vueApp.config.globalProperties.$toast) .use(vuetify) .use(createPinia()) .use(layoutsPlugin) .use(Link) .mount(el) return vueApp }, progress: { color: '#4B5563', }, })
vite.config.js
import { fileURLToPath } from 'url' import vue from '@vitejs/plugin-vue' import vueJsx from '@vitejs/plugin-vue-jsx' import AutoImport from 'unplugin-auto-import/vite' import Components from 'unplugin-vue-components/vite' import DefineOptions from 'unplugin-vue-define-options/vite' import { defineConfig } from 'vite' import Pages from 'vite-plugin-pages' import Layouts from 'vite-plugin-vue-layouts' import vuetify from 'vite-plugin-vuetify' import laravel from 'laravel-vite-plugin' // https://vitejs.dev/config/ export default defineConfig({ plugins: [ laravel({ input: ['resources/js/main.js'], refresh: true, }), vue({ template: { transformAssetUrls: { base: null, includeAbsolute: false, }, }, }), vueJsx(), // https://github.com/vuetifyjs/vuetify-loader/tree/next/packages/vite-plugin vuetify({ styles: { configFile: 'resources/styles/variables/_vuetify.scss', }, }), Pages({ dirs: ['./resources/js/pages'], }), Layouts({ layoutsDirs: './resources/js/layouts/', }), Components({ dirs: ['resources/js/@core/components', 'resources/js/views/demos'], dts: true, }), AutoImport({ eslintrc: { enabled: true, filepath: './.eslintrc-auto-import.json', }, imports: ['vue', 'vue-router', '@vueuse/core', '@vueuse/math', 'pinia'], vueTemplate: true, }), DefineOptions(), ], define: { 'process.env': {} }, resolve: { alias: { '@core-scss': fileURLToPath(new URL('./resources/styles/@core', import.meta.url)), '@': fileURLToPath(new URL('./resources/js', import.meta.url)), '@themeConfig': fileURLToPath(new URL('./themeConfig.js', import.meta.url)), '@core': fileURLToPath(new URL('./resources/js/@core', import.meta.url)), '@layouts': fileURLToPath(new URL('./resources/js/layouts', import.meta.url)), '@images': fileURLToPath(new URL('./resources/images/', import.meta.url)), '@styles': fileURLToPath(new URL('./resources/styles/', import.meta.url)), '@configured-variables': fileURLToPath(new URL('./resources/styles/variables/_template.scss', import.meta.url)), '@axios': fileURLToPath(new URL('./resources/js/plugins/axios', import.meta.url)), '@validators': fileURLToPath(new URL('./resources/js/@core/utils/validators', import.meta.url)), 'apexcharts': fileURLToPath(new URL('node_modules/apexcharts-clevision', import.meta.url)), }, }, build: { chunkSizeWarningLimit: 5000, }, optimizeDeps: { exclude: ['vuetify'], entries: [ './resources/js/**/*.vue', ], }, })
网络面板截图


resources/js/plugins/vuetify/index.js
import { createVuetify } from 'vuetify' import { VBtn } from 'vuetify/components' import defaults from './defaults' import { icons } from './icons' import theme from './theme' // Styles // eslint-disable-next-line import/no-unresolved import '@core-scss/template/libs/vuetify/index.scss' import 'vuetify/styles' export default createVuetify({ aliases: { IconBtn: VBtn, }, defaults, icons, theme, })
defaults.js
export default { IconBtn: { icon: true, size: 'small', color: 'default', variant: 'text', VIcon: { size: 24, }, }, VAlert: { density: 'comfortable', VBtn: { color: undefined, }, }, VAvatar: { // ℹ️ Remove after next release variant: 'flat', }, VBadge: { // set v-badge default color to primary color: 'primary', }, VBtn: { // set v-btn default color to primary color: 'primary', }, VChip: { elevation: 0, }, VPagination: { activeColor: 'primary', density: 'comfortable', size: '32', }, VTabs: { // set v-tabs default color to primary color: 'primary', VSlideGroup: { showArrows: true, }, }, VTooltip: { // set v-tooltip default location to top location: 'top', }, VList: { VListItem: { activeColor: 'primary', }, }, VCheckbox: { // set v-checkbox default color to primary color: 'primary', density: 'comfortable', hideDetails: 'auto', }, VRadioGroup: { color: 'primary', hideDetails: 'auto', }, VRadio: { hideDetails: 'auto', }, VSelect: { variant: 'outlined', color: 'primary', hideDetails: 'auto', }, VRangeSlider: { // set v-range-slider default color to primary color: 'primary', density: 'comfortable', thumbLabel: true, hideDetails: 'auto', }, VRating: { // set v-rating default color to primary density: 'compact', activeColor: 'warning', }, VProgressCircular: { // set v-progress-circular default color to primary color: 'primary', }, VSlider: { // set v-slider default color to primary color: 'primary', hideDetails: 'auto', }, VTextField: { variant: 'outlined', color: 'primary', hideDetails: 'auto', }, VAutocomplete: { variant: 'outlined', density: 'comfortable', color: 'primary', hideDetails: 'auto', }, VCombobox: { variant: 'outlined', color: 'primary', hideDetails: 'auto', }, VFileInput: { variant: 'outlined', color: 'primary', hideDetails: 'auto', }, VTextarea: { variant: 'outlined', density: 'comfortable', color: 'primary', hideDetails: 'auto', }, VSwitch: { // set v-switch default color to primary color: 'primary', hideDetails: 'auto', }, }
解决方案
问题根源是当前配置未启用Vuetify自动按需加载,且defaults.js中声明了大量未使用组件的默认配置,导致这些组件被强制引入。按以下步骤修改:
1. 开启Vuetify Vite插件的自动按需导入
修改vite.config.js中的vuetify插件配置,添加autoImport: true:
vuetify({ styles: { configFile: 'resources/styles/variables/_vuetify.scss', }, autoImport: true, // 开启自动按需导入组件 }),
2. 清理defaults.js中未使用组件的配置
defaults.js里对很多未用到的组件设置了默认值,这会触发组件的强制打包。只保留你实际使用的组件配置,比如只用到VBtn和VTextField的话,修改后的defaults.js如下:
export default { IconBtn: { icon: true, size: 'small', color: 'default', variant: 'text', VIcon: { size: 24, }, }, VBtn: { color: 'primary', }, VTextField: { variant: 'outlined', color: 'primary', hideDetails: 'auto', }, }
3. 手动导入基础组件(按需)
部分Vuetify基础组件(如VIcon)可能无法被自动检测,若项目中用到,需在resources/js/plugins/vuetify/index.js中手动导入并注册:
import { createVuetify } from 'vuetify' import { VBtn, VIcon } from 'vuetify/components' // 添加用到的组件 import defaults from './defaults' import { icons } from './icons' import theme from './theme' // Styles import '@core-scss/template/libs/vuetify/index.scss' import 'vuetify/styles' export default createVuetify({ aliases: { IconBtn: VBtn, }, defaults, icons, theme, components: { VBtn, VIcon // 注册手动导入的组件 } })
4. 重启Vite服务并清理缓存
执行命令重启服务,确保配置生效:
npm run dev -- --force
完成以上配置后,Vuetify只会打包项目中实际用到的组件,网络请求数量会大幅减少。
内容的提问来源于stack exchange,提问作者Aleksandar Đokić
相关产品推荐
相关产品推荐

