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);
}
};