使用Angular cli升级AngularJS项目——(二)同步混合应用路由的状态

前言

都9102年了,笔者所在的公司的主要项目仍是用AngularJS 1.6这种史诗的框架进行开发的。另外因为历史的缘由,代码的凌乱程度早已超越想象。为此,笔者决定痛下决心把整个项目重构了一遍...今后踏上了Angular升(跳)级(坑)之路。html

系列文章

  1. (一)引导运行混合应用
  2. (二)同步混合应用路由的状态

在这个系列文章中涉及到

  1. Angular与AngularJS如何共存开发
  2. 使用Angular Cli编译及开发Angular、AngularJS混合应用
  3. 如何在Hash模式中同步AngularJS、Angular的路由
    ---下面是坑,待填
  4. 在gulp或其余自动化构建工具中使用Angular CLI
  5. 在Angular中使用AngularJS的组件
  6. 在AngularJS中使用Angular的组件

本文使用的例子项目

AngularJS的例子项目angular-phonecathtml5

1、路由的管理

在上篇文章中,项目已经基本能正常运行了。但其实在项目里还有许多未解决的问题,其中包括:git

  1. 两个框架中的路由状态不一样步。
  2. 在切换路由后,刷新页面,路由消失。

在项目中使用的是双路由的策略。即ng1(AngularJS)、ngx(Angular)各自的页面其实仍是用相应的路由进行管理的。所以,两个路由间须要一个“桥梁”进行通讯,让他们彼此间的数据进行同步,而这个“桥梁”官方早已为咱们提供好了,那就是setUpLocationSync
在使用“桥梁”进行通讯前,还需对路由进行改造,让它们更符合咱们项目的需求。github

2、hash模式仍是history模式

在ngx的路由器中,默认使用的是history模式的路由形式。但ng1路由器中默认使用的则是hash模式的路由形式。所以,咱们首先要作的,就是要让它们的模式设置为一致。
hash和history模式各有利弊,它们之间的区别能够参考附录:LocationStrategy 以及浏览器 URL 样式。笔者更推荐使用history模式,由于他更符合浏览器的URL风格。
下面介绍一下它们各自的设置方法:gulp

history模式:

若是使用history模式,那么你主要修改ng1的路由器配置。bootstrap

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中设置了任何的前缀,你须要将其前缀去掉。bash

angular.
  module('phonecatApp').
  config(['$locationProvider',
    function config($locationProvider) {
      $locationProvider.hashPrefix('') 
    }
  ]);
复制代码

3、凹槽路由

在实际的项目中,有些页面是基于ng1写的,有些页面是基于ngx写的。在多数状况下,咱们天然是但愿能各自显示页面相互不被打扰。所以,咱们须要作一个“凹槽路由”让它们能自由切换。(文段参考 2)
那么。凹槽路由到底是什么?简单而言就是“空页面”。在显示ng1的页面时,ngx部分显示一个空白的页面不就能够了?同理的,在显示ngx的页面,咱们进行相应的操做。
在ng1中实现比较简单:markdown

angular.
  module('phonecatApp').
 config(['$routeProvider',
    function config($routeProvider) {
      $routeProvider.
        ..., // 此处省略其余路由
        otherwise({
          template: ''
        });
    }
  ]);
复制代码

在ngx中,你须要添加一个"空"的Componentapp

ng g component empty
复制代码

而后,在路由中添加

const routes: Routes = [
  ...,
  {
    path: '**',
    component: EmptyComponent
  }
]
复制代码

ALL DONE...

4、同步你的路由状态

如今,就让它们的状态同步吧。
在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…]

参考

  1. 从 AngularJS 升级到 Angular
  2. 升级 AngularJS 至 Angular

License

本做品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。