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

Orleans8.2入门测试

微软官方文档:快速入门:使用 ASP.NET Core 生成第一个 Orleans 应用 - .NET | Microsoft Learn

项目及引入的nuget库:

1、接口项目;2、接口实现项目;3、silo项目;4、客户端项目

其中Microsoft.Orleans.Streaming,为测试流功能额外添加。 

//IHello.cs代码
using System.Reflection;namespace GrainInterfaces
{/// <summary>/// Grain接口/// 从IGrainWithStringKey继承可以使用字符串作为Grain的唯一标识,便于理解/// 从IGrainObserver继承为了支持订阅方式双向通信/// </summary>public interface IHello : IGrainWithStringKey, IGrainObserver{/// <summary>/// 默认测试接口/// </summary>/// <param name="greeting"></param>/// <returns></returns>ValueTask<string> SayHello(string greeting);/// <summary>/// 传递复杂对象测试/// </summary>/// <param name="param"></param>/// <returns></returns>ValueTask<ResultA> GetResultA(ParamA param);#region 用订阅方式实现双向通信Task NotifyOtherGrain(string grainID, string message);/// <summary>/// 客户端订阅Grain上的通知消息/// 需要额外的一个IGrainObserver类型对象IChat支持/// </summary>/// <param name="observer"></param>/// <returns></returns>Task Subscribe(IChat observer);/// <summary>/// 解除订阅/// </summary>/// <param name="observer"></param>/// <returns></returns>Task Unsubscribe(IChat observer);/// <summary>/// Grains直接通信,只要有Grains的id,就可以很方便的从客户端或其他grains中拿到grains的引用/// </summary>/// <param name="sender">目标grains的id</param>/// <param name="message">要转发的消息</param>/// <returns></returns>Task ReceiveBrotherMessage(string sender, string message);#endregion}#region 订阅方式支持类定义/// <summary>/// Grain类不允许出现属性,为了方便客户端订阅,额外定义一个带Action委托的接口让Chat对象实现/// </summary>public interface IChatNotify{/// <summary>/// 收到服务端通知触发事件,客户端指定处理事件/// </summary>Action<string> ReceiveAct { get; set; }}/// <summary>/// 消息订阅支持接口/// </summary>[Alias("GrainInterfaces.IChat")]public interface IChat : IGrainObserver{/// <summary>/// 定义订阅模式消息接收函数/// 注意IChat在客户端实例化,此函数在客户端执行/// </summary>/// <param name="message"></param>/// <returns></returns>[Alias("ReceiveMessage")]Task ReceiveMessage(string message);}/// <summary>/// 消息订阅类实现(在客户端实例化)/// </summary>public class Chat : IChat, IChatNotify{public Action<string> ReceiveAct { get; set; } = null;public Task ReceiveMessage(string message){var log = $"===={message}   {Assembly.GetEntryAssembly()?.FullName}";Console.WriteLine(log);ReceiveAct?.Invoke(message);return Task.CompletedTask;}}#endregion/// <summary>/// 定义复杂函数参数类型/// 注解GenerateSerializer:告诉Orleans使用其默认的序列化反序列化方式/// </summary>[GenerateSerializer]public class ParamA{[Id(0)]public int ID { get; set; }[Id(1)]public int DataLen { get; set; }[Id(2)]public required string Name { get; set; }}/// <summary>/// 定义复杂函数返回类型/// 注解GenerateSerializer:告诉Orleans使用其默认的序列化反序列化方式/// </summary>[GenerateSerializer]public class ResultA{[Id(0)]public int ID { get; set; }[Id(1)]public required string Name { get; set; }[Id(2)]public required byte[] Data { get; set; }}/// <summary>/// 测试用全局数据定义/// </summary>public static class GlobalValueDefinition{public const string StreamProviderName = "StreamProvider";public const string GrainStorageName = "PubSubStore";public const string ImplicitStreamSubscriptionName = "RANDOMDATA";}
}
//HelloGrains.cs
using GrainInterfaces;
using Microsoft.Extensions.Logging;
using Orleans;
using Orleans.Streams;
using Orleans.Utilities;
using System.Net.NetworkInformation;namespace Grains
{/// <summary>/// Hello Grain定义/// [ImplicitStreamSubscription(GlobalValueDefinition.ImplicitStreamSubscriptionName)]/// 这个注解告诉Orleans使用隐式订阅流(简化测试)/// </summary>[ImplicitStreamSubscription(GlobalValueDefinition.ImplicitStreamSubscriptionName)]public class HelloGrains :Grain, IHello{private readonly ILogger _logger;private readonly ObserverManager<IChat> _subManager;public HelloGrains(ILogger<HelloGrains> logger){_logger = logger;_subManager = new ObserverManager<IChat>(TimeSpan.FromMinutes(5), logger);Console.WriteLine($"====={GrainContext.GrainId}");}private IAsyncStream<string> _demoStream;public override Task OnActivateAsync(CancellationToken cancellationToken){#region 流的支持CreateStreamSubscribe().GetAwaiter().GetResult();#endregionreturn base.OnActivateAsync(cancellationToken);}private async Task CreateStreamSubscribe(){var guid = this.GetPrimaryKeyString();var streamProvider = this.GetStreamProvider(GlobalValueDefinition.StreamProviderName);var streamId = StreamId.Create(GlobalValueDefinition.ImplicitStreamSubscriptionName, guid);_demoStream = streamProvider.GetStream<string>(streamId);await _demoStream.SubscribeAsync<string>(async (data, token) => { Console.WriteLine($"*****Hello {data}");await Task.CompletedTask;});}#region 订阅支持public Task Subscribe(IChat observer) {_subManager.Subscribe(observer, observer);return Task.CompletedTask;}public Task Unsubscribe(IChat observer) { _subManager.Unsubscribe(observer); return Task.CompletedTask; }//向订阅者发送消息public Task SendUpdateMessage(string message){_subManager.Notify(s=>s.ReceiveMessage(message));return Task.CompletedTask;}#endregionpublic ValueTask<string> SayHello(string greeting){var response = $"Client {GrainContext.GrainId} said:{greeting}, so HelloGrain says: Hello";_logger.LogInformation(response);SendUpdateMessage("SendUpdateMessage:" + response);return ValueTask.FromResult(response);}public ValueTask<ResultA> GetResultA(ParamA param){_logger.LogInformation($"Invoke GetResultA({param.ID}  {param.Name})");var res = new ResultA{ID = param.ID + 1,Name = param.Name,Data = new byte[param.DataLen],};return ValueTask.FromResult(res);}public Task NotifyOtherGrain(string otherGrainID, string message){var otherGrainHello = GrainFactory.GetGrain<IHello>(otherGrainID);otherGrainHello.ReceiveBrotherMessage(this.GetPrimaryKeyString(), message);return Task.CompletedTask;}public Task ReceiveBrotherMessage(string sender, string message){SendUpdateMessage($"GetBrotherMessage({sender},{message})");return Task.CompletedTask;}}
}
//Silo服务启动源码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Orleans.Configuration;
using Orleans.Serialization;
using System.Net;
using GrainInterfaces;namespace Silo
{public partial class Form1 : Form{public Form1(){InitializeComponent();}private async void button1_Click(object sender, EventArgs e){//本地集群方式部署var primarySiloEndPoint = new IPEndPoint(IPAddress.Parse(textBox_IPAddr.Text), 11_111);var silo = new HostBuilder().UseOrleans(builder =>{builder.UseDevelopmentClustering(primarySiloEndPoint).Configure<ClusterOptions>(options =>{options.ClusterId = "cluster001";options.ServiceId = "service001";}).ConfigureEndpoints(siloPort: 11_111, gatewayPort: 30_001).ConfigureLogging(logging => logging.AddConsole()).AddMemoryStreams(GlobalValueDefinition.StreamProviderName) //unget引入 Microsoft.Orleans.Streaming.AddMemoryGrainStorage(GlobalValueDefinition.GrainStorageName);}).Build();await silo.RunAsync();}//#region 本地测试模式IHostBuilder builder = Host.CreateDefaultBuilder(args).UseOrleans(silo =>{silo.UseLocalhostClustering(11111, 30001, serviceId: "service001", clusterId: "cluster001").ConfigureLogging(logging => logging.AddConsole());}).UseConsoleLifetime();using IHost host = builder.Build();await host.RunAsync();//#endregion//#region 专用服务器集群部署//var primarySiloEndPoint = new IPEndPoint(IPAddress.Parse("192.168.0.109"), 11_111);//var silo = new HostBuilder()//    .UseOrleans(builder => //    {//        builder.UseDevelopmentClustering(primarySiloEndPoint)//        .Configure<ClusterOptions>(options =>//        {//            options.ClusterId = "cluster001";//            options.ServiceId = "service001";//        })//        .ConfigureEndpoints(siloPort: 11_111, gatewayPort: 30_001)//        .ConfigureLogging(logging => logging.AddConsole());//    }).Build();//await silo.RunAsync();//#endregion}
}
//客户端代码
using GrainInterfaces;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Orleans.Configuration;
using Orleans.Serialization.WireProtocol;
using Orleans.Streams;
using System.Net;
using System.Security.AccessControl;namespace WinFormsApp1
{public partial class Form1 : Form{public Form1(){InitializeComponent();}private IClusterClient client = null;private IHost host = null;private IHello friend1;private IHello friend2;private IChat chat;private async void button1_Click(object sender, EventArgs e){#region 开发者模式连接//IHostBuilder builder = Host.CreateDefaultBuilder()//    .UseOrleansClient(client =>//    {//        client.UseLocalhostClustering(30001, "service001", "cluster001");//    })//    .ConfigureLogging(logging => logging.AddConsole())//    .UseConsoleLifetime();//host = builder.Build();//await host.StartAsync();//client = host.Services.GetRequiredService<IClusterClient>();#endregionvar PRIMARY_SILO_IP_ADDRESS = IPAddress.Parse(textBox_IPAddr.Text);//var PRIMARY_SILO_IP_ADDRESS_1 = IPAddress.Parse("192.168.100.101");#region 专用服务器群集模式连接var gateways = new IPEndPoint[] {new IPEndPoint(PRIMARY_SILO_IP_ADDRESS, 30_001)};host = Host.CreateDefaultBuilder().UseOrleansClient(clientBuilder =>clientBuilder.UseStaticClustering(gateways).Configure<ClusterOptions>(options =>{options.ClusterId = "cluster001";options.ServiceId = "service001";})#region Stream测试.AddMemoryStreams(GlobalValueDefinition.StreamProviderName)#endregion).Build();await host.StartAsync();client = host.Services.GetRequiredService<IClusterClient>();#endregion#region 订阅friend1 = client.GetGrain<IHello>("user1");friend2 = client.GetGrain<IHello>("user2");Chat c = new Chat();(c as IChatNotify).ReceiveAct += ShowLog;chat = client.CreateObjectReference<IChat>(c);await friend1.Subscribe(chat);#endregion#region StreamTestawait CreateStreamSubscribe();#endregion}private IAsyncStream<string> _demoStream;private async Task CreateStreamSubscribe(){var clientStreamName = "clientStream001";var streamProvider = client.GetStreamProvider(GlobalValueDefinition.StreamProviderName);var streamId = StreamId.Create(GlobalValueDefinition.ImplicitStreamSubscriptionName, clientStreamName);_demoStream = streamProvider.GetStream<string>(streamId);await _demoStream.SubscribeAsync<string>(async (data, token) =>{ShowLog($"*****Hello {data}");await Task.CompletedTask;});}private async void button2_Click(object sender, EventArgs e){await host.StopAsync();}private async void button3_Click(object sender, EventArgs e){string response = await friend1.SayHello("Hi friend!");ShowLog($"{friend1.GetPrimaryKeyString()}  {response}");}private void ShowLog(string log){Invoke(new Action(() =>{textBox1.AppendText($"{log}\r\n");}));}private async void button4_Click(object sender, EventArgs e){string response = await friend2.SayHello("Hi friend!");ShowLog($"{friend2.GetPrimaryKeyString()}  {response}");}private async void button5_Click(object sender, EventArgs e){IHello friend = client.GetGrain<IHello>("user1");var dateLen = Convert.ToInt32(textBox_DataLen.Text);var result = await friend.GetResultA(new ParamA { ID = 1, Name = "henreash", DataLen = dateLen });ShowLog($"GetResultA({result.ID} {result.Name}  {result.Data.Length})");}private void button6_Click(object sender, EventArgs e){friend2.NotifyOtherGrain(friend1.GetPrimaryKeyString(), "让user2转告user1");}private async void button7_Click(object sender, EventArgs e){await _demoStream.OnNextAsync("Send test stream message!");}}
}

结论:Orleans部署不需要额外的平台,编译后的进程直接启动;通过配置即可做集群扩展.grain和grain间,grain和客户端间很方便实现双向tcp通信.关闭silo服务,客户端访问失败,启动soli服务后客户端可继续访问.

服务器增加Dashboard

silo引入nuget库OleansDashboard(8.2.0),配置增加如下内容

可方便查看silo服务器的性能数据 

 

 

相关文章:

Orleans8.2入门测试

微软官方文档&#xff1a;快速入门&#xff1a;使用 ASP.NET Core 生成第一个 Orleans 应用 - .NET | Microsoft Learn 项目及引入的nuget库&#xff1a; 1、接口项目&#xff1b;2、接口实现项目&#xff1b;3、silo项目&#xff1b;4、客户端项目 其中Microsoft.Orleans.St…...

【Linux 25】网络套接字 socket 概念

文章目录 &#x1f308; 一、IP 地址概念⭐ 1. IP 地址的作用⭐ 2. 源 IP 地址和目的 IP 地址 &#x1f308; 二、端口号概念⭐ 1. 源端口号和目的端口号⭐ 2. 端口号范围划分⭐ 3. 端口号 VS 进程 ID⭐ 4. 套接字 socket 的概念 &#x1f308; 三、传输层的典型代表协议⭐ 1. …...

python openai 通过Function Call 创建自动化任务

目录 一、什么是Function Call(函数掉用) 1. 功能概述 2. 工作原理 二、如何实现函数调用 1、定义自己的get_weather 函数 2、给助手添加函数调用 3、写好instrction,指导assistant去掉用你定义的方法。 4、最后也是最重要的,捕获 Assistant 的 Function Call 三、…...

设计模式之责任链的通用实践思考

责任链模式通常一般用在方法的拦截、监控、统计方面&#xff0c;比较典型的就是Spring的AOP拦截。 但写一些小的基础能力框架的时候&#xff0c;用AOP比较中&#xff0c;所以一般都是自己针对特定的功能写一些定制的责任链工具类&#xff0c;不太喜欢总是做一些定制化的东西&am…...

前端用canvas绘图并支持下载

1.根据数据绘制饼图 /** 绘制环形图 */ const drawPieCharts () > {const {canWithdrawalPriceFront,noWithdrawalPriceFront,haveWithdrawalPriceFront,} this.state;const myCanvas this.cavasRef.current;// ts-ignoreconst ctx myCanvas.getContext(2d);if (ctx) {…...

【Mac】Homebrew

1、Homebrew 简介 官网地址&#xff1a;https://brew.sh Homebrew 是一款Mac OS平台下的软件包管理工具&#xff0c;拥有安装、卸载、更新、查看、搜索等很多实用的功能。 Homebrew 主要有四个部分组成: brew、homebrew-core 、homebrew-bottles、homebrew-cask。 源说明br…...

Python笔记之线程库threading

Python笔记之线程库threading 参考博文 Python多线程笔记——简单函数版和类实现版 code review! Python 的 threading 库用于在程序中创建和管理线程。线程允许程序并发执行多个任务。以下是 threading 库的详解和一些简洁示例。 基本概念 线程&#xff1a;在一个进程中&a…...

go 包管理

Go语言所依赖的所有的第三方库都放在GOPATH目录下面 gomodule是Go语言默认的依赖管理工具 Modules是相关Go包的集合&#xff0c;是源代码交换和版本控制的单元&#xff0c;用于指定使用哪些源文件 GO111MODULEoff禁用gomodule&#xff0c;编译时从GOPATH和vendor文件夹中查找包…...

Js内建对象

数组解构 const arr ["1","2","3"]let a,b,c// 解构赋值 //将数组的第一个元素赋值给第一个变量&#xff0c;第二个元素赋值给第二个变量&#xff0c;依次类推[a,b,c] arr console.log(a,b,c) // 1 2 3 // 声明变量同时解构 let [a,b,c] [&qu…...

AXI接口的实现逻辑和底层原理,在FPGA中如何实现AXI接口,一篇文章足以搞明白!!!

AXI&#xff08;Advanced eXtensible Interface&#xff09;接口是一个点对点的接口&#xff0c;用于连接高性能的片上系统&#xff08;SoC&#xff09;中的处理器、外围设备、内存和其他IP核。以下是对AXI接口的详细解析&#xff0c;包括FPGA实现的原理、逻辑、速度以及详细的…...

《GBDT 算法的原理推导》 11-12计算损失函数的负梯度 公式解析

本文是将文章《GBDT 算法的原理推导》中的公式单独拿出来做一个详细的解析&#xff0c;便于初学者更好的理解。 公式(11-12)是GBDT算法中非常关键的一步&#xff0c;它表示了如何通过计算损失函数的负梯度来指导下一棵树的生长。 公式(11-12)如下&#xff1a; r m i − [ ∂ …...

mysql设计

大家好&#xff0c;我是捡田螺的小男孩。 昨天一位粉丝&#xff0c;咨询了一个并发的问题~ 我提供了一个乐观锁兜底的方案&#xff0c;然后发现他们的表&#xff0c;都没有加version字段的,我想到&#xff0c;这不是表设计通用字段嘛。因此&#xff0c;本文跟大家聊聊&#xf…...

Android 斗鱼面经

Android 斗鱼面经 文章目录 Android 斗鱼面经一面二面 一面 先简单描述一下JVM JRE JDK的关系 :::info JVM&#xff08;Java Virtual Machine&#xff09; Java 虚拟机。它只认识 xxx.class 这种类型的文件&#xff0c;它能够将 class 文件中的字节码指令进行识别并调用操作…...

【机器学习】26. 聚类评估方法

聚类评估方法 1. Unsupervised Measure1.1. Method 1: measure cohesion and separationSilhouette coefficient Method 2&#xff1a;Correlation between two similarity matricesMethod 3&#xff1a;Visual Inspection of similarity matrix 2. Supervised measures3. 决定…...

linux 最多能创建多少个 TCP 连接?

linux 最大允许TCP连接数 约束一&#xff1a;服务器的端口范围约束二&#xff0c;服务器文件描述符限制约束三&#xff1a;系统线程约束四&#xff1a;系统内存总结 tcp连接四元组&#xff1a;源ip&#xff0c;源端口 <> 目标ip&#xff0c;目标端口 连续对同一个目标ip及…...

我为何要用wordpress搭建一个自己的独立博客

我在csdn有一个博客&#xff0c;这个博客是之前学习编程时建立的。 博客有哪些好处呢&#xff1f; 1&#xff0c;可以写自己的遇到的问题和如何解决的步骤 2&#xff0c;心得体会&#xff0c;经验&#xff0c;和踩坑 3&#xff0c;可以转载别人的好的技术知识 4&#xff0c;宝贵…...

Linux系统每日定时备份mysql数据

一、创建存储脚本的文件夹 创建文件夹&#xff0c;我的脚本放在/root/dbback/mysql mkdir ... cd /root/dbback/mysql 二、编写脚本 vi backup_mysql.sh 复制脚本内容 DB_USER"填写用户名" DB_PASSWORD"填写密码" DB_NAME"数据库名称" # …...

书生大模型第一关Linux基础知识

任务一&#xff1a;完成SSH连接与端口映射并运行hello_world.py 1.SSH及其端口映射 2.在VSCode中安装插件&#xff1a; 3.创建开发机 最后点击创建&#xff0c;然后可能需要等待一段较长的时间&#xff0c;大概需要5分钟左右&#xff0c;如果需要排队则更长时间 然后选择…...

机器学习之fetch_olivetti_faces人脸识别--基于Python实现

fetch_olivetti_faces 数据集下载 fetch_olivetti_faceshttps://github.com/jikechao/olivettifaces sklearn.datasets.fetch_olivetti_faces(*, data_homeNone, shuffleFalse, random_state0, download_if_missingTrue, return_X_yFalse, n_retries3, delay1.0)[source] L…...

【系统设计】深入理解HTTP缓存机制:从Read-Through缓存到HTTP缓存的交互流程

在现代Web开发中&#xff0c;缓存机制扮演着至关重要的角色。它不仅提升了用户体验&#xff0c;还极大地优化了资源的使用效率。在这篇博文中&#xff0c;我们将从“Read-Through”缓存的概念出发&#xff0c;深入探讨HTTP缓存的工作原理和交互流程&#xff0c;并详细描述max-a…...

Science Advances 上海理工大学与美国杜克大学(Duke University)共同开发了一种仿生复眼相机

编辑丨%科学家开发了一种 AI 辅助的仿生复眼相机。炎炎夏日&#xff0c;相信各位读者都有被蚊子骚扰过的恼火记忆。但往往想要清剿蚊子的时候&#xff0c;却被它灵巧地躲开&#xff0c;再难找到。诸如蚊子这种节肢动物的视觉系统已经进化了 5 亿多年&#xff0c;从寒武纪一直到…...

数据结构中无向图的邻接矩阵详解

在计算机科学的浩瀚宇宙中&#xff0c;数据结构无疑是那把开启高效编程大门的关键钥匙。对于计算机专业的大学生们来说&#xff0c;数据结构课程是专业学习路上的一座重要里程碑&#xff0c;而其中的图结构更是充满魅力与挑战&#xff0c;像一幅神秘的画卷等待我们去展开。今天…...

阻止H5页面中键盘收起的问题

在移动端H5开发中&#xff0c;当输入框失去焦点时&#xff0c;键盘会自动收起&#xff0c;但有时我们需要阻止这种行为。以下是几种解决方案&#xff1a; 常见原因 输入框失去焦点触发键盘收起页面滚动或触摸其他区域导致键盘收起某些浏览器(特别是iOS Safari)的默认行为 解…...

基于AOD-Net与GAN的深度学习去雾算法开发

基于AOD-Net与GAN的深度学习去雾算法开发 1. 引言 1.1 图像去雾研究意义 大气散射现象导致的图像质量退化对计算机视觉应用产生严重影响… 2. 理论基础 2.1 大气散射物理模型 经典模型描述为: I ( x ) = J ( x ) t ( x...

JS手写代码篇---手写节流函数

8、节流函数 什么是节流函数&#xff1f; 指规定一个单位时间&#xff0c;在这个单位时间内&#xff0c;只能有一次触发事件的回调函数执行&#xff0c;如果在同一个单位时间内某事件被触发多次&#xff0c;只有一次能生效。 与防抖函数有什么区别&#xff1f; 防抖函数是延…...

c/c++的opencv霍夫变换

OpenCV中的霍夫变换 (C/C) Hough Transform 霍夫变换 (Hough Transform) 是一种在图像分析中用于检测几何形状&#xff08;如直线、圆形等&#xff09;的特征提取技术。它通过一种投票机制在参数空间中寻找特定形状的实例。OpenCV 库为 C 开发者提供了强大且易用的霍夫变换函数…...

ansible template 文件中如果包含{{}} 等非ansible 变量处理

在 Ansible 模板中&#xff0c;如果你的 Python 脚本里有大量 {}、f""、或者其他 Jinja 会误解析的语法&#xff0c;就需要用 {% raw %}…{% endraw %} 把它们包起来&#xff0c;只在需要替换变量的那一行单独“放行”。例如&#xff1a; {% raw %} #!/usr/bin/env …...

07-后端Web实战(部门管理)

5. 修改部门 对于任何业务的修改功能来说&#xff0c;一般都会分为两步进行&#xff1a;查询回显、修改数据。 5.1 查询回显 5.1.1 需求 当我们点击 "编辑" 的时候&#xff0c;需要根据ID查询部门数据&#xff0c;然后用于页面回显展示。 5.1.2 接口描述 参照参照…...

Keepalived 配置 VIP 的核心步骤

Keepalived 配置 VIP 的核心步骤主要涉及安装软件、主备节点配置及服务管理。以下是具体操作指南: 一、安装 Keepalived ‌Ubuntu/Debian 系统‌ sudo apt update sudo apt install keepalived ‌CentOS/RHEL 系统‌ sudo yum install keepalived 注:需确保已配置 EPE…...

Vue+element-ui,实现表格渲染缩略图,鼠标悬浮缩略图放大,点击缩略图播放视频(一)

Vueelement-ui&#xff0c;实现表格渲染缩略图&#xff0c;鼠标悬浮缩略图放大&#xff0c;点击缩略图播放视频 前言整体代码预览图具体分析基础结构主要标签作用videoel-popover 前言 如标题&#xff0c;需要实现这样的业务 此处文章所实现的&#xff0c;是静态视频资源。 注…...