react-router 环境使用锚点的方法

锚点是通过在界面中增加一些特征(比如 id),然后在 URL 地址后面加上 #id 就可以访问到指定页面的指定位置,这样可以让我们快速跳转到页面的某个位置,但是在 react-router 中这种方法遇到了问题,因为 react-router 会把 # 当做是 hash 来处理。导致即使跳转到指定页面后,# 后面的锚点也不生效。针对这个问题,在 react-router 的一个 issue 中大家也展开了激烈的讨论。以下是我看过以后整理的几种解决办法。

只有某些页面需要

当只有某些页面需要使用锚点的时候,可以在访问到该页面后使用 react 生命周期中 componentDidMount 阶段来判断 # 后面的字符串,然后使用 dom 操作取到这个字符串所属的 dom,然后滚动到该位置。具体代码如下:

componentDidMount() {
  // Decode entities in the URL
  // Sometimes a URL like #/foo#bar will be encoded as #/foo%23bar
  window.location.hash = window.decodeURIComponent(window.location.hash);
  const scrollToAnchor = () => {
    const hashParts = window.location.hash.split('#');
    if (hashParts.length > 2) {
      const hash = hashParts.slice(-1)[0];
      document.querySelector(`#${hash}`).scrollIntoView();
    }
  };
  scrollToAnchor();
  window.onhashchange = scrollToAnchor;
}

说不准哪些页面会使用

以上方法适用于具有生命周期的 react component,而且是在组件的生命周期中实现这个功能,若需要所有页面都实现这个功能,明显我们不可能在所有 component 中都增加这个方法。这就产生另外一个方案,就是在 Router 的 onUpdate 函数中实现该功能。onUpdate 函数在路由跳转后会被调用一次,如下所示:

import React from 'react';
import { render } from 'react-dom';
import { Router, Route, browserHistory } from 'react-router';

const routes = (
  // your routes
);

function hashLinkScroll() {
  const { hash } = window.location;
  if (hash !== '') {
    // Push onto callback queue so it runs after the DOM is updated,
    // this is required when navigating from a different page so that
    // the element is rendered on the page before trying to getElementById.
    setTimeout(() => {
      const id = hash.replace('#', '');
      const element = document.getElementById(id);
      if (element) element.scrollIntoView();
    }, 0);
  }
}

render(
  <Router
    history={browserHistory}
    routes={routes}
    onUpdate={hashLinkScroll}
  />,
  document.getElementById('root')
)

当地址改变后,hashLinkScroll 会被调用,函数内部实现原理与上面的原理是一样的,拿到 # 后面的 id 并找到指定 dom,然后滚动到该 dom 的位置。

总结

两种方案各有优劣,可以根据自己的情况来选择使用。


Warning: Missing argument 1 for cwppos_show_review(), called in /home/wwwroot/www.mycode.net.cn/wp-content/themes/flat/content-single.php on line 29 and defined in /home/wwwroot/www.mycode.net.cn/wp-content/plugins/wp-product-review/includes/legacy.php on line 18

Notice: Undefined variable: post_id in /home/wwwroot/www.mycode.net.cn/wp-content/plugins/wp-product-review/includes/legacy.php on line 20

发表评论