前言
都9102年了,笔者所在的公司的主要项目还是用AngularJS 1.6这种史诗的框架进行开发的。另外由于历史的原因,代码的凌乱程度早已超越想象。为此,笔者决定痛下决心把整个项目重构了一遍...从此踏上了Angular升(跳)级(坑)之路。
系列文章
在这个系列文章中涉及到
- Angular与AngularJS如何共存开发
- 使用Angular Cli编译及开发Angular、AngularJS混合应用
- 如何在Hash模式中同步AngularJS、Angular的路由
---下面是坑,待填 - 在gulp或其他自动化构建工具中使用Angular CLI
- 在Angular中使用AngularJS的组件
- 在AngularJS中使用Angular的组件
本文使用的例子项目
AngularJS的例子项目angular-phonecat
一、路由的管理
在上篇文章中,项目已经基本能正常运行了。但其实在项目里还有许多未解决的问题,其中包括:
- 两个框架中的路由状态不同步。
- 在切换路由后,刷新页面,路由消失。
在项目中使用的是双路由的策略。即ng1(AngularJS)、ngx(Angular)各自的页面其实还是用相应的路由进行管理的。因此,两个路由间需要一个“桥梁”进行通信,让他们彼此间的数据进行同步,而这个“桥梁”官方早已为我们提供好了,那就是setUpLocationSync
。
在使用“桥梁”进行通信前,还需对路由进行改造,让它们更符合我们项目的需求。
二、hash模式还是history模式
在ngx的路由器中,默认使用的是history模式的路由形式。但ng1路由器中默认使用的则是hash模式的路由形式。因此,我们首先要做的,就是要让它们的模式设置为一致。
hash和history模式各有利弊,它们之间的区别可以参考附录:LocationStrategy 以及浏览器 URL 样式。笔者更推荐使用history模式,因为他更符合浏览器的URL风格。
下面介绍一下它们各自的设置方法:
history模式:
如果使用history模式,那么你主要修改ng1的路由器配置。
angular.
module('phonecatApp').
config(['$routeProvider','$locationProvider',
function config($routeProvider,$locationProvider) {
$locationProvider.html5Mode(true) // 设置为html5 mode
}
]);
hash模式:
如果使用hash模式,那么你主要修改的就是Angular的路由配置。
@NgModule({
imports: [RouterModule.forRoot(routes,{
useHash: true
})]
})
export class AppRoutingModule {
}
在ng1.6你可能使用的是hash-bang模式,或者在ng1中设置了任何的前缀,你需要将其前缀去掉。
angular.
module('phonecatApp').
config(['$locationProvider',
function config($locationProvider) {
$locationProvider.hashPrefix('')
}
]);
三、凹槽路由
在实际的项目中,有些页面是基于ng1写的,有些页面是基于ngx写的。在多数情况下,我们自然是希望能各自显示页面相互不被打扰。因此,我们需要做一个“凹槽路由”让它们能自由切换。(文段参考 2)
那么。凹槽路由究竟是什么?简单而言就是“空页面”。在显示ng1的页面时,ngx部分显示一个空白的页面不就可以了?同理的,在显示ngx的页面,我们进行相应的操作。
在ng1中实现比较简单:
angular.
module('phonecatApp').
config(['$routeProvider',
function config($routeProvider) {
$routeProvider.
..., // 此处省略其他路由
otherwise({
template: ''
});
}
]);
在ngx中,你需要添加一个"空"的Component
ng g component empty
然后,在路由中添加
const routes: Routes = [
...,
{
path: '**',
component: EmptyComponent
}
]
ALL DONE...
四、同步你的路由状态
现在,就让它们的状态同步吧。
在history mode下非常简单。只需用到官方提供的setUpLocationSync
即可。
export class AppModule implements DoBootstrap {
ngDoBootstrap(appRef: ApplicationRef) {
this.upgrade.bootstrap(document.documentElement, ['phonecatApp']);
// setUpLocationSync 需在UpgradeModule.bootstrap后引用
setUpLocationSync(upgrade);
}
}
那在hash mode下呢?那就没这么简单了。
笔者曾经直接使用setUpLocationSync
这个方法导致笔者直接怀疑人生——每次跳转路由都会出现死循环。
为什么会出现这种情况呢?setUpLocationSync
这个方法是@angular/router
提供的函数,一般不会出现问题。但这次导致循环的问题,就是出现在这个函数身上。在upgrage.ts中,可以看到
ngUpgrade.$injector.get('$rootScope')
.$on('$locationChangeStart', (_: any, next: string, __: string) => {
const url = resolveUrl(next);
const path = location.normalize(url.pathname);
router.navigateByUrl(path + url.search + url.hash);
});
在ng1触发locationChangeStart这个事件后,这个函数直接将URL中的path + url.search + url.hash
传递给ngx的Router,然而hash mode中应该需要将hash作为路径传递。因此,要在hash mode中同步状态,需要将router.navigateByUrl(path + url.search + url.hash);
改为this.router.navigateByUrl(url.hash + url.search)
。
在本文的例子中在原有的setUpLocationSync
的基础上实现了路由器的同步:
location-sync.service.ts
附:相邻路由出口?
在参考文章中升级 AngularJS 至 Angular提到了“相邻路由出口”。
在实际运用中,通过它可以做一些如懒加载的ng1的模块等实现。需要注意的是,如果在组件(Component)中放入ng1的入口,那么调用UpgradeModule.bootstrap需要在该组件视图加载完毕后,如ngAfterViewInit
钩子等等。
源码
在此,你可以获取到本文修改后的angular-phonecat项目。 [github.com/yskun/angul…]
参考
License
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。