2020-09-21

前端路由

路由的概念来源于服务端,在服务端在中描述的是URL与处理函数之间的映射关系。
在web前端单页面应用中,路由描述的是URL与UI之间的映射关系,这种映射是单向的,即URL变化引起UI更新(无需刷新页面)

如何实现前端路由

  • 如何改变URL却不引起页面刷新?
  • 如何检测URL变化?
hash实现
  • hash是URL中的hash(#)及后面的那部分,常用作锚点在页面内导航,改变URL的hash部分不会引起页面刷新
  • 通过hashchange事件监听URL的变化,改变URL的方式只有几种:通过浏览器前进后退改变URL、通过a标签改变URL、通过window.location改变URL,这几种情况改变URL都会触发hashchange事件
  <ul>
        <!-- 定义路由 -->
        <li><a href="#/home">home</a></li>
        <li><a href="#/about">about</a></li>    
        <!-- 渲染路由对应的 UI -->
        <div id="routerView"></div>
 </ul>  
//页面加载完不会促发hashchange, 这里主动触发一次hashchange
window.addEventListener('DOMContentLoaded',onLoad)
var routerView = null
function onLoad(){
 routerView.document.querySelector('#routerView')
 onHashChange
}
//监听URL的改变
window.addEventListern('hashchange',onHashChange)
//路由变化时,根据URL更新UI视图
function onHashChange(){
	switch(location.hash){
		case '#/home':
		 routerView.innerHTML = "我是home页面";
		 return;
		 case '#/about':
		 routerView.innerHTML = "我是about页面"
		 return;
		 default: return;		 
	}

}
history实现
  • history提供给了pushState和replaceState两个方法,这两个方法改变URL的path部分不会引起页面刷新
  • history提供类似hashchange事件的popstate事件,但popstate事件有些不同:通过浏览器前进改变URL时会触发popstate事件,通过pushState/replaceState或a标签改变URL不会触发popstate事件。好在我们可以拦截pushState/replaceState的调用和 a 标签的点击事件来检测URL变化,所以监听URL变化可以实现,只是没有hashchange那么方便。
function onLoad(){
	routerView.document.querySelector('#routerView')
	onPopState()
	//拦截a标签点击事件默认行为,点击时使用pushState修改URL并更新手动UI,从而实现点击链接更新URL和UI的效果
	vae linkList = document.querySelector('a[href]')	
	linkList.forEach(el => el.addEventListener('click',function(e){
	e.preventDefault()
	history.pushState(null,'', el.getAttribute('href'))
	onPopState()
}))
}
//history提供popstate监听URL的变化(前进后退改变URL时触发)
function onPopState(){
	switch(location.pathname){
		case '/home':
		 routerView.innerHTML = 'history模式下:我是home页面'
		 return
		case '/about':
		 routerView.innerHTML = 'history模式下: 我是about页面'
		 return
		default: return
	}
}

Vue版本前端路由实现

基于hash实现
使用方式和vue-router类似(vue-router通过插件机制注入路由,但是这样隐藏了实现细节,为了保持代码只管,这里没有使用vue插件封装)

<div>
      <ul>
        <li><router-link to="/home">home</router-link></li>
        <li><router-link to="/about">about</router-link></li>
      </ul>
      <router-view></router-view>
    </div>
const routes = {
	'/home': {
		template: '<h2>Home</h2>'
	},{
	'/about': {
		template: '<h2>About</h2>'
	}
	}
}

const app = new Vue({
  el: '.vue.hash',
  components: {
    'router-view': RouterView,
    'router-link': RouterLink
  },
  beforeCreate () {
    this.$routes = routes
  }
})

router-view实现

<template>
  <component :is="routeView" />
</template>

<script>
import utils from '~/utils.js'
export default {
  data () {
    return {
      routeView: null
    }
  },
  created () {
    this.boundHashChange = this.onHashChange.bind(this)
  },
  beforeMount () {
    window.addEventListener('hashchange', this.boundHashChange)
  },
  mounted () {
    this.onHashChange()
  },
  beforeDestroy() {
    window.removeEventListener('hashchange', this.boundHashChange)
  },
  methods: {
    onHashChange () {
      const path = utils.extractHashPath(window.location.href)
      this.routeView = this.$root.$routes[path] || null
      console.log('vue:hashchange:', path)
    }
  }
}
</script>
<template>
  <a @click.prevent="onClick" href=''><slot></slot></a>
</template>

<script>
export default {
  props: {
    to: String
  },
  methods: {
    onClick () {
      window.location.hash = '#' + this.to
    }
  }
}
</script>
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页