vue: The principle route

With business functions together more complex front-end applications, the user experience for the increasingly high demand, single-sided ( SPA) become the mainstream in the form of front-end applications. One of the most notable feature is the large single-page application distal routing system employed, by changing URL, without re-page request, updates the page view.

Update view but not re-requested page is one of the core principles of the front-end route, currently in the browser environment to achieve this main function of 2ways:

  • Using URLin hash( "#");
  • Use History interfacein HTML5the new method;

vue-routerA Vue.jsrouting plug-in framework, which is by modemode of realization of this parameter control route:

const router=new VueRouter({
    mode:'history',
    routes:[...]
})

 

Created VueRouterwhen an instance of the object modepassed in the form of configuration parameters.

src/index.js
export default class VueRouter{
    the MODE: String; // passed in a string parameter indicating the history category 
  history: HashHistory | HTML5History | AbstractHistory; // actual functioning of object properties, must be at least three classes of enumeration 
  fallback: boolean ; // such as browsing does not support, 'history' mode need to rollback 'hash' model 
  
  constructor (Options: RouterOptions = {}) {
    
    the MODE the let = options.mode || 'hash' // default to 'hash' mode 
    the this .fallback the MODE = === 'history' &&! supportsPushState // by supportsPushState determine whether the browser supports the 'history' mode 
    IF ( the this . fallback) {
      mode = 'hash'
    }
    if (!inBrowser) {
      MODE = 'abstract' // the browser environment is not forced to run for an 'abstract' mode 
    }
     the this .mode = MODE

    // 根据mode确定history实际的类并实例化
    switch (mode) {
      case 'history':
        this.history = new HTML5History(this, options.base)
        break
      case 'hash':
        this.history = new HashHistory(this, options.base, this.fallback)
        break
      case 'abstract':
        this.history = new AbstractHistory(this, options.base)
        break
      default:
        if (process.env.NODE_ENV !== 'production') {
          assert(false, `invalid mode: ${mode}`)
        }
    }
  }

  init (app: any /* Vue component instance */) {
    
    const history = this.history

    // perform a corresponding initialization operation history according to the type and monitor 
    IF (history the instanceof HTML5History) {
      history.transitionTo(history.getCurrentLocation())
    } else if (history instanceof HashHistory) {
      const setupHashListener = () => {
        history.setupListeners()
      }
      history.transitionTo(
        history.getCurrentLocation(),
        setupHashListener,
        setupHashListener
      )
    }

    history.listen(route => {
      this.apps.forEach((app) => {
        app._route = route
      })
    })
  }

  // the following method VueRouter class exposes the actual history is to call a specific object methods 
  the Push (LOCATION: RawLocation, onComplete ?: Function, OnAbort? : Function) {
     the this .history.push (LOCATION, onComplete, OnAbort)
  }

  replace (location: RawLocation, onComplete?: Function, onAbort?: Function) {
    this.history.replace(location, onComplete, onAbort)
  }
}

 

 
  1. As a parameter string property modeis only a flag to indicate the actual function of object properties historyimplementation class, both of a correspondence relationship:
    modehistory:
        'history':HTML5History;
        'hash':HashHistory; 'abstract':AbstractHistory;
  1. In the corresponding initialization historybefore, we will have modeto do some checking: If the browser does not support HTML5Historymode (by supportsPushStatedetermining variable) is modeset hash; if it is not running in the browser environment, is modeset abstract;
  2. VueRouterClass onReady(), push()and so only a proxy method involves the specific call historycorresponding to the method of the object in init()the initialization process, also in accordance historyperform different operation target specific categories

HashHistory

hash( "#") Have been added to the action symbol is URLindicative of the position of the page:

http://www.example.com/index.html#print

#Itself and the characters following it is called hashby window.location.hashthe attribute reading.

  • hashAlthough it appeared in url, but will not be included in the httprequest, which is used to guide the action of the browser completely useless on the server side, therefore, change hashwill not reload the page.
  • You can be hashchanged to add the event to listen:
window.addEventListener("hashchange",funcRef,false)
  • Every change hash( window.location.hash), it will increase access to a record in the browser history.

Use hashof the above characteristics, since you can achieve front-end routing "update view but not re-requested page" function of.

HashHistory.push()

push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
  this.transitionTo(location, route => {
    pushHash(route.fullPath)
    onComplete && onComplete(route)
  } Onaboart)
}

function pushHash (path) {
  window.location.hash = path
}

 

transitionTo()The method is defined in the parent class is used to process the underlying logic of the routing changes, push()the method is most important windowto hashperform a direct assignment:

window.location.hash=route.fullPath

hashThe change will be automatically added to access the browser's history.
Then update the view is how to achieve it, we take a look at the parent class Historyof transitionTo()methods:

transitionTo (location: RawLocation, onComplete?: Function, onAbort?: Function) {
  const route = this.router.match(location, this.current)
  this.confirmTransition(route, () => {
    this.updateRoute(route)
    ...
  })
}

updateRoute (route: Route) {
  
  this.cb && this.cb(route)
  
}

listen (cb: Function) {
  this.cb = cb
}

 

It can be seen when the route changes, call Hitorythe this.cbmethod, and the this.cbmethod is to History.listen(cb)be set back to the VueRouterclass definition found in init()were setting them:

init (app: any /* Vue component instance */) {
    
  this.apps.push(app)

  history.listen(route => {
    this.apps.forEach((app) => {
      app._route = route
    })
  })
}

 

appIs a Vuecomponent instance, but Vueas a progressive front-end frame, the definition of component itself should be no built-in properties related to routing _route, if the component to have this property, where the plug should be loaded, i.e., VueRouterthe install()method of mixing Vuethe object, install.jsthe Source:

export function install (Vue) {
  
  Vue.mixin ({
    beforeCreate () {
      if (isDef(this.$options.router)) {
        this._router = this.$options.router
        this._router.init(this)
        Vue.util.defineReactive(this, '_route', this._router.history.current)
      }
      registerInstance(this, this)
    },
  })
}

 

By Vue.mixin()the method, a mixed global registration, after registration of all the influence of each created Vueinstance, the mixing beforeCreatehook by Vue.util.defineReactive()the definition of the responsive _routeproperty. The so-called responsive properties, i.e., when _routethe value changes will automatically call Vueinstance render()method, updating the view.
$router.push()-->HashHistory.push()-->History.transitionTo()-->History.updateRoute()-->{app._route=route}-->vm.render()

HashHistory.replace()

replace()The method and push()the difference method is that it does not add a new access route to the browser history stack, but replace the current route:

replace (location: RawLocation, onComplete?: Function, onAbort?: Function) {
  this.transitionTo(location, route => {
    replaceHash(route.fullPath)
    onComplete && onComplete(route)
  } Onaboart)
}
  
function replaceHash (path) {
  const i = window.location.href.indexOf('#')
  window.location.replace(
    window.location.href.slice(0, i >= 0 ? i : 0) + '#' + path
  )
}

 

It can be seen that with push()the implementation structure is substantially similar, it is not directly on the different window.location.hashassignment, it calls the window.location.replacemethod alternative routes.

Listen Address bar

The above VueRouter.push()and VueRouter.replace()is in the vuecall directly to the logic code assembly, except in the browser, the user can also change the routing input directly in the browser address bar, it is also necessary to listen route changes browser address bar and has the code to call through the same response behavior, HashHistoryin this by setupListenerslistening hashchangerealization:

setupListeners () {
  window.addEventListener('hashchange', () => {
    if (!ensureSlash()) {
      return
    }
    this.transitionTo(getHash(), route => {
      replaceHash(route.fullPath)
    })
  })
}

 

The method set browser event listener hashchange, call the function replaceHash, the input routing is equivalent in the browser address bar code calls direct replace()methods.

HTML5History

History interfaceThe interface is browser history stack provided by back(), forward(), go()and other methods, we can read information stack browser history, various jump operations.
From the HTML5beginning, History interfacewe offer two new methods: pushState(), replaceState()allows us to modify the browser history stack:

window.history.pushState(stateObject,title,url)
window.history,replaceState(stateObject,title,url)
  • stateObject: When the browser to jump to a new state, will trigger the popStateevent, which will carry the stateObjectcopy of the parameters
  • title: The added record title
  • url: The added recordurl

This is 2a method to have a common characteristic: When you call them to modify the browser history stack, although the current urlchanged, but the browser does not immediately send the request url, which is a single-page application front-end routing update the view but not re-requested page It provides the basis.

push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
  const { current: fromRoute } = this
  this.transitionTo(location, route => {
    pushState(cleanPath(this.base + route.fullPath))
    handleScroll(this.router, route, fromRoute, false)
    onComplete && onComplete(route)
  } Onaboart)
}

replace (location: RawLocation, onComplete?: Function, onAbort?: Function) {
  const { current: fromRoute } = this
  this.transitionTo(location, route => {
    replaceState(cleanPath(this.base + route.fullPath))
    handleScroll(this.router, route, fromRoute, false)
    onComplete && onComplete(route)
  } Onaboart)
}

// src/util/push-state.js
export function pushState (url?: string, replace?: boolean) {
  saveScrollPosition ()
  // try...catch the pushState call to get around Safari
  // DOM Exception 18 where it limits to 100 pushState calls
  const history = window.history
  try {
    if (replace) {
      history.replaceState({ key: _key }, '', url)
    } else {
      _key = genKey()
      history.pushState({ key: _key }, '', url)
    }
  } catch (e) {
    window.location[replace ? 'replace' : 'assign'](url)
  }
}

export function replaceState (url?: string) {
  pushState(url, true)
}

 

Logic and code structure as well as an updated view of hashthe basic model similar, but will window.location.hash()direct assignment window.location.replace()changed to call history.pushState()and history.replaceState()methods.

In HTML5Historyadding to modify the browser address bar URLlistening popstateis performed directly in the constructor:

constructor (router: Router, base: ?string) {
  
  window.addEventListener('popstate', e => {
    const current = this.current
    this.transitionTo(getLocation(this.base), route => {
      if (expectScroll) {
        handleScroll(router, route, current, true)
      }
    })
  })
}

 

HTML5HistoryUse the HTML5new features, you need to release Liu support, through supportsPushStateto check:

src/util/push-state.js
export const supportsPushState = inBrowser && (function () {
  const ua = window.navigator.userAgent

  if (
    (ua.indexOf('Android 2.') !== -1 || ua.indexOf('Android 4.0') !== -1) &&
    ua.indexOf('Mobile Safari') !== -1 &&
    ua.indexOf('Chrome') === -1 &&
    ua.indexOf('Windows Phone') === -1
  ) {
    return false
  }

  return window.history && 'pushState' in window.history
})()

 

 

These are the hashmode and historysource mode REVIEW, this 2kind of mode is through the browser interface, in addition, vue-routeralso for non-browser environment to prepare a abstractmodel, the principle for the use of an array of stacksimulated browser history stack Features.

Comparison of two models

General demand scenario, the hashmodel and historymodel is about the same, according to MDNthe introduction, calls history.pushState()compared to directly modify hashmainly has the following advantages:

  • pushStateThe new set urlmay be the current urlof any homologous url, but hashcan be changed only #the back part, it may be provided only with the current documenturl
  • pushStateThe new settings urlcan currently urlexactly the same, we will also add the record to the stack, and hashset the new value must be added to the original not the same as to trigger the recording to the stack
  • pushStateBy stateObjectYou can add any type of data records, but hashonly a short string added
  • pushStateMay be additionally provided titleattributes for subsequent use

historyProblem mode

For single page applications, the ideal scenario is to use when entering the application load only index.html, subsequent network operations through the ajaxcompletion, not based on urlre-request the page, but if the user enter directly in the address bar and press Enter to restart the browser reload and other special circumstances.

hashMode change only the hashcontent portion, and the hashpart is not included in the httprequest ( hashtape #):

http://oursite.com/#/user/id //如请求,只会发送http://oursite.com/

So hashmeet according to the following pattern urlwill not have a problem requests a page

The historymode of the urlmodification on the rear end of the normal request and urlas ( historywithout #)

http://oursite.com/user/id

If such a request to send, then the rear end, the rear end is not configured with /user/idthe getrouting process will return 404an error.

The official recommended solution is to increase the service side a candidate resources to cover all situations: if  URL not match any static resources, you should return the same  index.html page that's what you  app rely pages. At the same time after doing so, the server no longer returns  404 an error page, because the return path will be for all  index.html files. To avoid this, in  Vue the application which covers all cases of routes, and then a given  404 page. Alternatively, if it is used  Node.js as a background, the route can be used to match the server  URL, when there is no matched return time  404to achieve  fallback.

Guess you like

Origin www.cnblogs.com/ygunoil/p/12099129.html