vue router实现原理

1,261 阅读3分钟

分析vue router实现原理之前 ,先来简单过一遍vue router的基本使用

使用步骤

1、首先通过@vue/cli创建vue的基础项目 并选中(Router)

2、在router/index.js文件中

import Vue from 'vue'
import VueRouter from 'vue-router'

//1、注册路由插件
//通过vue.use()方法用来注册插件用,它会调用传入对象的install方法,
//如果传入的是一个函数会直接调用这个函数,后面在具体分析vue.use源码是怎么实现的
Vue.use(VueRouter)

//2、路由规则
const routes=[
	{
    	path:'/',
        name:'home',
        component:Home
    }
]
// 3、创建路由对象
const router = new VueRouter({
	routes
})

export default router

3、注册路由对象

main.js中


import Vue from 'vue'
import router from './router'

new Vue({
	router,
    render:h=>h(App)
}).$mount(#app)

4、router-view使用

在app.vue中

<template>
	<!-- 4、创建路由组件的占位-->
	<router-view/>
</template>

当地址栏中路径发生变化会在router-view位置渲染对应的组件

动态路由

const routes=[
	{
    	path:'/detail/:id',
        name:'detail',
        //方式1:开启props,会把url中的参数传递给组件
        //在组件中通过props接收参数(优点是不依赖于路由规则)
        //方式2:不开启props,组件中通过 $route.params.id获取参数
        props:true,
        //路由懒加载,提高程序性能
        component:()=>import ('../views/detail.vue')
    }
]

嵌套路由

会将外部的layout的path和children的path进行合并,首页的路径可以写成'',加载的时候会先加载layout组件在加载children的组件

const routes=[
	{
    	path:'/',
        component:layout,
        children:[
        	{
            	path:'',
                name:index,
                component:Index,
            },
            {
            	path:'detail/:id',
                name:detail,
                props:true,
        		component:()=>import ('../views/detail.vue')
            }
        ]
    }
]

编程式导航

//push方法会记录历史
//字符串方式
this.$router.push('/')
//对象方式
this.$router.push({name:'detail',params:{id:1}})
//replace不会记录历史
this.$router.replace('/login')
//go -1返回上一个页面
this.$router.go(-1)

hash和history模式区别

hash模式是基于锚点,和onhashchange事件

history模式是基于H5中的history API

  • history.pushState() IE10以后才支持
  • push方法时候路径会方式变化,会向服务器发送请求,history.pushState()会改变浏览器的地址栏的地址但是不会向服务器发送请求
  • history.replaceState()
  • replaceState() 是修改了当前的历史记录项而不是新建一个

history模式

  1. history模式需要服务器的支持
  2. 单页应用中,服务器不存在http://10.12.22.33/login这样的地址就会返回找不到该页面
  3. 服务端应该除了返回静态资源外都返回单页应用中的index.html

在服务端如何配置?

1.node服务器

需要导入处理history的插件

2.nginx服务

修改配置文件

http{
	server{
    	location /{
        	root html;
            index index.html index.htm;
            try_files $uri $uri/ /index.html;
            # 尝试访问当前访问路径 没有的话访问当前路径下的index.html 找不到访问html/index.html
        }
    }
}

实现原理

hash模式

  • url中#后面的内容作为路径地址
  • 监听hashchange事件
  • 根据当前地址找到对应的组件并渲染

history模式

  • history.pushState()方法改变地址栏
  • 监听popstate事件
  • 根据路径地址找对应的组件渲染

模拟vue router

实现思路

  • 创建 VueRouter 插件,静态方法 install
    • 判断插件是否已经被加载
    • 当 Vue 加载的时候把传入的 router 对象挂载到 Vue 实例上(注意:只执行一次)
  • 创建 VueRouter 类
    • 初始化,options、routeMap、data(简化操作,创建 Vue 实例作为响应式数据记录当前路径)
    • initRouteMap() 遍历所有路由信息,把组件和路由的映射记录到 routeMap 对象中
    • 注册 popstate 事件,当路由地址发生变化,重新记录当前的路径
    • 创建 router-link 和 router-view 组件
    • 当路径改变的时候通过当前路径在 routerMap 对象中找到对应的组件,渲染 router-view

模拟代码

  • 创建VueRouter插件 ```vue let _Vue =null export default class VueRouter{ static install(Vue){ //如果插件已经安装直接返回 if(VueRouter.install.installed && Vue===_Vue) return

       VueRouter.install.installed = true
       _Vue = Vue
       
       Vue.mixin({
       	beforeCreate(){
          		 //判断 router 对象是否已经挂载了 Vue 实例上
           	if(this.$options.router){
                 //把vue实例传入的router注册到vue实例
               	_Vue.prototype.$router=this.$options.router
               }
           }
       })
    

    } } ```

  • 实现VueRouter类 构造函数

    constructor(options){
    	this.options = options
        //记录路径及对应的组件
        this.routeMap = {}
        this.data = _Vue.observable({
          current: window.location.pathname //"/"
        });
    }
    
  • 实现 VueRouter 类 - initRouteMap()

    initRouteMap(){
    	//遍历所有的路由信息,记录路径和组件的映射
        this.options.routes.forEach(route=>{
        	this.routeMap[route.path]=route.component
        })
    }
    
  • 实现 VueRouter 类 - 注册事件

```vue
initEvents(){
	//当路径变化之后,重新获取当前路径并记录到 current
	window.addEventListener("popstate", () => {
    	 this.data.current = window.location.pathname;
    });
}
```
  • 实现vuerouter类 - router-link 和router-view组件 ```vue initComponents(Vue) { const self = this; Vue.component("router-link", { props: { to: String }, //运行时不支持template模版 需要vue.config.js 设置 runtimeCompiler:true // template: '' render(h) { return h( "a", { attrs: { href: this.to }, on: { click: this.clickHandler } }, [this.slots.default] ); }, methods: { clickHandler(e) { e.preventDefault(); history.pushState({}, "", this.to); this.router.data.current = this.to; } } });

    Vue.component("router-view", { render(h) { //根据当前路径找到对应的组件,注意 this 的问题 let cm = self.routerMap[self.data.current]; if (!cm) { cm = self.routerMap["*"]; } return h(cm); } }); } ```

  • 实现init

    init () {
      this.initRouteMap()
      this.initEvents()
      this.initComponents(_Vue)
    }
    
    // 插件的 install() 方法中调用 init() 初始化
    if (this.$options.router{
    	_Vue.prototype.$router=this.$options.router
        // 初始化插件的时候,调用 init
    	this.$options.router.init()
    }