zoukankan      html  css  js  c++  java
  • Vue--vue-Router

    一.vue路由的基本使用

    为什么需要路由?

    因为我们通过component切换组件无法给组件传递参数

    component切换组件

     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>
    View Code

    使用vue路由需要vue-router.js

       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 (/_blank/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 })));
    View Code
    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>
     
  • 相关阅读:
    vue检查用户名是否重复
    后端注册接口完善
    django添加检查用户名和手机号数量接口
    Vue联调,图片及短信验证码
    swift webView 提出这样的要求你能忍吗?
    iOS 如何给Xcode7项目添加“.pch”文件
    swift 定制自己的Button样式
    Swift 为你的webView定制标题
    swift 如何获取webView的内容高度
    如何在MAC上使用SVN,简单几行命令搞定
  • 原文地址:https://www.cnblogs.com/mrszhou/p/7864805.html
Copyright © 2011-2022 走看看