重写vue源码:vue-router的实现

0.前端路由原理

1.history模式原理:
利用history.pushState这个api,将路径推进栈中,页面不会刷新,网址会发生改变。然后window监听load和popstate事件,根据location.pathname的值,来替换router-view的内容。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title></title>
  </head>
  <body>
    <a href="/home">主页</a>
    <a href="/about">关于</a>
    <div class="router-view"></div>
    <script src="./history.js"></script>
  </body>
</html>
const router_view = document.querySelector('.router-view');
const a_list = document.querySelectorAll('a');
for (let i = 0; i < a_list.length; i++) {
  a_list[i].onclick = (e) => {
    e.preventDefault();
    const href = e.target.getAttribute('href') || '/';
    history.pushState({}, null, path);
    routerChange(href);
  };
}
window.addEventListener('load', () => {
  routerChange(location.pathname);
});
window.addEventListener('popstate', () => {
  routerChange(location.pathname);
});
function routerChange(path) {
  switch (path) {
    case '/home':
      router_view.innerHTML = 'Home';
      break;
    case '/about':
      router_view.innerHTML = 'About';
      break;
    default:
      router_view.innerHTML = '';
      break;
  }
}


2.hash路由原理:
浏览器改变网址后面的#/xxx部分是不会刷新页面的,我们就可以通过监听hashchange事件,从而变更router-view里面的内容。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title></title>
  </head>
  <body>
    <a href="#/home">主页</a>
    <a href="#/about">关于</a>
    <div class="router-view"></div>
    <script src="./hash.js"></script>
  </body>
</html>
const router_view = document.querySelector('.router-view');
window.addEventListener('load', () => {
  routerChange(location.hash.slice(1));
});
window.addEventListener('hashchange', () => {
  routerChange(location.hash.slice(1));
});
function routerChange(path) {
  switch (path) {
    case '/home':
      router_view.innerHTML = 'Home';
      break;
    case '/about':
      router_view.innerHTML = 'About';
      break;
    default:
      router_view.innerHTML = '';
      break;
  }
}

1.解析vue-router

vue-router的基本使用:

import Vue from 'vue';
import VueRouter from '../extends/my-router';
import Home from '../views/Home.vue';

Vue.use(VueRouter);

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home,
  },
  {
    path: '/about',
    name: 'About',
    // route level code-splitting
    // this generates a separate chunk (about.[hash].js) for this route
    // which is lazy-loaded when the route is visited.
    component: () =>
      import(/* webpackChunkName: "about" */ '../views/About.vue'),
  },
];

const router = new VueRouter({
  mode: 'hash',
  routes,
});

export default router;
  1. Vue.use(VueRouter):表示VueRouter是一个插件,它是一个方法或者VueRouter.install是一个方法
  2. new VueRouter(): 表示VueRouter是一个构造函数,所以它具有VueRouter.install这个一个方法。

2.重写vue-router

class HistoryRoute {
  constructor() {
    this.current = null;
  }
}
class VueRouter {
  constructor(options) {
    this.mode = options.mode || 'hash';
    this.routes = options.routes || [];
    this.routeMap = this.createMap(this.routes);
    this.history = new HistoryRoute();
    this.init();
  }
  init() {
    if (this.mode === 'history') {
      window.addEventListener('popstate', () => {
        this.history.current = location.pathname ? location.pathname : '/';
      });
      window.addEventListener('load', () => {
        this.history.current = location.pathname ? location.pathname : '/';
      });
    } else {
      window.addEventListener('hashchange', () => {
        this.history.current = location.hash ? location.hash.slice(1) : '/';
      });
      window.addEventListener('load', () => {
        this.history.current = location.hash ? location.hash.slice(1) : '/';
      });
    }
  }
  createMap(routes) {
    const routeMap = {};
    routes.forEach((route) => {
      routeMap[route.path] = route.component;
    });
    return routeMap;
  }
}
VueRouter.install = function(Vue) {
  Vue.mixin({
    beforeCreate() {
      if (this.$options && this.$options.router) { // 跟组件组件
        this._root = this;
        this._router = this.$options.router;
        // 我们需要将this._router.history变成响应式的,这样才能动态改变组件
        Vue.util.defineReactive(this, 'xxx', this._router.history);
      } else { // 子组件
        this._root = this.$parent && this.$parent._root;
      }
      // 在每一个组件实例上添加$router
      Object.defineProperty(this, '$router', {
        get: () => {
          return this._root._router;
        },
      });
      // 在每个组件实例上添加$route
      Object.defineProperty(this, '$route', {
        get: () => {
          return this._root_router.history.current;
        },
      });
      // 注册router-link组件
      Vue.component('router-link', {
        props: {
          to: String,
        },
        render(h) {
          return h('a', { attrs: { href: this.to } }, this.$slots.default);
        },
      });
      // 注册router-view组件
      Vue.component('router-view', {
        render(h) {
          console.log(this.$router);
          console.log(this.$router.history.current);
          return h(this.$router.routeMap[this.$router.history.current]);
        },
      });
    },
  });
};
export default VueRouter;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值