react-router原理之Link跳轉

Randal發表於2018-05-24

react-router原理之路徑匹配一文中講了react-router如何根據url進行路徑匹配進而完成不同的元件渲染。接下來繼續講一下如何改變url。仍舊以官網為例,演示地址

const BasicExample = () => (
  <Router>
    <div>
      <ul>
        <li>
          <Link to="/">Home</Link>
        </li>
        <li>
          <Link to="/about">About</Link>
        </li>
        <li>
          <Link to="/topics">Topics</Link>
        </li>
      </ul>
	  ...
      <Route exact path="/" component={Home} />
      <Route path="/about" component={About} />
      <Route path="/topics" component={Topics} />
    </div>
  </Router>
);
複製程式碼

可以看出Link的作用就是接受點選然後觸發url地址的變更,Link元件最終在瀏覽器中是以何種方式存在的呢?通過檢視頁面的Element可以看到Link最終會轉成<a>標籤,上面的例子中的Link在瀏覽器中的樣子是這樣的

<ul>
	<li><a href="/">Home</a></li>
	<li><a href="/about">About</a></li>
	<li><a href="/topics">Topics</a></li>
</ul>
複製程式碼

既然最終Link被轉換成上面<a>標籤的形式,那麼和程式碼裡直接寫a標籤有什麼區別呢?

最大的區別表象體現在是否有主文件請求,<a>標籤一定會出現,而Link則是通常不出現(除非特殊的配置)

Link不只是<a>標籤

Link在最後渲染的時候其實是建立了<a>標籤,同時新增了一個onClick的監聽事件,onClick事件處理函式中做了兩件事:

  • 如果Link上定義了onClick方法,則執行該方法
  • 判定是否應該阻止a標籤的預設跳轉
    • 如果阻止的話,則根據replace的props值決定執行history.push還是執行history.replace。history包的地址是www.npmjs.com/package/his…,Link只負責觸發url變更,Route只負責根據url渲染元件,history的作用是執行url變更,並同時通知Route重新渲染。關於history(點選這裡)[https://juejin.im/post/5b06b4e4f265da0ddc0c03a4]。
    • 如果不阻止的話,則其實與直接<a>標籤的寫法類似了,當點選操作觸發時會產生主文件的請求。
render() {
    ....
    return (
      <a {...props} onClick={this.handleClick} href={href} ref={innerRef} />
    );
  }
  
  handleClick = event => {
    if (this.props.onClick) this.props.onClick(event);

    if (
      !event.defaultPrevented && // onClick prevented default
      event.button === 0 && // ignore everything but left clicks
      !this.props.target && // let browser handle "target=_blank" etc.
      !isModifiedEvent(event) // ignore clicks with modifier keys
    ) {
      event.preventDefault();

      const { history } = this.context.router;
      const { replace, to } = this.props;

      if (replace) {
        history.replace(to);
      } else {
        history.push(to);
      }
    }
  };
複製程式碼

Link什麼情況下阻止預設行為

同時下列四個條件時就會阻止預設行為

  • Link的onClick回撥中不包含event.preventDefault()程式碼
  • 點選按鍵是左鍵
  • Link的props未定義target
  • 點選操作觸發時沒有功能鍵(ctrl、shift等)被按下
!event.defaultPrevented &&  event.button === 0 && !this.props.target && !isModifiedEvent(event)
複製程式碼

特殊的Link

react-router中除了Link還有一個NavLink,NavLink是一種特殊的Link,它的特殊體現在當與url匹配時生成的<a>標籤上會帶有一些樣式資訊(通過activeClassName和activeStyle定義樣式)。

<NavLink
  to="/faq"
  activeClassName="selected"
>FAQs</NavLink>

<NavLink
  to="/faq"
  activeStyle={{
    fontWeight: 'bold',
    color: 'red'
   }}
>FAQs</NavLink>
複製程式碼

NavLink的特性需要做url的匹配查詢,之前在講路徑匹配的時候講到Route就是負責用來匹配路徑渲染元件的,因此這裡正適合引入Route來做匹配。匹配成功渲染帶樣式的Link,匹配失敗則渲染不帶樣式的Link。

Route定義路徑關聯元件的方式有三種:

  • render
  • component
  • children

render與component方式定義的元件當不匹配時是不會渲染元件的

children方式則只是將匹配規則傳入進來,具體是否渲染由children自己定義。children的這個特性剛好符合滿足NavLink的要求。

明白了NavLink的大體思路後可以去看看原始碼的具體實現。

NavLink具體實現

render() {
  return (
    <Route
      path={escapedPath}
      exact={exact}
      strict={strict}
      location={location}
      children={({ location, match }) => {
        const isActive = !!(getIsActive ? getIsActive(match, location) : match);

        return (
          <Link
            to={to}
            className={
              isActive
                ? [className, activeClassName].filter(i => i).join(" ")
                : className
            }
            style={isActive ? { ...style, ...activeStyle } : style}
            aria-current={(isActive && ariaCurrent) || null}
            {...rest}
          />
        );
      }}
    />
  );
}
複製程式碼

進入下一篇react-router原理之幕後history

相關文章