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: '
aaa
'
} } 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> 默认会被渲染成一个 `` 标签 -->
            <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: '
foo
'
} const Bar = { template: '
bar
'
} // 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
    }
}

你可能感兴趣的:(vuejs,vue.js)