迈向angularjs2系列(6):路由机制
目录
1.angular-seed的路由
2.路由机制的探索
3.懒加载
一:angular-seed的路由
step1:安装种子项目

$ git clone --depth 1 https://github.com/AngularClass/angular2-seed.git developer-hub
//git cmd进入到项目目录后运行此命令,我的项目的名称为developer-hub
$ cd developer-hub //webstorm命令行进入项目目录
$ npm install //json文件修改好之后再运行install命令
$ npm start
"compilerOptions": { "lib": ["es6", "dom"] }

● dist放最后生产用的文件
● node-modules放node模块
● src 源代码
● .gitignore文件是git用于配置不需要加入版本管理的文件,比如最后两行是
.awcache
.DS_Store
●LICENSE,我的是
Apache License
Version 2.0, January 2004
●package.json
{ "name": "angular2-seed", "version": "0.0.0", "description": "A simple Angular 2 Seed featuring Angular 2 and Webpack 2", "author": "PatrickJS <github@gdi2290.com>",//作者 "main": "index.js", //主入口 "files": [ "dist", "src" ], "scripts": {//npm脚本 "test": "echo \"Error: no test specified\" && exit 1", "start": "npm run server:dev", "start:hmr": "npm run server:dev -- --env.HMR", "server:dev": "webpack-dev-server --env.ENV development", "debug:start": "node-nightly --inspect --debug-brk node_modules/webpack-dev-server/bin/webpack-dev-server.js --env.ENV development", "debug:build": "node-nightly --inspect --debug-brk node_modules/webpack/bin/webpack.js --env.ENV development", "predll": "rimraf dist/dll/*.*", "dll": "webpack --config webpack.dll.ts --env.ENV development", "prebuild": "rimraf dist/*.*", "build": "webpack --env.ENV development", "build:hmr": "npm run build -- --env.HMR" }, "dependencies": { //项目 "@angular/common": "~2.0.1", "@angular/compiler": "~2.0.1", "@angular/compiler-cli": "~0.6.3", "@angular/core": "~2.0.1", "@angular/http": "~2.0.1", "@angular/forms": "~2.0.1", "@angular/router": "~3.0.1", "@angular/platform-browser": "~2.0.1", "@angular/platform-browser-dynamic": "~2.0.1", "@angular/platform-server": "~2.0.1", "@angularclass/conventions-loader": "^1.0.12", "@angularclass/form-validators": "^1.0.11", "@angularclass/resolve-angular-routes": "^1.0.8", "@angularclass/hmr-loader": "~3.0.1", "@angularclass/hmr": "~1.2.0", "core-js": "^2.4.1", "rxjs": "5.0.0-beta.12", "zone.js": "~0.6.25" }, "devDependencies": { //dev项目依赖 "@types/core-js": "^0.9.28", "@types/node": "^4.0.30", "assets-webpack-plugin": "^3.4.0", "awesome-typescript-loader": "^2.2.1", "cross-spawn": "^4.0.0", "es6-promise": "^3.1.2", "es6-shim": "^0.35.0", "ie-shim": "^0.1.0", "ignore-loader": "^0.1.1", "json-loader": "^0.5.4", "raw-loader": "^0.5.1", "rimraf": "^2.5.4", "string-replace-loader": "github:gdi2290/string-replace-loader", "to-string-loader": "^1.1.4", "ts-helpers": "github:gdi2290/ts-helpers", "ts-loader": "^0.8.2", "ts-node": "^1.2.2", "typescript": "2.0.2", "webpack": "~2.1.0-beta.25", "webpack-dev-middleware": "^1.6.1", "webpack-dev-server": "^2.1.0-beta.8" }, "license": "Apache-2.0", //许可证 "bugs": { "url": "https://github.com/gdi2290/angular2-webpack2-starter/issues" }, "homepage": "https://github.com/gdi2290/angular2-webpack2-starter#readme", //主页 "repository": { //仓库 "type": "git", "url": "git+https://github.com/gdi2290/angular2-webpack2-starter.git" } }
说明1:main选项是主入口模块的ID
说明2:files选项是项目包含的两个目录。
说明3:scripts选项是npm script(npm脚本)
scripts也是对象,它的属性包含了很多,比如 "start": "npm run server:dev", 的意思是start命令对应的脚本是npm run server:dev。
说明4:dependencies选项是项目依赖。
比如@angular之类的依赖可以在node_modules找到对应的目录

说明5:devDependencies是项目开发依赖,跟开发环境、调试打包有关咯。
第二看src源代码目录了。

●app目录
—about组件
—home组件
app.html文件
●main.browser.ts文件负责使用根模块(NgModule)去启动应用。
●index.html是src目录下默认打开的文件。里面做了app组件的使用和一些文件的引入。可以测试一下,先把原文内容注释掉,等测试完毕之后再放开。
index.html我的内容:
<!DOCTYPE html> <html 🆖> <head> <title>Angular 2 Webpack 2 starter by @AngularClass</title> <base href="/"> </head> <body> <div style="color:red;">冰雪奇缘</div> </body> </html>
打开localhost:3000,那么显示如下

OK!记得再退回到之间的内容里哦。
●dll.ts文件
// Polyfills就像验光师为近视患者配的眼镜,它可以让浏览器支持还不能支持的功能 export function polyfills(env?: any) { return [ // 'ie-shim', 'core-js/es6/symbol', 'core-js/es6/object', 'core-js/es6/function', 'core-js/es6/parse-int', 'core-js/es6/parse-float', 'core-js/es6/number', 'core-js/es6/math', 'core-js/es6/string', 'core-js/es6/date', 'core-js/es6/array', 'core-js/es6/regexp', 'core-js/es6/map', 'core-js/es6/set', 'core-js/es6/weak-map', 'core-js/es6/weak-set', 'core-js/es6/typed', 'core-js/es6/reflect', // 'core-js/es6/promise', // problem with firefox 'core-js/es7/reflect', // zone.js 'zone.js/dist/zone', 'zone.js/dist/long-stack-trace-zone', // typescript helpers 'ts-helpers', ]; } // Angular 2 and other Vendor imports //本意是供应商,提供angular2或者其他导入 export function vendors(env?: any) { return [ '@angular/platform-browser', '@angular/platform-browser-dynamic', '@angular/compiler', '@angular/router', '@angular/forms', '@angular/common', '@angular/core', '@angular/http', '@angularclass/form-validators', '@angularclass/hmr', ]; } // RxJS是javascript的扩展 export function rxjs(env?: any) { return [ 'rxjs/Observable', 'rxjs/Subscription', 'rxjs/Subject', 'rxjs/BehaviorSubject', 'rxjs/add/operator/map', 'rxjs/add/operator/mergeMap', 'rxjs/add/operator/distinctUntilChanged', ]; }
打开localhost:3000,查看源代码可以看到像下面的脚本引入,就是他们咯

●custom-typings.d.ts
使用typings启动只能提示,这里是可以定制的。
step3:创建一个contact组件
以此熟悉一下源代码的单页逻辑结构。app目录下新建contact目录:

我直接把+about目录删掉,接下来填充如下代码。
app/app.html:
<div> <h1>Hello Angular 2 and Webpack 2</h1> <a href="#" routerLink="">Home</a> <a href="#" routerLink="contact">Contact</a> <!--contact路由的跳转链接,也就是路由使用咯--> </div> <main> <router-outlet></router-outlet> <!--这里是组件内容投掷的地方--> </main> <!-- <footer> <pre>appStore = {{ appStore.getState() | json }}</pre> AngularClass </footer> 我不喜欢这块代码,直接删掉咯。 -->
app/index.ts:
mport { RouterModule } from '@angular/router';
import { NgModule } from '@angular/core';
import HomeModule from './home';
export const ROUTER_CONFIG = [
{ path: '', loadChildren: () => HomeModule },
{ path: 'contact', loadChildren: () => System.import('./contact')},
//组件代码外的路由配置
];
@NgModule({
providers: [
],
declarations: [
// Components / Directives/ Pipes
],
imports: [
RouterModule.forChild(ROUTER_CONFIG),
],
})
export default class AppModule {
static routes = ROUTER_CONFIG;
}
组件app/contact/index.ts:
import { CommonModule } from '@angular/common'
import { RouterModule } from '@angular/router';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { ANGULARCLASS_FORM_VALIDATOR_DIRECTIVES } from '@angularclass/form-validators';
import { Contact } from './contact';
//引入contact.ts的模块
export const ROUTER_CONFIG = [
{ path: '', component: Contact, pathMatch: 'full' }
];
@NgModule({
declarations: [
// Components / Directives/ Pipes
Contact,
//模块定义Contact
...ANGULARCLASS_FORM_VALIDATOR_DIRECTIVES
],
imports: [
RouterModule.forChild(ROUTER_CONFIG),
FormsModule,
CommonModule
]
})
export default class ContactModule {
static routes = ROUTER_CONFIG;
}
组件app/contact/contact.ts:
import { Component } from '@angular/core';
@Component({
templateUrl:"./contact.html"
})
export class Contact {
localState = {
email: ''
};
constructor() {
}
}
app/contact/contact.html:
<div>我是contact页面</div>
最终可以看到contact路由的显示结果

二: 探索angular2的路由
这里的构建系统使用另一个脚手架。
1.搭建脚手架
git clone https://github.com/mgechev/switching-to-angular2.git router-steps
npm install
npm start
非常顺利,它直接打开浏览器可以看到结果

把多余的代码删掉,只剩下目录。如图。

打开,http://localhost:5555/dist/dev/ch6/ts/step-2/可以看到浏览器的结果

接下来关注step-1的内容,其目录为

2.启动应用的根组件app.ts
除了定义组件的Component、启动应用的bootstrap函数、本组件的其他内容,还引入了路由和路径策略。
import {APP_BASE_HREF, LocationStrategy, HashLocationStrategy} from '@angular/common';
//LocationStrategy是一个抽象类,定义了基于哈希HashLocationStrategy的路由和基于
//HTML5的路由
//HashLocationStrategy不支持服务端渲染
import {Route, Redirect, ROUTER_DIRECTIVES, ROUTER_PROVIDERS, RouteConfig} from '@angular/router-deprecated';
//ROUTER_PROVIDERS包含了路由的一组PROVIDER
//RouteConfig是一个装饰器
//Route用来定义单独的路由
//用来定义转发规则,从而定义路由的层级结构,所以ng2支持嵌套路由
之后的app.ts使用component装饰器定义组件,并使用bootstrap启动应用。
bootstrap(App, [ ROUTER_PROVIDERS, { provide: LocationStrategy, useClass: HashLocationStrategy } ]); //第二个参数是provider列表,共整个APP访问 //名称为LocationStrategy的provider,设置为Hash策略,默认值是基于HTML5的策略
基于HTML5的PathLocationStrategy必须提供APP_BASE_HREF。
3.使用@RouteConfig配置路由
@RouteConfig([ new Route({ component: Home, name: 'Home', path: '/' }), new Route({ component: AddDeveloper, name: 'AddDeveloper', path: '/dev-add' }), // new Route({ component: DeveloperDetails, name: 'DeveloperDetails', path: '/dev-details/:id/...' }), new Redirect({ path: '/add-dev', redirectTo: ['/dev-add'] }) //当用户打开/add-dev时,会被转发到dev-add ]) class App {}
@RouteConfig装饰器接收路由数组作为参数。有两种类型的路由,Route和Redirect。
Route必须定义3个属性。
●component,路由关联的是哪个组件
●name,路由名称,模板使用它。
●path ,路径,浏览器地址栏使用它。
转发器只有两个属性
●path需要被转发的路径。
●redirectTo目标路径
4.routerLink和router-outlet的用法
ch6/ts/step-1/app.ts模板代码:
<nav class="navbar navbar-default"> <ul class="nav navbar-nav"> <li><a [routerLink]="['/Home']">Home</a></li> <li><a [routerLink]="['/AddDeveloper']">Add developer</a></li> </ul> </nav> <router-outlet></router-outlet>
●routerLink用来给指定路由添加链接。接收数组,包含名称和值。
●routeroutlet用来定义路由的渲染容器。
那么打开http://localhost:5555/dist/dev/ch6/ts/step-1/#/dev-add,可看到浏览器结果。可跳转的哦。

总体上分为3个部分,boostrap启动的provider列表、路由配置和路由的使用。
三: AsyncRoute懒加载
好棒呢,轻松实现懒加载。
只有一步: 引入AsyncRoute类
替换掉@RouteConfig里面的Route就行了。
@RouteConfig([ new AsyncRoute({ loader: () => System.import('./home') .then(m => m.Home), name: 'Home', path: '/' }), new AsyncRoute({ loader: () => System.import('./add_developer') .then(m => m.AddDeveloper), name: 'AddDeveloper', path: '/dev-add' }), // new Route({ component: DeveloperDetails, name: 'DeveloperDetails', path: '/dev-details/:id/...' }), new Redirect({ path: '/add-dev', redirectTo: ['/dev-add'] }) ])
完整的代码是
import {Component} from '@angular/core';
import {bootstrap} from '@angular/platform-browser-dynamic';
import {APP_BASE_HREF, LocationStrategy, HashLocationStrategy} from '@angular/common';
import {AsyncRoute, Redirect, ROUTER_DIRECTIVES, ROUTER_PROVIDERS, RouteConfig} from '@angular/router-deprecated';
import {DeveloperCollection} from './developer_collection';
import {Developer} from './developer';
@Component({
selector: 'app',
template: `
<nav class="navbar navbar-default">
<ul class="nav navbar-nav">
<li><a [routerLink]="['/Home']">Home</a></li>
<li><a [routerLink]="['/AddDeveloper']">Add developer</a></li>
</ul>
</nav>
<router-outlet></router-outlet>
`,
providers: [DeveloperCollection],
directives: [ROUTER_DIRECTIVES]
})
@RouteConfig([
new AsyncRoute({
loader: () =>
System.import('./home')
.then(m => m.Home),
name: 'Home',
path: '/'
}),
new AsyncRoute({
loader: () =>
System.import('./add_developer')
.then(m => m.AddDeveloper),
name: 'AddDeveloper',
path: '/dev-add'
}),
// new Route({ component: DeveloperDetails, name: 'DeveloperDetails', path: '/dev-details/:id/...' }),
new Redirect({ path: '/add-dev', redirectTo: ['/dev-add'] })
])
class App {}
bootstrap(App, [
ROUTER_PROVIDERS,
{ provide: LocationStrategy, useClass: HashLocationStrategy }
]);
AsyncRoute类构造函数接受一个对象作为参数
●loader:一个函数,返回一个需要兑现的promise。
●name:路由的名字
●path路由所对应的请求路径。
注意:这里使用的System类,只是其中一种方式而已,使用require.js也能实现相同的功能。
到底是不是懒加载,我们可以查看一下。
打开http://localhost:5555/dist/dev/ch6/ts/step-1-async网址,375个请求,并没有把所有路由组件都加载了

点击另外一个路由,这时375个请求变成了376个请求,也就是第二个路由。
![]()

浙公网安备 33010602011771号