当前位置: 首页 > news >正文

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 APIapp.$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?.guardsreturn 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 importloader:()=>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// 用户配置参数 timeoutif(options.timeout) {timer = setTimeout(() => {timeout.value = true;}, options.timeout);}onUmounted(() => clearTimeout(timer))// 调用 load 函数加载组件// import(), ES6load().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 打包

相关文章:

  • 算法-可完成的最大任务数
  • Linux防火墙(以iptables为例)
  • 十种常用数据分析模型
  • 万界星空科技定制化MES系统帮助实现数字化生产
  • 自建公式,VBA在Excel中解一元一次方程
  • docker命令总结
  • upload-labs 21关解析
  • 手把手教你写Java项目(1)——流程
  • 什么是深拷贝和浅拷贝?
  • 微服务架构的优势 与 不足
  • 常见排序算法之选择排序
  • 内网安全-隧道搭建穿透上线内网穿透-nps自定义上线内网渗透-Linux上线-cs上线Linux主机
  • 微信生态系统介绍
  • Android 待办类应用提醒功能的实现及其问题
  • ⌈ 传知代码 ⌋ 高速公路车辆速度检测软件
  • [原]深入对比数据科学工具箱:Python和R 非结构化数据的结构化
  • 4个实用的微服务测试策略
  • AHK 中 = 和 == 等比较运算符的用法
  • C++入门教程(10):for 语句
  • ES6, React, Redux, Webpack写的一个爬 GitHub 的网页
  • IDEA 插件开发入门教程
  • JavaSE小实践1:Java爬取斗图网站的所有表情包
  • JAVA多线程机制解析-volatilesynchronized
  • js
  • js学习笔记
  • Laravel5.4 Queues队列学习
  • Redis在Web项目中的应用与实践
  • Redis字符串类型内部编码剖析
  • RxJS 实现摩斯密码(Morse) 【内附脑图】
  • Vue 重置组件到初始状态
  • WePY 在小程序性能调优上做出的探究
  • 第十八天-企业应用架构模式-基本模式
  • 力扣(LeetCode)357
  • 模仿 Go Sort 排序接口实现的自定义排序
  • 模型微调
  • 使用 Xcode 的 Target 区分开发和生产环境
  • 问:在指定的JSON数据中(最外层是数组)根据指定条件拿到匹配到的结果
  • 验证码识别技术——15分钟带你突破各种复杂不定长验证码
  • 自制字幕遮挡器
  • No resource identifier found for attribute,RxJava之zip操作符
  • 【运维趟坑回忆录 开篇】初入初创, 一脸懵
  • CMake 入门1/5:基于阿里云 ECS搭建体验环境
  • 国内开源镜像站点
  • ​Base64转换成图片,android studio build乱码,找不到okio.ByteString接腾讯人脸识别
  • ​Linux Ubuntu环境下使用docker构建spark运行环境(超级详细)
  • #QT(一种朴素的计算器实现方法)
  • $(selector).each()和$.each()的区别
  • $.ajax()
  • $LayoutParams cannot be cast to android.widget.RelativeLayout$LayoutParams
  • (8)Linux使用C语言读取proc/stat等cpu使用数据
  • (Java企业 / 公司项目)点赞业务系统设计-批量查询点赞状态(二)
  • (笔试题)合法字符串
  • (二)linux使用docker容器运行mysql
  • (附源码)springboot车辆管理系统 毕业设计 031034
  • (六) ES6 新特性 —— 迭代器(iterator)