Angular2 RC6 Route学习

前言

  Angularjs2终于最新的week Conf没有显示会出现RC7,也就意味着下次就是stable了,不会有break changes了。闲话少说。 路由的功能在SPA应用的地位无可置疑,angularjs1的ng-route由于难以实现多层路由而基本被ui-route取代,angular2则改变了这一现状,开始支持嵌套路由,还设置了UrlTree。我们一起来看一下吧。(PS.以下内容basehref默认’\’,例子笔者尽量选择官方例子)
  首先,要能知道我们先看什么后看文件,去github上看一下,我们大致分下类:
  首先就是index.ts文件,作为@angular/router 的入口文件,里面提供了好多router内的export,不过都是选择性的一些export类型和方法,当然也会包含RouterModule的部分类型和方法。笔者认为基本上index.ts就是router的全部内容。
  其次,核心的,也就是import也很多的route_module.ts文件,看名字就是知道,封装成RouterModule让我们用的。其中提供了一堆的const,咱们只看export的,有ROUTER_CONFIGURATION及ROUTER_FORROOT_GUARD以及ROUTER_PROVIDERS: Provider[]和集大成者class RouterModuleclass RouterModule 其constructor(@Optional() @Inject(ROUTER_FORROOT_GUARD) guard: any) {} 只有一个装饰器。module有两个重要的静态方法static forRoot(routes: Routes, config?: ExtraOptions): ModuleWithProvidersstatic forChild(routes: Routes): ModuleWithProviders 。这两个方法都返回ModulewithProviders,结果就是返回一个选定的ngModule和一个注射器providers[],来对构造函数的装饰器进行注射实例化。providers数组里面不是useFactory用的就是我们用的。剩下就是一些optional方法让我们继续深度配置router,这段话有点逻辑混乱。整理来说route_module.ts文件,通过构造函数里面的一个类symbol类装饰器来提供给我们一堆的依赖注入(providers[]),结果就是实例化一个RouterModule时,选择forRoot和forChildern都会返回一堆依赖关系实例,这一大堆实例中我们就举其中一个例子,让大家感受下这个RouterModule里的依赖关系的多和复杂:forRoot中provider了一个ROUTER_PROVIDERS,这是一个provider[],其中又provider了一个Router,然后这个Router是使用setupRouter进行实例化(useFactory),其中又依赖了deps:[ApplicationRef,UrlSerializer,RouterOutletMap,Location,Injector,NgModuleFactoryLoader,Compiler, ROUTES, ROUTER_CONFIGURATION],最终得到了一个Router类的实例。更不用说多重provide(multi: true在forRoot与forChildren中都有的provideRoutes(routes: Routes) 中使用)以及@Self(或@SkipSelf)的使用了。不管怎么样,这一个Module基本就是我们angular2的路由精华所在了,要什么有什么。看不懂没关系,回头再看源代码就感觉RouterModule就是个纸老虎,真正的内容都是它import的内容。
  紧随这两个文件之后,也是这两个文件都import的router.ts,是路由的真实核心文件,config.ts是实用文件。后者为前者的一个构造函数属性。
  在之后就是基础,我称之为材料文件的:router_state.ts,interface.ts,shared.ts,url_tree.ts
  最后就是我称之为工具文件的:directives文件夹(里面包含三个指令)及utils文件夹(一个collection一个tree),create_router_state.ts,create_url_tree.ts,router_config_loader.ts,router_outlet_map.ts,apply_redirects.ts和private_export.ts

我们自上而下来看,先看最简单的config.ts文件,其核心就是routes(或route)。

Routes

  
  Routes-an array of route configurations-Route[]    
  

  我们通过import { Routes } from ' @angular/router';来导入angular2为我们提供的路由进行我们的路由配置,首先我们看一下Routes为我们准备了什么:
  path* is a string that uses the route matcher DSL.
   path也就是我们的URL,可选属性有”、’**’、’team:id’,但不允许’\’。其中由于”是所有url的前缀(见pathMatch),在路由配置时要注意
  
  pathMatch is a string that specifies the matching strategy.
   这个匹配策略属性一般被设置为full用来匹配”的情况,与redirectTo一起(仅能)使用来进行默认地址(即’\’)的设置,还有一个属性为prefix,前缀匹配(默认)。
  
  component is a component type.
   选择进行渲染的组件,有父组件、子组件和主组件、辅组件(见outlet)
  
  redirectTo is the url fragment which will replace the current matched segment.
   重定向,如果属性字符串前面有’\’如’\user\23’则表示绝对地址。
  
  outlet is the name of the outlet the component should be placed into.
   相当于Angularjs1中的ng-view,组件渲染的目的地址,可选属性:’aux’,即辅助outlet,在设置此属性时要配置primary和aux outlet,此时可称为无父组件路由,达到了兄弟组件间分享路由参数的目的
  
  canActivate is an array of DI tokens used to look up CanActivate handlers.
  canActivateChild is an array of DI tokens used to look up CanActivateChild handlers.
  canDeactivate is an array of DI tokens used to look up CanDeactivate handlers.
  
  data is additional data provided to the component via ActivatedRoute.
   字符串数组,提供给相应的渲染组件
  
  resolve is a map of DI tokens used to look up data resolvers.
   字符串数组,将data解析
  
  children is an array of child route definitions.Route[]数组,显而易见,children可以进行层层嵌套

因为Routes是Route[],我们通过interface Route小温习一下:

    export interface Route {
      path?: string;
      pathMatch?: string;
      component?: Type;
      redirectTo?: string;
      outlet?: string;
      canActivate?: any[];
      canActivateChild?: any[];
      canDeactivate?: any[];
      canLoad?: any[];
      data?: Data;
      resolve?: ResolveData;
      children?: Route[];
      loadChildren?: LoadChildren;
    }
注:a Type is a MyCustomComponent class

举个小例子:

 [{
   path: 'team/:id',
   component: Team,
   children: [
     {
       path: '',
       component: WrapperCmp,
       children: [
         {
           path: 'user/:name',
           component: User
         }
       ]
     }
   ]
 }]

当我们访问 /team/11/user/jim, 路由就会实例化wrapper component with the user component in it.

结束了以上基础,我们基本可以深入angular2的Route去了。

继续Routes

首先,在上面的行程中,我们有几个概念或者说是疑惑:
-data、resolve到底在component中怎么使用的
-canActivate与其他相似的那几个是什么,作用如何
-canLoad与loadChildren是什么,作用如何  

让我们前往router/router_state.ts和router/interfaces.ts以及router.ts开始探索吧
  
  注:其实没有import interfaces.ts里面的那几个函数,我们就看看同名接口的定义,主要还是router.ts和route_state.ts里面的类及方法,下段可以跳过。  
  
首先是interface里面的定义:
interfaces.ts文件: import {Route} from './config';
import {ActivatedRouteSnapshot, RouterStateSnapshot} from './router_state';
没有工具文件,只import了这两个材料文件。

export interface CanActivate {
    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):
    Observable|Promise|boolean;
}

export interface CanActivateChild {
    canActivateChild(childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot):
    Observable|Promise|boolean;
}
export interface CanDeactivate {
    canDeactivate(component: T, route: ActivatedRouteSnapshot, state: RouterStateSnapshot):
    Observable|Promise|boolean;
}

export interface Resolve {
    resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):
    Observable|Promise|any;
}

export interface CanLoad { canLoad(route: Route): Observable|Promise|boolean; }
都说了上面的内容都是废话,你还看~native~simple~~~~~

关于LoadChildren要多说几句,从下面代码可以看出来,LoadChildren不仅表现为一个字符串,而且还可以表现为Type,这就有意思了,我上文提到过,一个Type就是一个component类的class。这样我们可以通过LoadChildren来导入子路由,而不是在一个route文件里把所有的父子路由都表示出来(充满了children的层层嵌套),只找到了LoadChildren的type定义。在config.ts文件中,对Data,ResolveData,LoadChildren及LoadChildrenCallBack进行了类型定义,供其他文件进行引用。

LoadChildren:

    export type LoadChildren = string | LoadChildrenCallback;
    export type LoadChildrenCallback = () => Type| Promise>| Observable>;

然后是route_state.ts中的定义:
route_state.ts文件:import {Data, ResolveData, Route} from './config';import {PRIMARY_OUTLET, Params} from './shared';除了工具文件就引用了这两个材料文件。

  Contains the information about a component loaded in an outlet at a particular moment in time.即包含已在一个outlet中渲染的组件的某一时刻信息:  

export class ActivatedRouteSnapshot {
  _routeConfig: Route;
  _urlSegment: UrlSegmentGroup;
  _lastPathIndex: number;
  _resolve: InheritedResolve;
  _routerState: RouterStateSnapshot;
  constructor(
      public url: UrlSegment[], public params: Params, public queryParams: Params,
      public fragment: string, public data: Data, public outlet: string,
      public component: Type|string, routeConfig: Route, urlSegment: UrlSegmentGroup,
      lastPathIndex: number, resolve: InheritedResolve) {
    this._routeConfig = routeConfig;
    this._urlSegment = urlSegment;
    this._lastPathIndex = lastPathIndex;
    this._resolve = resolve;
  } 
  get routeConfig(): Route { return this._routeConfig; }    
  get root(): ActivatedRouteSnapshot { return this._routerState.root; } 
  get parent(): ActivatedRouteSnapshot { return this._routerState.parent(this); }   
  get firstChild(): ActivatedRouteSnapshot { return this._routerState.firstChild(this); }   
  get children(): ActivatedRouteSnapshot[] { return this._routerState.children(this); } 
  get pathFromRoot(): ActivatedRouteSnapshot[] { return this._routerState.pathFromRoot(this); }
  toString(): string {
    const url = this.url.map(s => s.toString()).join('/');
    const matched = this._routeConfig ? this._routeConfig.path : '';
    return `Route(url:'${url}', path:'${matched}')`;
  }
}

  The state of the router at a particular moment in time.即Router某个时刻的状态,由下可知,就是组件信息树:

 export class RouterStateSnapshot extends Tree {
    constructor(public url: string, root: TreeNode) {
    super(root);
    setRouterStateSnapshot(this, root);
  }
  toString(): string { return serializeNode(this._root); }
}
function setRouterStateSnapshot(state: U, node: TreeNode): void {
  node.value._routerState = state;
  node.children.forEach(c => setRouterStateSnapshot(state, c));
}   
function serializeNode(node: TreeNode): string {
  const c = node.children.length > 0 ? ` { ${node.children.map(serializeNode).join(", ")} } ` : '';
  return `${node.value}${c};

  基本就是ActiveRouted类和RouterState类的翻版,这四个类两两基本可以等效使用(不是等价)。
  
  接下来是router.ts文件中的声明:

const resolveData$ = mergeMap.call(preactivation2$, (shouldActivate: boolean) => {
    if (shouldActivate) {
return map.call(preActivation.resolveData(), () => shouldActivate);
    } else {return of (shouldActivate);
    }});

class CanActivate {
  constructor(public path: ActivatedRouteSnapshot[]) {}
  get route(): ActivatedRouteSnapshot { return this.path[this.path.length - 1]; }
}

class CanDeactivate {
  constructor(public component: Object, public route: ActivatedRouteSnapshot) {}
}

  在Route构造函数中,canActivate是任意类型的。canActivate中文官网被翻译为路由守卫,功能是对路由跳转进行权限限制,通俗来讲就是没登录你就不能去这个地址,没相应等级授权你也不许去。
  

走向router.ts

  我们终于仓促的看完一部分重要文件,出于现在有点小开心,就直接来看router.ts这大boss吧。
  前提import {ActivatedRoute, ActivatedRouteSnapshot, RouterState, RouterStateSnapshot, advanceActivatedRoute, createEmptyState} from './router_state';import {NavigationCancelingError, PRIMARY_OUTLET, Params} from './shared'; 除了工具文件,就引用了这两个材料文件。
  首先我们要明确,这个文件能够提供给我们最大的用处是哪里:
  今日上午先写到这,要休息一下,一会再填坑~~  
    
      
        
        

笔者能力有限,如有错误及遗漏请各位批评指正!

你可能感兴趣的:(Angular2)