最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Vue-Router的实现原理

    正文概述 掘金(serendipity_tang)   2020-12-17   399

    vue的基础结构

    el

     new Vue({
        el: '#app',
        data: {
            company: {
                name: '掘金',
                address: '上海自由世纪广场'
            }
        }
    })
    vue中会把data中的数据填充到el指向的模板中,并把模板渲染到浏览器
    

    render

    new Vue({
        data: {
            company: {
                name: '掘金',
                address: '上海自由世纪广场'
            }
        },
        render(h) {
            return h('div', [
                h('p', '公司名称:' + this.company.name),
                h('p', '公司地址:' + this.company.address)
            ])
        }
    }).$mount('#app')
    h函数的作用是创建虚拟dom,render是把h函数创建的虚拟dom返回,$mount方法的作用是把虚拟dom转化为真实dom,渲染到浏览器
    

    vue的生命周期

    1. 初始化---初始化事件,生命周期相关成员,包括h函数
    2. beforeCreate
    3. 初始化注入---会把props,data,method等注入到vue的实例上
    4. created---可以访问到上面注入的成员
    5. 接下来是把模板编译成render函数---首先判断选项中是否设置了el选项,如果没有设置就调用$mount方法,把el转换成template,接下来把模板变成render函数,接下里判断是否设置了模板,如果没有设置的话会把el的外层html作为模板,然后把template模板编译到渲染函数中,渲染函数就是用来生成虚拟dom的
    6. beforeMount---挂载之前,无法获取新元素的内容
    7. 挂载dom,把新的结构渲染到页面上
    8. mounted---可以访问新的dom
    9. 当调用destroy()方法的时候,首先会触发beforeDestory,
    10. 然后执行清理的工作---解除绑定,销毁子组件和事件监听等

    11.destoryed

    如果我们使用单文件组件的时候,模板编译是在打包和构建的时候完成的,不在运行期间去编译模板

    组件---可复用的vue实例

    vue-router的原理实现

    使用步骤

    1. 创建视图组件
    2. 使用Vue.use(VueRouter)注册路由插件Vue.use()用来注册插件,接收一个参数,参数是函数的话,会自动调用这个函数,是一个对象的话,会调用对象中的install方法来注册插件
    3. 定义路由数组,路由规则
    4. 创建router对象const router = new VueRouter({ routes }),创建的时候,需要把路由规则传递进来,然后导出
    5. main.js中导入,创建实例的时候需要把这个导入的路由对象注册上去,当做参数传递进vue实例中,传入router的作用是给vue实例注入$route$router两个属性

    $route$router

    $route路由规则,存储的是路由的数据,包括参数,路由路径等
    $routervue-router的实例,就是路由对象,他(的原型上)提供了一些路由相关的方法,包括pushreplacego等,路由模式等

    关于路由的路径解释

    1. 嵌套路由中,父组件中一定会有router-view占位
    2. 路由的相对路径和绝对路径的区别就是相对路径(不带‘/’)会拼接父级路由,绝对路径(带‘/’)是配置的完整路由
    routes: [
        {
          path: '/vue',  //父级路径
          component: Info,
          children: [
            { path: 'home', component: Home },  //相对路径
            { path: '/about', component: About }  //绝对路径
          ]
        }
      ]
     
    

    相对路径:地址栏最后的路径显示为localhost:8080/#/vue/home
    绝对路径:地址栏最后的路径显示为localhost:8080/#/about

    编程式导航

    this.$router.push() this.$router.replace() this.$router.go()类似这样的页面跳转的方法,就叫做编程式导航

    hash和history模式的区别

    表现形式区别 一个带#一个没有
    原理的区别

    history.pushState()是ie10以后才支持的
    然后history.pushState()history.push()的区别是当调用history.push()路径发生变化,需要向服务器发送请求,而history.pushState()不会发送请求,只会改变浏览器的地址,并且把这个地址记录到历史记录中去,从而实现客户端路由

    history模式的使用

    • history需要服务取得支持
    • 单页面应用中只有一个页面index.html,服务端不存在http://xxxxxxx/login这样的页面,正常访问单页应用不会有任何问题,但是当前浏览器地址是这个的时候,同时我们刷新浏览器的时候,会像服务器去请求login这个页面,而服务器不存在这个页面,于是返回404
    • 所以在服务端应该除了静态资源都返回单页应用的index.html

    vue-cli自带的服务器已经处理好了这个问题,所以在本地开启的服务中,是没有这样的问题的

    node服务器配置

    如果在nodejs服务器中处理history模式的问题的时候我们需要导入connect-history-api-fallack模块,把这个模块注册到node服务器中,如下

    const path = require('path')
    const history = require('connect-history-api-fallack')	// 导入处理history模式的模块
    const express = require('express')
    
    const app = express()
    app.use(history())	// 注册处理history模式的中间件
    // 处理静态资源的中间件,网站根目录设为 ../web
    app.use(express.static(path.join(__dirname, '../web')))
    
    app.listen(3000, () => {
    	console.log('服务器开启了')
    })
    

    我们再尝试下再nginx服务其中配置处理history模式

    nginx服务器的配置
    • 从官网下载nginx的压缩包,解压,目录不能有中文
    • 打开命令行,切换到nginx目录
    • 启动nginxstart nginx 启动nginx -s reload 重启nginx -s stop 停止
    • 把打包好的前端代码拷贝到nginx文件下的html文件夹下面,之后我们需要修改的文件在conf目录下的nginx.conf文件
    // 找到http对象下的server下的location
    location / {
    	root html;
        index index.html index.htm;
        try_files $uri	$uri/	/index.html;
    }
    // try_files的意思是试着访问一下这个文件,$uri当前浏览器请求的路径所对应的文件,找到了就直接返回,找不到就继续往后找
    // 找不到,再把这个请求的路径当做一个目录($uri/),再去寻找这个目录下的默认首页index.html或者index.htm,找到直接返回,找不到继续往后找
    // 没找到,因为我们访问的是单页应用,前两次寻找没找到,意味着请求路径不存在,所以需要返回单页面应用的首页,也就是根目录文件夹下的index.html
    

    VueRouter实现原理

    hash模式

    History模式

    • 通过history.pushState()方法改变地址栏,并把当前地址记录到浏览器的访问历史中,仅仅是改变地址栏,并不会跳转路径
    • 通过监听popstate事件,可以监听到浏览器历史操作的变化,popstate事件的处理函数中,可以记录改变后的地址,点击浏览器的前进后退按钮,或者调用history的back和forward方法的时候,这个事件才会被触发
    • 根据当前的路由地址找到对应的组件,进行重新渲染

    关于vue这里需要普及一个版本问题

    // Runtime+complier
    new Vue({
      router,
      components: { App },
      template: '<App/>'
    }).$mount('#app')
    
    // Runtime-only版本
    new Vue({
      router,
      render: h => h(App)
    }).$mount('#app')
    

    手写一个支持history模式的Vue-router

    let _Vue = null
    export default class VueRouter {
        static install (Vue) {
            // 判断是否已安装
            if(VueRouter.install.installed) return
            VueRouter.install.installed = true
            // 把Vue构造函数记录到全局变量
            _Vue = Vue
            // 把创建Vue实例传入的router对象注入到Vue实例上,new Vue()那边
            // _Vue.prototype.$router = this.options.router
            _Vue.mixin({
                beforeCreated() {
                    if(this.$options.router){
                        _Vue.prototype.$router = this.options.router    // this.options 创建vue实例,初始化的时候传入的参数
                        // mixin中this指向的就是vue实例
    
                        this.$options.install.init()
                    }
                }
            })
        }
    
        constructor(options) {
            this.options = options // 记录构造函数中传入的options
            this.routeMap = {}     // 存储 路由地址:对应组件的键值对
            this.data = _Vue.observable({   // 使用vue提供的observable方法创建响应式对象,可以直接用在渲染函数和计算属性里面
                current: '/'      // 记录当前路由地址
            })
        }
    
        init() {
            this.createrouteMap()
            this.initComponents(_Vue)
            this.initEvent()
        }
    
        createrouteMap() {
            // 路由树中处理成键值对的形式,存储到routeMap中
            this.options.routes.forEach(route => {
                this.routeMap[route.path] = route.component
            })
        }
    
        initComponents (Vue) {
            Vue.component('router-link', {
                props: {
                    to: String
                },
                // template: '<a :href="to"><slot></slot></a>'  // vue-cli 默认使用运行时版本的vue,没有编译器
                render (h) {
                    return h('a', {
                        attrs: {
                            href: this.to
                        },
                        on:{
                            click: this.clickHandler
                        }
                    }, [this.$slots.default])
                },
                methods: {
                    clickHandler (e) {
                        history.pushState({}, '', this.to)  // 改变浏览器的地址栏,把当前地址加入到历史记录中
                        this.$router.data.current = this.to // data是响应式的对象,值改变之后,会重新加载对应的组件,重新渲染
                        e.preventDefault() // 阻止默认行为 使浏览器不跳转不刷新
                    }
                }
            })
            const self = this
            Vue.component('router-view', {
                render (h) {    // h函数的作用就是创建虚拟dom
                    // 找到当前的路由地址 self.data.current
                    const component =  self.routeMap[self.data.current]
                    return h(component)  // h函数可以直接把一个组件转换成虚拟dom
                }
            })
        }
    
        initEvent() {   // 处理浏览器后退前进
            window.addEventListener('popstate', () => { 
                // 取出当前地址栏中的地址,我们要的仅仅是路径部分,第一个/后面的部分
                this.data.current = window.location.pathname
            })
        }
    }
    

    起源地下载网 » Vue-Router的实现原理

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元