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

    正文概述 掘金(唯小南)   2020-12-08   343

    Vue Router 是 Vue.js 官方的路由管理器,可以让构建单页面应用变得易如反掌

    单页面应用(Single Page Application,SPA)

    应用只有一个完整的页面,它在加载页面时,不会加载全部页面,只加载一个首页的容器,其它页面的内容在首页容器某个位置,需要页面切换时只更新某个指定的容器中内容

    • 单页面应用的核心之一是:更新视图而不重新请求页面

    单页面应用不仅仅是在页面交互是无刷新的,连页面跳转都是无刷新的,为了实现单页应用,所以就有了前端路由。类似于服务端路由,前端路由实现起来其实也很简单,就是匹配不同的 url 路径,进行解析,然后动态的渲染出区域 html 内容

    但当url变化,都会造成页面的刷新,目前有两种方式实现:

    • 一种方式是用hash实现路由
    • 另一种方式是用history

    Vue-Router在实现单页面前端路由时,提供了两种方式:Hash模式和History模式

    Vue-Router的使用

    通过Vue-cli安装Vue项目时可以把Vue-Router一起安装到项目中,或者另外安装引入

    Vue-Router 使用步骤

    1、安装依赖

    yarn add vue-router --dev
    

    2、在Vue中使用 注册路由组件

    import Vue from 'vue'
    import VueRouter from 'vue-router'
    // Vue.use 是用来注册插件 会调用传入对象的install方法
    Vue.use(VueRouter)
    

    3、定义路由规则 路由规则是一个数组

    const routes = [
      {
        path: '/', // 路径
        name: 'Home', // 路由名称  路径和名称在push和replace中都可以作为跳转目标
        component: Home // 对应的组件
      }
    ]
    

    4、创建路由对象

    const router = new VueRouter({  router // ES6的语法  需要传入一个属性router的数组})
    

    5、在Vue实例创建时传入创建路由实例

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

    new Vue 时传入的router是为了给Vue实例注入 routerrouter,router,route 这两个属性

    • $route 存储当前的路由规则信息,路径、参数等信息
    • $router 是提供一些操作路径的方法 push、replace、go 还能通过currentRoute拿到当前路由规则信息

    6、在首页(主页面)使用显示路由组件,和使用显示跳转(不是必须,可以用编程式导航代替)

    // app.vue
    <div id="app">
      <div id="nav">
        <router-link to="/">Home</router-link> |
        <router-link to="/about">About</router-link> |
        <router-link to="/info">Info</router-link>
      </div>
      <!-- 这里会被具体的路由组件内容替换掉 -->
      <router-view/>
    </div>
    

    动态路由

    在路由规则里 path后面跟上 :/参数名,就可以在url上传参数给路由对应的组件

    还可以再路由规则中指定props: true,指定后url上面的参数会作为props传给组件

    {
        path: '/detail/:id', // :id 即一个占位符 通过这个占位来匹配变化的位置
        name: 'Detail',
        props: true,  // 开启了props为true后,url上面的参数会作为props传给组件
        // 即路由懒加载 仅当访问此路由地址时才会加载对应组件 提高性能
        component: () => import(/* webpackChunkName: "detail" */ '../views/Detail.vue')
      }
    
    // Detail.vue
    // 通过$route来获取参数
    {{ $route.params.id }}
    
    // 或者因为在路由规则开启了props:true,所以在props中接收参数 页面直接使用
    {{ id }}
    
    export default {
      props: ['id']
    }
    

    嵌套路由

    当多个路由的组件都有相同的内容,可以将这些相同内容提取到公共的组件当中,在公共组件里面通过路由的方式(使用)显示不同的路由组件

    {  path: '/',
       component: Layout,
       children: [
         // '/'会显示 Layout Index
         {
            name: 'index',
            path: '',
            component: Index
         },
         // '/detail/20' 会显示 Layout detail
         {
            name: 'detail',
            path: 'detail/:id',
            props: true,
            component: () => import('../view/detail.vue')
         }
       ]
    }
    
    // Layout.vue
    <div>
       <div>Layout</div>  <div>
          <!-- '/'会显示 Layout Index -->
          <!-- '/detail/20' 会显示 Layout detail -->
          <router-view></router-view>
       </div>
    </div>
    

    编程式导航

    除了用外,更多情况下是使用$router的方法来实现路由跳转

    • push 方法 会记录本次历史

    • replace 方法 不会记录本次历史 会替换历史记录

    • go 方法 会以0为基准跳转到相对页面

      // 跳转 会记录本次历史 this.router.push(/)this.router.push('/') this.router.push(′/′)this.router.push({ name: 'Home' }) this.router.push(name:Detail,params:id:2)//跳转不会记录本次历史this.router.push({ name: 'Detail', params: { id: 2 }}) // 跳转 不会记录本次历史 this.router.push(name:′Detail′,params:id:2)//跳转不会记录本次历史this.router.replace('/login') // 按照当前历史进行跳转 负数是回退 正数是向前 this.$router.go(-2)

    Hash模式和History模式 区别

    两种方式都是,当路径法发生变化,不会向服务器发送请求,使用js监视路径的变化根据不同的地址渲染不同的内容

    Hash模式

    • url里面有#,#后面是路由地址
    • 原理实现是基于锚点 和 onhashchange事件

    History模式

    • url里面没有#
    • 原理实现是基于HTML5中History API
    • history.pushState()
    • history.replaceState()

    History模式 服务端支持

    History需要服务器支持,因为History没有 # 号,所以当用户刷新页面之类的操作时,浏览器还是会给服务器发送请求。为了避免出现这种情况,所以这个实现需要服务器的支持,把除了静态资源外所有路由都重定向到根页面

    在node中,可以使用 connect-history-api-fallback 来重定向到根页面

    const history = require('connect-history-api-fallback')
    // 使用
    app.use(history)
    

    在nginx中,在配置文件中进行修改

    // 对应的conf文件
    location / {
      root html;
      index index.html index.htm;
      // 访问路径 有路径返回路径 没有就返回index.html
      try_files $uri $uri /index.html;
    }
    

    一个简单Vue-Router实现

    Vue-Router中有两种模式

    Hash 模式

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

    History 模式

    • 通过 history.pushState() 方法改变地址栏
    • 监听 popstate 事件
    • 根据当前路由地址找到对应组件重新渲染

    使用自定义的Vue-Router

    需要先在Vue中使用自定义的Vue-Router,注入到Vue实例中

    router/index.js
    import Vue from 'vue'
    // 导入自定义的Vue-Router
    import VueRouter from '../vuerouter'
    // 使用
    Vue.use(VueRouter)
    // 路由规则
    const routes = [...]
    // 创建实例
    const router = new VueRouter({
      mode: 'history', // 指定模式
      routes // 传入路由规则
    })
    

    自定义的VueRouer需要实现的功能

    • Vue.use()会调用传入对象的静态install方法,需要在这里实现注入到Vue中,保存Vue实例给其他地方用
    • install里面mixin混合时可以在Vue实例beforeCreate生命周期中,可以进行一些初始化init()
    • constructor 构造器中要把传入的options保存起来
    • 初始化init() 把路由规则数组转成对象方便获取;初始化组件;监听浏览器的历史前进后退事件
    • 编码方式的路由跳转功能

    install()

    // 用_Vue记录Vue实例
    let _Vue = null
    // vue.use(VueRouter) 会调用这个方法
      static install (Vue) {
        // 判断当前插件是否已经安装
        if (VueRouter.install.installed) {
          return
        }
        VueRouter.install.installed = true
        // 把vue构造函数记录到全局变量
        _Vue = Vue
        // 混入
        _Vue.mixin({
          beforeCreate () {
            // vue实例才有options.router
            if (this.$options.router) {
              // 把router注入到vue实例
              _Vue.prototype.$router = this.$options.router
              // 执行一下init
              this.$options.router.init()
            }
          }
        })
     }
    

    constructor()

    constructor (options) {
        // 保存一下 options mode
        this.options = options
        this.routeMap = {}
        this.mode = this.options.mode || 'hash'
        // 构造器会晚于install的执行
        // install里面会给_Vue赋值为Vue实例
        // 这里给data创建为响应式数据 数据改变就重新渲染
        this.data = _Vue.observable({
          current: ''
        })
      }
    

    init()

    // 把路由规则routes转成一个routeMap 键值对
        this.createRouteMap()
        // 初始化 <router-view> 和 <router-link> 组件
        this.initComponents(_Vue)
        // 监听后退 前进
        this.initEvent()
        // 有第一个规则 就默认显示第一个
        if (this.options.routes[0]) {
          this.routerPush(this.options.routes[0].path)
        }
    }
    

    createRouteMap()

    createRouteMap () {
        // 遍历所有路由规则 把路由规则解析成键值对 存储到routeMap
        this.options.routes.forEach(route => {
          this.routeMap[route.path] = route.component
        })
      }
    

    initComponents()

    需要用编码的方式创建,这个方法用render函数来实现

    initComponents (Vue) {
        const self = this
        Vue.component('router-link', {
          props: {
            to: String
          },
          render (h) {
            return h('a', {
              attrs: {
                href: this.to
              },
              on: {
                click: this.clickHandler
              }
              // 默认插槽
            }, [this.$slots.default])
          },
          methods: {
            clickHandler (e) {
              self.routerPush(this.to)
              e.preventDefault() // 阻止事件默认行为
            }
          }
        })
        Vue.component('router-view', {
          render (h) {
            const component = self.routeMap[self.data.current]
            return h(component)
          }
        })
      }
    

    initEvent()

    两种模式对应了两个window的变化事件

    initEvent () {
        if (this.mode === 'hash') {
          window.addEventListener('hashchange', () => {
            this.data.current = location.hash.substr(1)
          })
        } else if (this.mode === 'history') {
          window.addEventListener('popstate', () => {
            this.data.current = window.location.pathname
          })
        }
      }
    

    routerPush()

    页面跳转

    routerPush (to) {
        // 这里的this是指向VueRouter创建的实例
        if (this.mode === 'hash') {
          // 修改hash值
          window.location.hash = to
          // this.data === _Vue.prototype.$router.data
          this.data.current = to
        } else if (this.mode === 'history') {
          // 改变浏览器地址栏 pushiState 不向服务器发送请求
          history.pushState({}, '', to)
          this.data.current = to
        }
      }
    

    起源地下载网 » Vue-Router 使用和原理简单实现

    常见问题FAQ

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

    发表评论

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

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

    联系作者

    请选择支付方式

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