简易Vue-Router源码实现

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);
    },
});
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值