vuejs路由和组件系统

前端路由原理

  • createRouter

    * hash
      * window.addEventListener('hashChange')
      *	两种实现路由切换的模式:UI组件(router-link,router-view),Api(push()方法)
    * history 
      * HTML5新增的API ,可以通过不刷新页面前提下,修改页面路由-history.pushState() 
    
  • useRouter

  • router-link

  • router-view

手写实现一个路由跳转
/**
 * mini 版本 vue-router
 */

import { ref, provide, inject } from 'vue';
import RouterLink from './router-link.vue';
import RouterView from './router-view.vue'

// 保证全局唯一性,可以用 Symbol 创建
const ROUTER_KEY = '__router__'

// 
class Router {
  constructor(options) {
    // 记录访问历史
    this.history = options.history;
    // 初始化传入路由表
    this.routes = options.routes;
    // 当前激活的路由
    this.current = ref(this.history.url)

    // hashChange 事件触发把当前激活路由的值记录下来
    this.history.bindEvents(() => {
      this.current.value = window.location.hash.slice(1)
    })
    
  }

  push(to) {
    location.hash = '#' + to;
  
    window.history.pushState({}, '', to)
  }

  beforeEach(fn) {
    this.guards = fn;
  }

  // app 插件的注册调用函数
  // provie/inject,pinia 
  install(app) {
    app.provide(ROUTER_KEY, this)

    // 兼容 options API
    app.$router = this;

    // 注册全局组件
    app.component('router-link', RouterLink)
    app.component('router-view', RouterView)
  }
}

// 1. hash
// hashChange -> View 
function createWebHashHistory() {
  function bindEvents(fn) {
    window.addEventListener('hashchange', fn)
  }

  return {
    bindEvents,
    url: window.location.hash.slice(1) || '/'
  }
}

// TODO
function createWebHistory() {

}

// 组合式 API,获取当前 vue-router 的实例
// options API, this.$router 来获取vue-router 的实例
function useRouter() {
  return inject(ROUTER_KEY)
}

// 暴露一个创建对应类的实例的方法
function createRouter(options) {
  return new Router(options)
}

export { createRouter, useRouter, createWebHashHistory }



router-link

<template>
  <a :href="'#' + props.to">
    <slot />
  </a>
</template>

<script setup>

// a -> href
// router-link to
import { defineProps } from 'vue';

let props = defineProps({
  to: { type: String, required: true }
})

</script>

<style lang="css" scoped></style>

router-view

<template>
  <!-- 动态组件 -->
  <component :is="comp"></component>
</template>

<script setup>

import { computed } from 'vue'
import { useRouter } from './mini-router'

// 获取到了 Router 的实例
let router = useRouter();

console.log('router 实例', router)

const comp = computed(() => {
  // 根据注册的路由表和当前激活的 route 匹配
  const route = router.routes.find(route => {
    // 百分百等于,静态路由
    return route.path === router.current.value
  })

  // 路由守卫的激活
  const ret = route?.guards

  return route.component;
})

</script>

<style lang="scss" scoped></style>
特性原理解析
  • 路由匹配规则:静态路由、动态路由、正则匹配

    const router = new VueRouter({
       routes: [
         // 动态路径参数 以冒号开头
         { path: '/user/:id', component: User }
       ]
     })
    
  • 嵌套路由:

    const router = new VueRouter({
       routes: [
         {
           path: '/user/:id',
           component: User,
           children: [
             {
               // 当 /user/:id/profile 匹配成功,
               // UserProfile 会被渲染在 User 的 <router-view> 中
               path: 'profile',
               component: UserProfile
             },
             {
               // 当 /user/:id/posts 匹配成功
               // UserPosts 会被渲染在 User 的 <router-view> 中
               path: 'posts',
               component: UserPosts
             }
           ]
         }
       ]
     })
    
  • 路由守卫:
    在这里插入图片描述

  • 路由元信息:路由表中配置meta字段

  • 滚动行为记录:这个功能只在支持 history.pushState 的浏览器中可用。

const router = new VueRouter({
  routes: [...],
  scrollBehavior (to, from, savedPosition) {
    // return 期望滚动到哪个的位置
  }
})
  • 路由懒加载和异步组件
const Foo = () => import('./Foo.vue')
把组件按组分块
const Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue')
const Bar = () => import(/* webpackChunkName: "group-foo" */ './Bar.vue')
const Baz = () => import(/* webpackChunkName: "group-foo" */ './Baz.vue')
  • 过渡动效
<transition>
  <router-view></router-view>
</transition>

<!-- 使用动态的 transition name -->
<transition :name="transitionName">
  <router-view></router-view>
</transition>
内置组件
  • 异步组件 defineAsyncComponent 、 <component :is=“xxx” / >
import { defineAsyncComponent } from 'vue'
const AsyncHelloWorld = defineAsyncComponent({
	//es6 import
	loader:()=>import('xxx.vue'),
	loadingComponent:LoadingComp,
	delay:100,
	timeout:300,
	errorComponent:xxx
})

异步组件的实现

// 异步组件的实现
// options = object {
//  loader: () => Promoise(void)
// }
// options 
function defineAsyncComponent(options) {
  if (typeof options === 'function') {
    options = {
      loader: options,
    };
  }

  const { loader } = options;

  let InnerComp = null;

  // 记录重试次数
  let retries = 0;
  // 封装 load 函数用来加载异步组件
  function load() {
    return (
      loader()
        // 捕获加载器的错误
        .catch((err) => {
          // 如果用户指定了 onError 回调,则将控制权交给用户
          if (options.onError) {
            // 返回一个新的 Promise 实例
            return new Promise((resolve, reject) => {
              // 重试方法
              const retry = () => {
                resolve(load());
                retries++;
              };
              // 失败
              const fail = () => reject(err);
              // 作为 onError 回调函数的参数,让用户来决定下一步怎么做
              options.onError(retry, fail, retries);
            });
          } else {
            throw error;
          }
        })
    );
  };
  // 创建一个 vue 组件
  return {
    name: 'AsyncComponentWrapper',
    setup() {
      // 标识异步组件是否加载成功
      const loaded = ref(false);
      const error = shallowRef(null);
      const loading = ref(false);
      // timeout 默认不超时
      const timeout = ref(false);

      let loadingTimer = null;
      if (options.delay) { // 100ms 
        loadingTimer = setTimeout(() => {
          loading.value = true;
        }, options.delay);
      } else {
        loading.value = true;
      }

      let timer = null
      // 用户配置参数 timeout
      if(options.timeout) {
        timer = setTimeout(() => {
          timeout.value = true;
        }, options.timeout);
      }

      onUmounted(() => clearTimeout(timer))

      // 调用 load 函数加载组件
      // import(), ES6
      load()
        .then((c) => {
          InnerComp = c;
          loaded.value = true;
        })
        .catch((err) => {
          error.value = err;
        })
        .finally(() => {
          loading.value = false;
          clearTimeout(loadingTimer);
        });
      
      // 占位内容...
      const Placeholer = { type: Text, children: '' }

      if(loaded.vlaue) {
        // 异步组价加载成功,渲染 InnerComp,否则渲染渲染出错组件
        return {
          type: InnerComp,
        }
      } else if(timeout.value && options.errorComponent) {
        // 超时,并且设置了 Error 组件
        return {
          type: options.errorComponent,
        }        
      } else if(error.value && options.errorComponent) {
        return {
          type: options.errorComponent,
        }
      }

      return Placeholer

    },
  };
}

// load 函数接收一个 onError 回调函数
function load(onError) {
  // 请求接口,得到 Promise 实例
  const p = fetch(100);
  // 捕获错误
  return p.catch((err) => {
    // 当错误发生时,返回一个新的 Promise 实例,并调用 onError 回调,
    // 同时将 retry 函数作为 onError 回调的参数
    return new Promise((resolve, reject) => {
      // retry 函数,用来执行重试的函数,执行该函数会重新调用 load 函数并发送请求
      const retry = () => resolve(load(onError));
      const fail = () => reject(err);
      onError(retry, fail);
    });
  });
}

function fetch(ms) {
  return new Promise((resolve, reject) => {
    // 请求会在  秒后失败
    setTimeout(() => {
      reject('err');
    }, ms);
  });
}

  • KeepAlive 组件
  • Teleport 组件
<Teleport to="body">
  <div v-if="open" class="modal">
    <p>Hello from the modal!</p>
    <button @click="open = false">Close</button>
  </div>
</Teleport>
  • Transition 组件
    < Transition> 会在一个元素或组件进入和离开 DOM 时应用动画
<Transition name="fade">
  ...
</Transition>
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.5s ease;
}

.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}
使用场景
  • keep-alive,多标签页交互,多 tab 切换
  • teleport,全局弹窗,dom 结构脱离组件树渲染
  • transition,实现组件过渡动画
  • defineAsyncComponent,声明一个异步组件,实现性能优化和分 chunk 打包

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/640286.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Qt下使用QImage和OpenCV实现图像的拼接与融合

文章目录 前言一、使用QImage进行水平拼接二、使用OpenCV进行水平拼接三、使用OpenCV进行图像融合四、示例完整代码总结 前言 本文主要讲述了在Qt下使用QImage和OpenCV实现图像的拼接与融合&#xff0c;并结合相应的示例进行讲解&#xff0c;以便大家学习&#xff0c;如有错误…

分割文本文件

分割一个.txt文件&#xff0c;可以选择在命令行中使用split指令&#xff0c;或者编写一段脚本进行操作。以下是一个简单的Python脚本来分割文本文件&#xff1a; def split_file(file, lines):# Open source filewith open(file, r) as source:count 0atEOF Falsewhile not …

齐护K210系列教程(三十四)_视觉PID巡线小车

视觉PID巡线小车 1.前言2.简介3.代码讲解3.1初始化3.2.色块查找3.3色块分析3.3.1 区域13.3.2 区域2 3.4 侦测关键点部分3.4.1正常巡线3.4.2 右转路口 3.4.3十字路口3.4. PID计算 4.完整代码5.小车端程序6.参考程序联系我们 1.前言 本课程主要讲述如何使用AIstart_k210主板完成…

SpringMVC接收请求参数的方式:

接收简单变量的请求参数 直接使用简单变量作为形参进行接收&#xff08;这里简单变量名称需要与接收的参数名称保持一致&#xff0c;否则需要加上RequestParam注解&#xff09;&#xff1a; 细节&#xff1a; 1&#xff1a;SpringMVC会针对常见类型&#xff08;八种基本类型及…

【Crypto】一眼就解密

文章目录 前言一眼就解密解题感悟 前言 Basic写累了&#xff0c;写写别的 一眼就解密 一眼md5试一试 小小flag 拿下&#xff01; 解题感悟 30秒搞定

Python第三方包安装与配置教程

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、Windows系统下的Python包安装 二、Linux系统下的Python包安装 三、配置Python环境 四…

打印9*9乘法表(递归或压缩矩阵)python

打印9*9表def print_multiplication_table(row, col):if row > 10:return # 递归结束条件if col row:print() # 换行print_multiplication_table(row 1, 1) # 递归调用下一行else:print(f"{row-1} * {col} {(row-1) * col}\t", end"") # 打印乘法…

Top3专业课150满分,怎么考的?

这个系列会邀请上岸学长学姐进行经验分享~ 今天经验分享的同学是小马哥上海交大819的全程班学员&#xff0c;专业课150分满分&#xff0c;这位同学也是819期末考试的第一名&#xff0c;非常厉害&#xff01;大家吸吸欧气&#xff01; 初试成绩单 前言 先介绍下自己&#xff0…

mysql binlog统一恢复误删数据库、表、数据(没有任何备份)

先将mysql文件夹中的my.ini进行设置 在 [mysqld]下边加上 # mysql-bin 是日志的基本名或前缀名&#xff0c;最后生成的日志文件是mysql-bin.000001类似&#xff0c;重启mysql数字会递增 log_binmysql-bin #binlog格式&#xff0c;statement&#xff0c;row&#xff0c;mixed可…

慢性乙型肝炎肝脏剪切波弹性成像的深度学习放射学显著改善了肝纤维化的诊断性能 | 文献速递-深度学习结合影像组学

慢性乙型肝炎肝脏剪切波弹性成像的深度学习放射学显著改善了肝纤维化的诊断性能 | 文献速递-深度学习结合影像组学 麦田医学 美好事物中转站 2024-05-21 11:03 Title 题目 Deep learning Radiomics of shear wave elastography significantly improved diagnostic performa…

【linux-kernel内核移植记录-踩坑以及注意事项】

目录 1. 环境介绍2.编译原厂的kernel2.1 通过tftp挂载原厂linux内核 3. 修改对应的驱动3.1 修改CPU频率3.2 修改MMC3.3 修改网络驱动 4. 总结 1. 环境介绍 ubuntu版本16.04I.MX6ULL开发板&#xff0c;阿尔法uboot正常启动&#xff0c;能ping通ubuntu&#xff0c;可通过tftpboo…

【0007day】总体标准差、样本标准差和无偏估计

文章目录 总体标准差和样本标准差无偏估计无偏性与无偏估计量 总体标准差和样本标准差 一些表示上的差别。 总体标准差 样本标准差 两者的区别 样本方差为什么除以n-1? 这主要是由于样本的方差会低估总体的方差&#xff08;抽样的过程中&#xff0c;按照概率来说&#xff0…

C++面向对象的第二大特性:继承

1.继承的介绍 首先容我先向大家举一个列子: 我这里定义了一个Person的类 class Person { protected:string name;int age;string address;}; 在这个基础上&#xff0c;我要定义一个关于Student , Worker 的类 由于Student Worker都具有Person类中的成员变量 &#xff0c…

【C语言】指针(三)

目录 一、字符指针 1.1 ❥ 使用场景 1.2 ❥ 有关字符串笔试题 二、数组指针 2.1 ❥ 数组指针变量 2.2 ❥ 数组指针类型 2.3 ❥ 数组指针的初始化 三、数组指针的使用 3.1 ❥ 二维数组和数组名的理解 3.2 ❥ 二维数组传参 四、函数指针 4.1 ❥ 函数的地址 4.2 ❥ 函数…

探索亚马逊云科技技术课程:大模型平台与提示工程的应用与优化

上方图片源自亚马逊云科技【生成式 AI 精英速成计划】技术开发技能课程 前言 学习了亚马逊云科技–技术开发技能课程 本课程分为三个部分&#xff0c;了解如何使用大模型平台、如何训练与部署大模型及生成式AI产品应用与开发&#xff0c;了解各类服务的优势、功能、典型使用案…

借助 CloudFlare 增强站点内容保护防采集

今天在一位站长的帮助下实测了 CloudFlare 增强站点内容保护实现防采集的功能,效果那是杠杠的,如果您的站点原创内容比较多的话,明月强烈建议试试 CloudFlare 这个内容保护,无论是 WordPress 、Typecho 都有非常好的效果,并且几乎没有任何误伤,搜索引擎爬虫蜘蛛更是不会影…

Adobe Animate AN v24.0.2 安装教程 (动画特效设计及合成工具)

Adobe系列软件安装目录 一、Adobe Photoshop PS 25.6.0 安装教程 (最流行的图像设计软件) 二、Adobe Media Encoder ME v24.3.0 安装教程 (视频和音频编码渲染工具) 三、Adobe Premiere Pro v24.3.0 安装教程 (领先的视频编辑软件) 四、Adobe After Effects AE v24.3.0 安装…

深度神经网络教程(个人总结版)

深度神经网络&#xff08;Deep Neural Networks, DNN&#xff09;是机器学习和人工智能的核心技术之一&#xff0c;已经广泛应用于图像识别、自然语言处理、语音识别、自动驾驶等领域。本文将详细介绍深度神经网络的背景、基本原理、架构、训练方法、优化技巧以及常见应用。 一…

vue通过for循环生成input框后双向绑定失效问题

有些时候页面上有太多的表单元素&#xff0c;一个个的写太过繁琐&#xff0c;拿 input 框举例&#xff0c;众多的 input 框&#xff0c;无非就是输入框前的说明和 input 框的 name 属性不一样 <el-form :inline"true" :model"formInline" size"mi…

Linux-笔记 应用编程函数总结

之前一直没做总结&#xff0c;这里总结一下。 一、文件I/O open #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int open(const char *pathname, int flags); 例子&#xff1a; int fd; fd open("./test_kondon", O_WRONLY …