前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >写给vue转react的同志们(6)

写给vue转react的同志们(6)

作者头像
饼干_
发布2022-09-19 15:19:22
4690
发布2022-09-19 15:19:22
举报

theme: channing-cyan

本系列文章将由浅慢慢深入,一步步带你领略react和vue的同工异曲之处,让你左手react,右手vue无忧。

前提要顾: 点击查看该系列专栏

Vue 与 React 的路由

路由的实现原理

众所周知,路由是前端必不可少的一部分,在实际业务中也是我们接触最多的一个模块。那其实不论 Vue 还是 React,他们实现路由的原理都大同小异,既通过 hash 和 history 这两种方式实现。

那我们简单了解一下他的实现原理吧。

hash 模式

hash 模式的原理主要是基于 onhashchange 事件去做文章:

代码语言:javascript
复制
window.onhashchange = () => {
    let hash = location.hash
    console.log(hash)
}

上图的 hash 打印出来的是 # 后面的路径(包括#)。

由于 hash 的变化都会被浏览器记录下来,使得浏览器的前进后退都可以使用,将页面状态和 url 关联起来,尽管没有请求服务器,这就是路由的最初模样。SPA(单页面应用) 的标配。

hash 模式下,发起的请求也不会被 hash 值影响(http请求中),不会重新加载页面。

history 模式

history 模式下的 url 就是正常的 url 比 hash 模式下的好看,但这也需要后台配置支持。

在 history 模式下用到了 onpopstate 这个事件:

代码语言:javascript
复制
window.onpopstate = function(event) {
  alert("location: " + document.location + ", state: " + JSON.stringify(event.state))
}

通过浏览器提供的 history api,url 更加好看了,但是取而代之的是刷新时,如果服务器中没有相应的资源就可能会报 404,这是因为刷新了又去请求了服务器。

Vue router

对原理有了简单的了解之后,我们来简单的看看 Vue router 吧。

作为当前国内最火热的框架 Vue,大家当然再熟悉不过了,他的基本使用我就不做过多叙述了。

在上面我们了解了路由的基本原理,我们可以通过这个原理来实现一个简单的 Vue 路由,所以我们简单梳理一下我们要做的功能点:

  • 作为插件,需要有 install 方法。
  • 监听 url 变化,这里只做 hash 模式下的监听。
  • router-view 和 router-link 简单实现

明确目标后,我们先新建一个路由类,并且将传进来的 options (路由配置)保存起来然后初始化:

代码语言:javascript
复制
class vueRouter {
    constructor(options) {
        // 配置选项
        this.$options = options
        // 路由映射关系
        this.routeMap = {}
        // 将 Vue 实例储存起来用于响应式
        this.app = new Vue({
            data() {
                return {
                    current: '/'
                }
            }
        })
    }
}

vueRouter.install = (_Vue) => {
    // 将 Vue 类存起来
    Vue = _Vue
    // 注册全局混入将该生命周期混入到全局用于初始化
    Vue.mixin({
        created() {
            if(this.$options.router) {
                this.$router = this.$options.router
                // init 初始化路由 
                // this.$options 是 new Vue() 时传入的参数
                this.$options.router.init()
            }
        }
    })
}

初始化做完后,需要监听对应的回调事件用于响应我们的路由,并且注册对应的组件。

代码语言:javascript
复制
initEvent() {
    // 监听浏览器的hashchange和load事件,使用bind改变this指向
    window.addEventListener('hashchange', this.handleHashChange.bind(this))
    window.addEventListener('load', this.handleHashChange.bind(this))
}

handleHashChange() {
    // 获取#后面的部分赋值给app的current
    this.app.current = location.hash.slice(1)
}

initRouteMap() {
    // 路由映射关系对应
    this.$options.routes.forEach(item => {
      this.routeMap[item.path] = item
    })
}

registerComponents() {
    // router-link 用于点击跳转
    Vue.component('v-router-link', {
      props: {
        to: String
      },
      render: function (h) {
        return h('a', { attrs: { href: `#${this.to}` } }, this.$slots.default)
      }
    })

    // router-view 展示当前路由对应组件即可
    Vue.component('v-router-view', {
        render: h => {
            const com = this.routeMap[this.app.current].component

            return h(com)
          }
    })
}

// 初始化方法
init() {
    this.initEvent()
    this.initRouteMap()
    this.registerComponents()
}

使用

代码语言:javascript
复制
// main.js
import Vue from 'vue'
import vueRouter from './vueRouter'
import xxx from './xxx'
import home from './home'

Vue.use(vueRouter)

export default new vueRouter({
    routes:[{
        path: '/home',
        component: home
    },
    {
        path: '/xxx',
        component: xxx
    }]
})

// home.vue
<template>
    <div id='app'>
        <v-router-link to='/xxx'>xxx</v-router-link>
        <v-router-view />
    </div>
</template>

React router

我们知道 React 做为一个开放式的框架(不像Vue那样 Vue Router、Vuex等捆绑在一起),自由度是比较高的,没有像 Vue 那样教科书一般的配置,需要我们自己选择插件。这其实不乏有优秀的插件(这里只说路由相关插件)如:React router、react-router-dom等等。

由于笔者使用 react-router-dom 比较多,这里拿他来做文章。

react-router-dom 是利用了 Context API,通过上下文对象将当前路由信息对象注入到<Router>组件,所以<Router>组件渲染的内容就是 Context API 提供的 Provider,然后接收<Router>组件中的当前路由信息对象。

与 Vue Router 相似我们同样需要监听 url 的变化在对应回调中拿到相应的数据。不同的是 react-router-dom 中需要创建上下文对象来供我们全局使用,通过 Context 来传递我们想要的数据,简单梳理一下:

  • 创建上下文,将当前路由注入。
  • 监听 url 变化。
  • 简单实现 Route 等相关组件。

创建一个上下文并导出。

代码语言:javascript
复制
//context.js
import React from "react"
export default React.createContext()

将当前路由注入上下文,并监听 url 控制渲染。

代码语言:javascript
复制
// hashRouter.js
import React, { Component } from "react"
import Context from "./context" // 引入上下文对象

export default class hashRouter extends Component {
    constructor(props) {
        super(props)
        // 将状态存入 state 中方便修改。
        this.state = {
            location: {
                // 获取浏览器地址栏中的hash值,如果不存在则默认为"/"
                pathname: window.location.hash.slice(1) || "/", 
                query: undefined
            }
            // 用于实现当前路由的切换
            history: {
                push: (to) => {
                    if (typeof to === "object") {
                        // 如果参数是对象的情况:
                        let { pathname, query } = to
                        window.location.hash = pathname // 更新浏览器hash值,触发浏览器hashchange事件
                        this.state.location.query = query // 更新query
                    } else {
                        // 如果参数是路径:
                        window.location.hash = to // 更新浏览器hash值
                    }
                }
            }
        }
     }
    componentDidMount() {
        window.addEventListener("hashchange", () => { // 监听浏览器地址栏hash值变化
            this.setState({ // 当hash值变化后更新当前路由信息, HashRouter组件内的子组件Route将会重新渲染
                location: {
                    ...this.state.location,
                    pathname: window.location.hash.slice(1) // 更新pathname
                }
            })
        })
    }
     render() {
        // 当前路由对象
        const currentRoute = {}
        return (
             // 使用 Provider 组件将当前路由信息对象注入上下文中,以便其 Route 等子组件能够获取到当前路由信息
            <Context.Provider value={currentRoute}>
                {this.props.children}
            </Context.Provider>
        )
    }
}

在上述代码完成之后,我们就可以开始封装 Route 组件了,通过 Context 获取当前路由信息,将匹配路径的对应组件渲染出来。

代码语言:javascript
复制
// Route.js
import React, { Component } from "react"
import context from "./context"

export default class Route extends Component {
    static contextType = context
    render() {
        const currentRoutePath = this.context.location.pathname // 从上下文中获取到当前路由的路径
        const { path, component:Component } = this.props // 获取给 Route 传递的 props 属性
        const props = {
            ...this.context
        }
        // 匹配路径
        if (currentRoutePath === path) {
            return (
                <Component {...props}></Component>
            )
        }
        return null
    }
}

使用

代码语言:javascript
复制
// router.js
import Route from './Route.js'
const router = (
    <Route
        path='/'
        component = () => import('./home.jsx')
    />
    <Route
        path='/xxx'
        component = () => import('./xxx.jsx')
    />
)
export default router
代码语言:javascript
复制
// main.js
import React from 'react'
import router from './router.js'
class App extends React.Component { 
  constructor(props) {
    super(props)
    this.state = {
        // init something ...
    }
  }
  render() {
      <div>
          {router}
      </div>
  }
}

最后

以上的 Vue 和 React 实现的 router 只是最基本的路由功能,如 Vue Router 中的 keepalive、路由守卫等一些不错的功能没有去叙说。如 react-router-dom 中的 Route 组件的 exact(精确匹配)、Link(类似a标签)、Redirect(重定向)等一些不错的辅助功能也没有叙说,且只说了 hash 模式(history 模式就是监听另一个事件,逻辑都差不多)

都看到这里了,不点个赞再走吗?

欢迎在下方给出你的建议和留言。

关注公众号:饼干前端,获取更多前端知识~

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-12-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • theme: channing-cyan
  • Vue 与 React 的路由
    • 路由的实现原理
      • hash 模式
      • history 模式
    • Vue router
      • 使用
    • React router
      • 使用
  • 最后
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档