1、hash模式和history模式的区别
-
表现形式的区别
hash模式路径中带#,#后内容作为路由地址
history模式正常路径 需要服务端配置支持 -
原理的区别
hash模式基于锚点,以及onhaschange事件,根据路由地址不同呈现不同内容
history模式基于HTML5中的history API
- history.pushState() IE10以后支持
- history.replaceState()
2、history 模式的使用
- 需要服务器的支持
- 单页面应用中,服务端不存在http://www.testurl.com/login这样的地址 如果返回找不到该地址
- 在服务端应该除了静态资源外都返回单页面应用的index.html
3、vue-router实现原理
Hash模式
- URL中 # 后面的内容作为路由地址,
直接通过 loacltion.url 切换浏览器地址,如果只改变了#后的地址,浏览器不会向服务器请求这个地址,会把这个地址记录到浏览器的访问历史中 - 监听 hashchange 事件 ,记录当前路由地址
- 根据当前路由地址找到对应组件重新渲染
History模式
- 通过 history.pushState() 方法改变地址栏 ,将地址记录到浏览器的访问历史中
- 监听 popstate 事件 ,监听浏览器历史记录改变
- 根据当前路由地址找到对应的组件重新渲染页面
位图
vue的构建版本
- 运行时版本:不支持template模板,需要打包时提前编译 编译时把template转换成render函数
运行时使用完整版vue 在根目录下创建一个vue.config.js文件,将runtimeCompiler设置为true
module.exports={
//选项...
runtimeCompiler:true
}
- 完整版:包含运行时和编译器,体积比运行时版本大10K左右,程序运行时把模板转换成render函数
- vue-cli使用的是运行时版本
- 如何切换完整版vue
创建vuerouter/index.js文件
//手写vue-router
let _Vue=null
//1、导出一个VueRouter类
export default class VueRouter{
//静态方法 install() 传递两个参数 一个是vue的构造函数 第二个参数可选
static install(Vue){ //方法和函数本身也是一个对象
//1、判断当前插件是否已经被安装
//需要一个变量记录是否安装 installed
if(VueRouter.install.installed){
return
}
VueRouter.install.installed=true //表示当前插件被安装
//2、把接收的Vue构造函数记录到全局变量 因为在下面vueRouter的实例方法中还要使用
_Vue=Vue
//3、把创建Vue实例时候传入的router对象注入到所有的vue的实例上
//混入
_Vue.mixin({ //给所有实例混入一个选项
//所有的vue实例所有的组件都会执行汇入的beforecreate这个钩子函数
beforeCreate(){ //beforeCreate中的this就是vue实例
//只需要执行一次 组件不执行 vue实例执行 组件的选项中没有router
if(this.$options.router){
_Vue.prototype.$router=this.$options.router
//此时this指向vm
//this.$options.router指向的才是挂载之前我们自定义的Vuerouter类
this.$options.router.init()
}
}
})
}
constructor (options) {
//记录构造函数中传入的选项options
this.options=options
//把options中的路由规则rules解析到routeMap中存储
//routeMap中内容是一个个键值对 键是路由地址 值是路由组件
//在VueRouter这个组件中根据当前的路由地址来routeMap中找到路由组件 渲染出来
this.routeMap={}
//data应该是一个响应式对象
//vue中observable方法 创建一个响应式对象
//创建的响应式对象可以用在渲染函数或计算属性中使用
this.data=_Vue.observable({//传入一个对象 内部转换成响应式对象
current:'/' //存储当前的路由地址 默认是/首页
})
}
init(){
this.createRouteMap()
this.initComponents(_Vue)
this.initEvent()
}
createRouteMap(){
//遍历所有的路由规则,把路由规则解析成键值对,存储到routeMap中
//this.options中routes所有的路由规则
this.options.routes.forEach(route=>{
this.routeMap[route.path]=route.component
})
}
initComponents(Vue){//Vue vue的构造函数
//创建router-link组件
Vue.component('router-link',{
props:{
to:String,
},
//运行时版本使用render函数
render(h){ //参数是一个h函数
return h('a',{ //第一个参数要创建的元素的选择器
attrs:{
href:this.to //要跳转的地址
}, //注册元素的属性
on:{
click:this.clickHandler
}//注册事件
},[this.$slots.default]) //生成的元素的子元素 默认插槽
},
methods:{
clickHandler(e){
//1、调用history.pushState() 改变地址栏但是不会向服务器发送请求
//data:触发pushState事件的时候,传给这个事件一个事件对象的参数
//title:网页标题
//当前要跳转的地址 this.to
history.pushState({},'',this.to)
//改变当前路径记录到当前路由地址 data.current中
//因为当前在router-link这个组件中 this是router-link
//当前router-link也是一个实例,之前我们通过mixin将$router挂载到了所有实例上
//所以当前使用this.$router.data.current即可
this.$router.data.current=this.to
e.preventDefault() //阻止a标签的默认事件--跳转
}
}
//template:'<a :href="to"><slot></slot></a>' //:href='to' :href-->href绑定参数to
})
const self=this
Vue.component('router-view',{
render(h){ //h函数创建虚拟Dom
//找到当前路由的地址
const component=self.routeMap[self.data.current]
return h(component) //返回虚拟Dom h函数可以将组建转换成虚拟Dom
}
})
}
initEvent(){ //注册popstate事件 监听浏览器箭头前进后退
window.addEventListener('popstate',()=>{
//因为此时使用的箭头函数 箭头函数不会改变this的指向
this.data.current=window.location.pathname //获取浏览器地址栏路径部分即文本地址,存储到data.current中
})
}
}
在router/index.js文件中导入自定义的vueRouter
import VueRouter from '../vuerouter'