Vue 项目打包并部署到 GitHub 为 demo

本文最后更新于:2024年10月27日 凌晨

一文搞清楚 Vue 项目打包并部署到 GitHub 为 demo 的整个流程

目标:将打包好的 Vue2 | Vue3 项目部署到 github pages 用作项目 demo

我将尽可能的讲述打包前后可能遇到的绝大部分问题和原因,希望能有所帮助。

打包前的问题

打包其实实际上不会碰到什么大问题,我们讨论的问题实际是打包之后想要将打包结果也就是静态页面进行部署而发生的问题:

  1. 白屏问题
  2. 刷新 404 问题
  3. 跨域问题

但要想讨论上述问题,我们得研究好打包前的配置问题,也就是为什么会出现上述问题?

请求地址配置问题

首先我这里默认你的项目中二次封装了 axios 并配置了 base 请求基地址(别告诉我请求地址是 localhost 哈),那么你的请求配置方法可能是:

  1. 主动暴露 base
1
2
3
4
5
base: 'http://your.domain.com'
api: '/api/end-point'

# The final request form may look like:
http://your.domain.com/api/end-point

或者你是这样配置的(环境为 Vue3+vite):

1
2
3
4
5
base: import.meta.env.VITE_BASE_URL
api: '/api/end-point'

# The final request form may look like:
http://your.domain.com/api/end-point
  1. 配置 proxy 转发

如果你并没有在二次封装 axios 的过程中暴露具体请求地址,而是配置了 proxy 进行代理转发,你的 proxy 配置可能是:

1
2
3
4
5
6
7
8
9
10
# vite.config.js
devServer: {
  proxy: {
    '/dev': {
      target: 'http://your.domain.com',
      pathRewrite: { '^/dev': '' }
      ......
    }
  }
}

配置 proxy 的开发环境下,只要请求匹配到 /api 就会把请求转发到 target 并且将 /dev 删除,浏览器请求为(假设你的项目运行在 8088 端口):http://localhost:8088/dev/api/end-point,实际请求为:http://your.domain.com/api/end-point

proxy 的缺点

这两者在开发环境进行请求都没有问题,都能完成任务,但是一旦进行了打包,脚手架环境消失,这第 2 种配置方法就失效了,请求会只剩下 /dev/api/end-point 这种形式。

可能你会问:玩呢?这怎么发请求,我明明还是见到浏览器发出请求了啊,只不过失败了。

是的,/dev/api/end-point 这种形式的确不能正常发请求,不过浏览器会在只给出如 /dev/api/end-point 的形式的请求时,自动在前面拼接上下文环境,比如在本地测试项目,浏览器就会自动拼接上 http://localhost:8088 使得最终形式为:http://localhost:8088/dev/api/end-point。不过变成这种情况的话,基本也就无法部署了(本地部署除外),因为你已经失去了脚手架的 proxy 环境,不过你依然可以在 Vue 项目的同系统环境下使用 Nginx(类似 Nginx 的也行)尝试进行监听和转发,也就是反向代理。详情请见下节中的 history 使用

那么第 1 种配置方法会在打包之后发生什么问题吗?不会,它可以正常工作(除非请求地址是 localhost,如果为 localhost 则会引起跨域问题)。

Js 引入地址配置问题

如果说请求地址影响着数据显示问题,那么 js 引入地址影响的是打包后的页面白屏问题。

这个问题基本上就是打包后的文件引入问题,我们应当将导出后的 js 等文件的引入地址配置为相对地址。

在 vite 或 vue-cli 中 应当配置 如下:

1
2
# vite.config.js
base: './',
1
2
# vue.config.js
publicPath: './',

对于 vue2 的 vue-cli,官方文档中不推荐直接配置 output.publicPath 而是始终使用 **publicPath**

这将使得打包后的 index.html 页面的 js 引入地址为相对地址,看起来就像这样:

1
<link href="css/app.363852d2.css" rel="stylesheet">

请注意结果为 css/app.363852d2.css 而非 /js/app.5bd045ab.js

解决打包后的白屏问题

出现白屏问题,白屏分为:

  1. 跨域造成的
  2. js 未正确引入造成的:没有配置成相对路径,详见 js 配置
  3. 其他……

解决跨域问题

相关链接:什么是跨域问题?

其实也谈不上解决,毕竟这个问题不是真正的跨域问题,真正的跨域问题只靠前端不靠服务器是不可能解决的

一般这种情况下的跨域问题主要是打包后的文件是以 file 协议进行读取的,而 file 协议不像 http (s) 协议支持跨域,所以控制台 CROS 跨域报错

image-20241010215831443

解决方案参考:vite 打包本地文件,file 类型 URL 引发跨域等问题探讨

但我不太推荐上面的解决方案,因为利用 Nginx(或其他网络服务器)的反向代理就够了。

详情见 history 使用

其他

诚然,我不可能见过所有的白屏问题,所以欢迎探讨。

解决打包后的刷新 404 问题

为什么会出现有基本结构但是我刷新就报 404 错误呢?

这个问题的发生满足以下条件:

  1. 配置了默认访问或 / 访问重定向到某具体路由页面,如 /home

    1
    2
    3
    4
    5
    6
    7
    // Vue2
    path: '/',
    redirect: '/home',
      
    // Vue3
    path: '/',
    redirect: '/article/manage',
  2. 配置了路由模式为不带#符号的 history 历史模式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // Vue2
    const router = new VueRouter({
      mode: 'history',
      routes
    })
    export default router
    
    // Vue3
    const router = createRouter({
      // history: createWebHashHistory()
      history: createWebHistory(),
      routes
    })
    export default router

为什么说满足以上条件就会触发这个问题呢?

这是因为当你第一次访问项目,你的访问地址可能是:http://localhost:8088 或者 http://localhost:8088/ 而因为你的项目满足第一条,所以你的项目自动重定向到了你指定的某个具体路由页面,比如:http://localhost:8088/home

如果你按照上述方式访问,这个时候为第一次访问,是没有任何问题的,js 正常加载,页面正常显示。一旦当你刷新之后,访问地址就变成了 http://localhost:8088/home

这是什么地址?浏览器会认为 /home 是一个后端路由地址将其携带到后端服务器:” 嘿哥们,你这儿有这样一个路由地址吗?“,然而后端实际上没有这样一个路由能匹配,于是就造成了 404 not found 的结果。

那为什么别人做的网站 / 项目不会这样呢?当然是因为别人已经配置过了,没有经过正确配置之前,人人都一样的。

那是不是 Hash 哈希模式就可以呢?是的,hash 模式可以解决这个问题。

这是因为设置为 hash 模式后,如果有 http://your.domain.com/#/homepage 这样一个地址,那么其中的#/homepage 就是 hash 值,hash 值只在客户端(如浏览器)中使用,是不会带给服务器的,所以使用 hash 模式时,不存在刷新 404 问题。

怎么使用 history 模式

我不想出现 404 错误,我也不想使用 Hash 模式,我该怎么使用 history 模式来让网站变得更优雅呢?

思路:让服务器那边在收到未配置的 GET 路由时,都返回 index.html 即可。

什么意思呢?如果浏览器将这样一个后端不认识的路由真的带给了服务器,那么服务器就把 index.html 塞回去,index.html 回去了意味着它所引入的 js 就工作了,js 工作了那么前端路由就活过来了,于是就可以正确处理路由地址了,404 not found 问题也就迎刃而解了。

这也是为什么某些文章中使用 Nginx 配置里有 try_files $uri $uri/ /index.html; 存在了。

换句话说,如果你是本地部署使用 Nginx,它可以解决你的刷新 404 问题,并且,Nginx 也能解决打包后 proxy 失效带来的请求问题,配置 conf/nginx.conf 如下:

  • 请注意 Nginx 的根目录最好不要是 C 盘,防止权限不足
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
http {
	...略...
	
	server {
		...略...
		
    # 配置Nginx监听的项目根地址
    location / {
        try_files $uri $uri/ /index.html;
        # Vue3项目的打包目录
        root D:/your/own/dist;
        index index.html; # 默认首页文件名
    }
    # 配置请求转发
    location /dev/ {
    	# 配置请求监听到之后的转发目标
    	proxy_pass http://your.domain.com
    }
	}
}

相关链接:Nginx 下载

GitHub Pages 上的 history 路由模式可行性

我的看法

相信看过上面你已经想到了:我能不能对部署在 GItHub 上的 demo 也这样配置?

我只能说,我没试过,但我觉得不行:因为 Nginx 和 demo 不在同一个系统环境

上述配置能成的根本原因是:在前端将路由也带来的情况下,Nginx 进行监听并匹配成功,然后将 index.html 进行返回。

这里的重点在于,Nginx 要能成功监听到项目的根地址,但对于部署到 GitHub 上的 demo 来说,浏览器会直接向 GitHub Pages 请求资源,而 GitHub Pages 只支持静态文件托管,它是没有针对 history 模式的服务端配置的。

而且,我总不可能将 Nginx 也部署到 GitHub 上吧?

一个可能的方法

不过,ChatGPT 有一个它的解决办法:在 dist 打包目录内中加入一个 404.html 页面用于手动返回 index.html

1
2
3
4
5
<script type="text/javascript">
  // 如果页面找不到路径,重定向到index.html,让Vue处理
  var redirect = window.location.pathname;
  window.location.href = '/index.html?redirect=' + redirect;
</script>

我无法验证该方法的可行性,因为我已经将我的 pages 给我生成的域名用来部署了 hexo 博客,我的所有其他仓库的 pages 项目都是在该域名下,这导致我的 demo 无法使用该方法,我也就无法验证了。

相关链接:GitHub Pages 部署 hexo 静态博客 -Fluid 主题

我参考的那篇文章找不到了……

一个可能需要的配置参考

别忘了给你的 Nginx 服务器加上 ssl监听并正确转发请求(什么是 ssl 证书?),因为 GItHub 的网络环境是 https 的,如果你发送 http 请求,浏览器将会进行拦截。

可自行搜索需要的免费 ssl 证书签发厂家,如果你的远程服务器没有域名,那么你需要的 ssl 证书为 ip 证书,我使用的正是这种类型,可以搜索:zerossl

我的 Nginx 配置示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

#user  nobody;
worker_processes  4;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;

events {
    worker_connections  1024;
}

http {
    # 监听 HTTP 请求并重定向到 HTTPS
    server {
        listen 80;  # 监听 HTTP
        server_name your_domain;  # 替换为你的域名或 IP

        # 强制 HTTP 到 HTTPS 重定向
        return 301 https://$host$request_uri;
    }

    # 监听 HTTPS 请求
    server {
        listen 443 ssl;  # 监听 HTTPS
        server_name your_domain;  # 替换为你的域名或 IP

        ssl_certificate C:/ssl/certificate.crt;  # SSL 证书路径
        ssl_certificate_key C:/ssl/private.key;  # SSL 私钥路径

        ssl_protocols TLSv1.2 TLSv1.3;  # 启用的 SSL 协议
        ssl_ciphers HIGH:!aNULL:!MD5;  # 启用的加密算法

        location / {
            root C:/my/test;  # 指定网站根目录
            index flag.txt;  # 默认主页文件
        }

        location /api {
            proxy_pass http://localhost:1337;  # 代理到 Strapi
            proxy_http_version 1.1;  # 使用 HTTP/1.1
            proxy_set_header Upgrade $http_upgrade;  # 处理 WebSocket
            proxy_set_header Connection 'upgrade';  # 处理 WebSocket
            proxy_set_header Host $host;  # 传递主机头
            proxy_set_header X-Real-IP $remote_addr;  # 传递客户端真实 IP
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;  # 传递转发 IP
            proxy_set_header X-Forwarded-Proto $scheme;  # 传递原始协议
        }

        location /upload {
            proxy_pass http://localhost:1337;  # 代理到 Strapi
            proxy_http_version 1.1;  # 使用 HTTP/1.1
            proxy_set_header Upgrade $http_upgrade;  # 处理 WebSocket
            proxy_set_header Connection 'upgrade';  # 处理 WebSocket
            proxy_set_header Host $host;  # 传递主机头
            proxy_set_header X-Real-IP $remote_addr;  # 传递客户端真实 IP
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;  # 传递转发 IP
            proxy_set_header X-Forwarded-Proto $scheme;  # 传递原始协议
        }

        # 其他 location 配置...
    }
}

我的 demo 使用的数据来自 Strapi什么是 Strapi?),我将该无头 CMS 部署到了和 Nginx 同一系统环境之下,于是有了上述配置。

部署到 GitHub Pages

可选配置

终于到了激动人心的部署时刻,在打包之前请根据上文做好自己项目的配置,除此之外还有一个配置对于 vite 打包的项目可选

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// vite.config.js
build: {
  rollupOptions: {
    output: {
      entryFileNames: 'js/[name]-[hash].js', // 指定 JS 文件的输出路径及命名规则
      chunkFileNames: 'js/[name]-[hash].js', // 指定分片文件的输出路径及命名规则
      assetFileNames: (assetInfo) => {
        // 设置不同类型文件的输出路径及命名规则
        if (assetInfo.type === 'asset' && /\.(jpe?g|png|gif|svg)$/i.test(assetInfo.name)) {
          return 'img/[name].[hash].[ext]' // 图像文件输出路径及命名规则
        }
        if (assetInfo.type === 'asset' && /\.(ttf|woff|woff2|eot)$/i.test(assetInfo.name)) {
          return 'fonts/[name].[hash].[ext]' // 字体文件输出路径及命名规则
        }
        return '[ext]/name1-[hash].[ext]' // 其他资源文件输出路径及命名规则
      }
    }
  }
}

添加上述 build 配置项,将会将打包后的 dist 目录按类型分 cssjsimg 文件夹。当然只是可选,非必须

配置项参考文章:vue 项目配置 vite 打包输出文件夹(css,img,js,font)

Github 部署

好了,请将项目 build 打包为一个 dist 文件夹。然后按照以下步骤进行部署:

  1. 转到 GitHub 创建一个新的仓库(已有仓库跳第二步),填写 Repository name 仓库名并将仓库设为 Public 公开就可以了,其他不用设置,点击 Create repository 创建仓库即可。

  2. 复制仓库的 ssh 链接(我没有用过 git 进行远程仓库绑定),例如:

    1
    git@github.com:yourUserName/article-management-admin.git
  3. 找到本地刚新鲜打包的 dist 文件夹,进入文件夹中,目录结构应该为:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    # vue-cli 打包结果
    ├─css
    ├─img
    ├─js
    ├─.nojekyll
    ├─favicon.ico
    └─index.html
    
    # vite 打包结果
    ├─asset
    ├─favicon.ico
    └─index.html
  4. dist文件夹内,执行 git 仓库初始化

    1
    git init
  5. 添加所有文件到暂存区,并执行 commit 提交
    注:vite 打包的项目可能会存在以_下划线开头的 js 文件,而 GitHub Pages 可能忽略该文件,导致项目 demo 无法找到文件报 404 错误,请检查你的 dist 文件夹下的 asset 目录中是否存在上述文件。如果确实存在,请在 dist 目录下创建一个空文件以禁用 GitHub Pages 的 Jekyll 默认处理行为,文件名为:.nojekyll
    参考文章:github pages 不能识别下划线开头的文件

    1
    2
    git add .
    git commit -m "-Write down your message here."
  6. 绑定远程仓库

    1
    git remote add origin <remote-url>
  7. 你可以将该分支(我的默认初始化分支为 main)先 push 推送出去,已有仓库的可以跳到 8

    1
    git push -u origin main
  8. 创建孤立的空分支 gh-pages 用于推送

    1
    git checkout --orphan gh-pages
  9. 推送代码到 gh-pages 分支用于部署静态页面

    1
    git push -u origin gh-pages
  10. 转到 GitHub 仓库页,请切换仓库分支为 gh-pages 并等待 GitHubcheck 操作:
    image-20241011025535410

  11. GitHub 的 check 操作完成后,点击仓库 Settings 设置项,然后在左侧侧边栏选择 Pages,点击构建好的链接或者右边的 Visit site 即可。
    image-20241011025932731

小白篇到此,完结撒花🎉🎉🎉

有更多需求可以自行探索,相信你已经入门了。


Vue 项目打包并部署到 GitHub 为 demo
https://4rozen.github.io/archives/Vue/31657.html
作者
4rozeN
发布于
2023年10月26日
许可协议