Vue--vue-Router
一.vue路由的基本使用
为什么需要路由?
因为我们通过component切换组件无法给组件传递参数
component切换组件
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <meta http-equiv="X-UA-Compatible" content="ie=edge"> 7 <title>Document</title> 8 <script src="../vue2.4.4.js"></script> 9 </head> 10 11 <body> 12 <!-- 定义一个vue的管理区块,(MVVM中的View) --> 13 <template id="account"> 14 <div> 15 <a href="#" @click="componentId='login'">登录</a> 16 <a href="#" @click="componentId='register'">注册</a> 17 <!-- :is 相当于给component绑定组件,绑定is后面的值对应的组件 --> 18 <component :is="componentId"></component> 19 </div> 20 </template> 21 <div id="app"> 22 <account></account> 23 </div> 24 </body> 25 <script> 26 Vue.component("account",{ 27 template:"#account", 28 // 在父组件中添加一个componentId的属性,将来给上面模板中的component使用 29 data:function() { 30 return { 31 componentId:"login" 32 } 33 }, 34 // methods:{ 35 // register:function() { 36 // this.componentId = "register"; 37 // } 38 // }, 39 components:{ 40 "login":{ 41 template:"<span>login</span>" 42 }, 43 "register":{ 44 template:"<span>register</span>" 45 } 46 } 47 }); 48 // 实例化vue对象(MVVM中的View Model) 49 new Vue({ 50 // vm控制的区块为id为app的div,此div中的所有vue指令均可以被vm解析 51 el:'#app', 52 data:{ 53 // 数据 (MVVM中的Model) 54 }, 55 methods:{ 56 } 57 }) 58 </script> 59 </html>
使用vue路由需要vue-router.js
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * vue-router v3.0.1 3 * (c) 2017 Evan You 4 * @license MIT 5 */ 6 (function (global, factory) { 7 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 8 typeof define === 'function' && define.amd ? define(factory) : 9 (global.VueRouter = factory()); 10 }(this, (function () { 'use strict'; 11 12 /* */ 13 14 function assert (condition, message) { 15 if (!condition) { 16 throw new Error(("[vue-router] " + message)) 17 } 18 } 19 20 function warn (condition, message) { 21 if ("development" !== 'production' && !condition) { 22 typeof console !== 'undefined' && console.warn(("[vue-router] " + message)); 23 } 24 } 25 26 function isError (err) { 27 return Object.prototype.toString.call(err).indexOf('Error') > -1 28 } 29 30 var View = { 31 name: 'router-view', 32 functional: true, 33 props: { 34 name: { 35 type: String, 36 default: 'default' 37 } 38 }, 39 render: function render (_, ref) { 40 var props = ref.props; 41 var children = ref.children; 42 var parent = ref.parent; 43 var data = ref.data; 44 45 data.routerView = true; 46 47 // directly use parent context's createElement() function 48 // so that components rendered by router-view can resolve named slots 49 var h = parent.$createElement; 50 var name = props.name; 51 var route = parent.$route; 52 var cache = parent._routerViewCache || (parent._routerViewCache = {}); 53 54 // determine current view depth, also check to see if the tree 55 // has been toggled inactive but kept-alive. 56 var depth = 0; 57 var inactive = false; 58 while (parent && parent._routerRoot !== parent) { 59 if (parent.$vnode && parent.$vnode.data.routerView) { 60 depth++; 61 } 62 if (parent._inactive) { 63 inactive = true; 64 } 65 parent = parent.$parent; 66 } 67 data.routerViewDepth = depth; 68 69 // render previous view if the tree is inactive and kept-alive 70 if (inactive) { 71 return h(cache[name], data, children) 72 } 73 74 var matched = route.matched[depth]; 75 // render empty node if no matched route 76 if (!matched) { 77 cache[name] = null; 78 return h() 79 } 80 81 var component = cache[name] = matched.components[name]; 82 83 // attach instance registration hook 84 // this will be called in the instance's injected lifecycle hooks 85 data.registerRouteInstance = function (vm, val) { 86 // val could be undefined for unregistration 87 var current = matched.instances[name]; 88 if ( 89 (val && current !== vm) || 90 (!val && current === vm) 91 ) { 92 matched.instances[name] = val; 93 } 94 } 95 96 // also register instance in prepatch hook 97 // in case the same component instance is reused across different routes 98 ;(data.hook || (data.hook = {})).prepatch = function (_, vnode) { 99 matched.instances[name] = vnode.componentInstance; 100 }; 101 102 // resolve props 103 var propsToPass = data.props = resolveProps(route, matched.props && matched.props[name]); 104 if (propsToPass) { 105 // clone to prevent mutation 106 propsToPass = data.props = extend({}, propsToPass); 107 // pass non-declared props as attrs 108 var attrs = data.attrs = data.attrs || {}; 109 for (var key in propsToPass) { 110 if (!component.props || !(key in component.props)) { 111 attrs[key] = propsToPass[key]; 112 delete propsToPass[key]; 113 } 114 } 115 } 116 117 return h(component, data, children) 118 } 119 }; 120 121 function resolveProps (route, config) { 122 switch (typeof config) { 123 case 'undefined': 124 return 125 case 'object': 126 return config 127 case 'function': 128 return config(route) 129 case 'boolean': 130 return config ? route.params : undefined 131 default: 132 { 133 warn( 134 false, 135 "props in \"" + (route.path) + "\" is a " + (typeof config) + ", " + 136 "expecting an object, function or boolean." 137 ); 138 } 139 } 140 } 141 142 function extend (to, from) { 143 for (var key in from) { 144 to[key] = from[key]; 145 } 146 return to 147 } 148 149 /* */ 150 151 var encodeReserveRE = /[!'()*]/g; 152 var encodeReserveReplacer = function (c) { return '%' + c.charCodeAt(0).toString(16); }; 153 var commaRE = /%2C/g; 154 155 // fixed encodeURIComponent which is more conformant to RFC3986: 156 // - escapes [!'()*] 157 // - preserve commas 158 var encode = function (str) { return encodeURIComponent(str) 159 .replace(encodeReserveRE, encodeReserveReplacer) 160 .replace(commaRE, ','); }; 161 162 var decode = decodeURIComponent; 163 164 function resolveQuery ( 165 query, 166 extraQuery, 167 _parseQuery 168 ) { 169 if ( extraQuery === void 0 ) extraQuery = {}; 170 171 var parse = _parseQuery || parseQuery; 172 var parsedQuery; 173 try { 174 parsedQuery = parse(query || ''); 175 } catch (e) { 176 "development" !== 'production' && warn(false, e.message); 177 parsedQuery = {}; 178 } 179 for (var key in extraQuery) { 180 parsedQuery[key] = extraQuery[key]; 181 } 182 return parsedQuery 183 } 184 185 function parseQuery (query) { 186 var res = {}; 187 188 query = query.trim().replace(/^(\?|#|&)/, ''); 189 190 if (!query) { 191 return res 192 } 193 194 query.split('&').forEach(function (param) { 195 var parts = param.replace(/\+/g, ' ').split('='); 196 var key = decode(parts.shift()); 197 var val = parts.length > 0 198 ? decode(parts.join('=')) 199 : null; 200 201 if (res[key] === undefined) { 202 res[key] = val; 203 } else if (Array.isArray(res[key])) { 204 res[key].push(val); 205 } else { 206 res[key] = [res[key], val]; 207 } 208 }); 209 210 return res 211 } 212 213 function stringifyQuery (obj) { 214 var res = obj ? Object.keys(obj).map(function (key) { 215 var val = obj[key]; 216 217 if (val === undefined) { 218 return '' 219 } 220 221 if (val === null) { 222 return encode(key) 223 } 224 225 if (Array.isArray(val)) { 226 var result = []; 227 val.forEach(function (val2) { 228 if (val2 === undefined) { 229 return 230 } 231 if (val2 === null) { 232 result.push(encode(key)); 233 } else { 234 result.push(encode(key) + '=' + encode(val2)); 235 } 236 }); 237 return result.join('&') 238 } 239 240 return encode(key) + '=' + encode(val) 241 }).filter(function (x) { return x.length > 0; }).join('&') : null; 242 return res ? ("?" + res) : '' 243 } 244 245 /* */ 246 247 248 var trailingSlashRE = /\/?$/; 249 250 function createRoute ( 251 record, 252 location, 253 redirectedFrom, 254 router 255 ) { 256 var stringifyQuery$$1 = router && router.options.stringifyQuery; 257 258 var query = location.query || {}; 259 try { 260 query = clone(query); 261 } catch (e) {} 262 263 var route = { 264 name: location.name || (record && record.name), 265 meta: (record && record.meta) || {}, 266 path: location.path || '/', 267 hash: location.hash || '', 268 query: query, 269 params: location.params || {}, 270 fullPath: getFullPath(location, stringifyQuery$$1), 271 matched: record ? formatMatch(record) : [] 272 }; 273 if (redirectedFrom) { 274 route.redirectedFrom = getFullPath(redirectedFrom, stringifyQuery$$1); 275 } 276 return Object.freeze(route) 277 } 278 279 function clone (value) { 280 if (Array.isArray(value)) { 281 return value.map(clone) 282 } else if (value && typeof value === 'object') { 283 var res = {}; 284 for (var key in value) { 285 res[key] = clone(value[key]); 286 } 287 return res 288 } else { 289 return value 290 } 291 } 292 293 // the starting route that represents the initial state 294 var START = createRoute(null, { 295 path: '/' 296 }); 297 298 function formatMatch (record) { 299 var res = []; 300 while (record) { 301 res.unshift(record); 302 record = record.parent; 303 } 304 return res 305 } 306 307 function getFullPath ( 308 ref, 309 _stringifyQuery 310 ) { 311 var path = ref.path; 312 var query = ref.query; if ( query === void 0 ) query = {}; 313 var hash = ref.hash; if ( hash === void 0 ) hash = ''; 314 315 var stringify = _stringifyQuery || stringifyQuery; 316 return (path || '/') + stringify(query) + hash 317 } 318 319 function isSameRoute (a, b) { 320 if (b === START) { 321 return a === b 322 } else if (!b) { 323 return false 324 } else if (a.path && b.path) { 325 return ( 326 a.path.replace(trailingSlashRE, '') === b.path.replace(trailingSlashRE, '') && 327 a.hash === b.hash && 328 isObjectEqual(a.query, b.query) 329 ) 330 } else if (a.name && b.name) { 331 return ( 332 a.name === b.name && 333 a.hash === b.hash && 334 isObjectEqual(a.query, b.query) && 335 isObjectEqual(a.params, b.params) 336 ) 337 } else { 338 return false 339 } 340 } 341 342 function isObjectEqual (a, b) { 343 if ( a === void 0 ) a = {}; 344 if ( b === void 0 ) b = {}; 345 346 // handle null value #1566 347 if (!a || !b) { return a === b } 348 var aKeys = Object.keys(a); 349 var bKeys = Object.keys(b); 350 if (aKeys.length !== bKeys.length) { 351 return false 352 } 353 return aKeys.every(function (key) { 354 var aVal = a[key]; 355 var bVal = b[key]; 356 // check nested equality 357 if (typeof aVal === 'object' && typeof bVal === 'object') { 358 return isObjectEqual(aVal, bVal) 359 } 360 return String(aVal) === String(bVal) 361 }) 362 } 363 364 function isIncludedRoute (current, target) { 365 return ( 366 current.path.replace(trailingSlashRE, '/').indexOf( 367 target.path.replace(trailingSlashRE, '/') 368 ) === 0 && 369 (!target.hash || current.hash === target.hash) && 370 queryIncludes(current.query, target.query) 371 ) 372 } 373 374 function queryIncludes (current, target) { 375 for (var key in target) { 376 if (!(key in current)) { 377 return false 378 } 379 } 380 return true 381 } 382 383 /* */ 384 385 // work around weird flow bug 386 var toTypes = [String, Object]; 387 var eventTypes = [String, Array]; 388 389 var Link = { 390 name: 'router-link', 391 props: { 392 to: { 393 type: toTypes, 394 required: true 395 }, 396 tag: { 397 type: String, 398 default: 'a' 399 }, 400 exact: Boolean, 401 append: Boolean, 402 replace: Boolean, 403 activeClass: String, 404 exactActiveClass: String, 405 event: { 406 type: eventTypes, 407 default: 'click' 408 } 409 }, 410 render: function render (h) { 411 var this$1 = this; 412 413 var router = this.$router; 414 var current = this.$route; 415 var ref = router.resolve(this.to, current, this.append); 416 var location = ref.location; 417 var route = ref.route; 418 var href = ref.href; 419 420 var classes = {}; 421 var globalActiveClass = router.options.linkActiveClass; 422 var globalExactActiveClass = router.options.linkExactActiveClass; 423 // Support global empty active class 424 var activeClassFallback = globalActiveClass == null 425 ? 'router-link-active' 426 : globalActiveClass; 427 var exactActiveClassFallback = globalExactActiveClass == null 428 ? 'router-link-exact-active' 429 : globalExactActiveClass; 430 var activeClass = this.activeClass == null 431 ? activeClassFallback 432 : this.activeClass; 433 var exactActiveClass = this.exactActiveClass == null 434 ? exactActiveClassFallback 435 : this.exactActiveClass; 436 var compareTarget = location.path 437 ? createRoute(null, location, null, router) 438 : route; 439 440 classes[exactActiveClass] = isSameRoute(current, compareTarget); 441 classes[activeClass] = this.exact 442 ? classes[exactActiveClass] 443 : isIncludedRoute(current, compareTarget); 444 445 var handler = function (e) { 446 if (guardEvent(e)) { 447 if (this$1.replace) { 448 router.replace(location); 449 } else { 450 router.push(location); 451 } 452 } 453 }; 454 455 var on = { click: guardEvent }; 456 if (Array.isArray(this.event)) { 457 this.event.forEach(function (e) { on[e] = handler; }); 458 } else { 459 on[this.event] = handler; 460 } 461 462 var data = { 463 class: classes 464 }; 465 466 if (this.tag === 'a') { 467 data.on = on; 468 data.attrs = { href: href }; 469 } else { 470 // find the first <a> child and apply listener and href 471 var a = findAnchor(this.$slots.default); 472 if (a) { 473 // in case the <a> is a static node 474 a.isStatic = false; 475 var extend = _Vue.util.extend; 476 var aData = a.data = extend({}, a.data); 477 aData.on = on; 478 var aAttrs = a.data.attrs = extend({}, a.data.attrs); 479 aAttrs.href = href; 480 } else { 481 // doesn't have <a> child, apply listener to self 482 data.on = on; 483 } 484 } 485 486 return h(this.tag, data, this.$slots.default) 487 } 488 }; 489 490 function guardEvent (e) { 491 // don't redirect with control keys 492 if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey) { return } 493 // don't redirect when preventDefault called 494 if (e.defaultPrevented) { return } 495 // don't redirect on right click 496 if (e.button !== undefined && e.button !== 0) { return } 497 // don't redirect if `target="_blank"` 498 if (e.currentTarget && e.currentTarget.getAttribute) { 499 var target = e.currentTarget.getAttribute('target'); 500 if (/\b_blank\b/i.test(target)) { return } 501 } 502 // this may be a Weex event which doesn't have this method 503 if (e.preventDefault) { 504 e.preventDefault(); 505 } 506 return true 507 } 508 509 function findAnchor (children) { 510 if (children) { 511 var child; 512 for (var i = 0; i < children.length; i++) { 513 child = children[i]; 514 if (child.tag === 'a') { 515 return child 516 } 517 if (child.children && (child = findAnchor(child.children))) { 518 return child 519 } 520 } 521 } 522 } 523 524 var _Vue; 525 526 function install (Vue) { 527 if (install.installed && _Vue === Vue) { return } 528 install.installed = true; 529 530 _Vue = Vue; 531 532 var isDef = function (v) { return v !== undefined; }; 533 534 var registerInstance = function (vm, callVal) { 535 var i = vm.$options._parentVnode; 536 if (isDef(i) && isDef(i = i.data) && isDef(i = i.registerRouteInstance)) { 537 i(vm, callVal); 538 } 539 }; 540 541 Vue.mixin({ 542 beforeCreate: function beforeCreate () { 543 if (isDef(this.$options.router)) { 544 this._routerRoot = this; 545 this._router = this.$options.router; 546 this._router.init(this); 547 Vue.util.defineReactive(this, '_route', this._router.history.current); 548 } else { 549 this._routerRoot = (this.$parent && this.$parent._routerRoot) || this; 550 } 551 registerInstance(this, this); 552 }, 553 destroyed: function destroyed () { 554 registerInstance(this); 555 } 556 }); 557 558 Object.defineProperty(Vue.prototype, '$router', { 559 get: function get () { return this._routerRoot._router } 560 }); 561 562 Object.defineProperty(Vue.prototype, '$route', { 563 get: function get () { return this._routerRoot._route } 564 }); 565 566 Vue.component('router-view', View); 567 Vue.component('router-link', Link); 568 569 var strats = Vue.config.optionMergeStrategies; 570 // use the same hook merging strategy for route hooks 571 strats.beforeRouteEnter = strats.beforeRouteLeave = strats.beforeRouteUpdate = strats.created; 572 } 573 574 /* */ 575 576 var inBrowser = typeof window !== 'undefined'; 577 578 /* */ 579 580 function resolvePath ( 581 relative, 582 base, 583 append 584 ) { 585 var firstChar = relative.charAt(0); 586 if (firstChar === '/') { 587 return relative 588 } 589 590 if (firstChar === '?' || firstChar === '#') { 591 return base + relative 592 } 593 594 var stack = base.split('/'); 595 596 // remove trailing segment if: 597 // - not appending 598 // - appending to trailing slash (last segment is empty) 599 if (!append || !stack[stack.length - 1]) { 600 stack.pop(); 601 } 602 603 // resolve relative path 604 var segments = relative.replace(/^\//, '').split('/'); 605 for (var i = 0; i < segments.length; i++) { 606 var segment = segments[i]; 607 if (segment === '..') { 608 stack.pop(); 609 } else if (segment !== '.') { 610 stack.push(segment); 611 } 612 } 613 614 // ensure leading slash 615 if (stack[0] !== '') { 616 stack.unshift(''); 617 } 618 619 return stack.join('/') 620 } 621 622 function parsePath (path) { 623 var hash = ''; 624 var query = ''; 625 626 var hashIndex = path.indexOf('#'); 627 if (hashIndex >= 0) { 628 hash = path.slice(hashIndex); 629 path = path.slice(0, hashIndex); 630 } 631 632 var queryIndex = path.indexOf('?'); 633 if (queryIndex >= 0) { 634 query = path.slice(queryIndex + 1); 635 path = path.slice(0, queryIndex); 636 } 637 638 return { 639 path: path, 640 query: query, 641 hash: hash 642 } 643 } 644 645 function cleanPath (path) { 646 return path.replace(/\/\//g, '/') 647 } 648 649 var isarray = Array.isArray || function (arr) { 650 return Object.prototype.toString.call(arr) == '[object Array]'; 651 }; 652 653 /** 654 * Expose `pathToRegexp`. 655 */ 656 var pathToRegexp_1 = pathToRegexp; 657 var parse_1 = parse; 658 var compile_1 = compile; 659 var tokensToFunction_1 = tokensToFunction; 660 var tokensToRegExp_1 = tokensToRegExp; 661 662 /** 663 * The main path matching regexp utility. 664 * 665 * @type {RegExp} 666 */ 667 var PATH_REGEXP = new RegExp([ 668 // Match escaped characters that would otherwise appear in future matches. 669 // This allows the user to escape special characters that won't transform. 670 '(\\\\.)', 671 // Match Express-style parameters and un-named parameters with a prefix 672 // and optional suffixes. Matches appear as: 673 // 674 // "/:test(\\d+)?" => ["/", "test", "\d+", undefined, "?", undefined] 675 // "/route(\\d+)" => [undefined, undefined, undefined, "\d+", undefined, undefined] 676 // "/*" => ["/", undefined, undefined, undefined, undefined, "*"] 677 '([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?|(\\*))' 678 ].join('|'), 'g'); 679 680 /** 681 * Parse a string for the raw tokens. 682 * 683 * @param {string} str 684 * @param {Object=} options 685 * @return {!Array} 686 */ 687 function parse (str, options) { 688 var tokens = []; 689 var key = 0; 690 var index = 0; 691 var path = ''; 692 var defaultDelimiter = options && options.delimiter || '/'; 693 var res; 694 695 while ((res = PATH_REGEXP.exec(str)) != null) { 696 var m = res[0]; 697 var escaped = res[1]; 698 var offset = res.index; 699 path += str.slice(index, offset); 700 index = offset + m.length; 701 702 // Ignore already escaped sequences. 703 if (escaped) { 704 path += escaped[1]; 705 continue 706 } 707 708 var next = str[index]; 709 var prefix = res[2]; 710 var name = res[3]; 711 var capture = res[4]; 712 var group = res[5]; 713 var modifier = res[6]; 714 var asterisk = res[7]; 715 716 // Push the current path onto the tokens. 717 if (path) { 718 tokens.push(path); 719 path = ''; 720 } 721 722 var partial = prefix != null && next != null && next !== prefix; 723 var repeat = modifier === '+' || modifier === '*'; 724 var optional = modifier === '?' || modifier === '*'; 725 var delimiter = res[2] || defaultDelimiter; 726 var pattern = capture || group; 727 728 tokens.push({ 729 name: name || key++, 730 prefix: prefix || '', 731 delimiter: delimiter, 732 optional: optional, 733 repeat: repeat, 734 partial: partial, 735 asterisk: !!asterisk, 736 pattern: pattern ? escapeGroup(pattern) : (asterisk ? '.*' : '[^' + escapeString(delimiter) + ']+?') 737 }); 738 } 739 740 // Match any characters still remaining. 741 if (index < str.length) { 742 path += str.substr(index); 743 } 744 745 // If the path exists, push it onto the end. 746 if (path) { 747 tokens.push(path); 748 } 749 750 return tokens 751 } 752 753 /** 754 * Compile a string to a template function for the path. 755 * 756 * @param {string} str 757 * @param {Object=} options 758 * @return {!function(Object=, Object=)} 759 */ 760 function compile (str, options) { 761 return tokensToFunction(parse(str, options)) 762 } 763 764 /** 765 * Prettier encoding of URI path segments. 766 * 767 * @param {string} 768 * @return {string} 769 */ 770 function encodeURIComponentPretty (str) { 771 return encodeURI(str).replace(/[\/?#]/g, function (c) { 772 return '%' + c.charCodeAt(0).toString(16).toUpperCase() 773 }) 774 } 775 776 /** 777 * Encode the asterisk parameter. Similar to `pretty`, but allows slashes. 778 * 779 * @param {string} 780 * @return {string} 781 */ 782 function encodeAsterisk (str) { 783 return encodeURI(str).replace(/[?#]/g, function (c) { 784 return '%' + c.charCodeAt(0).toString(16).toUpperCase() 785 }) 786 } 787 788 /** 789 * Expose a method for transforming tokens into the path function. 790 */ 791 function tokensToFunction (tokens) { 792 // Compile all the tokens into regexps. 793 var matches = new Array(tokens.length); 794 795 // Compile all the patterns before compilation. 796 for (var i = 0; i < tokens.length; i++) { 797 if (typeof tokens[i] === 'object') { 798 matches[i] = new RegExp('^(?:' + tokens[i].pattern + ')$'); 799 } 800 } 801 802 return function (obj, opts) { 803 var path = ''; 804 var data = obj || {}; 805 var options = opts || {}; 806 var encode = options.pretty ? encodeURIComponentPretty : encodeURIComponent; 807 808 for (var i = 0; i < tokens.length; i++) { 809 var token = tokens[i]; 810 811 if (typeof token === 'string') { 812 path += token; 813 814 continue 815 } 816 817 var value = data[token.name]; 818 var segment; 819 820 if (value == null) { 821 if (token.optional) { 822 // Prepend partial segment prefixes. 823 if (token.partial) { 824 path += token.prefix; 825 } 826 827 continue 828 } else { 829 throw new TypeError('Expected "' + token.name + '" to be defined') 830 } 831 } 832 833 if (isarray(value)) { 834 if (!token.repeat) { 835 throw new TypeError('Expected "' + token.name + '" to not repeat, but received `' + JSON.stringify(value) + '`') 836 } 837 838 if (value.length === 0) { 839 if (token.optional) { 840 continue 841 } else { 842 throw new TypeError('Expected "' + token.name + '" to not be empty') 843 } 844 } 845 846 for (var j = 0; j < value.length; j++) { 847 segment = encode(value[j]); 848 849 if (!matches[i].test(segment)) { 850 throw new TypeError('Expected all "' + token.name + '" to match "' + token.pattern + '", but received `' + JSON.stringify(segment) + '`') 851 } 852 853 path += (j === 0 ? token.prefix : token.delimiter) + segment; 854 } 855 856 continue 857 } 858 859 segment = token.asterisk ? encodeAsterisk(value) : encode(value); 860 861 if (!matches[i].test(segment)) { 862 throw new TypeError('Expected "' + token.name + '" to match "' + token.pattern + '", but received "' + segment + '"') 863 } 864 865 path += token.prefix + segment; 866 } 867 868 return path 869 } 870 } 871 872 /** 873 * Escape a regular expression string. 874 * 875 * @param {string} str 876 * @return {string} 877 */ 878 function escapeString (str) { 879 return str.replace(/([.+*?=^!:${}()[\]|\/\\])/g, '\\$1') 880 } 881 882 /** 883 * Escape the capturing group by escaping special characters and meaning. 884 * 885 * @param {string} group 886 * @return {string} 887 */ 888 function escapeGroup (group) { 889 return group.replace(/([=!:$\/()])/g, '\\$1') 890 } 891 892 /** 893 * Attach the keys as a property of the regexp. 894 * 895 * @param {!RegExp} re 896 * @param {Array} keys 897 * @return {!RegExp} 898 */ 899 function attachKeys (re, keys) { 900 re.keys = keys; 901 return re 902 } 903 904 /** 905 * Get the flags for a regexp from the options. 906 * 907 * @param {Object} options 908 * @return {string} 909 */ 910 function flags (options) { 911 return options.sensitive ? '' : 'i' 912 } 913 914 /** 915 * Pull out keys from a regexp. 916 * 917 * @param {!RegExp} path 918 * @param {!Array} keys 919 * @return {!RegExp} 920 */ 921 function regexpToRegexp (path, keys) { 922 // Use a negative lookahead to match only capturing groups. 923 var groups = path.source.match(/\((?!\?)/g); 924 925 if (groups) { 926 for (var i = 0; i < groups.length; i++) { 927 keys.push({ 928 name: i, 929 prefix: null, 930 delimiter: null, 931 optional: false, 932 repeat: false, 933 partial: false, 934 asterisk: false, 935 pattern: null 936 }); 937 } 938 } 939 940 return attachKeys(path, keys) 941 } 942 943 /** 944 * Transform an array into a regexp. 945 * 946 * @param {!Array} path 947 * @param {Array} keys 948 * @param {!Object} options 949 * @return {!RegExp} 950 */ 951 function arrayToRegexp (path, keys, options) { 952 var parts = []; 953 954 for (var i = 0; i < path.length; i++) { 955 parts.push(pathToRegexp(path[i], keys, options).source); 956 } 957 958 var regexp = new RegExp('(?:' + parts.join('|') + ')', flags(options)); 959 960 return attachKeys(regexp, keys) 961 } 962 963 /** 964 * Create a path regexp from string input. 965 * 966 * @param {string} path 967 * @param {!Array} keys 968 * @param {!Object} options 969 * @return {!RegExp} 970 */ 971 function stringToRegexp (path, keys, options) { 972 return tokensToRegExp(parse(path, options), keys, options) 973 } 974 975 /** 976 * Expose a function for taking tokens and returning a RegExp. 977 * 978 * @param {!Array} tokens 979 * @param {(Array|Object)=} keys 980 * @param {Object=} options 981 * @return {!RegExp} 982 */ 983 function tokensToRegExp (tokens, keys, options) { 984 if (!isarray(keys)) { 985 options = /** @type {!Object} */ (keys || options); 986 keys = []; 987 } 988 989 options = options || {}; 990 991 var strict = options.strict; 992 var end = options.end !== false; 993 var route = ''; 994 995 // Iterate over the tokens and create our regexp string. 996 for (var i = 0; i < tokens.length; i++) { 997 var token = tokens[i]; 998 999 if (typeof token === 'string') { 1000 route += escapeString(token); 1001 } else { 1002 var prefix = escapeString(token.prefix); 1003 var capture = '(?:' + token.pattern + ')'; 1004 1005 keys.push(token); 1006 1007 if (token.repeat) { 1008 capture += '(?:' + prefix + capture + ')*'; 1009 } 1010 1011 if (token.optional) { 1012 if (!token.partial) { 1013 capture = '(?:' + prefix + '(' + capture + '))?'; 1014 } else { 1015 capture = prefix + '(' + capture + ')?'; 1016 } 1017 } else { 1018 capture = prefix + '(' + capture + ')'; 1019 } 1020 1021 route += capture; 1022 } 1023 } 1024 1025 var delimiter = escapeString(options.delimiter || '/'); 1026 var endsWithDelimiter = route.slice(-delimiter.length) === delimiter; 1027 1028 // In non-strict mode we allow a slash at the end of match. If the path to 1029 // match already ends with a slash, we remove it for consistency. The slash 1030 // is valid at the end of a path match, not in the middle. This is important 1031 // in non-ending mode, where "/test/" shouldn't match "/test//route". 1032 if (!strict) { 1033 route = (endsWithDelimiter ? route.slice(0, -delimiter.length) : route) + '(?:' + delimiter + '(?=$))?'; 1034 } 1035 1036 if (end) { 1037 route += '$'; 1038 } else { 1039 // In non-ending mode, we need the capturing groups to match as much as 1040 // possible by using a positive lookahead to the end or next path segment. 1041 route += strict && endsWithDelimiter ? '' : '(?=' + delimiter + '|$)'; 1042 } 1043 1044 return attachKeys(new RegExp('^' + route, flags(options)), keys) 1045 } 1046 1047 /** 1048 * Normalize the given path string, returning a regular expression. 1049 * 1050 * An empty array can be passed in for the keys, which will hold the 1051 * placeholder key descriptions. For example, using `/user/:id`, `keys` will 1052 * contain `[{ name: 'id', delimiter: '/', optional: false, repeat: false }]`. 1053 * 1054 * @param {(string|RegExp|Array)} path 1055 * @param {(Array|Object)=} keys 1056 * @param {Object=} options 1057 * @return {!RegExp} 1058 */ 1059 function pathToRegexp (path, keys, options) { 1060 if (!isarray(keys)) { 1061 options = /** @type {!Object} */ (keys || options); 1062 keys = []; 1063 } 1064 1065 options = options || {}; 1066 1067 if (path instanceof RegExp) { 1068 return regexpToRegexp(path, /** @type {!Array} */ (keys)) 1069 } 1070 1071 if (isarray(path)) { 1072 return arrayToRegexp(/** @type {!Array} */ (path), /** @type {!Array} */ (keys), options) 1073 } 1074 1075 return stringToRegexp(/** @type {string} */ (path), /** @type {!Array} */ (keys), options) 1076 } 1077 1078 pathToRegexp_1.parse = parse_1; 1079 pathToRegexp_1.compile = compile_1; 1080 pathToRegexp_1.tokensToFunction = tokensToFunction_1; 1081 pathToRegexp_1.tokensToRegExp = tokensToRegExp_1; 1082 1083 /* */ 1084 1085 // $flow-disable-line 1086 var regexpCompileCache = Object.create(null); 1087 1088 function fillParams ( 1089 path, 1090 params, 1091 routeMsg 1092 ) { 1093 try { 1094 var filler = 1095 regexpCompileCache[path] || 1096 (regexpCompileCache[path] = pathToRegexp_1.compile(path)); 1097 return filler(params || {}, { pretty: true }) 1098 } catch (e) { 1099 { 1100 warn(false, ("missing param for " + routeMsg + ": " + (e.message))); 1101 } 1102 return '' 1103 } 1104 } 1105 1106 /* */ 1107 1108 function createRouteMap ( 1109 routes, 1110 oldPathList, 1111 oldPathMap, 1112 oldNameMap 1113 ) { 1114 // the path list is used to control path matching priority 1115 var pathList = oldPathList || []; 1116 // $flow-disable-line 1117 var pathMap = oldPathMap || Object.create(null); 1118 // $flow-disable-line 1119 var nameMap = oldNameMap || Object.create(null); 1120 1121 routes.forEach(function (route) { 1122 addRouteRecord(pathList, pathMap, nameMap, route); 1123 }); 1124 1125 // ensure wildcard routes are always at the end 1126 for (var i = 0, l = pathList.length; i < l; i++) { 1127 if (pathList[i] === '*') { 1128 pathList.push(pathList.splice(i, 1)[0]); 1129 l--; 1130 i--; 1131 } 1132 } 1133 1134 return { 1135 pathList: pathList, 1136 pathMap: pathMap, 1137 nameMap: nameMap 1138 } 1139 } 1140 1141 function addRouteRecord ( 1142 pathList, 1143 pathMap, 1144 nameMap, 1145 route, 1146 parent, 1147 matchAs 1148 ) { 1149 var path = route.path; 1150 var name = route.name; 1151 { 1152 assert(path != null, "\"path\" is required in a route configuration."); 1153 assert( 1154 typeof route.component !== 'string', 1155 "route config \"component\" for path: " + (String(path || name)) + " cannot be a " + 1156 "string id. Use an actual component instead." 1157 ); 1158 } 1159 1160 var pathToRegexpOptions = route.pathToRegexpOptions || {}; 1161 var normalizedPath = normalizePath( 1162 path, 1163 parent, 1164 pathToRegexpOptions.strict 1165 ); 1166 1167 if (typeof route.caseSensitive === 'boolean') { 1168 pathToRegexpOptions.sensitive = route.caseSensitive; 1169 } 1170 1171 var record = { 1172 path: normalizedPath, 1173 regex: compileRouteRegex(normalizedPath, pathToRegexpOptions), 1174 components: route.components || { default: route.component }, 1175 instances: {}, 1176 name: name, 1177 parent: parent, 1178 matchAs: matchAs, 1179 redirect: route.redirect, 1180 beforeEnter: route.beforeEnter, 1181 meta: route.meta || {}, 1182 props: route.props == null 1183 ? {} 1184 : route.components 1185 ? route.props 1186 : { default: route.props } 1187 }; 1188 1189 if (route.children) { 1190 // Warn if route is named, does not redirect and has a default child route. 1191 // If users navigate to this route by name, the default child will 1192 // not be rendered (GH Issue #629) 1193 { 1194 if (route.name && !route.redirect && route.children.some(function (child) { return /^\/?$/.test(child.path); })) { 1195 warn( 1196 false, 1197 "Named Route '" + (route.name) + "' has a default child route. " + 1198 "When navigating to this named route (:to=\"{name: '" + (route.name) + "'\"), " + 1199 "the default child route will not be rendered. Remove the name from " + 1200 "this route and use the name of the default child route for named " + 1201 "links instead." 1202 ); 1203 } 1204 } 1205 route.children.forEach(function (child) { 1206 var childMatchAs = matchAs 1207 ? cleanPath((matchAs + "/" + (child.path))) 1208 : undefined; 1209 addRouteRecord(pathList, pathMap, nameMap, child, record, childMatchAs); 1210 }); 1211 } 1212 1213 if (route.alias !== undefined) { 1214 var aliases = Array.isArray(route.alias) 1215 ? route.alias 1216 : [route.alias]; 1217 1218 aliases.forEach(function (alias) { 1219 var aliasRoute = { 1220 path: alias, 1221 children: route.children 1222 }; 1223 addRouteRecord( 1224 pathList, 1225 pathMap, 1226 nameMap, 1227 aliasRoute, 1228 parent, 1229 record.path || '/' // matchAs 1230 ); 1231 }); 1232 } 1233 1234 if (!pathMap[record.path]) { 1235 pathList.push(record.path); 1236 pathMap[record.path] = record; 1237 } 1238 1239 if (name) { 1240 if (!nameMap[name]) { 1241 nameMap[name] = record; 1242 } else if ("development" !== 'production' && !matchAs) { 1243 warn( 1244 false, 1245 "Duplicate named routes definition: " + 1246 "{ name: \"" + name + "\", path: \"" + (record.path) + "\" }" 1247 ); 1248 } 1249 } 1250 } 1251 1252 function compileRouteRegex (path, pathToRegexpOptions) { 1253 var regex = pathToRegexp_1(path, [], pathToRegexpOptions); 1254 { 1255 var keys = Object.create(null); 1256 regex.keys.forEach(function (key) { 1257 warn(!keys[key.name], ("Duplicate param keys in route with path: \"" + path + "\"")); 1258 keys[key.name] = true; 1259 }); 1260 } 1261 return regex 1262 } 1263 1264 function normalizePath (path, parent, strict) { 1265 if (!strict) { path = path.replace(/\/$/, ''); } 1266 if (path[0] === '/') { return path } 1267 if (parent == null) { return path } 1268 return cleanPath(((parent.path) + "/" + path)) 1269 } 1270 1271 /* */ 1272 1273 1274 function normalizeLocation ( 1275 raw, 1276 current, 1277 append, 1278 router 1279 ) { 1280 var next = typeof raw === 'string' ? { path: raw } : raw; 1281 // named target 1282 if (next.name || next._normalized) { 1283 return next 1284 } 1285 1286 // relative params 1287 if (!next.path && next.params && current) { 1288 next = assign({}, next); 1289 next._normalized = true; 1290 var params = assign(assign({}, current.params), next.params); 1291 if (current.name) { 1292 next.name = current.name; 1293 next.params = params; 1294 } else if (current.matched.length) { 1295 var rawPath = current.matched[current.matched.length - 1].path; 1296 next.path = fillParams(rawPath, params, ("path " + (current.path))); 1297 } else { 1298 warn(false, "relative params navigation requires a current route."); 1299 } 1300 return next 1301 } 1302 1303 var parsedPath = parsePath(next.path || ''); 1304 var basePath = (current && current.path) || '/'; 1305 var path = parsedPath.path 1306 ? resolvePath(parsedPath.path, basePath, append || next.append) 1307 : basePath; 1308 1309 var query = resolveQuery( 1310 parsedPath.query, 1311 next.query, 1312 router && router.options.parseQuery 1313 ); 1314 1315 var hash = next.hash || parsedPath.hash; 1316 if (hash && hash.charAt(0) !== '#') { 1317 hash = "#" + hash; 1318 } 1319 1320 return { 1321 _normalized: true, 1322 path: path, 1323 query: query, 1324 hash: hash 1325 } 1326 } 1327 1328 function assign (a, b) { 1329 for (var key in b) { 1330 a[key] = b[key]; 1331 } 1332 return a 1333 } 1334 1335 /* */ 1336 1337 1338 function createMatcher ( 1339 routes, 1340 router 1341 ) { 1342 var ref = createRouteMap(routes); 1343 var pathList = ref.pathList; 1344 var pathMap = ref.pathMap; 1345 var nameMap = ref.nameMap; 1346 1347 function addRoutes (routes) { 1348 createRouteMap(routes, pathList, pathMap, nameMap); 1349 } 1350 1351 function match ( 1352 raw, 1353 currentRoute, 1354 redirectedFrom 1355 ) { 1356 var location = normalizeLocation(raw, currentRoute, false, router); 1357 var name = location.name; 1358 1359 if (name) { 1360 var record = nameMap[name]; 1361 { 1362 warn(record, ("Route with name '" + name + "' does not exist")); 1363 } 1364 if (!record) { return _createRoute(null, location) } 1365 var paramNames = record.regex.keys 1366 .filter(function (key) { return !key.optional; }) 1367 .map(function (key) { return key.name; }); 1368 1369 if (typeof location.params !== 'object') { 1370 location.params = {}; 1371 } 1372 1373 if (currentRoute && typeof currentRoute.params === 'object') { 1374 for (var key in currentRoute.params) { 1375 if (!(key in location.params) && paramNames.indexOf(key) > -1) { 1376 location.params[key] = currentRoute.params[key]; 1377 } 1378 } 1379 } 1380 1381 if (record) { 1382 location.path = fillParams(record.path, location.params, ("named route \"" + name + "\"")); 1383 return _createRoute(record, location, redirectedFrom) 1384 } 1385 } else if (location.path) { 1386 location.params = {}; 1387 for (var i = 0; i < pathList.length; i++) { 1388 var path = pathList[i]; 1389 var record$1 = pathMap[path]; 1390 if (matchRoute(record$1.regex, location.path, location.params)) { 1391 return _createRoute(record$1, location, redirectedFrom) 1392 } 1393 } 1394 } 1395 // no match 1396 return _createRoute(null, location) 1397 } 1398 1399 function redirect ( 1400 record, 1401 location 1402 ) { 1403 var originalRedirect = record.redirect; 1404 var redirect = typeof originalRedirect === 'function' 1405 ? originalRedirect(createRoute(record, location, null, router)) 1406 : originalRedirect; 1407 1408 if (typeof redirect === 'string') { 1409 redirect = { path: redirect }; 1410 } 1411 1412 if (!redirect || typeof redirect !== 'object') { 1413 { 1414 warn( 1415 false, ("invalid redirect option: " + (JSON.stringify(redirect))) 1416 ); 1417 } 1418 return _createRoute(null, location) 1419 } 1420 1421 var re = redirect; 1422 var name = re.name; 1423 var path = re.path; 1424 var query = location.query; 1425 var hash = location.hash; 1426 var params = location.params; 1427 query = re.hasOwnProperty('query') ? re.query : query; 1428 hash = re.hasOwnProperty('hash') ? re.hash : hash; 1429 params = re.hasOwnProperty('params') ? re.params : params; 1430 1431 if (name) { 1432 // resolved named direct 1433 var targetRecord = nameMap[name]; 1434 { 1435 assert(targetRecord, ("redirect failed: named route \"" + name + "\" not found.")); 1436 } 1437 return match({ 1438 _normalized: true, 1439 name: name, 1440 query: query, 1441 hash: hash, 1442 params: params 1443 }, undefined, location) 1444 } else if (path) { 1445 // 1. resolve relative redirect 1446 var rawPath = resolveRecordPath(path, record); 1447 // 2. resolve params 1448 var resolvedPath = fillParams(rawPath, params, ("redirect route with path \"" + rawPath + "\"")); 1449 // 3. rematch with existing query and hash 1450 return match({ 1451 _normalized: true, 1452 path: resolvedPath, 1453 query: query, 1454 hash: hash 1455 }, undefined, location) 1456 } else { 1457 { 1458 warn(false, ("invalid redirect option: " + (JSON.stringify(redirect)))); 1459 } 1460 return _createRoute(null, location) 1461 } 1462 } 1463 1464 function alias ( 1465 record, 1466 location, 1467 matchAs 1468 ) { 1469 var aliasedPath = fillParams(matchAs, location.params, ("aliased route with path \"" + matchAs + "\"")); 1470 var aliasedMatch = match({ 1471 _normalized: true, 1472 path: aliasedPath 1473 }); 1474 if (aliasedMatch) { 1475 var matched = aliasedMatch.matched; 1476 var aliasedRecord = matched[matched.length - 1]; 1477 location.params = aliasedMatch.params; 1478 return _createRoute(aliasedRecord, location) 1479 } 1480 return _createRoute(null, location) 1481 } 1482 1483 function _createRoute ( 1484 record, 1485 location, 1486 redirectedFrom 1487 ) { 1488 if (record && record.redirect) { 1489 return redirect(record, redirectedFrom || location) 1490 } 1491 if (record && record.matchAs) { 1492 return alias(record, location, record.matchAs) 1493 } 1494 return createRoute(record, location, redirectedFrom, router) 1495 } 1496 1497 return { 1498 match: match, 1499 addRoutes: addRoutes 1500 } 1501 } 1502 1503 function matchRoute ( 1504 regex, 1505 path, 1506 params 1507 ) { 1508 var m = path.match(regex); 1509 1510 if (!m) { 1511 return false 1512 } else if (!params) { 1513 return true 1514 } 1515 1516 for (var i = 1, len = m.length; i < len; ++i) { 1517 var key = regex.keys[i - 1]; 1518 var val = typeof m[i] === 'string' ? decodeURIComponent(m[i]) : m[i]; 1519 if (key) { 1520 params[key.name] = val; 1521 } 1522 } 1523 1524 return true 1525 } 1526 1527 function resolveRecordPath (path, record) { 1528 return resolvePath(path, record.parent ? record.parent.path : '/', true) 1529 } 1530 1531 /* */ 1532 1533 1534 var positionStore = Object.create(null); 1535 1536 function setupScroll () { 1537 // Fix for #1585 for Firefox 1538 window.history.replaceState({ key: getStateKey() }, ''); 1539 window.addEventListener('popstate', function (e) { 1540 saveScrollPosition(); 1541 if (e.state && e.state.key) { 1542 setStateKey(e.state.key); 1543 } 1544 }); 1545 } 1546 1547 function handleScroll ( 1548 router, 1549 to, 1550 from, 1551 isPop 1552 ) { 1553 if (!router.app) { 1554 return 1555 } 1556 1557 var behavior = router.options.scrollBehavior; 1558 if (!behavior) { 1559 return 1560 } 1561 1562 { 1563 assert(typeof behavior === 'function', "scrollBehavior must be a function"); 1564 } 1565 1566 // wait until re-render finishes before scrolling 1567 router.app.$nextTick(function () { 1568 var position = getScrollPosition(); 1569 var shouldScroll = behavior(to, from, isPop ? position : null); 1570 1571 if (!shouldScroll) { 1572 return 1573 } 1574 1575 if (typeof shouldScroll.then === 'function') { 1576 shouldScroll.then(function (shouldScroll) { 1577 scrollToPosition((shouldScroll), position); 1578 }).catch(function (err) { 1579 { 1580 assert(false, err.toString()); 1581 } 1582 }); 1583 } else { 1584 scrollToPosition(shouldScroll, position); 1585 } 1586 }); 1587 } 1588 1589 function saveScrollPosition () { 1590 var key = getStateKey(); 1591 if (key) { 1592 positionStore[key] = { 1593 x: window.pageXOffset, 1594 y: window.pageYOffset 1595 }; 1596 } 1597 } 1598 1599 function getScrollPosition () { 1600 var key = getStateKey(); 1601 if (key) { 1602 return positionStore[key] 1603 } 1604 } 1605 1606 function getElementPosition (el, offset) { 1607 var docEl = document.documentElement; 1608 var docRect = docEl.getBoundingClientRect(); 1609 var elRect = el.getBoundingClientRect(); 1610 return { 1611 x: elRect.left - docRect.left - offset.x, 1612 y: elRect.top - docRect.top - offset.y 1613 } 1614 } 1615 1616 function isValidPosition (obj) { 1617 return isNumber(obj.x) || isNumber(obj.y) 1618 } 1619 1620 function normalizePosition (obj) { 1621 return { 1622 x: isNumber(obj.x) ? obj.x : window.pageXOffset, 1623 y: isNumber(obj.y) ? obj.y : window.pageYOffset 1624 } 1625 } 1626 1627 function normalizeOffset (obj) { 1628 return { 1629 x: isNumber(obj.x) ? obj.x : 0, 1630 y: isNumber(obj.y) ? obj.y : 0 1631 } 1632 } 1633 1634 function isNumber (v) { 1635 return typeof v === 'number' 1636 } 1637 1638 function scrollToPosition (shouldScroll, position) { 1639 var isObject = typeof shouldScroll === 'object'; 1640 if (isObject && typeof shouldScroll.selector === 'string') { 1641 var el = document.querySelector(shouldScroll.selector); 1642 if (el) { 1643 var offset = shouldScroll.offset && typeof shouldScroll.offset === 'object' ? shouldScroll.offset : {}; 1644 offset = normalizeOffset(offset); 1645 position = getElementPosition(el, offset); 1646 } else if (isValidPosition(shouldScroll)) { 1647 position = normalizePosition(shouldScroll); 1648 } 1649 } else if (isObject && isValidPosition(shouldScroll)) { 1650 position = normalizePosition(shouldScroll); 1651 } 1652 1653 if (position) { 1654 window.scrollTo(position.x, position.y); 1655 } 1656 } 1657 1658 /* */ 1659 1660 var supportsPushState = inBrowser && (function () { 1661 var ua = window.navigator.userAgent; 1662 1663 if ( 1664 (ua.indexOf('Android 2.') !== -1 || ua.indexOf('Android 4.0') !== -1) && 1665 ua.indexOf('Mobile Safari') !== -1 && 1666 ua.indexOf('Chrome') === -1 && 1667 ua.indexOf('Windows Phone') === -1 1668 ) { 1669 return false 1670 } 1671 1672 return window.history && 'pushState' in window.history 1673 })(); 1674 1675 // use User Timing api (if present) for more accurate key precision 1676 var Time = inBrowser && window.performance && window.performance.now 1677 ? window.performance 1678 : Date; 1679 1680 var _key = genKey(); 1681 1682 function genKey () { 1683 return Time.now().toFixed(3) 1684 } 1685 1686 function getStateKey () { 1687 return _key 1688 } 1689 1690 function setStateKey (key) { 1691 _key = key; 1692 } 1693 1694 function pushState (url, replace) { 1695 saveScrollPosition(); 1696 // try...catch the pushState call to get around Safari 1697 // DOM Exception 18 where it limits to 100 pushState calls 1698 var history = window.history; 1699 try { 1700 if (replace) { 1701 history.replaceState({ key: _key }, '', url); 1702 } else { 1703 _key = genKey(); 1704 history.pushState({ key: _key }, '', url); 1705 } 1706 } catch (e) { 1707 window.location[replace ? 'replace' : 'assign'](url); 1708 } 1709 } 1710 1711 function replaceState (url) { 1712 pushState(url, true); 1713 } 1714 1715 /* */ 1716 1717 function runQueue (queue, fn, cb) { 1718 var step = function (index) { 1719 if (index >= queue.length) { 1720 cb(); 1721 } else { 1722 if (queue[index]) { 1723 fn(queue[index], function () { 1724 step(index + 1); 1725 }); 1726 } else { 1727 step(index + 1); 1728 } 1729 } 1730 }; 1731 step(0); 1732 } 1733 1734 /* */ 1735 1736 function resolveAsyncComponents (matched) { 1737 return function (to, from, next) { 1738 var hasAsync = false; 1739 var pending = 0; 1740 var error = null; 1741 1742 flatMapComponents(matched, function (def, _, match, key) { 1743 // if it's a function and doesn't have cid attached, 1744 // assume it's an async component resolve function. 1745 // we are not using Vue's default async resolving mechanism because 1746 // we want to halt the navigation until the incoming component has been 1747 // resolved. 1748 if (typeof def === 'function' && def.cid === undefined) { 1749 hasAsync = true; 1750 pending++; 1751 1752 var resolve = once(function (resolvedDef) { 1753 if (isESModule(resolvedDef)) { 1754 resolvedDef = resolvedDef.default; 1755 } 1756 // save resolved on async factory in case it's used elsewhere 1757 def.resolved = typeof resolvedDef === 'function' 1758 ? resolvedDef 1759 : _Vue.extend(resolvedDef); 1760 match.components[key] = resolvedDef; 1761 pending--; 1762 if (pending <= 0) { 1763 next(); 1764 } 1765 }); 1766 1767 var reject = once(function (reason) { 1768 var msg = "Failed to resolve async component " + key + ": " + reason; 1769 "development" !== 'production' && warn(false, msg); 1770 if (!error) { 1771 error = isError(reason) 1772 ? reason 1773 : new Error(msg); 1774 next(error); 1775 } 1776 }); 1777 1778 var res; 1779 try { 1780 res = def(resolve, reject); 1781 } catch (e) { 1782 reject(e); 1783 } 1784 if (res) { 1785 if (typeof res.then === 'function') { 1786 res.then(resolve, reject); 1787 } else { 1788 // new syntax in Vue 2.3 1789 var comp = res.component; 1790 if (comp && typeof comp.then === 'function') { 1791 comp.then(resolve, reject); 1792 } 1793 } 1794 } 1795 } 1796 }); 1797 1798 if (!hasAsync) { next(); } 1799 } 1800 } 1801 1802 function flatMapComponents ( 1803 matched, 1804 fn 1805 ) { 1806 return flatten(matched.map(function (m) { 1807 return Object.keys(m.components).map(function (key) { return fn( 1808 m.components[key], 1809 m.instances[key], 1810 m, key 1811 ); }) 1812 })) 1813 } 1814 1815 function flatten (arr) { 1816 return Array.prototype.concat.apply([], arr) 1817 } 1818 1819 var hasSymbol = 1820 typeof Symbol === 'function' && 1821 typeof Symbol.toStringTag === 'symbol'; 1822 1823 function isESModule (obj) { 1824 return obj.__esModule || (hasSymbol && obj[Symbol.toStringTag] === 'Module') 1825 } 1826 1827 // in Webpack 2, require.ensure now also returns a Promise 1828 // so the resolve/reject functions may get called an extra time 1829 // if the user uses an arrow function shorthand that happens to 1830 // return that Promise. 1831 function once (fn) { 1832 var called = false; 1833 return function () { 1834 var args = [], len = arguments.length; 1835 while ( len-- ) args[ len ] = arguments[ len ]; 1836 1837 if (called) { return } 1838 called = true; 1839 return fn.apply(this, args) 1840 } 1841 } 1842 1843 /* */ 1844 1845 var History = function History (router, base) { 1846 this.router = router; 1847 this.base = normalizeBase(base); 1848 // start with a route object that stands for "nowhere" 1849 this.current = START; 1850 this.pending = null; 1851 this.ready = false; 1852 this.readyCbs = []; 1853 this.readyErrorCbs = []; 1854 this.errorCbs = []; 1855 }; 1856 1857 History.prototype.listen = function listen (cb) { 1858 this.cb = cb; 1859 }; 1860 1861 History.prototype.onReady = function onReady (cb, errorCb) { 1862 if (this.ready) { 1863 cb(); 1864 } else { 1865 this.readyCbs.push(cb); 1866 if (errorCb) { 1867 this.readyErrorCbs.push(errorCb); 1868 } 1869 } 1870 }; 1871 1872 History.prototype.onError = function onError (errorCb) { 1873 this.errorCbs.push(errorCb); 1874 }; 1875 1876 History.prototype.transitionTo = function transitionTo (location, onComplete, onAbort) { 1877 var this$1 = this; 1878 1879 var route = this.router.match(location, this.current); 1880 this.confirmTransition(route, function () { 1881 this$1.updateRoute(route); 1882 onComplete && onComplete(route); 1883 this$1.ensureURL(); 1884 1885 // fire ready cbs once 1886 if (!this$1.ready) { 1887 this$1.ready = true; 1888 this$1.readyCbs.forEach(function (cb) { cb(route); }); 1889 } 1890 }, function (err) { 1891 if (onAbort) { 1892 onAbort(err); 1893 } 1894 if (err && !this$1.ready) { 1895 this$1.ready = true; 1896 this$1.readyErrorCbs.forEach(function (cb) { cb(err); }); 1897 } 1898 }); 1899 }; 1900 1901 History.prototype.confirmTransition = function confirmTransition (route, onComplete, onAbort) { 1902 var this$1 = this; 1903 1904 var current = this.current; 1905 var abort = function (err) { 1906 if (isError(err)) { 1907 if (this$1.errorCbs.length) { 1908 this$1.errorCbs.forEach(function (cb) { cb(err); }); 1909 } else { 1910 warn(false, 'uncaught error during route navigation:'); 1911 console.error(err); 1912 } 1913 } 1914 onAbort && onAbort(err); 1915 }; 1916 if ( 1917 isSameRoute(route, current) && 1918 // in the case the route map has been dynamically appended to 1919 route.matched.length === current.matched.length 1920 ) { 1921 this.ensureURL(); 1922 return abort() 1923 } 1924 1925 var ref = resolveQueue(this.current.matched, route.matched); 1926 var updated = ref.updated; 1927 var deactivated = ref.deactivated; 1928 var activated = ref.activated; 1929 1930 var queue = [].concat( 1931 // in-component leave guards 1932 extractLeaveGuards(deactivated), 1933 // global before hooks 1934 this.router.beforeHooks, 1935 // in-component update hooks 1936 extractUpdateHooks(updated), 1937 // in-config enter guards 1938 activated.map(function (m) { return m.beforeEnter; }), 1939 // async components 1940 resolveAsyncComponents(activated) 1941 ); 1942 1943 this.pending = route; 1944 var iterator = function (hook, next) { 1945 if (this$1.pending !== route) { 1946 return abort() 1947 } 1948 try { 1949 hook(route, current, function (to) { 1950 if (to === false || isError(to)) { 1951 // next(false) -> abort navigation, ensure current URL 1952 this$1.ensureURL(true); 1953 abort(to); 1954 } else if ( 1955 typeof to === 'string' || 1956 (typeof to === 'object' && ( 1957 typeof to.path === 'string' || 1958 typeof to.name === 'string' 1959 )) 1960 ) { 1961 // next('/') or next({ path: '/' }) -> redirect 1962 abort(); 1963 if (typeof to === 'object' && to.replace) { 1964 this$1.replace(to); 1965 } else { 1966 this$1.push(to); 1967 } 1968 } else { 1969 // confirm transition and pass on the value 1970 next(to); 1971 } 1972 }); 1973 } catch (e) { 1974 abort(e); 1975 } 1976 }; 1977 1978 runQueue(queue, iterator, function () { 1979 var postEnterCbs = []; 1980 var isValid = function () { return this$1.current === route; }; 1981 // wait until async components are resolved before 1982 // extracting in-component enter guards 1983 var enterGuards = extractEnterGuards(activated, postEnterCbs, isValid); 1984 var queue = enterGuards.concat(this$1.router.resolveHooks); 1985 runQueue(queue, iterator, function () { 1986 if (this$1.pending !== route) { 1987 return abort() 1988 } 1989 this$1.pending = null; 1990 onComplete(route); 1991 if (this$1.router.app) { 1992 this$1.router.app.$nextTick(function () { 1993 postEnterCbs.forEach(function (cb) { cb(); }); 1994 }); 1995 } 1996 }); 1997 }); 1998 }; 1999 2000 History.prototype.updateRoute = function updateRoute (route) { 2001 var prev = this.current; 2002 this.current = route; 2003 this.cb && this.cb(route); 2004 this.router.afterHooks.forEach(function (hook) { 2005 hook && hook(route, prev); 2006 }); 2007 }; 2008 2009 function normalizeBase (base) { 2010 if (!base) { 2011 if (inBrowser) { 2012 // respect <base> tag 2013 var baseEl = document.querySelector('base'); 2014 base = (baseEl && baseEl.getAttribute('href')) || '/'; 2015 // strip full URL origin 2016 base = base.replace(/^https?:\/\/[^\/]+/, ''); 2017 } else { 2018 base = '/'; 2019 } 2020 } 2021 // make sure there's the starting slash 2022 if (base.charAt(0) !== '/') { 2023 base = '/' + base; 2024 } 2025 // remove trailing slash 2026 return base.replace(/\/$/, '') 2027 } 2028 2029 function resolveQueue ( 2030 current, 2031 next 2032 ) { 2033 var i; 2034 var max = Math.max(current.length, next.length); 2035 for (i = 0; i < max; i++) { 2036 if (current[i] !== next[i]) { 2037 break 2038 } 2039 } 2040 return { 2041 updated: next.slice(0, i), 2042 activated: next.slice(i), 2043 deactivated: current.slice(i) 2044 } 2045 } 2046 2047 function extractGuards ( 2048 records, 2049 name, 2050 bind, 2051 reverse 2052 ) { 2053 var guards = flatMapComponents(records, function (def, instance, match, key) { 2054 var guard = extractGuard(def, name); 2055 if (guard) { 2056 return Array.isArray(guard) 2057 ? guard.map(function (guard) { return bind(guard, instance, match, key); }) 2058 : bind(guard, instance, match, key) 2059 } 2060 }); 2061 return flatten(reverse ? guards.reverse() : guards) 2062 } 2063 2064 function extractGuard ( 2065 def, 2066 key 2067 ) { 2068 if (typeof def !== 'function') { 2069 // extend now so that global mixins are applied. 2070 def = _Vue.extend(def); 2071 } 2072 return def.options[key] 2073 } 2074 2075 function extractLeaveGuards (deactivated) { 2076 return extractGuards(deactivated, 'beforeRouteLeave', bindGuard, true) 2077 } 2078 2079 function extractUpdateHooks (updated) { 2080 return extractGuards(updated, 'beforeRouteUpdate', bindGuard) 2081 } 2082 2083 function bindGuard (guard, instance) { 2084 if (instance) { 2085 return function boundRouteGuard () { 2086 return guard.apply(instance, arguments) 2087 } 2088 } 2089 } 2090 2091 function extractEnterGuards ( 2092 activated, 2093 cbs, 2094 isValid 2095 ) { 2096 return extractGuards(activated, 'beforeRouteEnter', function (guard, _, match, key) { 2097 return bindEnterGuard(guard, match, key, cbs, isValid) 2098 }) 2099 } 2100 2101 function bindEnterGuard ( 2102 guard, 2103 match, 2104 key, 2105 cbs, 2106 isValid 2107 ) { 2108 return function routeEnterGuard (to, from, next) { 2109 return guard(to, from, function (cb) { 2110 next(cb); 2111 if (typeof cb === 'function') { 2112 cbs.push(function () { 2113 // #750 2114 // if a router-view is wrapped with an out-in transition, 2115 // the instance may not have been registered at this time. 2116 // we will need to poll for registration until current route 2117 // is no longer valid. 2118 poll(cb, match.instances, key, isValid); 2119 }); 2120 } 2121 }) 2122 } 2123 } 2124 2125 function poll ( 2126 cb, // somehow flow cannot infer this is a function 2127 instances, 2128 key, 2129 isValid 2130 ) { 2131 if (instances[key]) { 2132 cb(instances[key]); 2133 } else if (isValid()) { 2134 setTimeout(function () { 2135 poll(cb, instances, key, isValid); 2136 }, 16); 2137 } 2138 } 2139 2140 /* */ 2141 2142 2143 var HTML5History = (function (History$$1) { 2144 function HTML5History (router, base) { 2145 var this$1 = this; 2146 2147 History$$1.call(this, router, base); 2148 2149 var expectScroll = router.options.scrollBehavior; 2150 2151 if (expectScroll) { 2152 setupScroll(); 2153 } 2154 2155 var initLocation = getLocation(this.base); 2156 window.addEventListener('popstate', function (e) { 2157 var current = this$1.current; 2158 2159 // Avoiding first `popstate` event dispatched in some browsers but first 2160 // history route not updated since async guard at the same time. 2161 var location = getLocation(this$1.base); 2162 if (this$1.current === START && location === initLocation) { 2163 return 2164 } 2165 2166 this$1.transitionTo(location, function (route) { 2167 if (expectScroll) { 2168 handleScroll(router, route, current, true); 2169 } 2170 }); 2171 }); 2172 } 2173 2174 if ( History$$1 ) HTML5History.__proto__ = History$$1; 2175 HTML5History.prototype = Object.create( History$$1 && History$$1.prototype ); 2176 HTML5History.prototype.constructor = HTML5History; 2177 2178 HTML5History.prototype.go = function go (n) { 2179 window.history.go(n); 2180 }; 2181 2182 HTML5History.prototype.push = function push (location, onComplete, onAbort) { 2183 var this$1 = this; 2184 2185 var ref = this; 2186 var fromRoute = ref.current; 2187 this.transitionTo(location, function (route) { 2188 pushState(cleanPath(this$1.base + route.fullPath)); 2189 handleScroll(this$1.router, route, fromRoute, false); 2190 onComplete && onComplete(route); 2191 }, onAbort); 2192 }; 2193 2194 HTML5History.prototype.replace = function replace (location, onComplete, onAbort) { 2195 var this$1 = this; 2196 2197 var ref = this; 2198 var fromRoute = ref.current; 2199 this.transitionTo(location, function (route) { 2200 replaceState(cleanPath(this$1.base + route.fullPath)); 2201 handleScroll(this$1.router, route, fromRoute, false); 2202 onComplete && onComplete(route); 2203 }, onAbort); 2204 }; 2205 2206 HTML5History.prototype.ensureURL = function ensureURL (push) { 2207 if (getLocation(this.base) !== this.current.fullPath) { 2208 var current = cleanPath(this.base + this.current.fullPath); 2209 push ? pushState(current) : replaceState(current); 2210 } 2211 }; 2212 2213 HTML5History.prototype.getCurrentLocation = function getCurrentLocation () { 2214 return getLocation(this.base) 2215 }; 2216 2217 return HTML5History; 2218 }(History)); 2219 2220 function getLocation (base) { 2221 var path = window.location.pathname; 2222 if (base && path.indexOf(base) === 0) { 2223 path = path.slice(base.length); 2224 } 2225 return (path || '/') + window.location.search + window.location.hash 2226 } 2227 2228 /* */ 2229 2230 2231 var HashHistory = (function (History$$1) { 2232 function HashHistory (router, base, fallback) { 2233 History$$1.call(this, router, base); 2234 // check history fallback deeplinking 2235 if (fallback && checkFallback(this.base)) { 2236 return 2237 } 2238 ensureSlash(); 2239 } 2240 2241 if ( History$$1 ) HashHistory.__proto__ = History$$1; 2242 HashHistory.prototype = Object.create( History$$1 && History$$1.prototype ); 2243 HashHistory.prototype.constructor = HashHistory; 2244 2245 // this is delayed until the app mounts 2246 // to avoid the hashchange listener being fired too early 2247 HashHistory.prototype.setupListeners = function setupListeners () { 2248 var this$1 = this; 2249 2250 var router = this.router; 2251 var expectScroll = router.options.scrollBehavior; 2252 var supportsScroll = supportsPushState && expectScroll; 2253 2254 if (supportsScroll) { 2255 setupScroll(); 2256 } 2257 2258 window.addEventListener(supportsPushState ? 'popstate' : 'hashchange', function () { 2259 var current = this$1.current; 2260 if (!ensureSlash()) { 2261 return 2262 } 2263 this$1.transitionTo(getHash(), function (route) { 2264 if (supportsScroll) { 2265 handleScroll(this$1.router, route, current, true); 2266 } 2267 if (!supportsPushState) { 2268 replaceHash(route.fullPath); 2269 } 2270 }); 2271 }); 2272 }; 2273 2274 HashHistory.prototype.push = function push (location, onComplete, onAbort) { 2275 var this$1 = this; 2276 2277 var ref = this; 2278 var fromRoute = ref.current; 2279 this.transitionTo(location, function (route) { 2280 pushHash(route.fullPath); 2281 handleScroll(this$1.router, route, fromRoute, false); 2282 onComplete && onComplete(route); 2283 }, onAbort); 2284 }; 2285 2286 HashHistory.prototype.replace = function replace (location, onComplete, onAbort) { 2287 var this$1 = this; 2288 2289 var ref = this; 2290 var fromRoute = ref.current; 2291 this.transitionTo(location, function (route) { 2292 replaceHash(route.fullPath); 2293 handleScroll(this$1.router, route, fromRoute, false); 2294 onComplete && onComplete(route); 2295 }, onAbort); 2296 }; 2297 2298 HashHistory.prototype.go = function go (n) { 2299 window.history.go(n); 2300 }; 2301 2302 HashHistory.prototype.ensureURL = function ensureURL (push) { 2303 var current = this.current.fullPath; 2304 if (getHash() !== current) { 2305 push ? pushHash(current) : replaceHash(current); 2306 } 2307 }; 2308 2309 HashHistory.prototype.getCurrentLocation = function getCurrentLocation () { 2310 return getHash() 2311 }; 2312 2313 return HashHistory; 2314 }(History)); 2315 2316 function checkFallback (base) { 2317 var location = getLocation(base); 2318 if (!/^\/#/.test(location)) { 2319 window.location.replace( 2320 cleanPath(base + '/#' + location) 2321 ); 2322 return true 2323 } 2324 } 2325 2326 function ensureSlash () { 2327 var path = getHash(); 2328 if (path.charAt(0) === '/') { 2329 return true 2330 } 2331 replaceHash('/' + path); 2332 return false 2333 } 2334 2335 function getHash () { 2336 // We can't use window.location.hash here because it's not 2337 // consistent across browsers - Firefox will pre-decode it! 2338 var href = window.location.href; 2339 var index = href.indexOf('#'); 2340 return index === -1 ? '' : href.slice(index + 1) 2341 } 2342 2343 function getUrl (path) { 2344 var href = window.location.href; 2345 var i = href.indexOf('#'); 2346 var base = i >= 0 ? href.slice(0, i) : href; 2347 return (base + "#" + path) 2348 } 2349 2350 function pushHash (path) { 2351 if (supportsPushState) { 2352 pushState(getUrl(path)); 2353 } else { 2354 window.location.hash = path; 2355 } 2356 } 2357 2358 function replaceHash (path) { 2359 if (supportsPushState) { 2360 replaceState(getUrl(path)); 2361 } else { 2362 window.location.replace(getUrl(path)); 2363 } 2364 } 2365 2366 /* */ 2367 2368 2369 var AbstractHistory = (function (History$$1) { 2370 function AbstractHistory (router, base) { 2371 History$$1.call(this, router, base); 2372 this.stack = []; 2373 this.index = -1; 2374 } 2375 2376 if ( History$$1 ) AbstractHistory.__proto__ = History$$1; 2377 AbstractHistory.prototype = Object.create( History$$1 && History$$1.prototype ); 2378 AbstractHistory.prototype.constructor = AbstractHistory; 2379 2380 AbstractHistory.prototype.push = function push (location, onComplete, onAbort) { 2381 var this$1 = this; 2382 2383 this.transitionTo(location, function (route) { 2384 this$1.stack = this$1.stack.slice(0, this$1.index + 1).concat(route); 2385 this$1.index++; 2386 onComplete && onComplete(route); 2387 }, onAbort); 2388 }; 2389 2390 AbstractHistory.prototype.replace = function replace (location, onComplete, onAbort) { 2391 var this$1 = this; 2392 2393 this.transitionTo(location, function (route) { 2394 this$1.stack = this$1.stack.slice(0, this$1.index).concat(route); 2395 onComplete && onComplete(route); 2396 }, onAbort); 2397 }; 2398 2399 AbstractHistory.prototype.go = function go (n) { 2400 var this$1 = this; 2401 2402 var targetIndex = this.index + n; 2403 if (targetIndex < 0 || targetIndex >= this.stack.length) { 2404 return 2405 } 2406 var route = this.stack[targetIndex]; 2407 this.confirmTransition(route, function () { 2408 this$1.index = targetIndex; 2409 this$1.updateRoute(route); 2410 }); 2411 }; 2412 2413 AbstractHistory.prototype.getCurrentLocation = function getCurrentLocation () { 2414 var current = this.stack[this.stack.length - 1]; 2415 return current ? current.fullPath : '/' 2416 }; 2417 2418 AbstractHistory.prototype.ensureURL = function ensureURL () { 2419 // noop 2420 }; 2421 2422 return AbstractHistory; 2423 }(History)); 2424 2425 /* */ 2426 2427 var VueRouter = function VueRouter (options) { 2428 if ( options === void 0 ) options = {}; 2429 2430 this.app = null; 2431 this.apps = []; 2432 this.options = options; 2433 this.beforeHooks = []; 2434 this.resolveHooks = []; 2435 this.afterHooks = []; 2436 this.matcher = createMatcher(options.routes || [], this); 2437 2438 var mode = options.mode || 'hash'; 2439 this.fallback = mode === 'history' && !supportsPushState && options.fallback !== false; 2440 if (this.fallback) { 2441 mode = 'hash'; 2442 } 2443 if (!inBrowser) { 2444 mode = 'abstract'; 2445 } 2446 this.mode = mode; 2447 2448 switch (mode) { 2449 case 'history': 2450 this.history = new HTML5History(this, options.base); 2451 break 2452 case 'hash': 2453 this.history = new HashHistory(this, options.base, this.fallback); 2454 break 2455 case 'abstract': 2456 this.history = new AbstractHistory(this, options.base); 2457 break 2458 default: 2459 { 2460 assert(false, ("invalid mode: " + mode)); 2461 } 2462 } 2463 }; 2464 2465 var prototypeAccessors = { currentRoute: { configurable: true } }; 2466 2467 VueRouter.prototype.match = function match ( 2468 raw, 2469 current, 2470 redirectedFrom 2471 ) { 2472 return this.matcher.match(raw, current, redirectedFrom) 2473 }; 2474 2475 prototypeAccessors.currentRoute.get = function () { 2476 return this.history && this.history.current 2477 }; 2478 2479 VueRouter.prototype.init = function init (app /* Vue component instance */) { 2480 var this$1 = this; 2481 2482 "development" !== 'production' && assert( 2483 install.installed, 2484 "not installed. Make sure to call `Vue.use(VueRouter)` " + 2485 "before creating root instance." 2486 ); 2487 2488 this.apps.push(app); 2489 2490 // main app already initialized. 2491 if (this.app) { 2492 return 2493 } 2494 2495 this.app = app; 2496 2497 var history = this.history; 2498 2499 if (history instanceof HTML5History) { 2500 history.transitionTo(history.getCurrentLocation()); 2501 } else if (history instanceof HashHistory) { 2502 var setupHashListener = function () { 2503 history.setupListeners(); 2504 }; 2505 history.transitionTo( 2506 history.getCurrentLocation(), 2507 setupHashListener, 2508 setupHashListener 2509 ); 2510 } 2511 2512 history.listen(function (route) { 2513 this$1.apps.forEach(function (app) { 2514 app._route = route; 2515 }); 2516 }); 2517 }; 2518 2519 VueRouter.prototype.beforeEach = function beforeEach (fn) { 2520 return registerHook(this.beforeHooks, fn) 2521 }; 2522 2523 VueRouter.prototype.beforeResolve = function beforeResolve (fn) { 2524 return registerHook(this.resolveHooks, fn) 2525 }; 2526 2527 VueRouter.prototype.afterEach = function afterEach (fn) { 2528 return registerHook(this.afterHooks, fn) 2529 }; 2530 2531 VueRouter.prototype.onReady = function onReady (cb, errorCb) { 2532 this.history.onReady(cb, errorCb); 2533 }; 2534 2535 VueRouter.prototype.onError = function onError (errorCb) { 2536 this.history.onError(errorCb); 2537 }; 2538 2539 VueRouter.prototype.push = function push (location, onComplete, onAbort) { 2540 this.history.push(location, onComplete, onAbort); 2541 }; 2542 2543 VueRouter.prototype.replace = function replace (location, onComplete, onAbort) { 2544 this.history.replace(location, onComplete, onAbort); 2545 }; 2546 2547 VueRouter.prototype.go = function go (n) { 2548 this.history.go(n); 2549 }; 2550 2551 VueRouter.prototype.back = function back () { 2552 this.go(-1); 2553 }; 2554 2555 VueRouter.prototype.forward = function forward () { 2556 this.go(1); 2557 }; 2558 2559 VueRouter.prototype.getMatchedComponents = function getMatchedComponents (to) { 2560 var route = to 2561 ? to.matched 2562 ? to 2563 : this.resolve(to).route 2564 : this.currentRoute; 2565 if (!route) { 2566 return [] 2567 } 2568 return [].concat.apply([], route.matched.map(function (m) { 2569 return Object.keys(m.components).map(function (key) { 2570 return m.components[key] 2571 }) 2572 })) 2573 }; 2574 2575 VueRouter.prototype.resolve = function resolve ( 2576 to, 2577 current, 2578 append 2579 ) { 2580 var location = normalizeLocation( 2581 to, 2582 current || this.history.current, 2583 append, 2584 this 2585 ); 2586 var route = this.match(location, current); 2587 var fullPath = route.redirectedFrom || route.fullPath; 2588 var base = this.history.base; 2589 var href = createHref(base, fullPath, this.mode); 2590 return { 2591 location: location, 2592 route: route, 2593 href: href, 2594 // for backwards compat 2595 normalizedTo: location, 2596 resolved: route 2597 } 2598 }; 2599 2600 VueRouter.prototype.addRoutes = function addRoutes (routes) { 2601 this.matcher.addRoutes(routes); 2602 if (this.history.current !== START) { 2603 this.history.transitionTo(this.history.getCurrentLocation()); 2604 } 2605 }; 2606 2607 Object.defineProperties( VueRouter.prototype, prototypeAccessors ); 2608 2609 function registerHook (list, fn) { 2610 list.push(fn); 2611 return function () { 2612 var i = list.indexOf(fn); 2613 if (i > -1) { list.splice(i, 1); } 2614 } 2615 } 2616 2617 function createHref (base, fullPath, mode) { 2618 var path = mode === 'hash' ? '#' + fullPath : fullPath; 2619 return base ? cleanPath(base + '/' + path) : path 2620 } 2621 2622 VueRouter.install = install; 2623 VueRouter.version = '3.0.1'; 2624 2625 if (inBrowser && window.Vue) { 2626 window.Vue.use(VueRouter); 2627 } 2628 2629 return VueRouter; 2630 2631 })));
vue-router的使用要分为六步:
1.引用js组件
2.定义组件
3.将组件注册到路由中
4.在vue中使用路由
5.使用连接来设置路由
6.设置占位
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <meta http-equiv="X-UA-Compatible" content="ie=edge"> 7 <title>Document</title> 8 <script src="../vue2.4.4.js"></script> 9 <!-- 1.引入文件 --> 10 <script src="../vue-router.js"></script> 11 </head> 12 13 <body> 14 <!-- 定义一个vue的管理区块,(MVVM中的View) --> 15 <div id="app"> 16 <!-- 5.这个是vueRouter中为我们提供的一个超链接,可以帮助我们点击, 17 点击以后跳转到相应的组件中 18 并且在组件中的内容展示在占位router-view --> 19 <router-link to="/login">登录</router-link> 20 <router-link to="/register">注册</router-link> 21 <!-- 6.设置占位 --> 22 <router-view></router-view> 23 </div> 24 25 </body> 26 27 <script> 28 //2.定义组件 29 var login = Vue.extend({ 30 template:"<h1>login</h1>" 31 }); 32 var register = Vue.extend({ 33 template:"<h1>register</h1>" 34 }); 35 36 // 3.将组件注册到路由中 37 var vueRouter = new VueRouter({//注册路由了 38 routes:[ 39 {path:"/login",component:login}, 40 {path:"/register",component:register}, 41 42 ]//数组,说明这里可以注册多个路由 43 }); 44 45 // 实例化vue对象(MVVM中的View Model) 46 new Vue({ 47 // vm控制的区块为id为app的div,此div中的所有vue指令均可以被vm解析 48 el:'#app', 49 data:{ 50 // 数据 (MVVM中的Model) 51 }, 52 // 4.在vue中使用路由 53 router:vueRouter 54 55 }) 56 </script> 57 </html>
二.使用vue中的路由给组件传递参数
参数传递分两步:
1.第一步修改路由:
在路由的路径后面加一个"/:" +键名
2.将来在设置路由的连接上必须传入这个参数
参数接受只要一步
通过以下代码接收:
this.$route.params.name
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <meta http-equiv="X-UA-Compatible" content="ie=edge"> 7 <title>Document</title> 8 <script src="../vue2.4.4.js"></script> 9 <!-- 1.引入文件 --> 10 <script src="../vue-router.js"></script> 11 </head> 12 13 <body> 14 <!-- 定义一个vue的管理区块,(MVVM中的View) --> 15 <div id="app"> 16 <!-- 5.这个是vueRouter中为我们提供的一个超链接,可以帮助我们点击, 17 点击以后跳转到相应的组件中 18 并且在组件中的内容展示在占位router-view --> 19 <router-link to="/login/nack">登录</router-link> 20 <router-link to="/register/20">注册</router-link> 21 <!-- 6.设置占位 --> 22 <router-view></router-view> 23 </div> 24 25 </body> 26 27 <script> 28 //2.定义组件 29 var login = Vue.extend({ 30 template:"<h1>login</h1>", 31 mounted:function(){ 32 var name = this.$route.params.name; 33 alert(name); 34 } 35 }); 36 var register = Vue.extend({ 37 template:"<h1>register</h1>", 38 mounted:function(){ 39 var age = this.$route.params.age; 40 alert(age); 41 } 42 }); 43 44 // 3.将组件注册到路由中 45 var vueRouter = new VueRouter({//注册路由了 46 routes:[ 47 {path:"/login/:name",component:login}, 48 {path:"/register/:age",component:register}, 49 50 ]//数组,说明这里可以注册多个路由 51 }); 52 53 // 实例化vue对象(MVVM中的View Model) 54 new Vue({ 55 // vm控制的区块为id为app的div,此div中的所有vue指令均可以被vm解析 56 el:'#app', 57 data:{ 58 // 数据 (MVVM中的Model) 59 }, 60 // 4.在vue中使用路由 61 router:vueRouter 62 63 }) 64 </script> 65 </html>
三.使用嵌套路由完成路由设置
嵌套组件:
1.修改路由
2.修改链接
3.在父组件中添加占位
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <meta http-equiv="X-UA-Compatible" content="ie=edge"> 7 <title>Document</title> 8 <script src="../vue2.4.4.js"></script> 9 <!-- 1.引入文件 --> 10 <script src="../vue-router.js"></script> 11 </head> 12 13 <body> 14 <!-- 定义一个vue的管理区块,(MVVM中的View) --> 15 <div id="app"> 16 <!-- 5.这个是vueRouter中为我们提供的一个超链接,可以帮助我们点击, 17 点击以后跳转到相应的组件中 18 并且在组件中的内容展示在占位router-view --> 19 <router-link to="/account/login">登录</router-link> 20 <router-link to="/account/register">注册</router-link> 21 <!-- 6.设置占位 --> 22 <router-view></router-view> 23 </div> 24 25 26 </body> 27 28 <script> 29 //2.定义组件 30 var account = Vue.extend({ 31 template:"<h1>account<router-view></router-view></h1>", 32 }); 33 var login = Vue.extend({ 34 template:"<h1>login</h1>", 35 }); 36 var register = Vue.extend({ 37 template:"<h1>register</h1>", 38 }); 39 40 // 3.将组件注册到路由中 41 var vueRouter = new VueRouter({//注册路由了 42 routes:[ 43 {path:"/",redirect:"/account"}, 44 // 在routers中只注册一个路由,将login和register加入这个路由中当作它的子路由(子组件) 45 {path:"/account",component:account,children:[ 46 {path:"login",component:login}, 47 {path:"register",component:register} 48 ]}, 49 ]//数组,说明这里可以注册多个路由 50 }); 51 52 // 实例化vue对象(MVVM中的View Model) 53 new Vue({ 54 // vm控制的区块为id为app的div,此div中的所有vue指令均可以被vm解析 55 el:'#app', 56 data:{ 57 // 数据 (MVVM中的Model) 58 }, 59 // 4.在vue中使用路由 60 router:vueRouter 61 }) 62 </script> 63 </html>