vue-router原理与源码分析

单页面应用(SPA)

一次性从服务器下载,切换页面不用再发送页面请求,只发送对应页面的图片等请求

Vue-router

核心

$router.go(-1) ===  history.go(-1)  === 浏览器后退操作

路由需要实现响应式
1.vue-router 实现响应式与vuex一样都是借助vue的响应式原理 不过vue-router是借助util下的defineReactive(需要看过vue响应式原理), 而vuex 是通过借助一个vue实例 将store变成vue实例的属性来实现

简易源码

hash模式
myVue-Router.js

let Vue

class VueRouter {
    constructor({routers}){
        let routerMap = {}
        routers.forEach(router => {
            let path = router.path
            if(!routerMap[path]){
                routerMap[path] = router
            }
        }); 
        // path /foo
        this.routerMap = routerMap
        //当前路由对象
        this.current = {
            path: '/',
            component: {
                template: '<div>aaa</div>'
            }
        }
        this.listener()
    }
    //增加监听器
    listener(){
        //刚加载时 默认传/
        window.addEventListener('load', () => {
            let hash = window.location.hash
            if(!hash){
                window.location.hash = '/'
            }
            let router = this.search(hash.slice(1))
            if(router){
                this.current.path = router.path
                this.current.component = router.component
            }
        })
        //hash值改变时  做操作
        window.addEventListener('hashchange', () => {
            let hash = window.location.hash
            let router = this.search(hash.slice(1))
            if(router){
                this.current.path = router.path
                this.current.component = router.component
            }
        })
    }
    search(path){
        if(this.routerMap[path]){
            return this.routerMap[path]
        }
        return null
    }   
}

VueRouter.install = function(vue, options){
    Vue = vue
    Vue.mixin({
        beforeCreate(){
            let vm = this
            if(vm.$options.router){
                //那个实例挂载了router
                vm._routerRoot = this
                vm._router = vm.$options.router
                //实现响应式  通过vue实现响应式的api
                Vue.util.defineReactive(vm, 'router', )
            }else{
                //往上找,
                vm._routerRoot = vm.$parent && vm.$parent._routerRoot
            }
        }
    })

    //生成router-link组件
    Vue.component('router-link', {
        props:{
            to: String
        },
        render(h){
            //h > creatElement  虚拟dom转为真实dom
            //相当于生成一个a标签
            return h('a', {attrs: {href: '#'+this.to}}, this.$slots.default)
        }
    })
    //生成router-view组件
    Vue.component('router-view', {
        render(h){
            let component = this._routerRoot.component
            return h(component)
        }
    })
}

if(typeof Vue !== 'undefined'){
    Vue.use(VueRouter)
}

index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script src="https://unpkg.com/vue/dist/vue.js"></script>
    <!-- <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script> -->
    <script src="./myVue-router.js"></script>

    <div id="app">
        <h1>Hello App!</h1>
        <p>
            <!-- 使用 router-link 组件来导航. -->
            <!-- 通过传入 `to` 属性指定链接. -->
            <!-- <router-link> 默认会被渲染成一个 `<a>` 标签 -->
            <router-link to="/foo">Go to Foo</router-link>
            <router-link to="/bar">Go to Bar</router-link>
        </p>
        <!-- 路由出口 -->
        <!-- 路由匹配到的组件将渲染在这里 -->
        <!-- <router-view></router-view> -->
    </div>
</body>
<script>
    // 0. 如果使用模块化机制编程,导入Vue和VueRouter,要调用 Vue.use(VueRouter)

    // 1. 定义 (路由) 组件。
    // 可以从其他文件 import 进来
    const Foo = { template: '<div>foo</div>' }
    const Bar = { template: '<div>bar</div>' }

    // 2. 定义路由
    // 每个路由应该映射一个组件。 其中"component" 可以是
    // 通过 Vue.extend() 创建的组件构造器,
    // 或者,只是一个组件配置对象。
    // 我们晚点再讨论嵌套路由。
    const routes = [
        { path: '/foo', component: Foo },
        { path: '/bar', component: Bar }
    ]

    // 3. 创建 router 实例,然后传 `routes` 配置
    // 你还可以传别的配置参数, 不过先这么简单着吧。
    const router = new VueRouter({
        routes // (缩写) 相当于 routes: routes
    })

    // 4. 创建和挂载根实例。
    // 记得要通过 router 配置参数注入路由,
    // 从而让整个应用都有路由功能
    const app = new Vue({
        router
    }).$mount('#app')

// 现在,应用已经启动了!
</script>

</html>

hash模式与history模式的对比

最直观的感受就是,
1.hash会比history多个#
2.在vue的路由配置中有mode选项 :
mode:“hash”;
mode:“history”;

细节对比:

概念区别:
1.hash —— 即地址栏 URL 中的 # 符号(此 hash 不是密码学里的散列运算)。比如这个 URL:http://www.abc.com/#/hello,hash 的值为 #/hello。它的特点在于:hash 虽然出现在 URL 中,但不会被包括在 HTTP 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面。
2.history —— 利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法。(需要特定浏览器支持)
通过这两个api完成URL跳转不会重新加载页面,history模式解决了hash模式存在的问题. hash的传参是基于URL的, 如果要传递复杂的数据, 会有体积限制, 而history模式不仅可以在URL里传参, 也可以将数据存放到一个特定的对象中

在请求方式区别:
1.hash :只会向服务器发送#之前的。如 http://www.abc.com,因此对于后端来说,即使没有做到对路由的全覆盖,也不会返回 404 错误。
2.history:前端的 URL 必须和实际向后端发起请求的 URL 一致,如 http://www.abc.com/book/id。如果后端缺少对 /book/id 的路由处理,将返回 404 错误

Vue.use()源码

比较简单
就是将传进来的插件挂到vue对象上,只是需要判断插件是对象还是函数,如果是对象 ,需要将插件的install属性改变this指向该插件

export function initUse(Vue) {
    Vue.use = function (plugin) {
        let installPlugins = this._installPlugins || this._installPlugins = []
        if(installPlugins.indexOf(plugin)>-1){
            return this
        }
        let args = toArry(arguments, 1)
        args.push(this)
        if(plugin instanceof 'object'){
            plugin.install.apply(plugin, args)
        }else if(plugin instanceof 'function'){
            plugin.apply(null, args)
        }
        return this
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值