vue3 + vite + element-plus + springboot打包上线所遇之坑

自我本经介绍:我自己是做Java开发的,对于前端其实并不熟悉,以下的前端技术都是为了做这个项目现学现卖的。

自己使用vue3 + vite + element-plus从头到尾打了个管理后台,在开发环境运行一切正常,于是准备打包发布到服务器上,就有了几天痛苦的解决问题的过程

  1. 打包npm run build,得到静态文件,丢进tomcat(后来改成了nginx)根目录(端口:8080),打开localhost:8080/login,正常显示,但是已刷新,抱歉,没了,显示404,一番查询+理解,发现是单页应用刷新都有这个问题,vue我用的是history模式,其实hash模式没这个问题,但是一方面我在开发环境用的是history模式,我不想改,另外hash模式地址栏不太好看,我有强迫症,非得要用history模式。因为单页应用服务器上并没有地址栏中的页面,而地址栏中的路径是需要vue通过路由来导航进入的,需要服务端返回index.html然vue来控制路由,于是查询发现tomcat可以配置WEB-INF/web.xml来解决404返回index.html问题,如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                             http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0">

    <!-- 让所有 404 页面返回 index.html -->
    <error-page>
        <error-code>404</error-code>
        <location>/index.html</location>
    </error-page>

</web-app>

于是把刷新变成404解决了
我的页面大概这样子:

点击任何一个左侧菜单,报错:
一番查询:几乎都是说动态路由的导入不能拼接字符串,我是这样的:
menu.component = async () => import(/* @vite-ignore */"../views" + p + "/index.vue")
需要改为import.meta.glob风格:const modules = import.meta.glob("../views/**/*.vue");并且通过addRouter的方式动态添加

router.addRoute({ name: "/layout", path: "/layout", component: modules['../views/layout/index.vue'], meta: { title: "首页", canClose: false }})
router.addRoute("/layout", {name: "/layout/system", path: "/layout/system", component: modules['../views/layout/index.vue']})
router.addRoute("/layout", {name: "/layout/index", path: "/layout/index", component: modules['../views/layout/index.vue'], meta: { title: "首页", canClose: false }})
router.addRoute("/layout/system", {name: "/layout/system/user", path: "/layout/system/user", component: () => import('../views/layout/system/user/index.vue'), meta: { title: "用户管理", canClose: true }})
router.addRoute("/layout/system", {name: "/layout/system/role", path: "/layout/system/role", component: modules['../views/layout/system/role/index.vue'], meta: { title: "角色管理", canClose: true }})
router.addRoute("/layout/system", {name: "/layout/system/dict", path: "/layout/system/dict", component: modules['../views/layout/system/dict/index.vue'], meta: { title: "字典管理", canClose: true }})
router.addRoute("/layout/system", {name: "/layout/system/resource", path: "/layout/system/resource", component: modules['../views/layout/system/resource/index.vue'], meta: { title: "资源管理", canClose: true }})
router.addRoute("/layout/system", {name: "/layout/system/enumInfo", path: "/layout/system/enumInfo", component: modules['../views/layout/system/enumInfo/index.vue'], meta: { title: "枚举管理", canClose: true }})

事实上addRouter这里通过modules的结果也是() => import(),所以用二者都可以。
但是...TypeError: Failed to fetch dynamically imported module: http://localhost:8080/views/layout/system/user/index.vue依旧。分析:打包之后按理说不应该请求.vue结尾的文件了(开发环境会请求.vue文件),但是始终会请求。不明白。
点击左侧菜单,控制台继续报错,并且页面无法渲染出来。继续gtp、deepseek查询了无数次,尝试了无数次无果。最后deepseek提示看此请求的来源, 是从什么地方发起的,经排查,是我在store里面addTab时添加的。代码如下:

    actions: {
        addTab(to: RouteLocationNormalized): void {
            const path = to.path;
            this.defaultActiveTab = path;
            // 存在就激活
            if (this.tabs.filter(e => e.path === path).length != 0) {
                this.activeTab = path;
                return;
            }

            // 不存在就添加并且激活
            const comp = defineAsyncComponent(() => import(/* @vite-ignore */"../../views" + path + "/index.vue"));
            const component = shallowRef(comp);
            const meta: Meta = (to.meta) as unknown as Meta;
            this.tabs.push({
                title: meta.title,
                path: path,
                content: '',
                canClose: meta.canClose,
                component,
                icon: meta.icon
            });
            this.activeTab = path;
            this.addDefaultTab();
        },

这里依然使用字符串拼接方式,于是改成modules方式,依然报错,gpt提问得知,这里需要改成这种造型:

            const modules = import.meta.glob("../../views/**/*.vue");
            const p = `../../views${path}/index.vue`;
            const comp = modules[p] ? defineAsyncComponent(modules[p]) : null;
            const component = shallowRef(comp);

于是就ok了。
最终我把静态文件放在了nginx,并且配置了404返回index.html,并且删除了WEB-INF下tomcat的配置

posted @ 2025-02-12 14:30  神一样的存在  阅读(105)  评论(0)    收藏  举报