所有分类
  • 所有分类
  • 后端开发
历史渊源揭秘:React Router History库的神奇妙用

历史渊源揭秘:React Router History库的神奇妙用

router中的核心history库的详细分析,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。以下是history库的:Router核心依赖history库进行了比较深入的介绍。

历史渊源

喂,不能否认,React Router那玩意儿中的History库确实有用。好几个版本换下来,除了基础功能,其他没啥变化。咱们先说说用jQuery那段日子,那时候网络太卡顿,SPA技术开始火爆起来。怎样实现网页刷新后内容不变?其实就靠#号,每次刷新的时候改下#号就行了。浏览器看到网址或#号变了,就会把新地址自动塞进History对象。这History里有个state数组,就是记录你访问过哪些页面的。所以当你按”后退”或”前进”时,其实是用到了History对象的forward/back方法,找到现在状态,再转到相应页面去的。

核心功能

你听过 pushState 和 replaceState 这两个东西吗?它们都是 history 里的好帮手~有了它们,你无需更改网址,就能轻松地更换页面的显示内容!像不像当年咱们喜欢用的 hashHistory?你只需要研究一下代码,就会发现它们其实就像是引导性的订阅列表。只要哈希值一变,它立马会启动相应的处理程序。现在,懂了?

const history = {
    length,        // 属性,history中记录的state的数量
    action,        // 属性,当前导航的action类型
    location,      // 属性,location对象,封装了pathname、search和hash等属性
    push,          // 方法,导航到新的路由,并记录在history中
    replace,       // 方法,替换掉当前记录在history中的路由信息
    go,            // 方法,前进或后退n个记录
    goBack,        // 方法,后退
    goForward,     // 方法,前进
    canGo,         // 方法,是否能前进或后退n个记录
    block,         // 方法,跳转前让用户确定是否要跳转
    listen         // 方法,订阅history变更事件
  };

主动更新

const history = {
    length,         // 属性,history中记录的state的数量
    state,          // 属性,pushState和replaceState时传入的对象
    back,           // 方法,后退
    forward,        // 方法,前进
    go,             // 方法,前进或后退n个记录
    pushState,      // 方法,导航到新的路由,并记录在history中
    replaceState    // 方法,替换掉当前记录在history中的路由信息
}
// 订阅history变更事件
window.onpopstate = function (event) {
    ...
}

历史渊源揭秘:React Router History库的神奇妙用

说到改网页,比如向上或向下滑动页面这些动作,就得用HTML5里的history对象这个小能手。你可以试试按下window.history.go,这样hash值就会改变,hashchange事件也跟着启动起来。然后,history库就会通知他的小伙伴们。

// 构造hashHistory对象
const createHashHistory = (props = {}) => {
    ...
    const globalHistory = window.history;    // 引用HTML5 history对象
    ...
    // transitionManager负责控制是否进行跳转,以及跳转后要通知到的订阅者,后面会详细讨论
    const transitionManager = createTransitionManager();
    ...
    // 注册history变更回调的订阅者
    const listen = listener => {
        const unlisten = transitionManager.appendListener(listener);
        checkDOMListeners(1);
        return () => {
            checkDOMListeners(-1);
            unlisten();
        };
    };
    
    // 监听hashchange事件
    const checkDOMListeners = delta => {
        listenerCount += delta;
        if (listenerCount === 1) {
            window.addEventListener(HashChangeEvent, handleHashChange);
        } else if (listenerCount === 0) {
            window.removeEventListener(HashChangeEvent, handleHashChange);
        }
    };
    
    // hashchange事件回调
    const handleHashChange = () => {
        ...
        // 构造内部使用的location对象,包含pathname、search和hash等属性
        const location = getDOMLocation();    
        ...
        handlePop(location);
    };
    
    // 处理hash变更逻辑
    const handlePop = location => {
        ...
        const action = "POP";
        // 给用户展示确认跳转的信息(如果有的话),确认后通知订阅者。如果用户取消跳转,则回退到之前状态
        transitionManager.confirmTransitionTo(location, action, getUserConfirmation, ok => {
            if (ok) {
                setState({action, location});    // 确认后通知订阅者
            } else {
                revertPop(location);             // 取消则回退到之前状态
            }
        });
    };
    
    // 更新action,location和length属性,并通知订阅者
    const setState = nextState => {
        Object.assign(history, nextState);
        history.length = globalHistory.length;
        transitionManager.notifyListeners(history.location, history.action);
    };
    ...
}

深入分析

这玩意儿可厉害了!让我们聊聊HTML5新出的history对象,那些跟浏览器历史纪录有关的话题。比如hashHistory,我给你说说到底是咋回事。而且还有个神奇功能,就是用它发布和订阅!每当你的浏览器历史记录有变化时,订阅的函数就会立刻启动!

const createTransitionManager = () => {
    ...
    // 内部维护的订阅者列表
    let listeners = [];
    // 注册订阅者
    const appendListener = fn => {
        let isActive = true;
        const listener = (...args) => {
            if (isActive) fn(...args);
        };
        listeners.push(listener);
        return () => {
            isActive = false;
            listeners = listeners.filter(item => item !== listener);
        };
    };
    //通知订阅者
    const notifyListeners = (...args) => {
        listeners.forEach(listener => listener(...args));
    };
    ...
}

关系解析

记住咯,History其实跟React没有啥子直接关系,它只是在React Router这个大家庭里待着而已,和 React没有明确的链接!

const push = (path, state) => {
    ...
    const action = "PUSH";
    const location = createLocation(path, undefined, undefined, history.location);
    transitionManager.confirmTransitionTo(location, action, getUserConfirmation, ok => {
        if (!ok)     // 如果取消,则不跳转
            return;
        ...
        pushHashPath(encodedPath);        // 用新的hash替换到url当中
        ...
        setState({action, location});     // 更新action,location和length属性,并通知订阅者
    });
};
// 用新的hash替换到url当中
const pushHashPath = path => (window.location.hash = path);

原文链接:https://www.icz.com/technicalinformation/web/2024/03/12278.html,转载请注明出处~~~
0

评论0

请先
注意:请收藏好网址www.icz.com,防止失联!站内免费资源持续上传中…!赞助我们
显示验证码
没有账号?注册  忘记密码?