.Net Core 在使用IOC后,我们不必再浪费精力在管理实例的生命周期上,交给IOC代替我们管理,减少我们成吨的代码,面向接口编程更是灵活到了极致,而IOC的三种生命周期应该怎么去使用呢,Transient(瞬态)、Scoped(作用域)、Singleton(单例)。
Transient(瞬态)
这个没什么好说的,就是每次注入的时候,容器自动 new 一个实例,用完就丢;
Scoped(作用域)
以Web来说,作用域的生命周期就是当次请求,请求开始后的第一次注入,就是它生命的开始,直到请求结束;
我个人常用来减少数据获取,提升请求响应,举一个例子:A服务是获取全国地级市信息的,以作用域的方式注册到IOC容器中,B、C、D 都注入了A服务并使用了它;一个业务接口,刚好涉及到了B、C、D,当接口被调用,代码执行到了B,第一次调用了 A 服务请求数据库获取了全国地级市数据;然后执行到了C,又一次使用了A服务获取了数据,最后D;一个请求下来,A被使用了3次,获取了3个同样的数据结果,这完全是在浪费资源;
1 public class AService 2 { 3 public async CityData GetCityDataAsync() 4 { 5 return await GetDatasFromApi(); 6 } 7 } 8 9 public class BService 10 { 11 private readonly AService _aService; 12 13 public BService(AService aService) 14 { 15 _aService = aService; 16 } 17 18 public async Task Execute() 19 { 20 await _aService.GetCityDataAsync(); 21 } 22 } 23 24 public class CService 25 { 26 private readonly AService _aService; 27 28 public CService(AService aService) 29 { 30 _aService = aService; 31 } 32 33 public async Task Execute() 34 { 35 await _aService.GetCityDataAsync(); 36 } 37 } 38 39 public class DService 40 { 41 private readonly AService _aService; 42 43 public DService(AService aService) 44 { 45 _aService = aService; 46 } 47 48 public async Task Execute() 49 { 50 await _aService.GetCityDataAsync(); 51 } 52 }
那我们应该怎么做呢,首先我们回顾一下 Scoped 的生命周期,可以说是一个实例,贯穿了整一个请求,那我们是不是可以定义一个变量,请求数据前先判断这个变量有没有值,没有就去获取数据,给它赋值,下次再调用的时候,直接返回这个变量的数据,这样不管这个服务被调用多少次,它也只是调用了一次数据库,大大节省了资源。
1 public class AService 2 { 3 private List<CityData> cityDatas; 4 5 public async List<CityData> GetCityDataAsync() 6 { 7 if(cityDatas == null|| !cityDatas.Any()) 8 { 9 cityDatas = await GetDatasFromApi(); 10 } 11 12 return cityDatas; 13 } 14 }
有人可能说会说了,不就是多调用几次数据库,现在服务器的性能这么好,不在乎这一点的资源;确实,如果只是多调用几次数据库,对于一些小系统来说,跟挠痒痒一样,那这里的调用数据库换成调用 WebApi 呢?如果还是调用第三方的 API 呢?一次Http请求,不往大的说,从请求到获取到数据,花个100ms很正常吧(网络非常好的情况当我没说),那这个接口不需要多,调用10次就1s了,还没算上其它业务逻辑的耗时呢,如果还需要调用其它的api,那响应时间就更长咯,难道你让用户打开一个页面,或者点击一个按钮,需要等上两三秒才有响应吗,这样的用户体验就非常糟糕了。
Singleton(单例)
来自依赖关系注入容器的服务实现的每一个后续请求都使用同一个实例。 如果应用需要单一实例行为,则允许服务容器管理服务的生存期。必须是线程安全的,并且通常在无状态服务中使用。
在单例中,不要直接注入作用域的服务,这会引起很多莫名其妙的错误,一定要使用的话,就自己创建,自己管理它的生命周期:
public class Scope { private readonly IServiceScopeFactory _serviceScopeFactory; public Scope(IServiceScopeFactory serviceScopeFactory) { _serviceScopeFactory = serviceScopeFactory; } public async Task CreateScope() { using var scope = _serviceScopeFactory.CreateScope(); var service = scope.ServiceProvider.GetRequiredService<IService>(); } }
ActivatorUtilities
有些情况下,例如当你不想把使用次数极低的类注册到容器中,或者这个类的构造函数需要传入一些参数,但是又需要用到容器中的服务的时候,你可以使用 ActivatorUtilities 中的 CreateInstance 去创建它,它会自动给构造函数注入所需的服务,并且还可以给构造函数传参,满足上面所说情况的需求。
1 public class TestTask : ITask 2 { 3 private readonly IServiceScopeFactory _serviceScopeFactory; 4 5 private readonly IRabbitMQService _rabbitMQService; 6 7 private readonly string _name; 8 9 public TestTask(IServiceScopeFactory serviceScopeFactory, IRabbitMQService rabbitMQService, string name) 10 { 11 _serviceScopeFactory = serviceScopeFactory; 12 _rabbitMQService = rabbitMQService; 13 _name = name; 14 } 15 16 /// <summary> 17 /// 18 /// </summary> 19 /// <param name="cancellationToken"></param> 20 /// <returns></returns> 21 /// <exception cref="NotImplementedException"></exception> 22 public async Task Execute(CancellationToken cancellationToken) 23 { 24 try 25 { 26 using var scope = _serviceScopeFactory.CreateScope(); 27 28 var executionService = scope.ServiceProvider.GetService<ITaskExecutionService>(); 29 30 if (executionService != null) 31 { 32 await _rabbitMQService.RabbitMQReceiveService.SingleAsync(executionService.ExecuteAsync); 33 } 34 } 35 catch (Exception err) 36 { 37 38 Console.WriteLine(err.Message); 39 } 40 } 41 }
使用 ActivatorUtilities 创建:
1 var testTask = ActivatorUtilities.CreateInstance<TestTask>(serviceProvider, "test"); 2 3 await testTask.Execute(new CancellationToken());
写在最后
star流程:
2、点击star,如下图,即可完成star,关注项目不迷路:
前提条件
1.系统、内核
CentOS7 要求64位系统、内核版本3.10以上
CentOS6 要求版本在6.5以上,系统64位、内核版本2.6.32-431以上
查看内核版本号
uname -r #查看内核版本 cat /etc/os-release #查看系统信息
2.开启centos-extras资源库
使用Centos 7,开启centos-extras
资源库
centos-extras
这个yum资源库默认是开启的,如果禁用了,请打开
推荐使用Overlay2作为存储驱动
卸载旧版本Docker容器
$ sudo yum remove docker \ docker-client \ docker-client-latest \ docker-common \ docker-latest \ docker-latest-logrotate \ docker-logrotate \ docker-engine
删除旧版本Docker文件
sudo rm /var/lib/docker/ -rf
Centos 7在线安装Docker-CE
本节适用于可访问网络的情况
设置yum仓库
安装必要依赖包
$ sudo yum install -y yum-utils \ device-mapper-persistent-data \ lvm2
添加阿里镜像稳定版仓库
$ sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
添加阿里源时有时会报错,如果报错,使用如下命令使用官方源
#删除异常源 sudo rm -f /etc/yum.repos.d/docker-ce.repo #使用官方源 sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
更新yum缓存
sudo yum makecache fast
安装Docker-CE
注意事项:本步骤分两部分,仅需按需求使用其一
1 安装最新版
sudo yum install -y docker-ce docker-ce-cli containerd.io
2 安装指定版本
列出可用版本
$ yum list docker-ce --showduplicates | sort -r docker-ce.x86_64 3:18.09.1-3.el7 docker-ce-stable docker-ce.x86_64 3:18.09.0-3.el7 docker-ce-stable docker-ce.x86_64 18.06.1.ce-3.el7 docker-ce-stable docker-ce.x86_64 18.06.0.ce-3.el7 docker-ce-stable
安装指定版本
<VERSION_STRING>需要替换为第二列的版本号,如:18.06.0.ce-3.el7
$ sudo yum install docker-ce-<VERSION_STRING> docker-ce-cli-<VERSION_STRING> containerd.io
启动Docker服务
sudo systemctl start docker
Centos 7离线安装Docker-CE
本节仅适用于无外网环境下
下载RPM包
官方提供的离线包主要需要下载
-
containerd.io-<VERSION_STRING>.x86_64.rpm
-
docker-ce-<VERSION_STRING>.x86_64.rpm
-
docker-ce-cli-<VERSION_STRING>.x86_64.rpm
注意事项:
在保证大版本相同下,
docker-ce
与docker-ce-cli
版本要尽量相同不要使用
beta
与rc
版本号带
.centos
与不带基本没什么区别
注:未提到的docker-ce-selinux
安装包在新版本中已经被废弃了,需要下载另外的一个依赖包container-selinux
安装RPM包
官方方式
使用rpm按顺序安装依赖包 或 sudo yum /path/to/package.rpm
,以下为举例
$ sudo rpm -ivh containerd.io-1.2.5-3.1.el7.x86_64.rpm $ sudo rpm -ivh docker-ce-cli-18.09.6-3.el7.x86_64.rpm $ sudo rpm -ivh container-selinux-2.95-2.el7_6.noarch.rpm $ sudo rpm -ivh docker-ce-18.09.6-3.el7.x86_64.rpm
以上均安装完成即可,以下是解决问题的思路离线安装解决问题这部分参考
官方安装方式可能出现的问题
1、在安装以上rpm包的时候,如果出现缺少依赖的错误,比如:
error: Failed dependencies: policycoreutils >= 2.5-11 is needed by container-selinux-2:2.95-2.el7_6.noarch selinux-policy >= 3.13.1-216.el7 is needed by container-selinux-2:2.95-2.el7_6.noarch selinux-policy-base >= 3.13.1-216.el7 is needed by container-selinux-2:2.95-2.el7_6.noarch selinux-policy-targeted >= 3.13.1-216.el7 is needed by container-selinux-2:2.95-2.el7_6.noarch
出现 “>=版本号”:说明你的系统上已经安装了这些包,只是这些包不是最新的,需要升级以policycoreutils
包为例,可以在Packages for Linux and Unix - pkgs.org上搜索对应的最新的rpm包,执行以下命令进行升级:
$ sudo rpm -Uvh policycoreutils-2.5-29.el7.x86_64.rpm
如果对应的这个包被其它包依赖(policycoreutils
被policycoreutils-python
包依赖),这时会出现错误提示:
error: Failed dependencies: policycoreutils = 2.5-8.el7 is needed by (installed) policycoreutils-python-2.5-8.el7.x86_64
解决办法:先以不依赖的方式安装policycoreutils,再下载新的policycoreutils-python包升级。
$ sudo rpm -Uvh policycoreutils-2.5-29.el7.x86_64.rpm --nodeps $ sudo rpm -Uvh policycoreutils-python-2.5-29.el7.x86_64.rpm
其它类似错误,参考以上方法即可。
2、如果出现的是:
error: Failed dependencies: xxxxxx is needed by xxxxxxxxxxxxxxxxxx
直接下载对应的包安装即可。
启动Docker服务
$ sudo systemctl start docker
安装后配置
添加国内镜像
只有内网的情况下可以不配置
国内镜像很多,如阿里云,网易蜂巢,DaoCloud,Docker中国区官方镜像等,这些都可以。
这里选择阿里的镜像:https://vot9n4p2.mirror.aliyuncs.com
,当然这是我自己免费申请的
sudo vim /etc/docker/daemon.json
,添加如下内容,注意替换镜像地址url
{ "registry-mirrors": ["https://vot9n4p2.mirror.aliyuncs.com"] }
修改Docker根目录
推荐配置
Docker默认安装在/var/lib/docker
目录下,一般而言,如果我们的服务器没有把/var
目录从/
分离出来,那么就会占用/
目录的空间,一般根目录设置都不会很大,镜像、容器等又比较占硬盘,推荐修改到大容量目录
sudo vim /etc/docker/daemon.json
,添加"data-root"
指定docker安装根目录,注意替换/path/to/path
{ ...省略其它配置..., "data-root": "/path/to/path" }
容器日志配置
推荐配置
当容器日志量变大,长期不处理的话,日志只会越来越多,占用空间不说,docker logs -f 容器
时,日志滚动很久才会到最新的日志
我们通过设置修改单个日志配置文件的最大值与当日志到达最大值后保留的日志文件数
sudo vim /etc/docker/daemon.json
{ ...省略其它配置..., "log-driver": "json-file", "log-opts": {"max-size":"50m", "max-file":"1"} }
日志量尽量设置得少一些,50m其实也很大,屏幕也要刷好久,最好在测试与生产环境将输出到控制台的日志写到文件或ELK中
添加自建私有仓库支持
如果没有可以不配置
有时我们需要将自己公司的docker镜像上传到自建私有仓库来分发镜像
一般而言,我们不会为私有仓库添加https支持,docker官方又要求需要使用https才能login\pull\push等操作
官方给出的解决办法是添加insecure-registries
,可同时添加多个
sudo vim /etc/docker/daemon.json
{ ...省略其它配置..., "insecure-registries": ["10.2.7.70:5000"] }
添加用户到docker组
只用root用户可以不配置
非root用户使用docker
命令需要加sudo
前缀,这是可以通过配置避免的
您现在应该考虑将用户添加到“docker”组
$ sudo usermod -aG docker your-user-name $ newgrp docker
重启使Docker配置生效
所有配置修改都需要重启docker服务才能生效
systemctl daemon-reload systemctl restart docker
通过docker info
查看配置是否生效
卸载Docker
使用yum包管理方式
$ sudo yum remove docker-ce
删除docker安装目录
删除docker根目录为危险操作,会删除docker容器、镜像、数据,请谨慎操作
$ sudo rm -rf /var/lib/docker #默认安装目录
通过docker info | grep "Docker Root Dir"
查看安装目录,并删除此目录
今天做了一个案例,可以好好做做能够将之前的内容结合起来,最主要的是能对组件化编码流程有一个大概的清晰认知,这一套做下来,明天自己再做一遍复习一下,其实组件化流程倒是基本上没什么问题了,主要是很多vue的方法需要多熟悉一下,毕竟打破了之前的一些对于传统js的认知,还需要多熟悉一下。
这两天可能内容不是很多,因为有点感冒了,状态不是很好,不想学多了怕接受的是不是很好。
六.TODOList案例
做这个案例主要是为了能够熟悉组件化编码流程,刚开始学做一个项目最好按照以下三个步骤来
1.实现静态组件
先把一个项目抽取组件,使用组件实现静态页面
比如这个案例App的子组件就可以拆分为三个上面输入框,中间列表展示,下面总结栏,其实组件的划分就是按照他的功能点来划分的,
比如子组件按照功能划分了三个组件,list里每一个item是不是有自己的功能可以勾选,可以删除,所以又可以进一步细分组件(如果你拆完一个组件发现很难起名字,说明你拆的不是很合理)
- 既然已经拆分好组件了,就可以去vue项目里面写我们的组件了,创建、导入、注册三步曲完成
-
接下来应该有我们的静态模板了,是这样的,一般情况下我们的一个项目大多数已经完成了一些了,所以不会从零开始,这个时候老板派给你个任务叫你把这个实现组件化开发,你就直接把html先一股脑的塞到 App.vue里面,可以启动服务器看到整体的一个效果,然后再去一步一步把代码拆分进我们的组件里面去
注意:有很多问题是语法报错,将那个lintonsave关闭即可
-
然后就可以开始拆分了,其他不说这个list里面每一条数据看出了拆分组件的重要性,我直接在list组件复用item就可以了
-
css拆分时要注意:base等公共样式放在App.vue,其他各自的样式各回各家但是要注意添加scoped
2.展示动态数据
将我们项目中需要动态真实的数据存起来 一般是数组加对象的的形式,一个对象一条数据里面有id、name等等
那么数据一般保存在哪里? 回顾一下之前props的案例,我们的数据是不是写在父组件里面的,通过子组件标签传给了他,子组件才能用props来接受外部传来的数据,所以我们的数据要定义在每条数据这个组件的父组件
在我们定义数据这里几个注意点:
- id为什么用字符串,因为数值型会有上限,字符串没有
- 既然数据都有了,所以这里就不用一个一个去写子组件标签了直接v-for列表渲染
- 最关键的:我们要将数据传到子组件里面去吧,这里不再是像以前那样,一个一个传,这里直接是传的一个对象,而且还是遍历出来的每一条对象,还必须要动态绑定,不动态绑定这里就是一个字符串
子组件这边,注意一下props接受参数要用引号包裹,然后就是怎么来让我们的复选框动态的获取到有没有勾选,让其动态绑定值为我们数据里面的值即可
3.添加
首先我们实现回车添加数据的逻辑,肯定是要生成一个对象然后添加进那个数组里面,这里的id由于灭有数据库支持,就采用了一个包,uuid可以生成全球唯一id,但是他体积表较大,这里用的是另一个体积小一点的 nanoid
接下来问题就来了,我们这个数据是在header里面添加的,而我们的数据是在list,我们学过父组件给子组件传数据(props),但是没有学过兄弟关系来传数据吧,这里才去最基础的方法来实现
我们吧list里面定义的数据data放到app组件里面去,然后app通过动态绑定将数据传到list的组件标签,这个时候list用props来接受传过来的值
最主要的是怎么把子组件header传到父组件app里面去:我们在app定义一个函数然后接受形参,将这个函数动态绑定给到header,header通过props就能够得到这个函数,然后重要的一步 我将这个函数写在键盘事件里面,同时参数是我们的创建的数据,就相当于调用了这个函数,调用不要紧,关键是这个函数式app送给你用的,你调用(送给你用的所以拿过来是存在于vc实例对象上)拿我app不就拿到了这个参数吗所以,app现在又有参数,又定义了数据,那么添加的逻辑就开始了
4.勾选
我们要在item里面来勾选,从而影响到数据里面的completed这个选项,我们的数据是定义在App里面的,有一句话叫做 数据在哪里,那么处理数据的方法就写在哪里,所以这里好像又要用到子组件给父组件东西了,给什么?我勾选的状态实在item里进行的,我必须拿到勾选的这一个id,才能在处理数据的方法里面以id为参数对数据记性遍历找到对应的那一个将他的completed的值改为非值
这里有一个注意点,把我们之前这个勾选的所有逻辑推翻,直接在item的input标签里面写一个v-model,我们说复选框v-model默认收集的是checked的值,如果我把v-model设置为todo.completed是不是就让我们的checked动态绑定了,他为true,checked就为true,第一步初始化数据的勾选完成了,这里还有一个更重要的,你该数据也同样可以改到data里面的数据,因为v-model本身就是双向绑定,但是我们前面也说过,props接收到的数据不能改,这里很明显是通过props传过来的数据改到的,为什么这里没报错呢,因为vue对于对象改动的检测有点类似于 const,一般数据确实数据变了就是改动了,但是数组和对象通过属性名或者下标去改某一个值并不叫改动,要变动整个数组和对象才叫改动,所以这里没有报错,这个方法是简单但是,官方定义的props传过来的数据不能改,最好还是不要改,万一哪天这个数据不是对象里的值了,就麻烦了,最好还是按照语法规则来
5.删除
主题逻辑刚上面差不多,点击删除返回id,app传过来一个函数接受这个id,然后数组的筛选方法filter让我们的数据等于筛选出来的新数组,这里就不考虑性能问题了,假装是个标记删除吧,删除了虽然还在但是也永远不会给你显示出来了,所以可以直接把他赋值给这个原数据,还有一个点confirm确认框,会弹出一个对话框内容为你传进去的参数,配合if来使用可以达到只有你点击确定了才会执行后面的逻辑
6.底部统计
既然是统计,那肯定要用计算节点来做,这里用到了数组的reduce方法,要注意的是,prev每次返回要等于他自己
7.底部交互
先完成我们点击了所有的勾选框,下面全选会勾上,但凡少勾选一个都不会钩上的逻辑,其实很简单,将我们全部的这个数量也弄成一个计算属性,然后将我们全选动态绑定,且返回值也由计算属性来决定,直接去比较已完成和全部是否相等
探后就是我们的全选功能,逻辑是这样,点击一下,通过e获取他的checked的值,然后又要动到数据了,需要app里面对数组做一个循环对所有的数据的completed的值都等于这个checked的值
但是这样有一个bug,当我们删完后,会是勾选状态,应该为不勾选并且隐藏掉
在我们刚才完成点击点击上面,下面全选也会对应去勾选上那里的逻辑完善一下,因为原来的写法,就是当已完成和总数一样的时候就会勾选上,那么最后删完了,也是一样的所以不行
通过v-if添加在footer即可
好了,可以忘掉这一章节实现的功能了,简便方法他来了
我们这里的全选框有些啥子东西,一个管初始化读取数据的,一个改数据的,又是input,读写,想到他没有v-model
我们用v-model去绑定isAll的值也就是判断有没有全选的值,即可完成读的操作,仔细想想是不是
但是这里改的话会报错,为什么?
因为我们的isAll是计算属性,你改动他的值,你setter都没有怎么改,所以这里要这么做,定义一个setter,并且把这个值给到我们刚才定义的循环让所有值都等于这个checked的App组件上的函数
为什么这里可以使用v-model前面是因为那个值时props传过来的值肯定不能改,而这里是我们写在这个组件的计算属性属于自己的
8.清除已完成的任务
最后一个功能,很简单直接一个点击事件,在app这边做一个数组筛选,筛选的是completed没被选中的
9.总结
组件化编码流程
- 拆分静态组件,按照功能拆分
- 实现动态组件,考虑好数据的位置,如果数据是一个组件在用就放到这个组件,如果数据是一些组件在用,就放到他们的共同的父组件(这种方法也叫做状态提升)
- 实现交互(从绑定事件开始)
- props不仅适合父给子传数据,还可以子给父传数据(父会给子一个函数)
- v-model注意修改的值是不是props过来的,不是他过来的还是可以用的还是很方便的
- 修改对象里面的值跟const一样改单个值不会被发现,要修改整个对象
-
基于svelte3.0自定义pc端虚拟滚动条组件svelteScrollbar。
svelte-scrollbar:运用svelte3.x创建的桌面pc版自定义美化滚动条组件。支持是否原生滚动条、自动隐藏、水平+垂直滚动(滚轮滑动)、自定义滚动条大小、背景色、间距及动态实时更新等功能。
svelteScrollbar功能及效果有些类似elementUI组件库中的el-scrollbar组件。
◆ 引入使用
在需要使用虚拟滚动条的页面引入组件。
import Scrollbar from '$lib/Scrollbar'
◆ 快速使用
使用 <Scrollbar></Scrollbar> 包住的内容,即可快速生成一个虚拟滚动条组件。
<!-- //原生滚动条 --> <Scrollbar native> <div>自定义内容信息。</div> </Scrollbar> <!-- //自动隐藏滚动条 --> <Scrollbar autohide={true}> <div>自定义内容信息。</div> </Scrollbar> <!-- //水平滚动条(支持滚轮滑动) --> <Scrollbar mousewheel> <div>自定义内容信息。</div> </Scrollbar> <!-- //自定义高度/最大高度 --> <Scrollbar height="200" maxHeight="350"> <div>自定义内容信息。</div> </Scrollbar> <!-- //自定义大小/间隙/颜色 --> <Scrollbar size="10px" gap="5" color="#09f"> <div>自定义内容信息。</div> </Scrollbar>
◆ 实现过程
svelteScrollbar支持如下自定义参数配置。
<script> // 是否开启原生滚动条 export let native = false // 是否自动隐藏滚动条 export let autohide = false // 滚动条尺寸 export let size = undefined // 滚动条颜色 export let color = '' // 滚动条层叠 export let zIndex = null // 滚动条区域高度 export let height = undefined // 滚动条区域最大高度 export let maxHeight = undefined // 滚动条间隙 export let gap = 0 // 是否开启水平滚轮滚动控制 export let mousewheel = false ... </script>
组件模板及js逻辑处理部分。
<div class="vui__scrollbar" bind:this={el} on:mouseenter={handleMouseEnter} on:mouseleave={handleMouseLeave}> <div class="vscroll__wrap" class:hidenative={!bool(native)} bind:this={wrap} on:scroll={handleScroll} on:mousewheel={handleMouseWheel} style="{wrapStyle}"> <slot /> </div> <div class="vscroll__bar vertical" class:ishide={!data.isShowBar} on:mousedown={e => handleClickTrack(e, 0)} > <div class="vscroll__thumb" bind:this={barY} style="background: {color}; height: {data.barHeight}px; width: {addUnit(size)}" on:mousedown={e => handleDragThumb(e, 0)}></div> </div> <div class="vscroll__bar horizontal" class:ishide={!data.isShowBar} on:mousedown={e => handleClickTrack(e, 1)}> <div class="vscroll__thumb" bind:this={barX} style="background: {color}; width: {data.barWidth}px; height: {addUnit(size)}" on:mousedown={e => handleDragThumb(e, 1)}></div> </div> </div> <script> /** * @Desc svelte3.x桌面端虚拟滚动条组件SvelteScrollbar * @Time andy by 2022-05 * @About Q:282310962 wx:xy190310 */ // ... import { onMount, afterUpdate, createEventDispatcher, tick } from 'svelte' const dispatch = createEventDispatcher() import util from './util' $: data = { // 滚动条宽度 barWidth: 0, // 滚动条高度 barHeight: 0, // 滚动条水平偏移率 ratioX: 1, // 滚动条垂直偏移率 ratioY: 1, // 鼠标是否按住滚动条 isTaped: false, // 鼠标是否悬停于滚动区域 isHover: false, // 显示滚动条 isShowBar: !bool(autohide) } const bool = (boolean) => JSON.parse(boolean) ? true : false const addUnit = (val) => val ? parseInt(val) + 'px' : null let observeTimer = null let c = {} // 滚动条对象 let el let wrap let barX let barY $: wrapStyle = `height: ${addUnit(height)}; max-height: ${addUnit(maxHeight)}` $: GAP = addUnit(gap) onMount(() => { console.log('监听滚动条开启...') updated() let observer = new MutationObserver(mutation => { updated() }) observer.observe(wrap, { attributes: true, childList: true, subtree: true, attributeFilter: [ 'style', 'class' ] }) window.addEventListener('resize', util.throttle(updated))