前言
笔者驽钝,最近由于工作关系才去学习了 Angular,发现在合适的项目下使用 Angular 开发真是非常畅快!MVC 框架需要一些学习成本才能上手,再加上 Angular 采用现在通行的约定优于配置(convention over configuration)理念,框架本身做了很多封装,所以实现起来需要开发人员对框架足够的熟悉。
Angular 一个典型的使用场景就是单页应用,那么如何在一个单页面中改变 URL,请与笔者一同学习。
$location
Angular 中使用内置的 $location 服务来监听、操作 URL,包括如下功能:
- 获取、监听、改变地址栏的 URL
- 与 URL 实现双向数据绑定(地址栏变动、前进后退或者点击页面的链接均会触发)。
- 将 URL 对象封装成了一套方法(protocol、host、port、path、search 和 hash)
相对于 BOM 原生的 window.location,使用 $location 更利于测试用例的编写(通过 $location 来注入假数据),提供的接口也更友好(官方一直强调是 jQuery-style getters and setters,我的理解就是支持链式写法),与 URL 实现了双向绑定,内部集成了 HTML5 的 History API,所以建议使用 $location 服务。
如果想实现类似 history.replaceState() 的功能,可以使用 replace 方法,代码如下。本方法只会实现一次 replace 历史记录的功能。
1 2 3 4 |
$location.path('/someNewPath'); $location.replace(); // or you can chain these as: $location.path('/someNewPath').replace(); |
$location 处于 Angular 的生命周期内,单个周期内的改变会统一在周期结束时生效,所以不必担心每次改变了 $location,URL 都会立刻变化。
注意:$location 无法使整个页面重新加载。如果改变 URL 后希望重新加载页面,请使用 $window.location.href。
配置 $location
配置 $location 服务,需要用 $locationProvider 设置参数:
-
html5Mode(mode): {boolean|Object}
- true or enabled:true - 设置为 HTML5 mode
- false or enabled:false - 设置为 Hashbang mode
- requireBase:true - 不需要根目录 default: enabled:false
-
hashPrefix(prefix): {string} Hashbang 风格的 URL 中 #号前面的前缀,习惯上用"!",虽然默认是""(没有! 的 #怎么能叫 shebang 呢。。)
1 |
$locationProvider.html5Mode(true).hashPrefix('!'); |
$locationProvider 用于配置应用中 Deep Linking 的存储方式,就提供了上述两个配置接口。
Hashbang 模式
$location 服务支持配置两种 URL 格式:Hashbang 模式(默认)和 HTML5 模式。两种模式下的 API 都是通用的。如下图
所谓的 Hashbang 就是在 URL 里会看到 #,所有的路由变化都是在 hash 里面控制的,具体到 URL 长这样:https://docs.angularjs.org/#!/guide/introduction?search=test
如果我们用原生的 window.location 打印,会发现变化的部分其实都是在 hash 里,略 eggache。
好处是各个浏览器都兼容,坏处是 URL 长相奇特,SEO 也不友好。
HTML5 模式
所以 Angular 提供了 HTML5 模式。使用了 HTML5 的 History API 来控制 URL 的变化,不再有啰嗦的 #。浏览器的兼容性可参考这里。鉴于兼容性和 Angular 内部的封装,建议采用本模式。
Angular 内部做了向下兼容,采用本模式后,在低版本的浏览器仍会用 Hashbang 来降级处理,两种模式的 URL 也能自动实现相互转换,无需开发者关注。不过,以下三种情况,Angular 不会做转换:
- 带有 target 的链接 <a href="/ext/link?a=b" target="_self">link</a>
- 跳往其他域名的绝对路径 <a href="http://angularjs.org/">link</a>
- 非当前根路径下的链接 <a href="/not-my-base/link">link</a>
记得设置页面的根目录。
如果你的应用挂在根目录(https://myapp.com/),则设置为:
1 2 3 4 |
<head> <base href="/"> ... </head> |
如果挂在子目录(https://myapp.com/subapp/),则设置为:
1 2 3 4 |
<head> <base href="/subapp/"> ... </head> |
不设置会导致 Angular 无法正确处理相对链接和回退到 hashbang 模式。
服务端也要做相应的配置!
因为是单页应用,所以其他链接请求到服务器时,根本找不到对应的 html,就会忧伤的返回 404。所以需要将所有到应用根路径下的请求都重定向到某个页面(比如 index.html)。这样浏览器里先由 index.html 启动应用,Angular 发现 URL 变化了再定位到真正请求的路由上。
码克斯列农 2016 年 7 月 25 日
我来读这篇文章,主要就是不明白 html5mode 到底有什么好处,因为 Shebang 对 Google 的 SEO 是其实是友好的,有助于 ajax 内容的索引,你文章中提到的与这点相反 “好处是各个浏览器都兼容,坏处是 URL 长相奇特,SEO 也不友好。”。从你的文章看,好处也只是提到通过 historyAPI 美化了 URL,而对 SEO 的影响没有深入探讨。
码克斯列农 2016 年 7 月 25 日
http://www.ruanyifeng.com/blog/2013/07/how_to_make_search_engines_find_ajax_content.html
孟楠 2015 年 11 月 20 日
最近在用 angular,想请教一下 html5 模式 在服务器端如何配置文件
TAT.yunsheng 2016 年 2 月 29 日
最后一段有解释呀
mon 2015 年 5 月 18 日
蜘蛛已经可以很好的解析 #的链接的
TAT.tennylv 2015 年 4 月 24 日
好文 通俗易懂!
4074 2015 年 4 月 22 日
感觉有点水~~
TAT.yunsheng 2015 年 4 月 23 日
不好意思,Angular 算是新手,有何不对愿闻其详