Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

实现一个vue-router #45

Open
YIngChenIt opened this issue Jul 3, 2020 · 0 comments
Open

实现一个vue-router #45

YIngChenIt opened this issue Jul 3, 2020 · 0 comments

Comments

@YIngChenIt
Copy link
Owner

实现一个vue-router

vue-router基本用法

我们先来看下vue-router的基本用法

// route/index.js
import VueRouter from 'vue-router'
import Vue from 'vue'

import Home from './../views/Home.vue'
import About from './../views/About.vue'

Vue.use(VueRouter)
export default new VueRouter({
    mode: 'hash',
    routes: [
        {
            path: '/home',
            component: Home
        },
        {
            path: '/about',
            component: About
        },
    ]
})

然后我们需要在App.vue里面添加几个组件

<template>
  <div id="app">
    <router-link to="/home">首页</router-link>
    <router-link to="/about">关于</router-link>
    <router-view></router-view>
  </div>
</template>

<script>
export default {
  name: 'app',
  mounted() {
    console.log(this.$router, this.$route)
  }
}
</script>

我们还可以发现vue-ruter帮我们提供了2个属性$router$route

实现mini的vue-router

因为vue-routerreact-router的原理基本一样,是基于hash或者H5 api来实现,这里就直接上代码了

class HistoryRoute {
    constructor() {
        this.current = null //存放当前的路径
    }
}
class VueRouter {
    constructor(options) {
        this.mode = options.mode || 'hash'
        this.routes = options.routes || []
        this.routesMap = this.createMap(this.routes)
        this.history = new HistoryRoute()
        this.init() //初始化路由
    }
    init() {
        // 判断当前是什么模式
        if (this.mode === 'hash') {
            // 先判断用户打开时候有没hash 没有的话就跳转到 #/
            location.hash ? '' : location.hash = '/'
            window.addEventListener('load', () => {
                this.history.current = location.hash.slice(1)
            })
            window.addEventListener('hashchange', () => {
                this.history.current = location.hash.slice(1)
            })
        } else { //history
            location.pathname ? '' : location.pathname = '/'
            window.addEventListener('load', () => {
                this.history.current = location.pathname
            })
            window.addEventListener('popstate', () => { //监听浏览器前进后退
                this.history.current = location.pathname
            })
        }
    }
    createMap(routes) { // 将传递的路由表进行改造成{'/home':Home, '/about':About}
        return routes.reduce((prev, next) => {
            prev[next.path] = next.component
            return prev
        }, {})
    }
}

VueRouter.install = (Vue) => {
    Vue.mixin({
        beforeCreate() {
            if (this.$options && this.$options.router) { // 根组件
                this._root = this
                this._router = this.$options.router
                //如果history中的current变化,也会刷新视图
                Vue.util.defineReactive(this, 'xx', this._router.history)
            } else { // 因为组件的渲染顺序是 父子孙 所以子和孙都是可以拿到对应的父组件
                this._root = this.$parent._root
                this._router = this.$parent._router
            }
            Object.defineProperty(this, '$router', {
                get() {
                    return this._root._router
                }
            })
            Object.defineProperty(this, '$route', {
                get() {
                    return {
                        current: this._root._router.history.current
                    }
                }
            })
        }
    })
    Vue.component('router-link', {
        props: {
            to: String
        },
        render(h) {
            const mode = this._self._root._router.mode
            return <a href={mode === 'hash' ? `#${this.to}` : this.to}>
                {this.$slots.default}
            </a>
        }
    })
    Vue.component('router-view', {
        render(h) {
            const current = this._self._router.history.current
            const routesMap = this._self._router.routesMap
            return h(routesMap[current])
        }
    })
}

export default VueRouter

那我们接下来总结几个难点吧

  • 我们使用vue-router的时候用的是插件的用法,即Vue.use(xxx), 所以我们需要提供一个install方法

  • 如果做到多个组件共享一个路由实例呢,可以根据我们组件的渲染顺序父子孙,先给根组件定义属性,然后子孙通过$parent
    来取

  • vue中通过this.$slots.defaule来拿到插槽的默认内容,react中是this.props.children

  • Vue.component()里面通过this._self拿到当前组件

  • 实现history中的current变化,也会刷新视图,源码中是通过 Vue.util.defineReactive(this, 'xx', this._router.history)这个方法实现的

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant