Hash模式
1、hash即URL中#后面的部分。
2、如果网页URL带有hash,页面会定位到id与hash一样的元素的位置,即锚点
3、hash的改变时,页面不会重新加载,会触发hashchange事件,而且也会被记录到浏览器的历史记录中
4、vue-router的hash模式,主要就是通过监听hashchange事件,根据hash值找到对应的组件进行渲染(源码里会先判断浏览器支不支持popstate事件,如果支持,则是通过监听popstate事件,如果不支持,则监听hashchange事件)
hash模式界面跳转不刷新
根据http协议所示,url中hash改变请求是不会发送到服务端的,不管你怎么location跳转,或者url上直接加上hash值回车,他都一样固执,就是不和服务器老大说。
但是我们的系统里又引入了vue-router,其中hashchange这个事件监听到了hash的变化,从而触发了组件的更新,也就绘制出了相应的页面
History模式
1、通过history.pushState修改页面地址
2、当history改变时会触发popstate事件,所以可以通过监听popstate事件获取路由地址
3、根据路由地址找到对应组件进行渲染
history模式,每次点击菜单导航会界面刷新?
看一下正确的history模式下,首页刷新到显示的整体流程:
1.将这个完整的url发到服务器nginx
2.ngix需要配置用这个uri在指给前端index.html(因为根本没有任何一个服务器提供了这个url路由,如果直接访问的话就是404,所以就要指回给前端,让前端自己去根据path来显示)
location / {
root /usr/share/nginx/html/store;//项目存放的地址
index index.html index.htm;
try_files $uri $uri/ /index.html;//history模式下,需要配置它
}
所以try_files $uri $uri/的意思就是,比如http://test.com/example先去查找单个文件example,如果example不存在,则去查找同名的文件目录/example/,如果再不存在,将进行重定向index.html(只有最后一个参数可以引起一个内部重定向)
凡是404的路由,都会被重定向到index.html,这样就显示正确了
3.此时nginx将这个请求指回了前端的index.html,index.html中开始加载js,js中已有vue-router的代码,vue-router自动触发了popstate这个事件,在这个事件回调中,绘制了这个path下对应的页面组件
vue-router使用
1.注册vue-router插件
import VueRouter from 'vue-router'
// 注册VueRouter插件
// Vue.use方法
// 1、如果传入的是方法,则调用传入的方法
// 2、如果传入的是对象,则会调用插件的install静态方法,并传入Vue构造函数
Vue.use(VueRouter)
2.创建Router实例
// 创建路由表
const routes = [
{
path: '/home',
component: Home,
},
{
path: '/about',
component: About,
},
]
// 实例化路由对象
const router = new VueRouter({
mode: 'hash',
routes
})
3.Vue实例上挂载router实例
// 实例化Vue时,在实例上注册router对象
new Vue({
render: h => h(App),
router
}).$mount('#app')
组件中使用
<router-view></router-view>
<router-link to="/home"><route-link>
// 通过this.$router获取路由对象
总结,VueRouter需要做以下这些事情
1、实现install静态方法
2、根据传入的路由配置,生成对应的路由映射
3、给Vue实例挂载router实例
4、注册全局组件和, router-view组件通过当前url找到对应组件进行渲染,并且url改变时,重新渲染组件,router-link则渲染为a标签
5、通过currentUrl变量保存当前url,并使数据变为响应式
6、监听hashchange或popState事件,浏览器记录改变时重新渲染router-view组件
代码实现
//1.实现一个插件
//2.要求必须要是一个install方法,将来会被vue调用
//3.两个组件 router-link router-view
let Vue; //保存Vue的构造函数,在插件中要使用
class VueRouter {
constructor(options) {
this.$options = options;
}
}
//插件:实现install方法,注册$router
//参数一:是Vue.use调用时传入的(参数_Vue是vue构造函数)
VueRouter.install = (_Vue) => {
Vue = _Vue;
// 1.挂载$router属性
//this.$router.push()
//全局混入(延迟下面的逻辑到router创建完毕并且附加到选项上时才执行)
Vue.mixin({
beforeCreate() {
//注意此钩子在每个创建实例的时候都会被调用
//根实例才有该选项
if (this.$options.router) {
Vue.prototype.$router = this.$options.router;
}
},
});
//2.实现两个组件:router-link,router-view
//<router-link to="/">home</router-link> ==> <a href="/">home</a>
Vue.component("router-link", {});
Vue.component("router-view", {});
};
export default VueRouter;
为什么要用混入方式写?为了在组件创建的时候给vue实例的原型绑定这个router实例,所以咱们就可以在vue组件的地方使用this.$router
创建router-link和router-view
Vue.component("router-link", {
props: {
to: {
type: String,
required: true,
},
},
render(h) {
return h(
"a",
{
attrs: {
href: `#${this.to}`,
},
},
this.$slots.default
);
},
});
Vue.component("router-view", {
render(h) {
let component = null;
//暂时先不渲染任何内容
return h(component);
},
});
监控URL变化
定义响应式的current属性,监听hashchang事件
class VueRouter {
constructor(options) {
this.$options = options;
//把this.current变为响应式的数据
//将来数据发生变化,router-view的render函数能够再次执行
let initial = window.location.hash.slice(1) || "/";
//把current变成响应式数据
Vue.util.defineReactive(this, "current", initial);
//将来数据一旦发生变化,router-view的render函数能够重新执行
this.current = "/";
window.addEventListener("hashchange", () => {
this.current = window.location.hash.slice(1) || "/";
console.log("current", this.current);
});
}
}
动态获取对应的组件,router-view组件
Vue.component("router-view", {
render(h) {
let component = null;
let { current, $options } = this.$router;
let routeItem = $options.routes.find((item) => item.path == current);
routeItem && (component = routeItem.component);
//获取当前路由所对应的组件并将他渲染出来
return h(component);
},
});