Skip to content
Go back

Vue-Router的简易实现

Updated:  at  05:46 PM
function createCurrentLocation(base = "") {
  const { pathname, search, hash } = window.location;
  const hasHash = base.indexOf('#') > -1;
  if (hasHash) {
    return base.slice(1) || '/';
  }
  return pathname + search + hash;
}

function buildState(back, current, forward, replace = false, computeScroll = false) {
  return {
    back,
    current,
    forward,
    replace,
    scroll: computeScroll ? { left: window.pageXOffset, top: window.pageYOffset } : null,
    position: window.history.length - 1,
  }
}


function useHistoryStateNavigation(base) {
  const currentLocation = {
    value: createCurrentLocation(base),
  };
  const historyState = {
    value: window.history.state,
  }
  // 维护当前位置和历史状态
  // push跳转还是replace跳转,跳转后的位置
  if (historyState.value === null) {
    changeLocation(currentLocation.value, buildState(null, currentLocation.value, null), true)
  }

  function changeLocation(to, state, replace = false) {
    const hasHash = base.indexOf('#') > -1;
    const url = hasHash ? base + to : to;
    window.history[replace ? 'replaceState' : 'pushState'](state, '', url)
    historyState.value = state;
  }

  function push(to, data) {
    // a -> b
    // 更新跳转之前的状态,保存下页面位置,然后替换掉当前状态
    const currentState = Object.assign(
      {},
      historyState.value,
      { forward: to, scroll: { left: window.pageXOffset, top: window.pageYOffset } }
    );
    // 本质没跳转,只是更新了状态
    changeLocation(currentState.current, currentState, true);

    const state = Object.assign(
      {},
      buildState(currentLocation.value, to, null),
      { postition: currentState.postition + 1 },
      data
    );
    // 执行跳转
    changeLocation(to, state, false);
    currentLocation.value = to;
  }

  function replace(to, data) {
    const state = Object.assign(
      {},
      buildState(historyState.value.back, to, historyState.value.forward, true),
      data
    );
    changeLocation(to, state, true);
    currentLocation.value = to; // 更新当前位置
  }

  return {
    location: currentLocation,
    state: historyState,
    push,
    replace
  };
}


function useHistoryListeners(base, historyState, currentLocation) {
  let listeners = []
  window.addEventListener('popstate', ({ state }) => {
    const to = createCurrentLocation(base);
    const from = currentLocation.value;
    const fromState = historyState.value;
    currentLocation.value = to;
    historyState.value = state;

    const isBack = state.position - fromState.position < 0

    listeners.forEach(listener => {
      listener(to, from, { isBack })
    })
  })

  function listen(listener) {
    listeners.push(listener)
  }
  return {
    listen
  }
}

function createWebHistory(base = "") {
  const historyNavigation = useHistoryStateNavigation(base);
  const { location, state } = historyNavigation;
  const historyListeners = useHistoryListeners(base, state, location);
  return {
    ...historyNavigation,
    ...historyListeners
  }
}

function createWebHashHistory(base = "") {
  return createWebHistory(base);
}

路由核心组件router-view 实现

// 简化版 router-view 实现
const RouterView = {
  name: 'RouterView',
  functional: true, // 使用函数式组件提升性能
  props: {
    name: { type: String, default: 'default' }
  },
  render(h, { parent, data, props }) {
    // 从父组件获取路由信息
    const route = parent.$route;
    const matched = route.matched[props.name || 'default'];
    
    // 如果没有匹配的路由,渲染空内容
    if (!matched) return h();
    
    // 获取匹配的组件
    const component = matched.components[props.name || 'default'];
    
    // 渲染组件,并传递路由信息
    return h(component, data);
  }
};


Previous Post
前端相关的各类知识汇总
Next Post
React之svg组件制作环形图