Vue.js 前端路由和异步组件是什么

Vue.js 前端路由和异步组件是什么

目录
  • 文章目标

    • P6

    • P6+ ~ P7

  • 一、背景

    • 二、前端路由特性

      • 三、面试!!!

        • 四、Hash 原理及实现

          • 1、特性

          • 2、如何更改 hash

          • 3、手动实现一个基于 hash 的路由

        • 五、History 原理及实现

          • 1、HTML5 History 常用的 API

          • 2、pushState/replaceState 的参数

          • 3、History 的特性

          • 4、面试!!!

          • 5、手动实现一个基于 History 的路由

        • 六、Vue-Router

          • 1、router 使用

          • 2、动态路由匹配

          • 3、响应路由参数的变化

          • 4、捕获所有路由或 404 Not found 路由

          • 5、导航守卫

          • 6、完整的导航解析流程

          • 7、导航守卫执行顺序(面试!!!)

          • 8、滚动行为(面试!!!)

          • 9、路由懒加载

        文章目标

        P6

        • 针对 react/vue 能够根据业务需求口喷 router 的关键配置,包括但不限于:路由的匹配规则、路由守卫、路由分层等;

        • 能够描述清楚 history 的主要模式,知道 historyrouter 的边界;

        P6+ ~ P7

        • 在没有路由的情况下,也可以根据也无需求,实现一个简单的路由;

        • 读过 router 底层的源码,不要求每行都读,可以口喷关键代码即可;

        一、背景

        远古时期,当时前后端还是不分离的,路由全部都是由服务端控制的,前端代码和服务端代码过度融合在一起。

        客户端 --> 前端发起 http 请求 --> 服务端 --> url 路径去匹配不同的路由 --> 返回不同的数据。

        这种方式的缺点和优点都非常明显:

        • 优点:因为直接返回一个 html,渲染了页面结构。SEO 的效果非常好,首屏时间特别快;

          • 在浏览器输入一个 url 开始到页面任意元素加载出来/渲染出来 --> 首屏时间;

        • 缺点:前端代码和服务端代码过度融合在一起,开发协同非常的乱。服务器压力大,因为把构建 html 的工作放在的服务端;

        后来 …随之 ajax 的流行,异步数据请求可以在浏览器不刷新的情况下进行。

        后来 …出现了更高级的体验 —— 单页应用

        • --> HTML 文件

        • 单页 --> 单个 HTML 文件

        在单页应用中,不仅在页面中的交互是不刷新页面的,就连页面跳转也都是不刷新页面的。

        单页应用的特点:

        • 页面中的交互是不刷新的页面的,比如点击按钮,比如点击出现一个弹窗;

        • 多个页面间的交互,不需要刷新页面(a/b/ca-> b -> c);加载过的公共资源,无需再重复加载;

        而支持起单页应用这种特性的,就是 前端路由

        二、前端路由特性

        前端路由的需求是什么?

        • 根据不同的 url 渲染不同内容;

        • 不刷新页面;

        也就是可以在改变 url 的前提下,保证页面不刷新。

        三、面试!!!

        Hash 路由和 History 路由的区别?

        • hash#history 没有 #

        • hash# 部分内容不会给服务端,主要一般是用于锚点, history 的所有内容都会给服务端;

        • hash 路由是不支持 SSR 的,history 路由是可以的;

        • hash 通过 hashchange 监听变化,history 通过 popstate 监听变化;

        四、Hash 原理及实现

        1、特性

        hash 的出现满足了这个需求,他有以下几种特征:

        • url 中带有一个 # 符号,但是 # 只是浏览器端/客户端的状态,不会传递给服务端;

          • 客户端路由地址 www.baidu.com/#/user --> 通过 http 请求 --> 服务端接收到的 www.baidu.com/

          • 客户端路由地址 www.baidu.com/#/list/detail/1 --> 通过 http 请求 --> 服务端接收到的 www.baidu.com/

        • hash 值的更改,不会导致页面的刷新;

        location.hash = '#aaa';location.hash = '#bbb';// 从 #aaa 到 #bbb,页面是不会刷新的
        • 不同 url 会渲染不同的页面;

        • hash 值的更改,会在浏览器的访问历史中添加一条记录,所以我们才可以通过浏览器的返回、前进按钮来控制 hash 的切换;

        • hash 值的更改,会触发 hashchange 事件;

        location.hash = '#aaa';location.hash = '#bbb';window.addEventLisenter('hashchange', () => {});

        2、如何更改 hash

        我们同样有两种方式来控制 hash 的变化:

        • location.hash 的方式:

        location.hash = '#aaa';location.hash = '#bbb';
        • html 标签的方式:

        <a href="#user" rel="external nofollow" > 点击跳转到 user </a><!-- 等同于下面的写法 -->location.hash = '#user';

        3、手动实现一个基于 hash 的路由

        • ./index.html

        <!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>    <link rel="stylesheet" href="./index.css" rel="external nofollow"  />  </head>  <body>    <div class="container">      <a href="#gray" rel="external nofollow" >灰色</a>      <a href="#green" rel="external nofollow" >绿色</a>      <a href="#" rel="external nofollow" >白色</a>      <button onclick="window.history.go(-1)">返回</button>    </div>    <script type="text/javascript" src="index.js"></script>  </body></html>
        • ./index.css

        .container {  width: 100%;  height: 60px;  display: flex;  justify-content: space-around;  align-items: center;  font-size: 18px;  font-weight: bold;  background: black;  color: white;}a:link,a:hover,a:active,a:visited {  text-decoration: none;  color: white;}
        • ./index.js

        /*   期望看到的效果:点击三个不同的 a 标签,页面的背景颜色会随之变化*/class BaseRouter {  constructor() {    this.routes = {}; // 存储 path 以及 callback 的对应关系    this.refresh = this.refresh.bind(this); // 如果不 bind 的话,refresh 方法中的 this 指向 window    // 处理页面 hash 变化,可能存在问题:页面首次进来可能是 index.html,并不会触发 hashchange 方法    window.addEventListener('hashchange', this.refresh);    // 处理页面首次加载    window.addEventListener('load', this.refresh);  }  /**   * route   * @param {*} path 路由路径   * @param {*} callback 回调函数   */  route(path, callback) {    console.log('========= route 方法 ========== ', path);    // 向 this.routes 存储 path 以及 callback 的对应关系    this.routes[path] = callback || function () {};  }  refresh() {    // 刷新页面    const path = `/${location.hash.slice(1) || ''}`;    console.log('========= refresh 方法 ========== ', path);    this.routes[path]();  }}const body = document.querySelector('body');function changeBgColor(color) {  body.style.backgroundColor = color;}const Router = new BaseRouter();Router.route('/', () => changeBgColor('white'));Router.route('/green', () => changeBgColor('green'));Router.route('/gray', () => changeBgColor('gray'));

        五、History 原理及实现

        hash 有个 # 符号,不美观,服务端无法接受到 hash 路径和参数。

        历史的车轮无情撵过 hash,到了 HTML5 时代,推出了 History API

        1、HTML5 History 常用的 API

        window.history.back(); // 后退window.history.forward(); // 前进window.history.go(-3); // 接收 number 参数,后退 N 个页面window.history.pushState(null, null, path);window.history.replaceState(null, null, path);

        其中最主要的两个 APIpushStatereplaceState,这两个 API 都可以在不刷新页面的情况下,操作浏览器历史记录。

        不同的是,pushState 会增加历史记录,replaceState 会直接替换当前历史记录。

        2、pushState/replaceState 的参数

        • pushState:页面的浏览记录里添加一个历史记录;

        • replaceState:替换当前历史记录;

        他们的参数是⼀样的,三个参数分别是:

        • state:是一个对象,是一个与指定网址相关的对象,当 popstate 事件触发的时候,该对象会传入回调函数;

        • title:新页面的标题,浏览器支持不一,建议直接使用 null

        • url:页面的新地址;

        3、History 的特性

        History API 有以下几个特性:

        • 没有 #

        • history.pushState()history.replaceState() 不会触发 popstate 事件,这时我们需要手动触发页面渲染;

        • 可以使用 history.popstate 事件来监听 url 的变化;

        • 只有用户点击浏览器 倒退按钮前进按钮,或者使用 JavaScript 调用 backforwardgo 方法时才会触发 popstate

        4、面试!!!

        • pushState 时,会触发 popstate 吗?

          • pushState/replaceState 并不会触发 popstate 事件,这时我们需要手动触发页面的重新渲染;

        • 我们可以使用 popstate 来监听 url 的变化;

        • popstate 到底什么时候才能触发:

          • 点击浏览器后退按钮;

          • 点击浏览器前进按钮;

          • js 调用 back 方法;

          • js 调用 forward 方法;

          • js 调用 go 方法;

        5、手动实现一个基于 History 的路由

        • ./index.html

        • ./index.css

        .container {  width: 100%;  height: 60px;  display: flex;  justify-content: space-around;  align-items: center;  font-size: 18px;  font-weight: bold;  background: black;  color: white;}a:link,a:hover,a:active,a:visited {  text-decoration: none;  color: white;}
        • ./index.js

        class BaseRouter {  constructor() {    this.routes = {};    // location.href; => hash 的方式    console.log('location.pathname ======== ', location.pathname); // http://127.0.0.1:8080/green ==> /green    this.init(location.pathname);    this._bindPopState();  }  init(path) {    // pushState/replaceState 不会触发页面的渲染,需要我们手动触发    window.history.replaceState({ path }, null, path);    const cb = this.routes[path];    if (cb) {      cb();    }  }  route(path, callback) {    this.routes[path] = callback || function () {};  }  // ! 跳转并执行对应的 callback  go(path) {    // pushState/replaceState 不会触发页面的渲染,需要我们手动触发    window.history.pushState({ path }, null, path);    const cb = this.routes[path];    if (cb) {      cb();    }  }  // ! 演示一下 popstate 事件触发后,会发生什么  _bindPopState() {    window.addEventListener('popstate', e => {      /*         触发条件:          1、点击浏览器前进按钮          2、点击浏览器后退按钮          3、js 调用 forward 方法          4、js 调用 back 方法          5、js 调用 go 方法      */      console.log('popstate 触发了');      const path = e.state && e.state.path;      console.log('path >>> ', path);      this.routes[path] && this.routes[path]();    });  }}const Router = new BaseRouter();const body = document.querySelector('body');const container = document.querySelector('.container');function changeBgColor(color) {  body.style.backgroundColor = color;}Router.route('/', () => changeBgColor('white'));Router.route('/gray', () => changeBgColor('gray'));Router.route('/green', () => changeBgColor('green'));container.addEventListener('click', e => {  if (e.target.tagName === 'A') {    e.preventDefault();    console.log(e.target.getAttribute('href')); // /gray  /green 等等    Router.go(e.target.getAttribute('href'));  }});

        六、Vue-Router

        1、router 使用

        使用 Vue.js,我们已经可以通过组合组件来组成应用程序,当你要把 Vue Router 添加进来,我们需要做的是,将组件(components)映射到路由(routes),然后告诉 Vue Router 在哪里渲染它们。

        举个例子:

        <!-- 路由匹配到的组件将渲染在这里 --><div id="app">  <router-view></router-view></div>
        // 如果使用模块化机制编程,导入 Vue 和 VueRouter,要调用 Vue.use(VueRouter)// 1、定义(路由)组件// 可以从其他文件 import 进来const Foo = { template: '<div>foo</div>' };const Bar = { template: '<div>bar</div>' };// 2、定义路由//每个路由应该映射一个组件,其中 component 可以是通过 Vue.extend() 创建的组件构造器,或者只是一个组件配置对象const routes = [  { path: '/foo', component: Foo },  { path: '/bar', component: Bar },];// 3、创建 router 实例,然后传 routes 配置const router = new VueRouter({  routes,});// 4、创建和挂载根实例// 记得要通过 router 配置参数注入路由,从而让整个应用都有路由功能const app = new Vue({  router,}).$mount('#app');

        2、动态路由匹配

        我们经常需要把某种模式匹配到的所有路由,全部映射到同个组件,比如用户信息组件,不同用户使用同一个组件。

        可以通过 $route.params.id 或者参数。

        const router = new VueRouter({  routes: [    // 动态路径参数,以冒号开头    { path: '/user/:id', component: User },  ],});const User = {  template: '<div>User: {{ $route.params.id }}</div>',};

        3、响应路由参数的变化

        复用组件时,想对 路由参数 的变化作出响应的话,可以使用 watch 或者 beforeRouteUpdate

        举个例子:

        const User = {  template: '...',  watch: {    $route(to, from) {      // 对路由变化作出响应...    },  },};const User = {  template: '...',  beforeRouteUpdate(to, from, next) {    // 对路由变化作出响应...    // don't forget to call next()  },};

        4、捕获所有路由或 404 Not found 路由

        当时用通配符路由时,请确保路由的顺序是正确的,也就是说含有通配符的路由应该在 最后

        举个例子:

        5、导航守卫

        vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航。有多种方式植入路由导航过程中:

        • 全局的

          • 全局前置守卫:router.beforeEach

          • 全局解析守卫:router.beforeResolve

          • 全局后置钩子:router.afterEach

        • 单个路由独享的

          • 路由独享守卫:beforeEnter

        • 组件级的

          • beforeRouteEnter

          • beforeRouteUpdate

          • beforeRouteLeave

        6、完整的导航解析流程

        • 导航被触发;

        • 在失活的组件里调用离开守卫(前一个组件的 beforeRouteLeave);

        • 调用全局的 beforeEach 守卫;

        • 在重用的组件里调用 beforeRouteUpdate 守卫;

        • 在路由配置里调用 beforeEnter

        • 解析异步路由组件;

        • 在被激活的组件里调用 beforeRouterEnter

        • 调用全局的 beforeResolve 守卫;

        • 导航被确认;

        • 调用全局的 afterEach 钩子;

        • 触发 DOM 更新;

        • 用创建好的实例调用 beforeRouterEnter 守卫中传给 next 的回调函数;

        举个例子:

        // 全局const router = new VueRouter({  mode: 'history',  base: process.env.BASE_URL,  routes,});// 全局的导航守卫router.beforeEach((to, from, next) => {  console.log(`Router.beforeEach => from=${from.path}, to=${to.path}`);  // 可以设置页面的 title  document.title = to.meta.title || '默认标题';  // 执行下一个路由导航  next();});router.afterEach((to, from) => {  console.log(`Router.afterEach => from=${from.path}, to=${to.path}`);});// 路由独享const router = new VueRouter({  routes: [    {      path: '/foo',      component: Foo,      beforeEnter: (to, from, next) => {        // 配置数组里针对单个路由的导航守卫        console.log(`TestComponent route config beforeEnter => from=${from.path}, to=${to.path}`);        next();      },    },  ],});// 组件const Foo = {  template: `...`,  beforeRouteEnter(to, from, next) {    // 在渲染该组件的对应路由被 comfirm 前调用    // 不!能!获取组件实例 this,因为当守卫执行前,组件实例还没被调用  },  beforeRouteUpdate(to, from, next) {    // 在当前路由改变,但是该组件被复用时调用    // 举个例子来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用    // 可以访问组件实例 this  },  beforeRouteLeave(to, from, next) {    // 导航离开该组件的对应路由时调用    // 可以访问组件实例 this  },};

        next 必须调用:

        • next():进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed(确认的)。

        • next(false):中断当前的导航。如果浏览器的 URL 改变了(可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。

        • next("/") 或者 next({ path: "/" }):跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。可以向 next 传递任意位置对象,且允许设置诸如 replace: truename: "home" 之类的选项以及任何用在 router-linkto proprouter.push 中的选项。

        • next(error):如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。

        7、导航守卫执行顺序(面试!!!)

        • 【组件】前一个组件的 beforeRouteLeave

        • 【全局】的 router.beforeEach

          • 【组件】如果是路由参数变化,触发 beforeRouteUpdate

        • 【配置文件】里,下一个的 beforeEnter

        • 【组件】内部声明的 beforeRouteEnter

        • 【全局】的 router.afterEach

        8、滚动行为(面试!!!)

        vue-router 里面,怎么记住前一个页面的滚动条的位置???

        使用前端路由,当切换到新路由时,想要页面滚动到顶部,或者是保持原先的滚动位置,就像重新加载页面那样。

        Vue-router 能做到,而且更好,它让你可以自定义路由切换时页面如何滚动。

        【注意】:这个功能只在支持 history.pushState 的浏览器中可用。

        scrollBehavior 生效的条件:

        • 浏览器支持 history API

        • 页面间的交互是通过 goforwardback 或者 浏览器的前进/返回按钮

        window.history.back(); // 后退window.history.forward(); // 前进window.history.go(-3); // 接收 number 参数,后退 N 个页面

        举个例子

        // 1. 记住:手动点击浏览器返回或者前进按钮,记住滚动条的位置,基于 history API 的,其中包括:go、back、forward、手动点击浏览器返回或者前进按钮// 2. 没记住:router-link,并没有记住滚动条的位置const router = new VueRouter({  mode: 'history',  base: process.env.BASE_URL,  routes,  scrollBehavior: (to, from, savedPosition) => {    console.log(savedPosition); // 已保存的位置信息    return savedPosition;  },});

        9、路由懒加载

        当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。

        举个例子:

        const Foo = () => import(/* webpackChunkName: "foo" */ './Foo.vue');const router = new VueRouter({  routes: [{ path: '/foo', component: Foo }],});

        关于Vue.js 前端路由和异步组件是什么就分享到这里了,希望以上内容可以对大家有一定的参考价值,可以学以致用。如果喜欢本篇文章,不妨把它分享出去让更多的人看到。

        文章标题:Vue.js 前端路由和异步组件是什么,发布者:亿速云,转载请注明出处:https://worktile.com/kb/p/24495

        (0)
        打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
        亿速云的头像亿速云认证作者
        上一篇 2022年9月11日 上午12:00
        下一篇 2022年9月13日 下午11:27

        相关推荐

        • 电脑msr分区可不可以删除

          msr分区可以删除;msr分区是“Microsoft Reserved Partition”的缩写,是作为保留分区来使用,系统组件可以将MSR分区的部分分配到新的分区以供使用,msr分区不是必须的,可以被删除。 本教程操作环境:windows10系统、DELL G3电脑。 msr分区可以删除 MSR…

          2022年9月16日
          3.9K00
        • echarts如何改变图表的颜色

          echarts改变图表颜色的方法 1、 打开编辑器新建study.html,并在头部引入echarts插件 2、在body区域新建一个div,并给div设置宽和高,用来生成柱状图 3、在script标签中加入柱状图相关代码 4、通过设置itemStyle下的color属性来改变柱状图颜色 5、完成之…

          2022年9月26日
          1.1K00
        • 关于远程办公,微软MVP 15年研发团队的经验分享

          为了能够应对来势汹汹的疫情,众多互联网企业纷纷开启了远程办公模式。不知道各团队前两天的远程办公效果如何,我们 Worktile 管理层在大年初四就开始讨论远程办公的事情,并且将可能出现的问题都尽量提前想到并做了准备。从这两天实际执行的情况看,我所在的研发团队执行的还不错,基本没有受到什么明显的影响。…

          2022年3月20日
          44200
        • mysql如何查询存储引擎

          两种查询方法:1、使用“SHOW ENGINES;”语句,可以以表格形式显示系统所支持的引擎类型,其中“Support”列的值表示某种引擎是否能使用,“YES”表示可以使用,“NO”表示不能使用,“DEFAULT”表示该引擎为当前默认引擎。2、使用“SHOW VARIABLES LIKE &#821…

          2022年9月26日
          1.2K00
        • cad快速看图标注看不见怎么解决

          cad快速看图标注看不见解决方法: 1.把图纸上传到快看云盘,然后从云盘列表里打开这个图,点左侧的【同步标注】,名列前茅次使用云盘的请先登录账号、创建项目; 2.提示同步成功后,点云盘右上角的【成员】按钮,把对方的CAD快速看图账号添加为成员; 3.添加完成后,再点图纸左侧功能里的【成员协作】&#8…

          2022年9月1日
          1.2K00
        • mdf文件如何导入数据库

          mdf文件导入数据库文件方法: 1、打开SQL。 2、点击“连接”连接数据库。 3、右击数据库,单机选择“附加”。 4、点击“添加”。 5、即可将mdf导入数据库。 感谢各位的阅读,以上就是“mdf文件如何导入数据库”的内容了,经过本文的学习后,相信大家对mdf文件如何导入数据库这一问题有了更深刻的…

          2022年9月8日
          1.8K00
        • laravel是不是aop

          laravel不是aop;aop是“Aspect Oriented Programming”的缩写,意为面向切面编程,是通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术,而laravel不是面向切片编程,laravel中间件提供了一种方便的机制来检查和过滤进入应用程序的HTTP请求是…

          2022年9月1日
          51500
        • 电脑ntuser.dat文件可以删除吗

          ntuser.dat是什么文件可以删除吗: 答:可以删,但没必要的情况下较好不删,理由如下: 1、ntuser.dat是Windows 2000/XP的注册表文件之一,包含着用户特定的数据,是用户配置文件之一。 2、注册表包括多个文件,其中用户配置文件包括ntuser.dat、ntuser.ini、…

          2022年9月21日
          1.3K00
        • dint数据类型的含义是什么

          dint是带符号位的32位整数类型;dint的表示方法及范围是“L#-2147483648~L#+2147483647”,定义为双整数或长整数,字节是电脑里的数据量单位,在计算机中,数据只用0和1这种表现形式。 本教程操作环境:windows10系统、DELL G3电脑。 dint是什么数据类型 D…

          2022年9月6日
          1.8K00
        • excel下拉列表怎么填充颜色

          excel下拉列表填充颜色的方法: 1、首先打开软件,然后点击“数据有效性”选择“数据有校性”。 2、之后在允许中选择“序列”并输入下拉值,在依次选择“条件格式—>等于”。 3、在设置格式的值中输入对应的值,选择右侧的下拉,点击“自定义格式”。 4、之后在字体任务选择栏右下方选择颜色。 5、最…

          2022年8月27日
          4.8K00
        注册PingCode 在线客服
        站长微信
        站长微信
        电话联系

        400-800-1024

        工作日9:30-21:00在线

        分享本页
        返回顶部