开发和内网部署正常,反向代理后出现404和图片加载失败的解决方案;部署到公网后报错404;部署到公网后图片加载出错;动态渲染获取图片失败

目录

一、情景介绍

二、所有路径都报404

2.1、错误现象及解决方法

2.2、router的一种高级写法

三、部分图片丢失,加载失败

3.1、问题描述

3.2、不会丢失图片的写法

3.3、丢失图片的写法

3.4、修改方法

3.4.1、放弃动态渲染

3.4.2、可能原因

3.4.3、通过import提前加载图片

3.4.4、CommonJS中可能可行的写法

四、总结


一、情景介绍

        最近在做一个项目的收尾工作时,需要将开发好的前端项目部署到服务器上,在测试和修改阶段,是部署在内网服务器上的,用来提供内部人员测试和考核,并提供优化意见。也就是说,在开发阶段和内网部署阶段都没有问题。

        问题就出在部署到公网的过程中,首先想用某个特定的域名,但又是独立开发的项目,所以用了反向代理,将该域名下的一个路由指向了内网服务器连接的首页。大概就是将

https://×××.com/webName

        指向内网链接的首页,也就是重定向前的内网链接:

http://127.0.0.1:8001(这里用xxx.com和127.0.0.1来代替真实的域名和ip。)

        这样操作去做反向代理,然后就出了一堆问题。

二、所有路径都报404

2.1、错误现象及解决方法

        这里有个比较抽象的事情,是我的404是我自己项目的404(我自己写的404页面),不是该域名网站对应的404页面,也就是说确实访问到了我的项目,但是访问不到对应的页面。

        了解了一下,可能和路由模式有关,将路由模式从history改成hash,问题解决。这里history:createWebHistory()就是创建history的路由模式,如果是history:createWebHashHistory()就是创建hash的路由模式。相比起来,hash模式最为省心,所见即所得,就是链接里面会多一个#,稍微有点影响美观,但我能够接受。

/**
 * tips:只管理路由配置,包括路由设置,路由守卫等
 * date:2024/2/27
 * author:watermelo
 */
import {
  createRouter,
  createWebHistory,
  createWebHashHistory,
} from "vue-router";
import routes from "./routes";

const router = createRouter({
  // history: createWebHistory(),
  history: createWebHashHistory(),
  routes,
});

// 根据meta中的title属性,设置页面标题
router.beforeEach((to, from, next) => {
  document.title = to.meta.title || "Default Title";
  next();
});

export default router;

        此外,如果实在觉得hash模式的#不好看,也可以对history模式进行进一步配置,这里给个传送门:vue路由模式及 history 模式下服务端配置 - 知乎

2.2、router的一种高级写法

        这里顺便给大家推荐一种router的写法,就是将路由剥离出来,index.js只管理路由配置,包括路由设置,路由守卫等,将路径配置放到另一个routes.js中,这样非常方便管理,也很清晰,同时在项目更大(上百个子页面的时候),还能将routes.js进一步拆分,按照大类区分,将对应的routes.js放到对应的大类文件夹中,最后再在router文件夹下的routes.js中进行汇总,这是我在一个超大项目里学到的写法,真的很好用,小项目也可以用,清晰优雅可读性强。上面就是index.js,routes.js大致就像下面这样:

/**
 * tips:所有路径的总和,该js只管理路径
 * date:2024/2/27
 * author:watermelo
 *
 * Notes:
 * 1、
 * meta可配置的参数有:
 * meta:{
 * title:(string)
 * requireAuth:(true)
 * }
 *
 */
export default [
  // 专用测试url,用来测组件
  // {
  //   path: "/test1",
  //   name: "test",
  //   meta: {
  //     title: "SDGs&Sustainable",
  //   },
  //   component: () => import("@/components/pdfviewer.vue"),
  // },
  // {
  //   path: "/test2",
  //   name: "test",
  //   meta: {
  //     title: "SDGs&Sustainable",
  //   },
  //   component: () => import("@/components/foot.vue"),
  // },
  {
    path: "/:catchAll(.*)",
    name: "error",
    meta: {
      title: "404-页面不存在",
    },
    component: () => import("@/views/error/404.vue"),
  },
  {
    path: "/",
    redirect: "home",
    meta: {
      title: "SDGs&Sustainable",
    },
    component: () => import("@/views/dashboard/home.vue"),
  },
  {
    path: "/home",
    name: "home",
    meta: {
      title: "SDGs&Sustainable",
    },
    component: () => import("@/views/dashboard/home.vue"),
  },
  {
    path: "/chapters",
    name: "chapters",
    meta: {
      title: "SDGs&Sustainable",
    },
    component: () => import("@/views/dashboard/chapters.vue"),
    children: [
      {
        path: "", //默认加载子页面——导航页
        name: "chapters",
        meta: {
          title: "Chapter contents",
        },
        component: () => import("@/views/chapters/contents.vue"),
      },
      {
        path: "preface",
        name: "preface",
        meta: {
          title: "Preface",
        },
        component: () => import("@/views/chapters/preface.vue"),
      },
      {
        path: "chapter2",
        name: "chapter2",
        meta: {
          title: "Chapter2",
        },
        component: () => import("@/views/chapters/chapter2.vue"),
      },
      {
        path: "chapter3",
        name: "chapter3",
        meta: {
          title: "Chapter3",
        },
        component: () => import("@/views/chapters/chapter3.vue"),
      },
      {
        path: "chapter4",
        name: "chapter4",
        meta: {
          title: "Chapter4",
        },
        component: () => import("@/views/chapters/chapter4.vue"),
      },
      {
        path: "appendix",
        name: "appendix",
        meta: {
          title: "appendix",
        },
        component: () => import("@/views/chapters/appendix.vue"),
      },
    ],
  },
  {
    path: "/rankings",
    name: "rankings",
    meta: {
      title: "SDGs&Sustainable",
    },
    component: () => import("@/views/dashboard/rankings.vue"),
  },
  {
    path: "/interactiveMap",
    name: "interactiveMap",
    meta: {
      title: "SDGs&Sustainable",
    },
    component: () => import("@/views/dashboard/interactiveMap.vue"),
  },
  {
    path: "/dataScreen",
    name: "dataScreen",
    meta: {
      title: "SDGs&Sustainable",
    },
    component: () => import("@/views/dashboard/dataScreen.vue"),
  },
  {
    path: "/downloads",
    name: "downloads",
    meta: {
      title: "SDGs&Sustainable",
    },
    component: () => import("@/views/dashboard/downloads.vue"),
  },
  {
    path: "/ours",
    name: "ours",
    meta: {
      title: "SDGs&Sustainable",
    },
    component: () => import("@/views/dashboard/ours.vue"),
  },
];

三、部分图片丢失,加载失败

3.1、问题描述

        项目中有多种引入图片的方式,图片有放在assets里面的,有放在.vue文件旁边的(专用图),更多的放在public里面,一般是推荐放到public里面,引入最为省心。但是有一种写法图片丢失了。

3.2、不会丢失图片的写法

        基本上直白一点的写法都没啥问题,只要开发模式能访问到,打包部署后就没问题。

<!-- 图片直接放在组件文件旁边的情况(放在assets文件夹同理) -->

<div class="fig-container">
        <img class="fig" src="./chaptersImgs/chapter2Fig1.png" alt="" />
        <p class="figTitle" align="center">图2 指标体系构建思路图</p>
</div>



<!-- 图片放在public文件夹中的情况 -->
<img
    src="/dataCenterLogo.png"
    style="
        height: 5.5vh;
        width: auto;
        margin-right: 0.7vw;
          "
/>

3.3、丢失图片的写法

        很抽象的一点是,在我使用v-for生成模块,并使用动态路径获取图片来填充的时候,就获取不到图片了,需要知道的是在开发阶段和内网部署阶段都是能获取到图片的,写法是这样的:

      <!-- 使用v-for动态渲染模块,并填充图片,动态生成图片路径的方式 -->
      <el-row class="row" :gutter="0">
        <el-col
          v-for="(item, i) in urlReport"
          :span="6"
          class="chapter-col"
          justify="center"
          :key="i"
        >
          <div class="singleDownload" @click="downloadByUrl(item.url)">
            <div class="card-image-container">
              <img :src="'/download/report' + ( i + 1 ) + '.png'" class="card-image" />
            </div>
            <div class="description">
              {{ item.reportName }}
            </div>
          </div>
        </el-col>
      </el-row>

3.4、修改方法

3.4.1、放弃动态渲染

        按照这个实际需求和错误情况,如果放弃动态渲染,放弃使用v-for,肯定是能解决问题的,但是我不甘心啊,本来要渲染的模块就很多,之后可能还涉及后续的增删,纯复制粘贴太不优雅了,太没追求了!

3.4.2、可能原因

        我怀疑是vite打包的问题,对部分语法的处理不到位,vite作为一个好用的前端开发与构建工具,在部分语法、框架和传统组件的应用上有局限性是比较可能的。那就要想办法在保留v-for的同时,换个语法写图片的路径试试看。

3.4.3、通过import提前加载图片

        最后想了个法子,直接通过import提前加载图片,存到一个数组里,然后动态渲染的时候以此填充就行了,这样src后面的语法非常简单,只需要读取一下数组就行,就可能直接绕过这个bug。代码如下:

        这是html部分引入的写法:

      <el-row class="row" :gutter="0">
        <el-col
          v-for="(item, i) in urlReport"
          :span="6"
          class="chapter-col"
          justify="center"
          :key="i"
        >
          <div class="singleDownload" @click="downloadByUrl(item.url)">
            <div class="card-image-container">
              <img :src="reportPic[i]" class="card-image" />
            </div>
            <div class="description">
              {{ item.reportName }}
            </div>
          </div>
        </el-col>
      </el-row>

        这是js部分的代码,在vue3中,直接写在<script setup>里面就行,如果是vue2,就要写在mounted()里面,在虚拟DOM阶段,真实DOM挂载前完成图片的加载就行。


// 图片存放路径
const webPic = ref([]);
const reportPic = ref([]);


// 异步加载图片
Promise.all([import("/download/web1.png"), import("/download/web2.png")]).then(
  (images) => {
    webPic.value = images.map((image) => image.default);
  }
);
Promise.all([
  import("/download/report1.png"),
  import("/download/report2.png"),
  import("/download/report3.png"),
  import("/download/report4.png"),
  import("/download/report5.png"),
  import("/download/report6.png"),
  import("/download/report7.png"),
  // import("/download/report8.png"),
  import("/download/report9.png"),
  import("/download/report10.png"),
  import("/download/report11.png"),
  import("/download/report12.png"),
  import("/download/report13.png"),
]).then((images) => {
  reportPic.value = images.map((image) => image.default);
});

        改完后检查,开发模式没问题,部署到内网没问题,反向代理后在公网查看,还是没问题!完美解决!

3.4.4、CommonJS中可能可行的写法

        我突然联想到,vite只支持ES6语法,如果不是vite,支持CommonJS语法的话也可以通过require()来更简单的实现,因为require是可以直接写在src后面的,即“:src=require("./xxx")”。有兴趣的也可以试试,可能会更简单。

四、总结

        说实话还是没能搞懂为什么内网部署没问题,反向代理后就有问题了,但是能找到解决方案就是好事。

        博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值