Skip to content

malred/iroute

Repository files navigation

课程资源来源于互联网和博客,仅供学习交流使用,不作商业用途!!!

image.png

基础回顾

使用步骤

image.pngimage.pngimage.pngimage.pngimage.pngimage.png

不传入router对象,vue对象就没有router

image.pngimage.png

传入就有$route(路由规则)和$router(vueRouter对象,包含路由操作方法等)

image.pngimage.png

currentRoute就是当前路由规则

image.png

路由懒加载(()=>import()),提高性能

动态路由/:xx

image.png

获取动态路由的参数 [推荐使用props(父子组件传值),需要路由规则里props设置为true]

image.png image.png

嵌套路由

嵌套路由可以把相同的头和尾放到父路由,中间放router view用于选染子路由的内容,达到复用头尾的目的

image.pngimage.png

嵌套路由,会把父路由的路径和子路由的拼接起来

image.png image.pngimage.pngimage.png

编程式导航

(可以push(相对路径),也可以push({name:xx,params: xx}),name是路由规则里声明的,params可以传递参数

image.pngimage.png

replace不会记录历史,push会

image.png

go是跳转历史中的某一次,如果是负数则是后退,为-1则与back效果一致

image.png

hash和history模式区别(不管哪种模型都不会在路径变化时向浏览器发送请求(单页面),而是js根据不同url渲染不同内容,数据变化则用ajax或axios获取)

image.pngimage.png

history模式

image.pngimage.pngimage.png

配置404和history模式

image.png image.png image.png

这里的history模块是处理history模式的,此时没有注册到中间件(use)

image.png

访问时,history模块会往地址里push路径,但是不会刷新

image.png

如果刷新浏览器,没有对history模块的支持,则浏览器去寻找该地址,找不到,直接返回404

image.png

使用history支持,则刷新时找不到该路径(url对应资源),则会把当前单页面应用返回给浏览器,然后浏览器再根据该单页面应用去找对应路径资源

image.png

nginx配置history模式

image.pngimage.pngimage.png

默认80端口

image.png

把项目打包,把dist里的内容(不是复制dist文件夹)复制

image.png

黏贴打包好的文件到nginx的html文件夹下,dist里的index.html替换nginx默认的html

image.pngimage.png

nginx默认不支持history模式

image.png

解决: 修改conf/nginx.conf[改完要重启], location / 对应nginx的根目录,try_files是当访问不到url时,尝试请求根目录下的uri对应的文件($uri),找不到就找uri对应目录下的文件($uri/),否则返回当前单页面应用(/index.html)

image.png image.png

vueRouter实现原理

image.png

两种模式

image.pngimage.png

VueRouter 模拟实现

image.png image.png

router install方法(先创建一个带vue-router的vue2项目)

image.pngimage.png

// 3_vue\i - vue - router\src\vuerouter\index.js
let _Vue = null // 全局变量
export default class VueRouter {
    static install(Vue) {
        // 1,判断当前插件是否全局安装
        if (VueRouter.install.installed) {
            return
        }
        // 设置安装状态
        VueRouter.install.installed = true
        // 2,把Vue构造函数记录到全局变量
        _Vue = Vue
        // 3,把创建vue实例时传入的router对象注入到vue实例上
        // 所有的 JavaScript 对象都会从一个 prototype(原型对象)中继承属性和方法。
        // 使用 prototype 属性就可以给对象的构造函数添加新的属性 
        // 不能在这写,该静态方法的this指向方法本身,而我们需要的是this指向Vue对象
        // _Vue.prototype.$router = this.$options.router
        // 混入 
        _Vue.mixin({
            beforeCreate() {
                // (到时所有vue对象(包括组件)都会有该参数)则会多次执行同一个逻辑,应该简化
                // this->vue对象
                if (this.$options.router) { // 如果vue实例有router才执行
                    _Vue.prototype.$router = this.$options.router // vue对象的router挂载给这的全局变量
                    // 调用初始化方法
                    this.$options.router.init()
                }
            }
        })
    } 
}

构造函数

// 3_vue\i - vue - router\src\vuerouter\index.js
let _Vue = null // 全局变量
export default class VueRouter {
    static install(Vue) {
        // 1,判断当前插件是否全局安装
        if (VueRouter.install.installed) {
            return
        }
        // 设置安装状态
        VueRouter.install.installed = true
        // 2,把Vue构造函数记录到全局变量
        _Vue = Vue
        // 3,把创建vue实例时传入的router对象注入到vue实例上
        // 所有的 JavaScript 对象都会从一个 prototype(原型对象)中继承属性和方法。
        // 使用 prototype 属性就可以给对象的构造函数添加新的属性 
        // 不能在这写,该静态方法的this指向方法本身,而我们需要的是this指向Vue对象
        // _Vue.prototype.$router = this.$options.router
        // 混入 
        _Vue.mixin({
            beforeCreate() {
                // (到时所有vue对象(包括组件)都会有该参数)则会多次执行同一个逻辑,应该简化
                // this->vue对象
                if (this.$options.router) { // 如果vue实例有router才执行
                    _Vue.prototype.$router = this.$options.router // vue对象的router挂载给这的全局变量
                    // 调用初始化方法
                    this.$options.router.init()
                }
            }
        })
    }
    // 构造函数
    constructor(options) {
        // 记录构造函数传入的选项
        this.options = options
        // 存放路由规则: key路由地址,value路由组件
        this.routerMap = {}
        // 响应式对象: 记录当前路由地址,双向绑定
        this.data = _Vue.observable({ // 该方法可以把对象转为响应式
            current: '/' // 当前路由地址,默认是 / 
        })
    } 
}

createRouteMap(把传进来的路由规则转换为键值对形式放到routeMap里)

// 3_vue\i - vue - router\src\vuerouter\index.js
let _Vue = null // 全局变量
export default class VueRouter {
    static install(Vue) {
        // 1,判断当前插件是否全局安装
        if (VueRouter.install.installed) {
            return
        }
        // 设置安装状态
        VueRouter.install.installed = true
        // 2,把Vue构造函数记录到全局变量
        _Vue = Vue
        // 3,把创建vue实例时传入的router对象注入到vue实例上
        // 所有的 JavaScript 对象都会从一个 prototype(原型对象)中继承属性和方法。
        // 使用 prototype 属性就可以给对象的构造函数添加新的属性 
        // 不能在这写,该静态方法的this指向方法本身,而我们需要的是this指向Vue对象
        // _Vue.prototype.$router = this.$options.router
        // 混入 
        _Vue.mixins({
            beforeCreate() {
                // (到时所有vue对象(包括组件)都会有该参数)则会多次执行同一个逻辑,应该简化
                if (this.$options.router) { // 如果vue实例有router才执行
                    _Vue.prototype.$router = this.$options.router // vue对象的router挂载给这的全局变量             
                }
            }
        })
    }
    // 构造函数
    constructor(options) {
        // 记录构造函数传入的选项
        this.options = options
        // 存放路由规则: key路由地址,value路由组件
        this.routerMap = {}
        // 响应式对象: 记录当前路由地址,双向绑定
        this.data = _Vue.observable({ // 该方法可以把对象转为响应式
            current: '/' // 当前路由地址,默认是 / 
        })
    }
    createRouteMap() {
        // 遍历路由规则,解析成键值对形式存入routemap
        this.options.routes.forEach(route => {
            this.routerMap[route.path] = route.component
        })
    } 
}

initComponent

image.png

// 3_vue\i - vue - router\src\vuerouter\index.js
let _Vue = null // 全局变量
export default class VueRouter {
    static install(Vue) {
        // 1,判断当前插件是否全局安装
        if (VueRouter.install.installed) {
            return
        }
        // 设置安装状态
        VueRouter.install.installed = true
        // 2,把Vue构造函数记录到全局变量
        _Vue = Vue
        // 3,把创建vue实例时传入的router对象注入到vue实例上
        // 所有的 JavaScript 对象都会从一个 prototype(原型对象)中继承属性和方法。
        // 使用 prototype 属性就可以给对象的构造函数添加新的属性 
        // 不能在这写,该静态方法的this指向方法本身,而我们需要的是this指向Vue对象
        // _Vue.prototype.$router = this.$options.router
        // 混入 
        _Vue.mixin({
            beforeCreate() {
                // (到时所有vue对象(包括组件)都会有该参数)则会多次执行同一个逻辑,应该简化
                // this->vue对象
                if (this.$options.router) { // 如果vue实例有router才执行
                    _Vue.prototype.$router = this.$options.router // vue对象的router挂载给这的全局变量
                    // 调用初始化方法
                    this.$options.router.init()
                }
            }
        })
    }
    // 构造函数
    constructor(options) {
        // 记录构造函数传入的选项
        this.options = options
        // 存放路由规则: key路由地址,value路由组件
        this.routerMap = {}
        // 响应式对象: 记录当前路由地址,双向绑定
        this.data = _Vue.observable({ // 该方法可以把对象转为响应式
            current: '/' // 当前路由地址,默认是 / 
        })
    }
    createRouteMap() {
        // 遍历路由规则,解析成键值对形式存入routemap
        this.options.routes.forEach(route => {
            this.routerMap[route.path] = route.component
        })
    }
    // 初始化
    init() {
        this.createRouteMap()
        this.initComponents(_Vue)
    }
    initComponents(Vue) {
        // 找到vue对象里的<router-link></router-link>标签
        Vue.component('router-link', {
            props: {
                to: String
            },
            // 本质是a标签加插槽,将来把路由对应组件渲染到插槽里
            template: '<a :href="to"><slot></slot></a>'
        })
    }
}

测试使用

image.png

报错2: 使用了未注册的routerLink组件

image.png

报错1: 使用的是只有运行版本的build -> 什么意思? :

image.png

解决报错1的方法1: 使用完整的vue

image.pngimage.pngimage.pngimage.png

解决报错1的方法2: render(我们平常写的vue组件可以工作,是因为文件里的template被编译成render函数(预编译))

image.png

// 3_vue\i - vue - router\src\vuerouter\index.js
let _Vue = null // 全局变量
export default class VueRouter {
    static install(Vue) {
        // 1,判断当前插件是否全局安装
        if (VueRouter.install.installed) {
            return
        }
        // 设置安装状态
        VueRouter.install.installed = true
        // 2,把Vue构造函数记录到全局变量
        _Vue = Vue
        // 3,把创建vue实例时传入的router对象注入到vue实例上
        // 所有的 JavaScript 对象都会从一个 prototype(原型对象)中继承属性和方法。
        // 使用 prototype 属性就可以给对象的构造函数添加新的属性 
        // 不能在这写,该静态方法的this指向方法本身,而我们需要的是this指向Vue对象
        // _Vue.prototype.$router = this.$options.router
        // 混入 
        _Vue.mixin({
            beforeCreate() {
                // (到时所有vue对象(包括组件)都会有该参数)则会多次执行同一个逻辑,应该简化
                // this->vue对象
                if (this.$options.router) { // 如果vue实例有router才执行
                    _Vue.prototype.$router = this.$options.router // vue对象的router挂载给这的全局变量
                    // 调用初始化方法
                    this.$options.router.init()
                }
            }
        })
    }
    // 构造函数
    constructor(options) {
        // 记录构造函数传入的选项
        this.options = options
        // 存放路由规则: key路由地址,value路由组件
        this.routerMap = {}
        // 响应式对象: 记录当前路由地址,双向绑定
        this.data = _Vue.observable({ // 该方法可以把对象转为响应式
            current: '/' // 当前路由地址,默认是 / 
        })
    }
    createRouteMap() {
        // 遍历路由规则,解析成键值对形式存入routemap
        this.options.routes.forEach(route => {
            this.routerMap[route.path] = route.component
        })
    }
    // 初始化
    init() {
        this.createRouteMap()
        this.initComponents(_Vue)
    }
    initComponents(Vue) {
        // 找到vue对象里的<router-link></router-link>标签
        Vue.component('router-link', {
            props: {
                to: String
            },
            // 本质是a标签加插槽,将来把路由对应组件渲染到插槽里
            // 运行时版本vue不支持template
            // template: '<a :href="to"><slot></slot></a>'
            // 运行时版本解决方案: 手动编译template
            render(h) { // h用来创建虚拟dom(由vue传递)
                // 参数1: 要创建的元素的选择器
                // 参数2: 给创建的元素添加属性
                // 参数3: 创建的元素的子元素(array)
                return h('a', {
                    attrs: {
                        href: this.to
                    }
                    //默认插槽
                }, [this.$slots.default]) // a标签子元素放插槽
            }
        })
    }
}

route-view

// 3_vue\i - vue - router\src\vuerouter\index.js
let _Vue = null // 全局变量
export default class VueRouter {
    static install(Vue) {
        // 1,判断当前插件是否全局安装
        if (VueRouter.install.installed) {
            return
        }
        // 设置安装状态
        VueRouter.install.installed = true
        // 2,把Vue构造函数记录到全局变量
        _Vue = Vue
        // 3,把创建vue实例时传入的router对象注入到vue实例上
        // 所有的 JavaScript 对象都会从一个 prototype(原型对象)中继承属性和方法。
        // 使用 prototype 属性就可以给对象的构造函数添加新的属性 
        // 不能在这写,该静态方法的this指向方法本身,而我们需要的是this指向Vue对象
        // _Vue.prototype.$router = this.$options.router
        // 混入 
        _Vue.mixin({
            beforeCreate() {
                // (到时所有vue对象(包括组件)都会有该参数)则会多次执行同一个逻辑,应该简化
                // this->vue对象
                if (this.$options.router) { // 如果vue实例有router才执行
                    _Vue.prototype.$router = this.$options.router // vue对象的router挂载给这的全局变量
                    // 调用初始化方法
                    this.$options.router.init()
                }
            }
        })
    }
    // 构造函数
    constructor(options) {
        // 记录构造函数传入的选项
        this.options = options
        // 存放路由规则: key路由地址,value路由组件
        this.routerMap = {}
        // 响应式对象: 记录当前路由地址,双向绑定
        this.data = _Vue.observable({ // 该方法可以把对象转为响应式
            current: '/' // 当前路由地址,默认是 / 
        })
    }
    createRouteMap() {
        // 遍历路由规则,解析成键值对形式存入routemap
        this.options.routes.forEach(route => {
            this.routerMap[route.path] = route.component
        })
    }
    // 初始化
    init() {
        this.createRouteMap()
        this.initComponents(_Vue)
    }
    initComponents(Vue) {
        // 注册router-link组件
        Vue.component('router-link', {
            props: {
                to: String
            },
            // 本质是a标签加插槽,将来把路由对应组件渲染到插槽里
            // 运行时版本vue不支持template
            // template: '<a :href="to"><slot></slot></a>'
            // 运行时版本解决方案: 手动编译template
            render(h) { // h用来创建虚拟dom(由vue传递)
                // 参数1: 要创建的元素的选择器
                // 参数2: 给创建的元素添加属性
                // 参数3: 创建的元素的子元素(array)
                return h('a', {
                    attrs: {
                        href: this.to
                    },
                    // 注册事件
                    // 为了防止a被点击后向浏览器发送请求,导致history模式出错,需要阻止a标签发送请求
                    on: {
                        click: this.clickHandler
                    }
                    //默认插槽
                }, [this.$slots.default]) // a标签子元素放插槽
            },
            methods: {
                clickHandler(e) {
                    // 改变浏览器地址栏但是不发送请求
                    // 参数1是触发pushState时调用的事件函数
                    // 参数2是标题
                    // 参数3是要跳转的地址
                    history.pushState({}, '', this.to)
                    // 记录当前地址->data.current
                    this.$router.data.current = this.to
                    // 之前给vue挂载了$router,所有的vue对象/组件都可以访问到
                    this.$router.data.current
                    // 阻止默认行为(即a标签的发送请求)
                    e.preventDefault();
                }
            },
        })
        // 注册router-view组件
        // 保存当前对象,给render方法用
        const self = this
        Vue.component('router-view', {
            render(h) {
                // 获取路由地址 
                const component = self.routerMap[self.data.current]
                // 根据路由地址寻找路由对应的组件
                return h(component) // 把组件变成响应式,挂到虚拟dom
            }
        })
    }
}

image.png

initEvent ( 上面的代码,当浏览器点击返回(<-)时,并没有返回到上一个页面)

// 3_vue\i - vue - router\src\vuerouter\index.js
let _Vue = null // 全局变量
export default class VueRouter {
    static install(Vue) {
        // 1,判断当前插件是否全局安装
        if (VueRouter.install.installed) {
            return
        }
        // 设置安装状态
        VueRouter.install.installed = true
        // 2,把Vue构造函数记录到全局变量
        _Vue = Vue
        // 3,把创建vue实例时传入的router对象注入到vue实例上
        // 所有的 JavaScript 对象都会从一个 prototype(原型对象)中继承属性和方法。
        // 使用 prototype 属性就可以给对象的构造函数添加新的属性 
        // 不能在这写,该静态方法的this指向方法本身,而我们需要的是this指向Vue对象
        // _Vue.prototype.$router = this.$options.router
        // 混入 
        _Vue.mixin({
            beforeCreate() {
                // (到时所有vue对象(包括组件)都会有该参数)则会多次执行同一个逻辑,应该简化
                // this->vue对象
                if (this.$options.router) { // 如果vue实例有router才执行
                    _Vue.prototype.$router = this.$options.router // vue对象的router挂载给这的全局变量
                    // 调用初始化方法
                    this.$options.router.init()
                }
            }
        })
    }
    // 构造函数
    constructor(options) {
        // 记录构造函数传入的选项
        this.options = options
        // 存放路由规则: key路由地址,value路由组件
        this.routerMap = {}
        // 响应式对象: 记录当前路由地址,双向绑定
        this.data = _Vue.observable({ // 该方法可以把对象转为响应式
            current: '/' // 当前路由地址,默认是 / 
        })
    }
    createRouteMap() {
        // 遍历路由规则,解析成键值对形式存入routemap
        this.options.routes.forEach(route => {
            this.routerMap[route.path] = route.component
        })
    }
    // 初始化
    init() {
        this.createRouteMap()
        this.initComponents(_Vue)
        this.initEvent()
    }
    // 注册组件
    initComponents(Vue) {
        // 注册router-link组件
        Vue.component('router-link', {
            props: {
                to: String
            },
            // 本质是a标签加插槽,将来把路由对应组件渲染到插槽里
            // 运行时版本vue不支持template
            // template: '<a :href="to"><slot></slot></a>'
            // 运行时版本解决方案: 手动编译template
            render(h) { // h用来创建虚拟dom(由vue传递)
                // 参数1: 要创建的元素的选择器
                // 参数2: 给创建的元素添加属性
                // 参数3: 创建的元素的子元素(array)
                return h('a', {
                    attrs: {
                        href: this.to
                    },
                    // 注册事件
                    // 为了防止a被点击后向浏览器发送请求,导致history模式出错,需要阻止a标签发送请求
                    on: {
                        click: this.clickHandler
                    }
                    //默认插槽
                }, [this.$slots.default]) // a标签子元素放插槽
            },
            methods: {
                clickHandler(e) {
                    // 改变浏览器地址栏但是不发送请求
                    // 参数1是触发pushState时调用的事件函数
                    // 参数2是标题
                    // 参数3是要跳转的地址
                    history.pushState({}, '', this.to)
                    // 记录当前地址->data.current
                    this.$router.data.current = this.to
                    // 之前给vue挂载了$router,所有的vue对象/组件都可以访问到
                    this.$router.data.current
                    // 阻止默认行为(即a标签的发送请求)
                    e.preventDefault();
                }
            },
        })
        // 注册router-view组件
        // 保存当前对象,给render方法用
        const self = this
        Vue.component('router-view', {
            render(h) {
                // 获取路由地址 
                const component = self.routerMap[self.data.current]
                // 根据路由地址寻找路由对应的组件
                return h(component) // 把组件变成响应式,挂到虚拟dom
            }
        })
    }
    // 解决浏览器点击返回时,返回上一页没有渲染对应页面
    initEvent() {
        // 监听浏览器后退
        window.addEventListener('popstate', () => {
            // 改变data.current,渲染对应组件(响应式router-xxx标签会跟着改变)
            // this -> initEvent
            this.data.current = window.location.pathname
        })
    }
}

About

实现vue-route(history模式)

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published