react-router4 - 实现Link组件进行点击跳转

原生Link组件用法

 <Router>
   <Link to='/'>Home</Link>
   <Link to='/about'>About</Link>
   <Link to='/mine'>Mine</Link>
   <div>
     <Route component={Home} path='/' exact></Route>
     <Route component={About} path='/about'></Route>
     <Route component={Mine} path='/mine'></Route>
   </div>
 </Router>

  1. 导出Link 组件, 该组件 接收一个 to 属性,为要跳转的路径, 标签里边可以写文本等内容

实现自己的Link 组件

  1. 入口文件先导出这个组件
import Route from './Route';
import HashRouter from './HashRouter';
import Link from './Link';
export { HashRouter, Route, Link };
  1. 创建Link.js
import React from 'react';
class Link extends React.Component {
  render() {
    // 拿到传入的 to属性对应的值, 拿到内部的子节点或文本
    const { to, children } = this.props;
    // 转化成 a 标签
    return <a href={`#` + to}>{children}</a>;
  }
}
export default Link;

改进Link 组件

  • 现在使用的是 a 标签的 默认锚点行为进行跳转的,把它改成 方法跳转的
import React, { Component } from 'react';
import Context from './Context';

class Link extends Component {
  static contextType = Context;
  // 阻止默认行为,进行事件跳转
  hashPush = (e, to) => {
    e.preventDefault();
    this.context.history.push(to);
  };
  render() {
    const { to, children } = this.props;
    return (
      <a href={`#` + to} onClick={(e) => this.hashPush(e, to)}>
        {children}
      </a>
    );
  }
}
export default Link;

在 HashRouter文件上添加 跳转方法

同时这里处理一个问题, 就是初始化默认hash值的问题,让它默认为 #/, 不再去掉了,而是在 Route文件中处理

import React, { Component } from 'react';
import Context from './Context';
class HashRouter extends Component {
  state = {
    location: {
      pathname: '#/', // 默认hash 值路径
    },
    history: {
      push(path) {
        // 路径跳转方法
        window.location.hash = '#' + path;
      },
    },
  };
  UNSAFE_componentWillMount() {
    // 监听hash 值的变化
    window.addEventListener('hashchange', () => {
      this.setState((state) => ({
        ...state,
        location: {
          pathname: window.location.hash || '#/',
        },
      }));
    });
  }
  render() {
    return (
      <Context.Provider value={this.state}>
        {this.props.children}
      </Context.Provider>
    );
  }
}
export default HashRouter;

Route.js

这里把 正则匹配的那个单独抽出来了, 因为 下边还有组件要用到

import React, { Component } from 'react';
import Context from './Context';
import { isMatch } from './utils';
class Route extends Component {
  static contextType = Context;
  render() {
    // // 拿到  <Route component={Home} path='/' exact></Route> 这上边的属性
    let { component: Component } = this.props;
    if (isMatch(this.context, this.props)) {
      return <Component />;
    }
    return null;
  }
}
export default Route;

utils.js

在这里统一处理 # 符号问题

import { pathToRegexp } from 'path-to-regexp';

/**
 * @returns 匹配的结果 true | false
 * @param {*} context 上下文
 * @param {*} component props
 */
export function isMatch(context, props) {
  const { path, exact = false, to } = props;
  // 拿到上下文中的 hash 值
  let { pathname } = context.location;
  // 截取掉 # 号
  pathname = pathname.slice(1);
  // 转成正则
  // path || to 如果是Route 组件上都是path, 但Redirect 上是 to 属性,这里需要处理
  let regexp = pathToRegexp(path || to, [], { end: exact });
  return pathname.match(regexp);
}

代码及效果演示

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章