Laravel 10 Vite执行npm run build后部分文件内容丢失
Laravel 10 + Vite构建时global.js函数被移除的问题原因分析
问题描述
在Laravel 10项目中使用Vite构建工具时,执行npm run build命令后,部分脚本编译正常,但global.js文件仅保留Swal.mixin代码块,其余所有函数全部丢失。即使将该文件单独添加到vite.config.js的input列表中,问题依然存在。
vite.config.js 配置
import laravel from 'laravel-vite-plugin'; import vue from '@vitejs/plugin-vue'; export default defineConfig({ plugins: [ laravel({ input: [ 'resources/sass/app.scss', 'resources/css/app.css', 'resources/js/app.js', 'resources/js/global.js', 'resources/js/addresses.js', 'resources/js/global.js', 'resources/js/afip/padron.js', 'resources/js/categories/index.js', 'resources/js/categories/selectize_input.js', 'resources/js/categories/show.js', 'resources/js/clients/create.js', 'resources/js/clients/index.js', 'resources/js/login/login.js', 'resources/js/notifications/index.js', 'resources/js/providers/create.js', 'resources/js/providers/index.js', 'resources/js/users/index.js', ], refresh: true, }), vue({ template: { transformAssetUrls: { base: null, includeAbsolute: false, }, }, }), ], resolve: { alias: { vue: 'vue/dist/vue.esm-bundler.js', }, }, build: { minify: false, } });
编译前的global.js
/** * SweetAlert2 boxes */ const defaultBox = Swal.mixin({ allowOutsideClick: false, allowEscapeKey: true, allowEnterKey: true, customClass: { confirmButton: "btn-primary", cancelButton: "btn-secondary" }, showCancelButton: false, showConfirmButton: false }); function Box(message = '', title = 'aviso', type = 'success') { return defaultBox.mixin({ title: title, html: message, icon: type }) } function BoxInfo({message, action = function () {}, title = "Aviso", type = "success"}) { Box(message, title, type).fire({ showConfirmButton: true, confirmButtonText: "Aceptar" }).then(function () { action(); }); } function BoxConfirm({ message, preConfirm = function () {}, type = "info", title = "Aviso", confirmText = "Aceptar", cancelText = "Cancelar"}) { Box(message, title, type).fire({ showConfirmButton: true, showCancelButton: true, confirmButtonText: confirmText, cancelButtonText: cancelText, allowOutsideClick: () => !Swal.isLoading(), showLoaderOnConfirm: true, preConfirm() { return preConfirm(); } }); } /** * Bootstrap 5 Toast creator * * @param message * @param background */ function CreateToast({ message, background = 'text-bg-primary'}) { let parent = document.createElement('div'); parent.setAttribute('role', 'alert'); parent.setAttribute('aria-live', 'assertive'); parent.setAttribute('aria-atomic', 'true'); parent.classList.add('toast', background); let child = document.createElement('div'); child.classList.add('toast-body'); let body = document.createElement('div'); body.classList.add('d-flex', 'justify-content-between'); let content = document.createElement('div'); content.classList.add('toast-content'); content.innerHTML = message; let button = document.createElement('button'); button.setAttribute('type', 'button'); button.classList.add('btn-close'); button.setAttribute('data-bs-dismiss', 'toast'); button.setAttribute('aria-label', 'Close'); body.appendChild(content); body.appendChild(button); child.appendChild(body); parent.appendChild(child); parent.addEventListener('hidden.bs.toast', (event) => { event.currentTarget.remove(); }); document.getElementById('toast-container').appendChild(parent); let toast = new bootstrap.Toast(parent); toast.show(); } /** * Table functions */ function SelectAll(elem) { const table = elem.closest('table'); document.querySelectorAll("#select_all_icon span").forEach(span => { span.classList.toggle("d-block"); span.classList.toggle("d-none"); }); table.querySelectorAll('input:not(#select_all)').forEach(input => { input.checked = elem.checked; }); } function SelectAlternate(elem) { const table = elem.closest('table'); table.querySelectorAll('input:not(#select_all)').forEach(input => { input.checked = !input.checked; }); } function DeleteSelected(elem, route) { const table = elem.closest('table'), selected = table.querySelectorAll('input:checked:not(#select_all)'), amount = selected.length; if (amount === 0) { CreateToast({ message: '¡No hay items seleccionados!', background: 'text-bg-danger', }); return; } BoxConfirm({ type: 'warning', message: '¿Estás seguro de querer eliminar masivamente ' + amount + ' item(s)? <strong><u>Esto no se puede deshacer</u></strong>.', confirmText: 'Eliminar', preConfirm: () => { const formData = new FormData(); selected.forEach(input => { formData.append(input.name, input.value); }); return new Promise((resolve, reject) => { axios.post(route, formData) .then(response => { if (response.status !== 200) { throw new Error(response.statusText) } resolve(true); $(selected).each(function () { $(table).DataTable().row($(this).parents("tr")).remove().draw() }); CreateToast({message: '¡' + amount + ' item(s) eliminado(s) correctamente', background: 'text-bg-success'}); }) .catch(error => { let errorMessage = ''; const errors = error.response.data.errors; for (const errorKey in errors) { if (errors.hasOwnProperty(errorKey)) { errorMessage = errors[errorKey][0]; break; } } CreateToast({message: 'Error al eliminar multiples items: ' + errorMessage, background: 'text-bg-danger'}); resolve(false); }); }); } }); } function DeleteSingle({ elem, route, name }) { const row = $(elem).parents("tr"), table = $(elem).closest('table'); BoxConfirm({ type: 'warning', message: name + ' será eliminado.<br>¿Estás seguro?. <strong><u>Esto no se puede deshacer</u></strong>', preConfirm: () => { return new Promise((resolve, reject) => { axios.delete(route) .then(response => { if (response.status !== 200) { throw new Error(response.statusText); } resolve(true); $(table).DataTable().row(row).remove().draw(); CreateToast({ message: '¡' + name + ' eliminado correctamente!', background: 'text-bg-success' }); }) .catch(error => { CreateToast({ message: 'Error al eliminar: ' + error.response.data.error, background: 'text-bg-danger' }); resolve(false); }); }); } }); } function DeleteSingleFromShow({ route, name }) { BoxConfirm({ type: 'warning', message: name + ' será eliminado.<br>¿Estás seguro?. <strong><u>Esto no se puede deshacer</u></strong>', preConfirm: () => { window.location.replace(route); } }); } /** * Other functions */ function ResetForm(form) { document.querySelector(form).reset(); }
编译后的global.js
Swal.mixin({ allowOutsideClick: false, allowEscapeKey: true, allowEnterKey: true, customClass: { confirmButton: "btn-primary", cancelButton: "btn-secondary" }, showCancelButton: false, showConfirmButton: false });
问题原因
这是Vite的**tree-shaking(摇树优化)**机制导致的。Vite在生产构建时默认会移除未被引用的代码,以此减小打包体积。
你的global.js里的所有函数(比如Box、CreateToast、SelectAll等)都是在文件内部定义但没有被导出,同时也没有在当前文件内被调用的顶级函数;如果其他文件也没有通过导入的方式使用这些函数,Vite会判定这些代码是“未使用的死代码”,从而在构建时将它们移除。
唯一保留的Swal.mixin代码,是因为它是一个立即执行的表达式,Vite默认不会移除这种直接执行的代码(即使它的返回值defaultBox没有被后续使用)。
另外,你在vite.config.js的input里重复添加了global.js,但这不会改变tree-shaking的逻辑——只要代码没有被导出或引用,依然会被移除。
内容的提问来源于stack exchange,提问作者DEFALTUSER
相关产品推荐
相关产品推荐

