前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >前端面试题锦集:第三期VueRouter

前端面试题锦集:第三期VueRouter

作者头像
terrence386
发布2022-07-15 09:49:52
5790
发布2022-07-15 09:49:52
举报

面试只是起点,能力才是终局。本期着重讨论vue-router

router-view组件

我们平时写vue项目的时候,遇到路由的时候习惯上直接使用router-view组件,但是这个组件时谁提供的呢?我们似乎很少考虑这个问题,其实<router-view/>, <link />vue-router提供的两个组件。

router-view组件中的使用父组件的$createElement方法,对定义的路由进行匹配,如果匹配到路由对应的组件,则对这个组件执行createElement,否则就创建一个空的Dom。示例代码如下:

代码语言:javascript
复制
// 定义routerView组件

export default {
  name: 'RouterView',
  render (_, { props, children, parent, data }) {
    const h = parent.$createElement
    const route = parent.$route
    
    const matched = route.matched[depth]
    const component = matched && matched.components[name]
    ...
    // 没匹配到组件 则渲染空节点
    if (!matched || !component) {
      cache[name] = null
      return h()
    }
    ...
    // 匹配到 则渲染该组件
    return h(component, data, children)
  }

}

link组件

了解了router-view组件后,思考一下link组件,link组件接收一个to属性标识路由地址,然后有一个click事件,用来进行路由跳转,嗯,大致就是这个样子。另外它在界面上会渲染为一个a标签,是因为它有个默认的Tag属性,默认值是a,然后渲染的时候根据这个tag属性进行渲染。

代码语言:javascript
复制
export default {
  name: 'RouterLink',
  props: {
    to: {
      type: toTypes,
      required: true
    },
    tag: {
      type: String,
      default: 'a'
    },
  }
  render (h: Function) {
    const router = this.$router
    const current = this.$route
    ...
    // 渲染
    return h(this.tag, data, this.$slots.default)
  }
}

vue-router提供了哪些API

  • push
  • go
  • back
  • forward
  • replace
  • addRoute
  • addRoutes
  • getRoutes
  • onReady
  • onEerror
  • beforeEach
  • beforeResolve
  • getMatchedComponents

如何捕获路由参数?

这个问题我们通常会说通过this.route.params获取,当然这些都是文档上写的正确的内容。但是假如我再问你,这个this.route.params具体是怎么获取的路由参数呢?怎么回答?

同理,this.$route.query呢? 是怎么实现的参数捕获?

对于路由参数,可以理解为有两种,一种是动态路由的参数,一种是常见的路径中的查询字符串。

动态路由参数也就是文档中说的如下的配置:

代码语言:javascript
复制
// 动态路由配置
const User = {
  template: '<div>User</div>'
}

const router = new VueRouter({
  routes: [
    // 动态路径参数 以冒号开头
    { path: '/user/:id', component: User }
  ]
})

动态路由参数的获取通过this.$route.params来获取。其实现逻辑大致是:

现根据当前路由进行匹配,匹配时会根据route参数中的namepath进行判断,然后创建route的时候将queryparams挂载成为route的两个参数

代码语言:javascript
复制
const normalizeLocation = () => {
  //...
  return {
    _normalized: true,
    path,
    query,
    hash
  }
}
// match方法
export const match(rawLocation,currentRoute,redirectedFrom){
  
  // 定义location
  const location = normalizeLocation(rawLocation,currentRoute,false,router)
  const { name } = location
  if(name){
    ...
    return _createRoute(record, location, redirectedFrom)
  }else if(location.path){
    ...
    return _createRoute(record, location, redirectedFrom)
  }
  // 什么都没有
  return _createRoute(null, location)
}
// createRoute

const _createRoute = (recordRoute,location,router){
  const route = {
    name:location.name || recordRoute.name,
    meta:recordRoute.meta || {},
    path:location.path || '/',
    hash:location.hash || '',
    query:,
    params:location.params || {},
    ...
  }
  return Object.freeze(route)
}

路由守卫

文档上的写法是路由守卫。然后提供了几个router的方法:

  • 全局前置守卫
代码语言:javascript
复制
const router = new VueRouter({ ... })

router.beforeEach((to, from, next) => {
  // ...
})
  • 全局后置钩子
代码语言:javascript
复制
const router = new VueRouter({ ... })

router.afterEach((to, from) => {
  // ...
})
  • 路由独享的守卫
代码语言:javascript
复制
const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]
})
  • 组件内的守卫
代码语言:javascript
复制
const Foo = {
  template: `...`,
  beforeRouteEnter(to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不!能!获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创建
  },
  beforeRouteUpdate(to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
  },
  beforeRouteLeave(to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
  }
}

我们的重点不在于文档提供的这些方法怎么用,而是要思考下面这个问题:

什么是钩子函数? 如何实现这些钩子函数?

对于经常写react代码的同学,我们通常会用到诸如useState,useEffect之类的钩子方法。同样可以思考这个问题。

钩子方法是一种非常简单有效的隔离变化的一种手段。我们可以把vueRouter看成是一个模板,钩子方法就是模板内的一个方方法,在初始化的时候根据钩子方法的结果去执行不同的逻辑。比如:

代码语言:javascript
复制
// 模板
class VueRouter {
  read(){
    console.log('ready')
  }
  push(){
    console.log('push')
  }
  go(){
    console.log('go')
  }
  testHook(){
    return window.confirm( '执行ready?' );
  }
  init(){
    if(this.testHook()){
      this.read()
    }
  }
}

let testRouter = new VueRouter();
testRouter.init()

钩子函数最简单的写法大概就是这个样子。

数据获取的时机

这个是说从服务端获取数据的时机,也就是我们平时请求接口的时机。

一般来说我们通常是在createdmounted中进行请求,这个其实是在路由导航完成之后进行的数据获取。

还有一种方法是在导航完成之前进行请求,比如:

代码语言:javascript
复制
export default {
  data () {
    return {
      post: null,
      error: null
    }
  },
  beforeRouteEnter (to, from, next) {
    getPost(to.params.id, (err, post) => {
      next(vm => vm.setData(err, post))
    })
  },
  // 路由改变前,组件就已经渲染完了
  // 逻辑稍稍不同
  beforeRouteUpdate (to, from, next) {
    this.post = null
    getPost(to.params.id, (err, post) => {
      this.setData(err, post)
      next()
    })
  },
  methods: {
    setData (err, post) {
      if (err) {
        this.error = err.toString()
      } else {
        this.post = post
      }
    }
  }
}

这种方法有一个问题是,在为后面的视图获取数据时,用户会停留在当前的界面,需要对这个问题进行单独处理。

滚动行为

VueRouter还提供了一个支持页面滚动的方法scrollBehavior。这个功能只在支持 history.pushState 的浏览器中可用。

代码语言:javascript
复制
const router = new VueRouter({
  routes: [...],
  scrollBehavior (to, from, savedPosition) {
    // return 期望滚动到哪个的位置
  }
})

scrollBehavior 方法接收 to 和 from 路由对象。第三个参数 savedPosition 当且仅当 popstate 导航 (通过浏览器的 前进/后退 按钮触发) 时才可用。

我们可以理解这个方法是对window.scrollTo方法的封装。因为在代码的最后你会发现这样的一段:

代码语言:javascript
复制
function scrollToPosition(shouldScroll, position){
  // ...略
   if (position) {
    // $flow-disable-line
    if ('scrollBehavior' in document.documentElement.style) {
      window.scrollTo({
        left: position.x,
        top: position.y,
        // $flow-disable-line
        behavior: shouldScroll.behavior
      })
    } else {
      window.scrollTo(position.x, position.y)
    }
  }
}

在vue-router的实例方法中addRoute,addRoutes有时候可以用来处理一些复杂的业务逻辑,必须权限控制之类的,当让这个需要根据实际情况进行调整。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-09-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 JavaScript高级程序设计 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • router-view组件
  • vue-router提供了哪些API
  • 如何捕获路由参数?
  • 路由守卫
  • 数据获取的时机
  • 滚动行为
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档