既上一篇文章记录了组内单页引擎升级路由为 History API 方式,考虑到不支持该方式的浏览器/WebView,需要多页降级。如果不对 Nginx 进行配置,就会出现404,因为多数通过 pushState
得到的 URL 并没有真实对应的资源。
采用 History API 方案的 URL 格式如下:
http[s]://hostname/resource/project/page[/view]?arg1=value1
其中 resource 为资源目录,该目录下放置各个不同的项目文件夹,每个项目对应一个 project 目录。一个 project 中可能会有多个单页应用,每个单页应用对应 URL 中的一个 page。一个单页应用下的不同视图分别对应一个 view。因为采用的单页引擎支持默认路由,所以 view 并非必需。
比如 A 项目下有一个 refund 的单页应用,对应的 HTML 资源为 refund.html,为了 URL 的美观,在 URL 中去掉 html 后缀名。refund 管理两个视图 detail 和 help,且默认视图为 detail。对应该情况,URI 有以下形式:
其中第1个 URI 和第2个相同,只是采用了默认视图。这三种 URL 都需要 map 到 refund.html 这个实际存在的 HTML 资源。除去 map HTML 资源,还需要 map 页面请求的各种 JS 和 image 资源。这两种资源都是在页面中通过相对地址引用的,对应的 URL 分别为:
这两种资源 URL 和上面的 URL 资源组合起来,一共有两种形式的 URL:
项目中的实际目录结构如下:
resource_static --resource --A --refund.html --page --refund.js --img --icon.png
刚开始考虑针对两种类型的资源(HTML 和其他)分别做 rewrite,这样就得到下面的匹配规则:
# match img resource location ~* ^/resource/([/w-.]+)/([/w-.]+)/(img|page)/(.*)$ { rewrite ^/resource/([/w-.]+)/([/w-.]+)/(img|page)/(.*)$ /resource/$1/$3/$4 last; } # match HTML resource location ~* ^/resource/([/w-.]+)/.*$ { rewrite ^/resource/([/w-.]+)/([/w-.]+)$ /resource/$1/$2.html last; rewrite ^/resource/([/w-.]+)/([/w-.]+)/([/w-.]+)$ /resource/$1/$2.html last; } location / { root /resource_static/; indexindex.htmlindex; }
这就是最初也是根据上一步的 URI 分类得出的最直观版本。
针对3.1中的方案,可以保证新 URI 中所有情况都可以找到对应的资源。但如果单页要引入项目下一个新文件夹中的资源,这时候就要修改上述第1条配置,添加对应目录名,这样扩展性就非常差。
换个角度考虑,其实 rewrite 的目的只是为了去掉由于 URI 中 view 对应的新增虚拟目录导致静态资源寻找多了 page 这一层。所以只要去掉包含这一层的 URL 即可。考虑采用 try_files 直接实现:
root /resource_static/; location ~* ^/resource/([/w-.]+)/([/w-.]+)(/.*)?$ { try_files /resource/$1/$2/$3 /resource/$1/$3 /resource/$1/$2 /resource/$1/$2.html =404; }
上面这条配置中,location 后对应的 URI 可以匹配上述所有静态资源的 URI,并且没有写死任何目录名称。try_files 后第1、2个参数分别用来匹配默认视图和指定视图两种 URI 下的静态资源 URI,第3、4个参数用来匹配 HTML 或其它根目录资源,最后一个参数表示如果上述都不匹配,则直接返回404。这样。该条配置同样可以匹配目录下其它子目录或多级子目录的资源匹配。本质做的事情包括两点:针对目录下的资源请求,去掉 URI 中的 page 层;针对单页,添加 html 后缀名。
Nginx 中可以实现重定向的主要有3条指令:return,rewrite 和 try_files。关于3者分别的用法和他们之间的使用场景,可以参考这篇文章: 创建 Nginx URI 重写规则
。还有一点要注意的是,在 []
中写特殊字符,如 .
时,不需要转义。
综上,其实还是遇到问题不能只做到满足要求即可,而是要进一步深入看有么有更好的办法来解决当前解决方案中的一些瑕疵或者缺陷。借此机会,自己也对 Nginx 配置有了进一步了解。