/packages/react-router-dom/modules/Link
Link的使用
<Link to="/about">About</Link><Link to="/courses?sort=name" /><Linkto={{pathname: "/courses",search: "?sort=name",hash: "#the-hash",state: { fromDashboard: true }}}/><Link to={location => ({ ...location, pathname: "/courses" })} /><Link to={location => `${location.pathname}?sort=name`} /><Link to="/courses" replace /><Linkto="/"innerRef={node => {// `node` refers to the mounted DOM element// or null when unmounted}}/>let anchorRef = React.createRef()<Link to="/" innerRef={anchorRef} />const FancyLink = React.forwardRef((props, ref) => (<a ref={ref}>💅 {props.children}</a>))<Link to="/" component={FancyLink} />
源码分析
import { __RouterContext as RouterContext } from “react-router”;
RouterContext 是一个React的Context对象,可以做跨组件的数据传递以及组件变更
Link 中 return 了一个
ReactContext 是通过displayName 属性进行区分的 所以有了 Link.displayName = “Link”;
Link 是通过ReactContext的状态改变,来触发UI组件的更新的
import React from "react";import { __RouterContext as RouterContext } from "react-router";import PropTypes from "prop-types";import invariant from "tiny-invariant";import {resolveToLocation,normalizeToLocation} from "./utils/locationUtils.js";/*./utils/locationUtils.js 文件内容import { createLocation } from "history";export const resolveToLocation = (to, currentLocation) =>typeof to === "function" ? to(currentLocation) : to;export const normalizeToLocation = (to, currentLocation) => {return typeof to === "string"? createLocation(to, null, null, currentLocation): to;};*/// React 15 compatconst forwardRefShim = C => C;let { forwardRef } = React;if (typeof forwardRef === "undefined") {forwardRef = forwardRefShim;}function isModifiedEvent(event) {return !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey);}const LinkAnchor = forwardRef(({innerRef, // TODO: deprecatenavigate,onClick,...rest},forwardedRef) => {const { target } = rest;let props = {...rest,onClick: event => {try {if (onClick) onClick(event);} catch (ex) {event.preventDefault();throw ex;}if (!event.defaultPrevented && // onClick prevented defaultevent.button === 0 && // ignore everything but left clicks(!target || target === "_self") && // let browser handle "target=_blank" etc.!isModifiedEvent(event) // ignore clicks with modifier keys) {event.preventDefault();navigate();}}};// React 15 compatif (forwardRefShim !== forwardRef) {props.ref = forwardedRef || innerRef;} else {props.ref = innerRef;}/* eslint-disable-next-line jsx-a11y/anchor-has-content */return <a {...props} />;});if (__DEV__) {LinkAnchor.displayName = "LinkAnchor";}/*** The public API for rendering a history-aware <a>.*/const Link = forwardRef(({component = LinkAnchor,replace,to,innerRef, // TODO: deprecate...rest},forwardedRef) => {return (<RouterContext.Consumer>{context => {invariant(context, "You should not use <Link> outside a <Router>");const { history } = context;// normalize 标准化 [ˈnɔːməlaɪz]const location = normalizeToLocation(resolveToLocation(to, context.location),context.location);const href = location ? history.createHref(location) : "";const props = {...rest,href,navigate() {const location = resolveToLocation(to, context.location);const method = replace ? history.replace : history.push;method(location);}};// React 15 compatif (forwardRefShim !== forwardRef) {props.ref = forwardedRef || innerRef;} else {props.innerRef = innerRef;}return React.createElement(component, props);}}</RouterContext.Consumer>);});if (__DEV__) {const toType = PropTypes.oneOfType([PropTypes.string,PropTypes.object,PropTypes.func]);const refType = PropTypes.oneOfType([PropTypes.string,PropTypes.func,PropTypes.shape({ current: PropTypes.any })]);Link.displayName = "Link";Link.propTypes = {innerRef: refType,onClick: PropTypes.func,replace: PropTypes.bool,target: PropTypes.string,to: toType.isRequired};}export default Link;
