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

【Vue3+TS项目】硅谷甄选day04--顶部组件搭建+面包屑+路由鉴权

顶部组件搭建

顶部左侧折叠和面包屑实现

左侧菜单刷新折叠的问题解决---属性default-active

折叠之后图标不见:icon放在插槽外面----element的menu属性:collapse

project\src\layout\index.vue

// 获取路由对象
import { useRoute } from 'vue-router';
​
let $route = useRoute()
​
<el-menu :default-active="$route.path" background-color="$base-menu-background" text-color="white"active-text-color="yellowgreen"><Menu :menuList="userStore.menuRoutes" />
</el-menu>

顶部tabbar静态组件封装:拆分为左侧面包屑、右侧设置区域

面包屑组件:project\src\layout\tabbar\breadcrumb\index.vue

<template><!-- 顶部左侧静态 --><el-icon style="margin-right: 10px;"><Expand /></el-icon><!-- 左侧面包屑 --><el-breadcrumb separator-icon="ArrowRight"><el-breadcrumb-item>权限管理</el-breadcrumb-item><el-breadcrumb-item>用户管理</el-breadcrumb-item></el-breadcrumb>
</template><script setup lang="ts"></script>
<style scoped lang="scss"></style>

右侧设置组件:project\src\layout\tabbar\setting\index.vue

<template><el-button size="small" icon="Refresh" circle /><el-button size="small" icon="FullScreen" circle /><el-button size="small" icon="Setting" circle /><img src="/public/logo.png" style="width: 32px;height: 24px;margin: 0 10px;"><!-- 下拉菜单 --><el-dropdown><span class="el-dropdown-link">Admin<el-icon class="el-icon--right"><arrow-down /></el-icon></span><template #dropdown><el-dropdown-menu><el-dropdown-item>退出登入</el-dropdown-item></el-dropdown-menu></template></el-dropdown>
</template><script setup lang="ts"></script>
<style scoped lang="scss"></style>

project\src\layout\tabbar\index.vue

<template><div class="tabbar"><div class="tabbar_left"><Breadcrumb /></div><div class="tabbar_right"><Setting /></div></div>
</template><script setup lang="ts">
// 引入组件
import Breadcrumb from '@/layout/tabbar/breadcrumb/index.vue'
import Setting from '@/layout/tabbar/setting/index.vue'
</script>
<script lang="ts">
export default {name: "Tabbar"
}
</script>
<style scoped lang="scss">
.tabbar {width: 100%;height: 100%;display: flex;justify-content: space-between;
​.tabbar_left {display: flex;align-items: center;margin-left: 20px;
​}
​.tabbar_right {display: flex;align-items: center;}
}
</style>

菜单折叠的实现

定义控制折叠展开的变量 fold,放在仓库比较合适,因为其他组件也要根据fold进行变化,放在面包屑组件的传递给其他组件比较麻烦

project\src\store\modules\setting.ts

//小仓库:layout组件相关配置仓库
import { defineStore } from 'pinia'
​
const useLayOutSettingStore = defineStore('SettingStore', {state: () => {return {fold: false, //用户控制菜单折叠还是收起控制}},
})
​
export default useLayOutSettingStore

面包屑组件:project\src\layout\tabbar\breadcrumb\index.vue(通过点击图标切换仓库中fold的状态,其他组件读取时获取最新的)

// 获取小仓库中的控制折叠展开变量 fold
import useLayOutSettingStore from '@/store/modules/setting';
​
// 获取仓库
let layOutSettingStore = useLayOutSettingStore()
// 点击折叠展开图标的方法
const changeIcon = () => {layOutSettingStore.fold = !layOutSettingStore.fold
}
​
​
<el-icon style="margin-right: 10px;" @click="changeIcon"><!-- vue提供的动态展示组件 --><component :is="layOutSettingStore.fold ? 'Expand' : 'Fold'"></component>
</el-icon>

layout组件读取 fold 变量

project\src\layout\index.vue

// 获取小仓库中的控制折叠展开变量 fold
import useLayOutSettingStore from '@/store/modules/setting';
// 获取仓库
let layOutSettingStore = useLayOutSettingStore()
​
// :collapse="layOutSettingStore.fold ? true : false"
<el-menu :collapse="layOutSettingStore.fold ? true : false" :default-active="$route.path"background-color="$base-menu-background" active-text-color="yellowgreen"><Menu :menuList="userStore.menuRoutes" />
</el-menu>
​
//顶部导航和右侧内容展示区域添加动态类fold
:class="{ fold: layOutSettingStore.fold ? true : false }"

Collapse 有点问题,折叠的时候还有bug

顶部面包屑动态展示

通过获取路由匹配信息来实现

layout组件不需要展示到面包屑,直接展示首页,所以去掉layout路由中的元信息title和icon,然后再面包屑渲染时进行v-show判断

点击面包屑也可以进行路由跳转--element可以实现---to

project\src\layout\tabbar\breadcrumb\index.vue

// 获取路由
import { useRoute } from 'vue-router';
let $route = useRoute()
​<!-- 左侧面包屑 --><el-breadcrumb separator-icon="ArrowRight"><el-breadcrumb-item v-for="(item, index) in $route.matched" :key="index" v-show="item.meta.title" :to="item.path"><!-- 渲染图标 --><el-icon style="margin: 0 2px;"><component :is="item.meta.icon"></component></el-icon><!-- 渲染面包屑标题 --><span>{{ item.meta.title }}</span></el-breadcrumb-item></el-breadcrumb>

顶部tabbar右侧功能实现

刷新功能实现

简单来说就是路由组件销毁和重建,重新发请求获取数据

涉及顶部导航与内容展示区域间通信---刷新的变量放在小仓库中

小仓库:

refresh: false,//控制刷新效果

setting组件:project\src\layout\tabbar\setting\index.vue

// 获取仓库中的 刷新变量 refresh
import useLayOutSettingStore from '@/store/modules/setting'
​
let layOutSettingStore = useLayOutSettingStore()
​
// 点击刷新的回调
const refreshChange = () => {layOutSettingStore.refresh = !layOutSettingStore.refresh
}
​
//刷新按钮
<el-button size="small" icon="Refresh" circle @click="refreshChange" />

Main组件监听refresh变化,变化就重新发请求

project\src\layout\main\index.vue

import { watch, ref, nextTick } from 'vue'
// 获取仓库中的 刷新变量 refresh
import useLayOutSettingStore from '@/store/modules/setting'
​
let layOutSettingStore = useLayOutSettingStore()
// 控制组件销毁与创建
let flag = ref(true)
// 监听仓库中的refresh,如果发生变化说明用户点击刷新按钮
watch(() => layOutSettingStore.refresh, () => {//refresh变化,则销毁组件flag.value = false//等待组件完毕重新加载nextTick(() => {flag.value = true})
})
​
<component :is="Component" v-if="flag" />

全屏功能实现:dom操作

// 点击实现全屏切换
const fullScreen = () => {//DOM对象的一个属性:可以用来判断当前是不是全屏模式[全屏:true,不是全屏:false]let full = document.fullscreenElement;//切换为全屏模式if (!full) {//文档根节点的方法requestFullscreen,实现全屏模式document.documentElement.requestFullscreen();} else {//变为不是全屏模式->退出全屏模式document.exitFullscreen();}
}
​
<el-button size="small" icon="FullScreen" circle @click="fullScreen" />

退出登入业务

token的理解

登入成功,首页组件挂在完毕,通知用户小仓库拿着token去找服务器那用户的相关数据,并存在小仓库中

home组件:project\src\views\home\index.vue(后面可以在路由守卫中发请求)

import { onMounted } from 'vue';
//引入用户相关的仓库,获取当前用户的头像、昵称
import useUserStore from '@/store/modules/user';
//获取存储用户信息的仓库对象
let userStore = useUserStore();
//首页挂在完毕通知小仓库发请求拿数据
onMounted(() => {userStore.getUserInfo()
})

用户小仓库:project\src\store\modules\user.ts

//记得到type中进行类型定义
username: '',
avatar: ''
// 拿token向服务器请求用户数据
async getUserInfo() {// 获取用户信息进行存储【头像,用户名】let res = await reqUserInfo()if (res.code == 200) {this.username = res.data.checkUser.usernamethis.avatar = res.data.checkUser.avatar} else {}
}

用户有n个请求,那怎么携带token呢,放在请求拦截器里面

project\src\utils\request.ts

// 引入用户相关的仓库
import useUserStore from '@/store/modules/user'
​
// 第二步:request实例添加请求和响应拦截器
request.interceptors.request.use((config) => {// 获取用户小仓库,拿到登入成功的token数据携带给服务器let userStore = useUserStore()if (userStore.token) {config.headers.token = userStore.token}// config 配置对象,有headers属性请求头,经常给服务器端携带公共参数// 返回配置对象return config;
})

获取到用户信息之后,在tabbar组件的setting组件中展示一下

project\src\layout\tabbar\setting\index.vue

获取仓库,展示即可

退出登入功能实现:点击之后退到登入页,将用户信息和token清除掉

退出登入需要发送请求,告诉服务器token失效,下一次登入服务器重新返回新的token

project\src\layout\tabbar\setting\index.vue

// 退出登入需要进行路由跳转
import { useRouter } from 'vue-router';
// 获取路由器对象
let $router = useRouter()
​
//退出登录点击回调
const logout = async () => {//第一件事情:需要向服务器发请求[退出登录接口]******暂时没有//第二件事情:仓库当中关于用于相关的数据清空[token|username|avatar]//第三件事情:跳转到登录页面await userStore.userLogout();//跳转到登录页面$router.push({ path: '/login' });
}
​
<el-dropdown-item @click="logout">退出登入</el-dropdown-item>

用户小仓库:project\src\store\modules\user.ts

        //退出登入userLogout() {//目前没有退出接口//清除数据this.token = ''this.username = ''this.avatar = ''localStorage.removeItem('TOKEN')}

还有一些问题需要解决:登入成功之后不允许在跳转到login、用户信息需要持久化存储

路由鉴权与进度条实现

进度条是可以用全局路由守卫实现(前置,后置)

路由组件挂在完毕我们去请求获取用户信息进行展示,但是不适用大量组件,可以通过全局路由守卫进行实现,路由跳转的时候发请求就可以

创建路由鉴权文件

注意:在组件的外部使用小仓库会报错(同步的语句获取仓库不可以),需要获取小仓库的数据必须先有大仓库

发请求:本来我们是在各个组件中发请求获取用户信息,现在我们可以在前置路由守卫中判断有没有用户名,有的话可以放行到其他路由组件,没有的话先获取用户信息再放行

放在路由守卫中也可以解决我们当时在首页中发请求获取用户信息时,我们跳转其他路由组件数据丢失问题,因为路由守卫就算没有用户信息,也会发请求拿到

//获取用户相关的小仓库内部token数据,去判断用户是否登录成功
import useUserStore from './store/modules/user'
import pinia from './store'
const userStore = useUserStore(pinia)
project\src\permisstion.ts//路由鉴权:鉴权,项目当中路由能不能被的权限的设置(某一个路由什么条件下可以访问、什么条件下不可以访问)
import router from '@/router'
import setting from '@/settings'
//@ts-ignore
import nprogress from 'nprogress'
//引入进度条样式
import 'nprogress/nprogress.css'
nprogress.configure({ showSpinner: false })
//获取用户相关的小仓库内部token数据,去判断用户是否登录成功
import useUserStore from './store/modules/user'
import pinia from './store'
const userStore = useUserStore(pinia)
//全局守卫:项目当中任意路由切换都会触发的钩子
//全局前置守卫
router.beforeEach(async (to: any, from: any, next: any) => {//to:你将要访问那个路由//from:你从来个路由而来//next:路由的放行函数nprogress.start()//获取token,去判断用户登录、还是未登录const token = userStore.token//获取用户名字const username = userStore.username//用户登录判断if (token) {//登录成功,访问login,不能访问,指向首页if (to.path == '/login') {next({ path: '/' })} else {//登录成功访问其余六个路由(登录排除)//有用户信息if (username) {//放行next()} else {//如果没有用户信息,在守卫这里发请求获取到了用户信息再放行try {//获取用户信息await userStore.getUserInfo()//放行//万一:刷新的时候是异步路由,有可能获取到用户信息、异步路由还没有加载完毕,出现空白的效果next({ ...to })} catch (error) {//token过期:获取不到用户信息了//用户手动修改本地存储token//退出登录->用户相关的数据清空await userStore.userLogout()next({ path: '/login' })}}}} else {//用户未登录判断if (to.path == '/login') {next()} else {next({ path: '/login' })}}
})
//全局后置守卫
router.afterEach((to: any, from: any) => {document.title = `${setting.title} - ${to.meta.title}`
​nprogress.done()
})
​
//第一个问题:任意路由切换实现进度条业务 ---nprogress
//第二个问题:路由鉴权(路由组件访问权限的设置)
//全部路由组件:登录|404|任意路由|首页|数据大屏|权限管理(三个子路由)|商品管理(四个子路由)
​
//用户未登录:可以访问login,其余六个路由不能访问(指向login)
//用户登录成功:不可以访问login[指向首页],其余的路由可以访问
​

在main中引入

//引入路由鉴权文件
import './permisstion'

相关文章:

【Vue3+TS项目】硅谷甄选day04--顶部组件搭建+面包屑+路由鉴权

顶部组件搭建 顶部左侧折叠和面包屑实现 左侧菜单刷新折叠的问题解决---属性default-active 折叠之后图标不见&#xff1a;icon放在插槽外面----element的menu属性&#xff1a;collapse project\src\layout\index.vue // 获取路由对象 import { useRoute } from vue-route…...

某oa 11.10 未授权任意文件上传

漏洞简介 之前也对通达 oa 做过比较具体的分析和漏洞挖掘&#xff0c;前几天看到通达 oa 11.10 存在未授权任意文件上传漏洞&#xff0c;于是也打算对此进行复现和分析。 环境搭建 https://www.tongda2000.com/download/p2019.php 下载地址 &#xff1a;https://cdndown.tongda…...

Grounded Language-Image Pre-training(论文翻译)

文章目录 Grounded Language-Image Pre-training摘要1.介绍2.相关工作3.方法3.1统一构建3.2.语言感知深度融合3.3.使用可扩展的语义丰富数据进行预训练 4.迁移到既定的基准4.1.COCO上的zero-shot和监督迁移学习4.2.LVIS上的zero-shot 迁移学习4.3.Flickr30K实体上的 phrase gro…...

设计模式-行为型模式(模板方法、策略、观察者、迭代器、责任链、命令、状态、备忘录、访问者、中介者、解释器)

行为型模式&#xff1a;专注于对象之间的 协作 及如何通过彼此之间的交互来完成任务。行为型模式通常集中在描述对象之间的 责任 分配和 通信 机制&#xff0c;并提供了一些优雅解决特定问题的方案。 模板方法模式(Template Method Pattern)策略模式(Strategy Pattern)观察者模…...

全面探讨 Spring Boot 的自动装配机制

Spring Boot 是一个基于 Spring 框架的快速开发脚手架&#xff0c;它通过自动配置机制帮助我们快速搭建应用程序&#xff0c;从而减少了我们的配置量和开发成本。自动装配是 Spring Boot 的核心特点之一&#xff0c;它可以减少项目的依赖&#xff0c;简化配置文件&#xff0c;提…...

河道水位监测:河道水位监测用什么设备

中国地形复杂&#xff0c;气候多样&#xff0c;导致水资源分布不均&#xff0c;洪涝和干旱等问题时有发生。同时&#xff0c;人类活动也对水资源造成了很大压力&#xff0c;工业和农业用水增加&#xff0c;河道水位下降&#xff0c;生态环境受到威胁。因此&#xff0c;对河道水…...

嵌入式系统中u-boot和bootloader到底有什么区别

嵌入式软件工程师都听说过 u-boot 和 bootloader&#xff0c;但很多工程师依然不知道他们到底是啥。 今天就来简单讲讲 u-boot 和 bootloader 的内容以及区别。 Bootloader Bootloader从字面上来看就是启动加载的意思。用过电脑的都知道&#xff0c;windows开机时会首先加载…...

实验14:20211030 1+X 中级实操考试(id:2498)

实验14&#xff1a;20211030 1X 中级实操考试&#xff08;id&#xff1a;2498&#xff09; 一、项目背景说明二、表结构三、步骤【5 分】步骤 1&#xff1a;项目准备【5 分】步骤 2&#xff1a;完成实体类 Member【10 分】步骤 3&#xff1a;完成实体类 Goods【10 分】步骤 4&a…...

(字符串 ) 剑指 Offer 58 - II. 左旋转字符串 ——【Leetcode每日一题】

❓剑指 Offer 58 - II. 左旋转字符串 难度&#xff1a;简单 字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如&#xff0c;输入字符串"abcdefg"和数字2&#xff0c;该函数将返回左旋转两位得到的…...

EPICS编程

提纲 1&#xff09; 为什么在EPICS上编程 2&#xff09;构建系统特性&#xff1a;假设基本理解Unix Make 3&#xff09;在libCom中可用的工具 1&#xff09; 为什么在EPICS上编程 1、社区标准&#xff1a;EPICS合作者知道和明白EPICS结构 2、在很多操作系统之间代码移值性…...

17:00面试,还没10分钟就出来了,问的实在是太...

从外包出来&#xff0c;没想到死在另一家厂子 自从加入这家公司&#xff0c;每天都在加班&#xff0c;钱倒是给的不少&#xff0c;所以也就忍了。没想到8月一纸通知&#xff0c;所有人不许加班&#xff0c;薪资直降30%&#xff0c;顿时有吃不起饭的赶脚。 好在有个兄弟内推我去…...

docker都有那些工具,及工具面试题

docker介绍 Docker 是一种开源的容器化平台&#xff0c;可以帮助开发者将应用程序和依赖项打包到轻量级的容器中&#xff0c;然后部署到任何基于 Linux 的操作系统中。使用 Docker 可以大大简化开发、部署和管理应用程序的过程&#xff0c;使其更加快速、灵活和可靠。 Docker…...

LAMP网站应用架构

LAMP 一、LAMP概述1、各组件的主要作用2、构建LAMP各组件的安装顺序 二、编译安装Apache httpd服务1、关闭防火墙&#xff0c;将安装Apache所需软件包传到/opt目录下2.安装环境依赖包3.配置软件模块4.编译及安装5.优化配置文件路径&#xff0c;并把httpd服务的可执行程序文件放…...

C++虚函数virtual(动态多态)(纯虚函数)

怎么判断函数是虚函数还是普通函数&#xff1f; 用VS&#xff0c;在调用对象的方法的地方。。按altg &#xff0c;如果他跳转到正确的函数&#xff0c;那也就意味着他是编译时可以确定的。。。 但是如果他跳到了这个调用对象的基类的函数&#xff0c;那么也就意味着他是一个运行…...

【Java 接口】接口(Interface)的定义,implements关键字,接口实现方法案例

博主&#xff1a;_LJaXi Or 東方幻想郷 专栏&#xff1a; Java | 从入门到入坟 专属&#xff1a;六月一日 | 儿童节 Java 接口 接口简介 &#x1f383;接口的定义 &#x1f9e7;接口实现类名定义 &#x1f381;接口实现类小案例 &#x1f388;后话 &#x1f3b0; 接口简介 &…...

解决Vmware上的kali找不到virtualbox上的靶机的问题

解决kali找不到靶场ip问题的完整方法 1.配置靶机2.配置kali的虚拟网络3.配置kali中的eth0网络 1.配置靶机 靶机部署在Virtualbox上对其进行网络配置&#xff0c;选择连接方式为仅主机&#xff08;Host-Only&#xff09;网络。 2.配置kali的虚拟网络 在编辑中选择虚拟网络配…...

查看MySQL服务器是否启用了SSL连接,并且查看ssl证书是否存在

文章目录 一、查看MySQL服务器是否启用了SSL连接 1.登录MySQL服务器 2.查看SSL配置 二、查看证书是否存在 前言 查看MySQL服务器是否启用了SSL连接&#xff0c;并且查看ssl证书是否存在 一、查看MySQL服务器是否启用了SSL连接 1.登录MySQL服务器 在Linux终端中&#xf…...

华为OD机试真题 Java 实现【表示数字】【牛客练习题】

一、题目描述 将一个字符串中所有的整数前后加上符号“*”,其他字符保持不变。连续的数字视为一个整数。 数据范围:字符串长度满足1≤n≤100 。 二、输入描述 输入一个字符串。 三、输出描述 字符中所有出现的数字前后加上符号“*”,其他字符保持不变。 四、解题思路…...

使用Python进行接口性能测试:从入门到高级

前言&#xff1a; 在今天的网络世界中&#xff0c;接口性能测试越来越重要。良好的接口性能可以确保我们的应用程序可以在各种网络条件下&#xff0c;保持流畅、稳定和高效。Python&#xff0c;作为一种广泛使用的编程语言&#xff0c;为进行接口性能测试提供了强大而灵活的工…...

sed编辑器

文章目录 一.sed命令基础1.sed概念2.sed的工作流程3.命令格式4.sed命令的常用选项5.sed命令的操作符 二.sed命令的打印功能1.打印文本文件内容1.1 格式1.2 默认打印方式 2.指定行打印2.1 指定行号打印2.2 只打印文件的行数2.3 即打印文件的行号也打印文件的内容2.4 即显示行也显…...

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…...

国防科技大学计算机基础课程笔记02信息编码

1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制&#xff0c;因此这个了16进制的数据既可以翻译成为这个机器码&#xff0c;也可以翻译成为这个国标码&#xff0c;所以这个时候很容易会出现这个歧义的情况&#xff1b; 因此&#xff0c;我们的这个国…...

ESP32读取DHT11温湿度数据

芯片&#xff1a;ESP32 环境&#xff1a;Arduino 一、安装DHT11传感器库 红框的库&#xff0c;别安装错了 二、代码 注意&#xff0c;DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...

MODBUS TCP转CANopen 技术赋能高效协同作业

在现代工业自动化领域&#xff0c;MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步&#xff0c;这两种通讯协议也正在被逐步融合&#xff0c;形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...

C++ 基础特性深度解析

目录 引言 一、命名空间&#xff08;namespace&#xff09; C 中的命名空间​ 与 C 语言的对比​ 二、缺省参数​ C 中的缺省参数​ 与 C 语言的对比​ 三、引用&#xff08;reference&#xff09;​ C 中的引用​ 与 C 语言的对比​ 四、inline&#xff08;内联函数…...

Java入门学习详细版(一)

大家好&#xff0c;Java 学习是一个系统学习的过程&#xff0c;核心原则就是“理论 实践 坚持”&#xff0c;并且需循序渐进&#xff0c;不可过于着急&#xff0c;本篇文章推出的这份详细入门学习资料将带大家从零基础开始&#xff0c;逐步掌握 Java 的核心概念和编程技能。 …...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 在 GPU 上对图像执行 均值漂移滤波&#xff08;Mean Shift Filtering&#xff09;&#xff0c;用于图像分割或平滑处理。 该函数将输入图像中的…...

算法笔记2

1.字符串拼接最好用StringBuilder&#xff0c;不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...

WebRTC从入门到实践 - 零基础教程

WebRTC从入门到实践 - 零基础教程 目录 WebRTC简介 基础概念 工作原理 开发环境搭建 基础实践 三个实战案例 常见问题解答 1. WebRTC简介 1.1 什么是WebRTC&#xff1f; WebRTC&#xff08;Web Real-Time Communication&#xff09;是一个支持网页浏览器进行实时语音…...