我们构建项目时,为了节约带宽资源,加速网页的加载,一个有效的配置是启用压缩,现在浏览器通常支持三种压缩格式:Accept-Encoding:gzip, deflate, br
nuxt3也同样自带压缩功能,默认支持两种格式gzip,br
:
// nuxt.config.tsnitro: {compressPublicAssets: true, // 启动压缩}
然后构建完的静态资源会同时存在三种格式:1. 源文件 2.源文件被压缩成gzip格式 3.源文件被压缩成br格式
对比可以发现,压缩后大小减少很多,其中br
格式压缩率最高
nuxt3通过SSG构建完静态资源,它会提醒你直接执行命令npx serve .output/public
即可代理运行,并且会根据请求头判断客户端是否支持压缩格式,支持的话优先返回压缩后的文件。不得不说nuxt3在细节方面还是做得不错的。
第一种代理方式
一般情况下,传统的vue3项目,构建完网上的资料大多介绍使用nginx进行代理,确实是很成熟的方案,包括启用了压缩资源的代理方式,网上很多资料,我也没采用这种方式,就不说了,nuxt3构建的自然也可以使用nginx来进行代理
第二种代理方式
服务器安装好node环境,然后使用npx serve .output/public
一键代理,并且也支持指定端口,这个也不难,我就不细说了
PS:这种代理优先返回gzip格式,而不是压缩率更高的br格式,并且没找到设置的方法
第三种代理方式
重点当然是实现自定义代理,如果不考虑压缩格式,其实众多后端框架都已经自带静态资源的代理能力,也很容易实现。
然而,需要代理具有压缩格式的静态资源的自定义代理实现,网上居然找不到现成的方案,于是我就自己摸索了一下,发现也不难实现,以下是我使用golang-gin框架实现的自定义代理。
PS:静态资源代理的本质就是返回对应的文件,成熟的http框架都带有返回文件的接口
核心就是判断请求头是否支持压缩格式,支持的话,就返回对应压缩格式的文件,不支持的话就返回源文件,而返回文件就是用框架自带的File()
接口,一个成熟的http框架肯定有该接口的,和我们后端返回文件的是同一个接口
- 首先处理未明确指定文件的请求,如根目录的请求,这种请求都指向对应的
index.html
,所以手工指定成index.html
即可 - 判断文件类型,设置
"Content-Type"
请求头,这一步得根据源文件的格式来手工设置 - 判断请求的文件是否存在,不存在返回
404.html
(非必要) - 依次判断是否存在
br,gzip
等压缩格式的文件,如果有,则返回压缩后的文件,否则返回源文件
func ProxyStatic(c *gin.Context) {var file stringurlPath := c.Request.URL.Path// 路径以/结尾,或者直接以一个目录结尾的,默认其请求的是index.htmlif strings.HasSuffix(urlPath, "/") || !strings.Contains(urlPath, ".") {file = path.Join("./web", urlPath, "index.html")c.Header("Content-Type", `text/html; charset=utf-8`) // 因为后续可能返回它的压缩文件,必须得手工设置类型,否则c.File获取的是压缩文件的类型} else { // 其他则是带具体文件名的请求,当前我的文件类型里只有.js和.css,如果还有其他,需在此补充file = path.Join("./web", urlPath)if strings.HasSuffix(urlPath, ".js") {c.Header("Content-Type", `application/javascript; charset=utf-8`) // 因为后续可能返回它的压缩文件,必须得手工设置类型,否则c.File获取的是压缩文件的类型} else if strings.HasSuffix(urlPath, ".css") {c.Header("Content-Type", `text/css; charset=utf-8`) // 因为后续可能返回它的压缩文件,必须得手工设置类型,否则c.File获取的是压缩文件的类型}}// 判断文件是否存在,如果不存在则返回404.htmlif !fileExists(file) {file = path.Join("./web", "404.html")}// 下面是判断请求是否支持压缩,如果支持,先判断是否有对应的压缩文件,有则返回压缩文件,无则返回原文件acceptEncoding := c.Request.Header.Get("Accept-Encoding")if strings.Contains(acceptEncoding, "br") && fileExists(file+".br") {c.Header("Content-Encoding", "br") // 告诉客户端,返回的压缩文件是br格式c.File(file + ".br")} else if strings.Contains(acceptEncoding, "gzip") && fileExists(file+".gz") {c.Header("Content-Encoding", "gzip") // 告诉客户端,返回的压缩文件是gzip格式c.File(file + ".gz")} else {c.File(file)}
}
代码也很简单,根据自己实际情况略作修改即可,终点是要根据源文件的类型,手动设置"Content-Type"
,不能等c.File(file)
自动设置,因为它返回压缩文件时识别的类型是压缩格式,不是源文件的,会导致前端解析失败
路由就如下方式设置:
// 这些资源没对应压缩格式,直接使用自带的静态资源接口就可以router.StaticFile("/", "./web")router.Static("/images/", "./web/images")// 自定义静态资源代理router.GET("/dist/*name", app.ProxyStatic)
这样我整个项目都可以使用自己的代理程序,更自由了。服务器也不需要安装node,golang的性能还是强过node的,