龙空技术网

JavaScript的前端路由和实现SPA

陆荣涛 271

前言:

今天兄弟们对“js改变type”可能比较看重,各位老铁们都想要学习一些“js改变type”的相关文章。那么小编也在网上网罗了一些对于“js改变type””的相关知识,希望姐妹们能喜欢,各位老铁们一起来了解一下吧!

●前端路由

●路由: 就是一一对应关系的集合

●前端路由: 就是一个 url 地址, 对应哪个组件(页面)

●前端路由的本质

○根据地址栏变化(不重新想服务器发送请求), 去局部更新不同的页面内容, 完成前端业务场景切换

●前端路由的思路

○URL 地址栏中的 Hash 值发生了变化

○前端 JS 监听到 Hash 地址的变化 window.onhashchange = () => {}

○前端 JS 把当前 Hash 地址对应的组件渲染到浏览器中

●SPA

○ 单页面应用 (single page application)

○就是只有一张 Web 页面的应用, 是加载单个 HTML 页面并在用户与应用程序交互时, 动态更新该页面的 Web 应用程序

简单实现

<!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8">    <meta http-equiv="X-UA-Compatible" content="IE=edge">    <meta name="viewport" content="width=device-width, initial-scale=1.0">    <title>Document</title>    <style>        * {            padding: 0;            margin: 0;        }        html,        body,        .box {            width: 100%;            height: 100%;        }        .box {            display: flex;            flex-direction: column;        }        .box>.top {            width: 100%;            height: 100px;            background-color: brown;            font-size: 30px;            line-height: 100px;            text-align: center;        }        .box>.bottom {            flex: 1;            display: flex;        }        .box>.bottom>.slide {            width: 230px;            background-color: aqua;            box-sizing: border-box;            padding: 15px;            font-size: 25px;        }        .box>.bottom>.slide>a {            display: block;            margin: 10px 0;        }        .box>.bottom>.content {            width: 100%;            background-color: coral;            box-sizing: border-box;            padding: 15px;            font-size: 20px;        }    </style></head><body>    <div class="box">        <div class="top"> 顶部通栏 </div>        <div class="bottom">            <div class="slide">                <a href="#/pageA">pageA</a>                <a href="#/pageB">pageB</a>                <a href="#/pageC">pageC</a>                <a href="#/pageD">pageD</a>            </div>            <div class="content router-view">内容区</div>        </div>    </div>    <script>        // 准备一些渲染内容, 后续会根据 Hash 值去展示        const templateA = `<div>templateA</div>`        const templateB = `<div>templateB</div>`        const templateC = `<div>templateC</div>`        const templateD = `<div>templateD</div>`        // 获取 Dom 节点        const rw = document.querySelector('.router-view')        // 监听 hash 值的变化        window.onhashchange = function () {            // 这个函数执行, 表明hash锚点发生变化, 将内容区的文本切换为对应的文本            console.log(window.location.hash)            const { hash } = window.location    // 相当于: const hash = window.loaction.hash            // 根据当前的 Hash 值, 决定渲染哪一段内容            if (hash === '#/pageA') {                rw.innerHTML = templateA            } else if (hash === '#/pageB') {                rw.innerHTML = templateB            } else if (hash === '#/pageC') {                rw.innerHTML = templateC            } else if (hash === '#/pageD') {                rw.innerHTML = templateD            }        }    </script></body></html>

●当前所有的 展示内容, 都书写在了 这个 HTML 文件中, 这种写法在简单案例中没有什么不妥, 但是如果在实际开发中肯定不好

●因为每一个 展示的内容代码量都可能有很多, 所以放在一个文件内部会影响阅读体验, 所以我们可以将代码重新整理调整一下结构

代码结构调整

●SPA/components/templateA.js

const temAHtml = `<div id="pageA">pageA_template</div>`;const routerView = document.querySelector('.router-view')function rander () {    routerView.innerHTML = temAHtml}export default rander

●SPA/components/templateB.js

const temBHtml = `<div id="pageB">pageB_template</div>`;const routerView = document.querySelector('.router-view')function rander () {    routerView.innerHTML = temBHtml}export default rander

●SPA/components/templateC.js

const temCHtml = `<div id="pageC">pageC_template</div>`;const routerView = document.querySelector('.router-view')function rander () {    routerView.innerHTML = temCHtml}export default rander

● SPA/components/templateD.js

const temDHtml = `<div id="pageD">pageD_template</div>`;const routerView = document.querySelector('.router-view')function rander () {    routerView.innerHTML = temDHtml}export default rander

●SPA/router.js

// 按照策略模式组装一个路由表import temA from "./components/templateA.js";import temB from "./components/templateB.js";import temC from "./components/templateC.js";import temD from "./components/templateD.js";const router = [    {        name: "/pageA",        compoment: temA,    },    {        name: "/pageB",        compoment: temB,    },    {        name: "/pageC",        compoment: temC,    },    {        name: "/pageD",        compoment: temD,    },    {        // 如果当前的 路由是 /, 那么就重定向到 /pageA        name: "/",        redirect: "/pageA",    },];// 导出路由表export default router;

●SPA/index.js

// 1. 导入路由表import router from "./router.js";// 注册 hash 改变事件window.onhashchange = function () {    const hash = window.location.hash.slice(1);    const info = router.find((t) => t.name === hash);    info.compoment();}

●SPA/index.

<!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8">    <meta http-equiv="X-UA-Compatible" content="IE=edge">    <meta name="viewport" content="width=device-width, initial-scale=1.0">    <title>Document</title>    <style>        /* 一堆 CSS 代码, 与简单实现时一样, 这里不做重复书写 */    </style></head><body>    <div class="box">        <div class="top"> 顶部通栏 </div>        <div class="bottom">            <div class="slide">                <a href="#/pageA">pageA</a>                <a href="#/pageB">pageB</a>                <a href="#/pageC">pageC</a>                <a href="#/pageD">pageD</a>            </div>            <div class="content router-view"></div>        </div>    </div>    <!-- 注意使用模块发的方式导入文件 -->    <script src="./index.js" type="module"></script></body></html>

路由重定向

●路由重定向

○ 我们当前的的单页应用整体没什么大问题, 但是有一个小问题是首次进入页面的时候, 渲染区没有内容展示

○我们的解决方式也很简单, 就是在首次进入的时候, 将我们的路由重定向到 '/pageA'

○这样的话我们在首次进入页面的时候就能够展示出对应的内容

➢SPA/index.js

// 1. 导入路由表import router from "./router_b.js";// 注册 hash 改变事件window.onhashchange = hashChangeHandler;hashChangeHandler();function hashChangeHandler() {    // 首次进入页面的时候 hash 值为空字符, 默认给一个 '/'    const hash = window.location.hash.slice(1) || "/";    const info = router.find((t) => t.name === hash);    // 路由表中 重定向的优先级最高    if (info.redirect) return (window.location.hash = info.redirect);    info.compoment();}

➢SPA/router.js

import temA from "./components/templateA.js";import temB from "./components/templateB.js";import temC from "./components/templateC.js";import temD from "./components/templateD.js";const router = [    {        name: "/pageA",        compoment: temA,    },    {        name: "/pageB",        compoment: temB,    },    {        name: "/pageC",        compoment: temC,    },    {        name: "/pageD",        compoment: temD,    },    {        // 如果当前的 路由是 /, 那么就重定向到 /pageA        name: "/",        redirect: "/pageA",    },];// 导出路由表export default router;

懒加载

●现在的问题是在我们当前的完成方式中

○首先会去运行 index.html

○运行时会以模块化的方式引入 index.js

○在 index.js 中, 我们我们在代码开始, 导入了 router.js

○此时引入导入的特性, 会将 router.js 中的代码全都执行一次

○而在 router.js 中, 我们又导入了 components 中的 四个文件

○所以也会把这四个文件的内容全都执行一遍, 哪怕有些文件在首次执行的时候并没有使用到

●所以此时出现了一个问题

○假如我 router.js 中有 200 个文件, 而我首次渲染的时候只用到了其中一个

○但是我们现在的写法, 在首次运行时仍然会将我 没用到的那 199 个文件, 都运行一遍

○有一种更好的方式就是我用到了什么文件, 你再加载什么文件即可, 所以我们现在需要使用 "懒加载" 来解决

●换句话说, 懒加载其实就是, 用到了那个文件, 我在加载那个文件, 而不是在开始的时候, 一股脑的全加载完

●方式也很简单, 我们首先是需要调整一下引入的方式

○语法; import('地址')

○注意: 这种引入方式是按照 promise 的语法封装的函数

○返回值: 因为使用 promise 封装的, 所以他的返回值不是我们默认导出的内容, 而是一个 promise 对象

○所以我们的 index.js 也需要适当的调整

➢SPA/router.js

const router = [    {        name: "/pageA",        compoment: () => import("./components/templateA.js"),    },    {        name: "/pageB",        compoment: () => import("./components/templateB.js"),    },    {        name: "/pageC",        compoment: () => import("./components/templateC.js"),    },    {        name: "/pageD",        compoment: () => import("./components/templateD.js"),    },    {        // 如果当前的 路由是 /, 那么就重定向到 /pageA        name: "/",        redirect: "/pageA",    },];// 导出路由表export default router;

➢SPA/index.js

// 1. 导入路由表import router from "./router_b.js";// 注册 hash 改变事件window.onhashchange = hashChangeHandler;hashChangeHandler();function hashChangeHandler() {    const hash = window.location.hash.slice(1) || "/";    const info = router.find((t) => t.name === hash);    // 路由表中 重定向的优先级最高    if (info.redirect) return (window.location.hash = info.redirect);    // 调用 component 的到一个返回值, 如果是普通引入, 那么这里一定是 undefined, 我们直接 return 即可    const res = info.compoment();    if (res === undefined) return;    // 代码能运行到这里说明我们 调用 component 得到的是一个 promise 对象, 所以我们可以通过 then 方法以及他的参数去调用我们实际导出的内容    res.then((result) => result.default());}

标签: #js改变type