single-spa 同时使用 vue react,vue 路由跳转 undefined

310 阅读1分钟

一、复现路径

用 react 的 useNavigate 跳转到一个 vue 路由,vue 页面内用 router.push 就会跳转 ${host}undefined,如 juejin.cnundefined ,直接无法访问。

二、原因

vue-router push 方法两次跳转

function push(to, data) {
    const currentState = assign({},
        historyState.value, history.state, {
        forward: to,
        scroll: computeScrollPosition(),
    });
    changeLocation(currentState.current, currentState, true);
    const state = assign({}, buildState(currentLocation.value, to, null), { position: currentState.position + 1 }, data);
    changeLocation(to, state, false);
    currentLocation.value = to;
}

第一个 changeLocation(currentState.current, currentState, true) currentState.current 是 undefined,而 changeLocation 用 createBaseLocation() + base + to 组装url,直接拼成 undefined。

currentState.current undefined 是因为 react-router 用的 history 不会给 state 赋值 current

function getHistoryStateAndUrl(nextLocation, index) {
  return [
    {
      usr: nextLocation.state,
      key: nextLocation.key,
      idx: index,
    },
    createHref(nextLocation),
  ];
}
function push(to, state) {
  var _getHistoryStateAndUr = getHistoryStateAndUrl(
    nextLocation,
    index + 1
  ),
    historyState = _getHistoryStateAndUr[0],
    url = _getHistoryStateAndUr[1];
  globalHistory.pushState(historyState, "", url);
}

精简了一下代码,historyState 只有 usr, key, idx 三个属性,没有 current

三、解决

如果改 vue,用 router.replace 不用 router.push 能行,还可以考虑给 vue-router 加点兼容

if (currentState.current) {
    changeLocation(currentState.current, currentState, true);
}

还可以改 history 把 current 加上。

不过改动都有点大,考虑到 vue react 并存也只是临时状态,最好选择监听 popstate 事件纠正 state

useEffect(() => {
  window.addEventListener('popstate', ({ state }) => {
    setTimeout(() => {
      // 纠正react跳转state
      if (!history.state.current) {
        history.replaceState(
          null,
          null,
          `${window.location.pathname}${window.location.search}`
        )
      }
    }, 500)
  })
}, [])