Skip to content

Commit e1a7e03

Browse files
committed
feat(router): hash-cons ComponentInstructions
1 parent 76e1f86 commit e1a7e03

3 files changed

Lines changed: 41 additions & 7 deletions

File tree

‎modules/angular2/src/router/instruction.ts‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,12 @@ function stringifyAux(instruction: Instruction): string {
8383
*
8484
* `ComponentInstructions` is a public API. Instances of `ComponentInstruction` are passed
8585
* to route lifecycle hooks, like {@link CanActivate}.
86+
*
87+
* `ComponentInstruction`s are [https://en.wikipedia.org/wiki/Hash_consing](hash consed). You should
88+
* never construct one yourself with "new." Instead, rely on {@link PathRecognizer} to construct
89+
* `ComponentInstruction`s.
90+
*
91+
* You should not modify this object. It should be treated as immutable.
8692
*/
8793
export class ComponentInstruction {
8894
reuse: boolean = false;

‎modules/angular2/src/router/path_recognizer.ts‎

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,8 @@ export class PathRecognizer {
192192
specificity: number;
193193
terminal: boolean = true;
194194
hash: string;
195+
private cache: Map<string, ComponentInstruction> = new Map<string, ComponentInstruction>();
196+
195197

196198
// TODO: cache component instruction instances by params and by ParsedUrl instance
197199

@@ -252,23 +254,26 @@ export class PathRecognizer {
252254

253255
var auxiliary;
254256
var instruction: ComponentInstruction;
257+
var urlParams;
258+
var allParams;
255259
if (isPresent(currentSegment)) {
256260
// If this is the root component, read query params. Otherwise, read matrix params.
257261
var paramsSegment = beginningSegment instanceof RootUrl ? beginningSegment : currentSegment;
258262

259-
var allParams = isPresent(paramsSegment.params) ?
260-
StringMapWrapper.merge(paramsSegment.params, positionalParams) :
261-
positionalParams;
263+
allParams = isPresent(paramsSegment.params) ?
264+
StringMapWrapper.merge(paramsSegment.params, positionalParams) :
265+
positionalParams;
262266

263-
var urlParams = serializeParams(paramsSegment.params);
267+
urlParams = serializeParams(paramsSegment.params);
264268

265-
instruction = new ComponentInstruction(urlPath, urlParams, this, allParams);
266269

267270
auxiliary = currentSegment.auxiliary;
268271
} else {
269-
instruction = new ComponentInstruction(urlPath, [], this, positionalParams);
272+
allParams = positionalParams;
270273
auxiliary = [];
274+
urlParams = [];
271275
}
276+
instruction = this._getInstruction(urlPath, urlParams, this, allParams);
272277
return new PathMatch(instruction, nextSegment, auxiliary);
273278
}
274279

@@ -289,6 +294,18 @@ export class PathRecognizer {
289294
var nonPositionalParams = paramTokens.getUnused();
290295
var urlParams = serializeParams(nonPositionalParams);
291296

292-
return new ComponentInstruction(urlPath, urlParams, this, params);
297+
return this._getInstruction(urlPath, urlParams, this, params);
298+
}
299+
300+
private _getInstruction(urlPath: string, urlParams: List<string>, _recognizer: PathRecognizer,
301+
params: StringMap<string, any>): ComponentInstruction {
302+
var hashKey = urlPath + '?' + urlParams.join('?');
303+
if (this.cache.has(hashKey)) {
304+
return this.cache.get(hashKey);
305+
}
306+
var instruction = new ComponentInstruction(urlPath, urlParams, _recognizer, params);
307+
this.cache.set(hashKey, instruction);
308+
309+
return instruction;
293310
}
294311
}

‎modules/angular2/test/router/path_recognizer_spec.ts‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,17 @@ export function main() {
4040
.toThrowError(`Path "hi//there" contains "//" which is not allowed in a route config.`);
4141
});
4242

43+
it('should return the same instruction instance when recognizing the same path', () => {
44+
var rec = new PathRecognizer('/one', mockRouteHandler);
45+
46+
var one = new Url('one', null, null, {});
47+
48+
var firstMatch = rec.recognize(one);
49+
var secondMatch = rec.recognize(one);
50+
51+
expect(firstMatch.instruction).toBe(secondMatch.instruction);
52+
});
53+
4354
describe('querystring params', () => {
4455
it('should parse querystring params so long as the recognizer is a root', () => {
4556
var rec = new PathRecognizer('/hello/there', mockRouteHandler);

0 commit comments

Comments
 (0)