/packages/react-router-dom/modules/Link

Link的使用

  1. <Link to="/about">About</Link>
  2. <Link to="/courses?sort=name" />
  3. <Link
  4. to={{
  5. pathname: "/courses",
  6. search: "?sort=name",
  7. hash: "#the-hash",
  8. state: { fromDashboard: true }
  9. }}
  10. />
  11. <Link to={location => ({ ...location, pathname: "/courses" })} />
  12. <Link to={location => `${location.pathname}?sort=name`} />
  13. <Link to="/courses" replace />
  14. <Link
  15. to="/"
  16. innerRef={node => {
  17. // `node` refers to the mounted DOM element
  18. // or null when unmounted
  19. }}
  20. />
  21. let anchorRef = React.createRef()
  22. <Link to="/" innerRef={anchorRef} />
  23. const FancyLink = React.forwardRef((props, ref) => (
  24. <a ref={ref}>💅 {props.children}</a>
  25. ))
  26. <Link to="/" component={FancyLink} />

源码分析

import { __RouterContext as RouterContext } from “react-router”;
RouterContext 是一个React的Context对象,可以做跨组件的数据传递以及组件变更
Link 中 return 了一个 的实例
ReactContext 是通过displayName 属性进行区分的 所以有了 Link.displayName = “Link”;

Link 是通过ReactContext的状态改变,来触发UI组件的更新的

  1. import React from "react";
  2. import { __RouterContext as RouterContext } from "react-router";
  3. import PropTypes from "prop-types";
  4. import invariant from "tiny-invariant";
  5. import {
  6. resolveToLocation,
  7. normalizeToLocation
  8. } from "./utils/locationUtils.js";
  9. /*
  10. ./utils/locationUtils.js 文件内容
  11. import { createLocation } from "history";
  12. export const resolveToLocation = (to, currentLocation) =>
  13. typeof to === "function" ? to(currentLocation) : to;
  14. export const normalizeToLocation = (to, currentLocation) => {
  15. return typeof to === "string"
  16. ? createLocation(to, null, null, currentLocation)
  17. : to;
  18. };
  19. */
  20. // React 15 compat
  21. const forwardRefShim = C => C;
  22. let { forwardRef } = React;
  23. if (typeof forwardRef === "undefined") {
  24. forwardRef = forwardRefShim;
  25. }
  26. function isModifiedEvent(event) {
  27. return !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey);
  28. }
  29. const LinkAnchor = forwardRef(
  30. (
  31. {
  32. innerRef, // TODO: deprecate
  33. navigate,
  34. onClick,
  35. ...rest
  36. },
  37. forwardedRef
  38. ) => {
  39. const { target } = rest;
  40. let props = {
  41. ...rest,
  42. onClick: event => {
  43. try {
  44. if (onClick) onClick(event);
  45. } catch (ex) {
  46. event.preventDefault();
  47. throw ex;
  48. }
  49. if (
  50. !event.defaultPrevented && // onClick prevented default
  51. event.button === 0 && // ignore everything but left clicks
  52. (!target || target === "_self") && // let browser handle "target=_blank" etc.
  53. !isModifiedEvent(event) // ignore clicks with modifier keys
  54. ) {
  55. event.preventDefault();
  56. navigate();
  57. }
  58. }
  59. };
  60. // React 15 compat
  61. if (forwardRefShim !== forwardRef) {
  62. props.ref = forwardedRef || innerRef;
  63. } else {
  64. props.ref = innerRef;
  65. }
  66. /* eslint-disable-next-line jsx-a11y/anchor-has-content */
  67. return <a {...props} />;
  68. }
  69. );
  70. if (__DEV__) {
  71. LinkAnchor.displayName = "LinkAnchor";
  72. }
  73. /**
  74. * The public API for rendering a history-aware <a>.
  75. */
  76. const Link = forwardRef(
  77. (
  78. {
  79. component = LinkAnchor,
  80. replace,
  81. to,
  82. innerRef, // TODO: deprecate
  83. ...rest
  84. },
  85. forwardedRef
  86. ) => {
  87. return (
  88. <RouterContext.Consumer>
  89. {context => {
  90. invariant(context, "You should not use <Link> outside a <Router>");
  91. const { history } = context;
  92. // normalize 标准化 [ˈnɔːməlaɪz]
  93. const location = normalizeToLocation(
  94. resolveToLocation(to, context.location),
  95. context.location
  96. );
  97. const href = location ? history.createHref(location) : "";
  98. const props = {
  99. ...rest,
  100. href,
  101. navigate() {
  102. const location = resolveToLocation(to, context.location);
  103. const method = replace ? history.replace : history.push;
  104. method(location);
  105. }
  106. };
  107. // React 15 compat
  108. if (forwardRefShim !== forwardRef) {
  109. props.ref = forwardedRef || innerRef;
  110. } else {
  111. props.innerRef = innerRef;
  112. }
  113. return React.createElement(component, props);
  114. }}
  115. </RouterContext.Consumer>
  116. );
  117. }
  118. );
  119. if (__DEV__) {
  120. const toType = PropTypes.oneOfType([
  121. PropTypes.string,
  122. PropTypes.object,
  123. PropTypes.func
  124. ]);
  125. const refType = PropTypes.oneOfType([
  126. PropTypes.string,
  127. PropTypes.func,
  128. PropTypes.shape({ current: PropTypes.any })
  129. ]);
  130. Link.displayName = "Link";
  131. Link.propTypes = {
  132. innerRef: refType,
  133. onClick: PropTypes.func,
  134. replace: PropTypes.bool,
  135. target: PropTypes.string,
  136. to: toType.isRequired
  137. };
  138. }
  139. export default Link;