模拟前端路由

spa来源

传统的web, 浏览器输入一个url然后返回一个页面.后来, 人们发现这样太浪费资源啦, 比如, 我可能只是一个很小的操作,给空间好友点赞啊之类的 这个时候刷新页面肯定是不行的啊, 那就'局部刷新'吧,就是后来的ajax的应用.
再后来, 机智的开发者又想到, hash路径段(形如#header)是不会像服务器发请求的, 那么我们可以在点击一个锚点的时候加载一个小页面, 局部刷新, 再加上可以操作浏览器的历史记录来实现回退等功能. 那干脆就搞个前端路由吧. 于是前端路由就出现啦.
其实前端路由本质上也是一种局部刷新, 但是更规范. 这次刷新, 拉回来的是一个完整的组件, 这个组件包含了视图和数据. 这里的数据又分为两种, 一种是后端传过来的, 暂且成为数据库数据, 另一种是为了更好地管理视图而在前端生成的数据, 暂且成为视图数据.一通乱说过后, 我们就可以好好分析一下SPA到底是个什么东西啦.SPA是single page application的简称, 中文叫单页应用. 何为单页? 就是只有一个页面, 这个页面一般是index.html, 这是项目的入口文件. 自项目启动到结束, 浏览器中始终都是这个页面, 你看到的变化只是组件之间的创建和删除,就是当路由切换的时候,HTML文件依然是那个文件,只是每次往某个坑比如id=app的div里填的东西不一样.

传统:前端由多个页面组成,后端负责组织数据、实现路由、甚至生成页面内容;前端后端其实是混杂一起的;

当前:前端由单个页面或者少量页面构成(Single Page Application),前端框架负责组织路由,切换页面内容(一般将页面拆分成组件/Componet),后端只负责提供 API 服务并收发数据;
这样前后端就基本分离解耦了。

一.hash路由

我们知道a标签一般拿来跳转路由的,但当a标签加上#时,是不会发生跳转的, 同时触发个window.onhashchange事件或者用window.addEventListener('hashchange', handleHashChange);来监听,当监听到之后对html内容修改

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>前端hash路由</title>
<style type="text/css">
    * {
        box-sizing: border-box;
    }
</style>
<script type="text/javascript">
    var contentNode = null;
    function onLoad() {
        contentNode = document.querySelector("#content");
        handleHashChange() // 页面加载时不一定触发hashchange事件,这里手动触发一次
    }
    window.addEventListener('DOMContentLoaded', onLoad)
    // hash路由变化时,根据路由渲染对应 UI
    function handleHashChange (e) {
        console.log('handleHashChange', window.location.hash);
        contentNode.innerHTML= window.location.hash.slice(1)
    }
    // 监听路由变化
    window.addEventListener('hashchange', handleHashChange);
    
</script>
</head>
<body>
    <section>
        <aside>
            <ul>
                <li><a href='#index'>首页</a></li>
                <li><a href='#about'>关于</a></li>
            </ul>
        </aside>
        <section>
            <main>
                <div id="content">

                </div>
            </main>
        </section>
    </section>
</body>
</html>复制代码
1.当我们点击a标签(并且hash变化)或者通过浏览器回退(或通过js调用history.back()或者history.go(-1)),前进(或者history.forward()或者history.go(1)),都会触发hashchange事件
2.刷新或第一次进入是不会触发的,所以我们在页面onload之后,手动调用了一次

二.通过history API实现

popstate的说明

1.只有在做出浏览器动作时,才会触发
popstate
事件,如用户点击浏览器的回退按钮(或者在Javascript代码中调用history.back()),
前进(或者history.forward()或者history.go(1))
2.刷新或第一次进入不一定会触发popstate事件(不同的浏览器在加载页面时处理popstate事件的形式存在差异。页面加载时Chrome和Safari通常会触发(emit )popstate事件,但Firefox则不会。),所以我们可能需要手动调用一次
3.对于a标签,我们写的是正常路由,页面会跳转,必须阻止a标签默认行为,然后我们需要通过 history.pushState或者 history.replaceState修改title,url,并且存储历史记录
注意history.pushState或者 history.replaceState不会触发popstate事件,所以需要手动去修改html的内容
4.history.pushstate有安全限制,其中之一是不允许应用推动跨url-s历史起源;服务器ip的端口不同和直接在电脑本地运行的html都会报错,如下图:

为了测试该功能,我用webpack+webpack-dev-server 简单搭了个本地开发服务
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    <title>test</title>
    <script type="text/javascript">
        var contentNode = null;
        function onLoad() {
            contentNode = document.querySelector("#content");
            handlePopState();
            var linkList = document.querySelectorAll('a[href]')
            function handleClickLink (e) {
                e.preventDefault(); // 拦截 <a> 标签点击事件默认行为
                console.log(e.target.href)
                history.pushState(null, '', this.getAttribute('href')) // 点击时使用 pushState 修改 URL和title,并保存路由到历史记录
                handlePopState(e) // 并更新手动 UI,从而实现点击链接更新 URL 和 UI 的效果。
            }
            linkList.forEach(el => el.addEventListener('click', handleClickLink))
        }
        window.addEventListener('DOMContentLoaded', onLoad)
        // 路由变化时,根据路由渲染对应 UI
        function handlePopState (e) {
            console.log('handlePopState', window.location.pathname);
            switch (location.pathname) {
                case '/index':
                contentNode.innerHTML = 'Index'
                return
                case '/about':
                contentNode.innerHTML = 'About'
                return
                default:
                return
            }
        }
        // 监听路由变化
        window.addEventListener('popstate', handlePopState);
    </script>
</head>
<body>
    <div id="app"></div>
    <section>
        <aside>
            <ul>
                <li><a href='/index'>首页</a></li>
                <li><a href='/about'>关于</a></li>
            </ul>
        </aside>
        <section>
            <main>
                <div id="content">

                </div>
            </main>
        </section>
    </section>
</body>
</html>
复制代码


转载于:https://juejin.im/post/5cdcdb71f265da036902bcf7

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值