在 Vue 中手动实现一个 vue-router

纵饮孤独 提交于 2020-01-29 09:41:09


本文要点:

  1. 了解单页面应用 router 路由的原理。
  2. 通过实战手动实现一个 vue-router 加深单页面路由的理解。

单页面应用 router 路由实现原理

单页面应用可通过 hash 和 history 两种方式实现路由。实现路由要思考两个问题。第一、如何获取当前路径;第二、如何监听当前路径变化。

1. hash

hash 的方式即浏览器地址栏中含有 # 标志,该标志后面的部分即为 hash 值。示例:http://localhost:8080/#/index

1.1 hash 实现路由的原理

  1. 地址栏中 # 后面的值即为 hash 值。该 hash 值通常为路径 path,可通过 location.hash 获取当前路径。
  2. 监听当前路径变化的方法为 onhashchange

1.2 hash 实现路由的核心步骤

通过 hash 方式在 Vue 中实现一个简易路由,核心步骤如下:

  1. 获取路由参数 options;
  2. 初始化路由集合;
  3. 初始化当前路径,即默认路径;
  4. 当 load 时 和 hash 改变时监听 url 变化(主要是监听 hash 的变化)。
  5. 解析路由配置,即将路径 path 和组件 component 一一对应。
  6. 通过 Vue.component 方法将当前组件挂载到 Vue 中。
  7. 通过 Routes.installVue.mixin 方法将 Vue 实例注入到当前实例中。在 Vue.mixin 中添加 beforeCreate() 路由钩子函数,在 Vue 执行该钩子函数回调时会一起调用启动路由。

1.3 hash 实现路由的完整代码

routes.js
该 js 是实现路由的核心过程

import Vue from 'vue'
export default class Routes {
    constructor(options) {
        this.$options = options;
        this.routeMap = {};
        // 路由响应式
        this.app = new Vue({
            data: {
                //初始化路由为 /
                current: "/"
            }
        });
    }
    init() {
        this.bindEvents(); //监听url变化
        this.createRouteMap(this.$options); //解析路由配置
        this.initComponent(); // 实现组件
    }
    bindEvents() {
        window.addEventListener("load", this.onHashChange.bind(this));
        window.addEventListener("hashchange", this.onHashChange.bind(this));
    }
    onHashChange() {
        //获取当前的 hash 值
        this.app.current = window.location.hash.slice(1) || "/";
    }
    createRouteMap(options) {
        //遍历路由,获取每个 path 对应的组件
        options.routes.forEach(item => {
            this.routeMap[item.path] = item.component;
        });
    }
    initComponent() {
        // <router-view-vue></router-view-vue>
        Vue.component("router-view-vue", {
            render: h => {
                //hash 改变时组件也会改变
                const component = this.routeMap[this.app.current];
                return h(component);
            }
        });
    }
}
Routes.install = function (Vue) {
    // 混入
    Vue.mixin({
        beforeCreate() {
            // 这里的 this 是 Vue 实例
            if (this.$options.router) {
                // 仅在根组件执行一次,添加 push 方法
                Vue.prototype.$router = this.$options.router;
                Vue.prototype.$router.push = function (options) {
                    window.location.hash = "/"+options.name;
                };
                this.$options.router.init();
            }
        }
    });
};

router.js
该 js 是配置路由数组

import Index from './Index.vue';
import Login from './Login.vue';
const router = [
    {path:'/',name:"index",component:Index},
    {path:'/login',name:"login",component:Login},
];
export default router;

main.js
创建 Vue 实例

import Vue from 'vue'
import App from './App.vue'
import routes from './router'
import  Routes from './routes';
//阻止显示生产模式的提示
Vue.config.productionTip = false;
//使用路由
Vue.use(Routes);
//创建路由实例
const router = new Routes({
    routes // routes: routes 的简写
});
new Vue({
  render: h => h(App),
    router
}).$mount('#app')

App.vue
组件入口(根组件)

<template>
  <div id="app">
    <router-view-vue/>
  </div>
</template>

<script>
export default {
  name: 'App',
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

Index.vue
首页

<template>
    <div>
        <h3>首页</h3>
        <div @click="goLogin">
            去登录
        </div>
    </div>
</template>
<script>
    export default {
        name: 'Index',
        methods: {
            goLogin(){
                this.$router.push({name: 'login'});
            }
        }
    }
</script>

Login.vue
登录页

<template>
    <h3>
        登录页
    </h3>
</template>

2. history

history 的方式是浏览器地址栏中不含有 # 标志。示例:http://localhost:8080/index

2.1 history 实现路由的原理

  1. history 是 HTML5 新增的 API 。history.pushState 方法和 history.replaceState 方法可以改变 url 而不发生请求。通过 location.pathname可以获取当前路径。
  2. 监听浏览器前进、后退的方法为 onpopstate,而监听当前路径变化需要注册一个新的全局监听事件。

1.2 history 实现路由的核心步骤

通过 history 方式在 Vue 中实现一个简易路由,核心步骤如下:
同 hash 方法一样,不再赘述。

1.3 history 实现路由的完整代码

同 hash 方法大致一样,以下列出不同之处。

  1. 获取当前路径。在 history 中是通过 location.pathname 获取当前路径。
bindEvents() {
        window.addEventListener("load", this.onPushState.bind(this));
        window.addEventListener("pushState", this.onPushState.bind(this));
        window.addEventListener("popstate", this.onPushState.bind(this));
}
onPushState() {
    //获取当前路径
    this.app.current = location.pathname || "/";
}
  1. 添加 pushState 方法的监听。因为没法直接监听 pushState 的变化,所以只有注册一个全新的监听方法。
var _wr = function(type) {
    var orig = history[type];
    return function() {
        var rv = orig.apply(this, arguments);
        var e = new Event(type);
        e.arguments = arguments;
        window.dispatchEvent(e);
        return rv;
    };
};
history.pushState = _wr('pushState');
  1. push 方法。在 history 中是通过 history.pushState 来实现 push 方法。
 Vue.prototype.$router.push = function (options) {
     history.pushState(null,null,'/'+options.name);
};
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!