vue---手写vue-router

1、hash模式和history模式的区别

  • 表现形式的区别
    hash模式路径中带#,#后内容作为路由地址
    history模式正常路径 需要服务端配置支持

  • 原理的区别
    hash模式基于锚点,以及onhaschange事件,根据路由地址不同呈现不同内容
    history模式基于HTML5中的history API
    - history.pushState() IE10以后支持
    - history.replaceState()

2、history 模式的使用

  • 需要服务器的支持
  • 单页面应用中,服务端不存在http://www.testurl.com/login这样的地址 如果返回找不到该地址
  • 在服务端应该除了静态资源外都返回单页面应用的index.html

3、vue-router实现原理

Hash模式

  • URL中 # 后面的内容作为路由地址,
    直接通过 loacltion.url 切换浏览器地址,如果只改变了#后的地址,浏览器不会向服务器请求这个地址,会把这个地址记录到浏览器的访问历史中
  • 监听 hashchange 事件 ,记录当前路由地址
  • 根据当前路由地址找到对应组件重新渲染

History模式

  • 通过 history.pushState() 方法改变地址栏 ,将地址记录到浏览器的访问历史中
  • 监听 popstate 事件 ,监听浏览器历史记录改变
  • 根据当前路由地址找到对应的组件重新渲染页面

位图

在这里插入图片描述

vue的构建版本

  • 运行时版本:不支持template模板,需要打包时提前编译 编译时把template转换成render函数

运行时使用完整版vue 在根目录下创建一个vue.config.js文件,将runtimeCompiler设置为true

module.exports={
	//选项...
	runtimeCompiler:true
}
  • 完整版:包含运行时和编译器,体积比运行时版本大10K左右,程序运行时把模板转换成render函数
  • vue-cli使用的是运行时版本
  • 如何切换完整版vue

创建vuerouter/index.js文件

//手写vue-router
let _Vue=null
//1、导出一个VueRouter类
export default class VueRouter{

    //静态方法 install() 传递两个参数 一个是vue的构造函数 第二个参数可选
    static install(Vue){ //方法和函数本身也是一个对象 
        //1、判断当前插件是否已经被安装
        //需要一个变量记录是否安装 installed
        if(VueRouter.install.installed){
            return
        }
        VueRouter.install.installed=true //表示当前插件被安装

        //2、把接收的Vue构造函数记录到全局变量 因为在下面vueRouter的实例方法中还要使用
        _Vue=Vue


        //3、把创建Vue实例时候传入的router对象注入到所有的vue的实例上
        //混入
        _Vue.mixin({ //给所有实例混入一个选项
            //所有的vue实例所有的组件都会执行汇入的beforecreate这个钩子函数
            beforeCreate(){ //beforeCreate中的this就是vue实例
                //只需要执行一次 组件不执行 vue实例执行 组件的选项中没有router
                if(this.$options.router){
                    _Vue.prototype.$router=this.$options.router
                    //此时this指向vm 
                    //this.$options.router指向的才是挂载之前我们自定义的Vuerouter类
                    this.$options.router.init()
                }
                
            }
        })
    }

    constructor (options) {
        //记录构造函数中传入的选项options
        this.options=options
        //把options中的路由规则rules解析到routeMap中存储
        //routeMap中内容是一个个键值对 键是路由地址 值是路由组件
        //在VueRouter这个组件中根据当前的路由地址来routeMap中找到路由组件 渲染出来
        this.routeMap={}
        //data应该是一个响应式对象 
        //vue中observable方法 创建一个响应式对象
        //创建的响应式对象可以用在渲染函数或计算属性中使用
        
        this.data=_Vue.observable({//传入一个对象 内部转换成响应式对象
            current:'/'  //存储当前的路由地址 默认是/首页
        })
    
    }

    init(){
        this.createRouteMap()
        this.initComponents(_Vue)
        this.initEvent()
    }

    createRouteMap(){
        //遍历所有的路由规则,把路由规则解析成键值对,存储到routeMap中

        //this.options中routes所有的路由规则
        this.options.routes.forEach(route=>{
            this.routeMap[route.path]=route.component
        })
    }

    initComponents(Vue){//Vue vue的构造函数

        //创建router-link组件
        Vue.component('router-link',{
            props:{
                to:String,
            },
            //运行时版本使用render函数
            render(h){ //参数是一个h函数
                return h('a',{ //第一个参数要创建的元素的选择器
                    attrs:{
                        href:this.to //要跳转的地址
                    }, //注册元素的属性
                    on:{
                        click:this.clickHandler
                    }//注册事件
                },[this.$slots.default]) //生成的元素的子元素 默认插槽
            },
            methods:{
                clickHandler(e){
                    //1、调用history.pushState() 改变地址栏但是不会向服务器发送请求
                    //data:触发pushState事件的时候,传给这个事件一个事件对象的参数
                    //title:网页标题
                    //当前要跳转的地址 this.to
                    history.pushState({},'',this.to)

                    //改变当前路径记录到当前路由地址 data.current中
                    //因为当前在router-link这个组件中 this是router-link
                    //当前router-link也是一个实例,之前我们通过mixin将$router挂载到了所有实例上
                    //所以当前使用this.$router.data.current即可
                    this.$router.data.current=this.to

                    e.preventDefault() //阻止a标签的默认事件--跳转
                }
            }
            //template:'<a :href="to"><slot></slot></a>' //:href='to' :href-->href绑定参数to
        })
        const self=this
        Vue.component('router-view',{
            render(h){ //h函数创建虚拟Dom
                //找到当前路由的地址
                const component=self.routeMap[self.data.current]
                return h(component) //返回虚拟Dom h函数可以将组建转换成虚拟Dom
            }
        })
    }

    initEvent(){ //注册popstate事件 监听浏览器箭头前进后退
        window.addEventListener('popstate',()=>{
            //因为此时使用的箭头函数 箭头函数不会改变this的指向
            this.data.current=window.location.pathname //获取浏览器地址栏路径部分即文本地址,存储到data.current中
        })

    }
}

在router/index.js文件中导入自定义的vueRouter

import VueRouter from '../vuerouter'
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值