转载:TableView性能优化

news2024/11/16 21:56:10

转载:TableView性能优化

原文链接:https://juejin.cn/post/6955731915672387592

tableView性能优化

Cell重用、标识重用

使用 static 修饰重用标识名称能够保证这个标识只会创建一次,提高性能。接着调用dequeueReusableCellWithIdentifier:方法 获取缓存池中的Cell。如果没有就调用 initWithStyle:ReusIdentifier:方法 创建一个新的Cell。注意事先需要调用registerNib/registerClass方法TableView 注册一下重用标识。

动态高度

我们需要实现它的代理,来给出高度:

objectivec复制代码- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    // return xxx
}

这个代理方法实现后,上面的 rowHeight 的设置将会变成无效。在这个方法中,我们需要提高cell高度的计算效率,来节省时间。

自从iOS8之后有了 self-sizing cell的概念,cell可以自己算出高度,使用self-sizing cell需要满足以下三个条件:

(1)使用 Autolayout 进行 UI布局约束(要求cell.contentView的四条边都与内部元素有约束关系)。

(2)指定 TableViewestimatedRowHeight属性 的默认值。

(3)指定 TableView的rowHeight 属性为 UITableViewAutomaticDimension

ini复制代码- (void)viewDidload {
    self.myTableView.estimatedRowHeight = 44.0;
    self.myTableView.rowHeight = UITableViewAutomaticDimension;
}

除了提高cell高度的计算效率之外,对于已经计算出的高度,我们需要进行缓存,对于已经计算过的高度,没有必要进行计算第二次。

减少视图的数目

我们在 cell 上添加系统控件的时候,实际上系统都会调用底层的接口进行绘制,大量添加控件时,会消耗很大的资源并且也会影响渲染的性能。当使用默认的 UITableViewCell 并且在它的 ContentView 上面添加控件时会相当消耗性能。所以目前最佳的方法还是继承 UITableViewCell,并重写drawRect方法

重绘操作仍然在 drawRect方法 中完成,但是苹果不建议直接调用 drawRect方法,当然如果你强直直接调用此方法,当然是没有效果的。苹果要求我们调用UIView类中的 setNeedsDisplay方法,则程序会自动调用 drawRect方法 进行重绘。(调用 setNeedsDisplay 会自动调用 drawRect)。

使用hidden隐藏图层

避免动态添加图层。在初始化cell的时候一并将所有图层预先创建好,通过hidden属性控制子图层的显示或隐藏,因为单纯的显示操作要比创建快的多。

在快速滚动时考虑使用界面外壳

使用有外壳的界面.png

当用户快速滚动列表视图时,虽然使用了所有的优化,但视图的重用和渲染仍然需要超过 16 毫秒,还有可能出现偶发的丢帧现象,从而导致不流畅的体验。

在这些情况下,使用一个界面外壳是一个较好的选择,外壳可以被预先定义,它的唯一目的就是告诉终端用户这些部分即将展示一些数据。当滚动速度降低,并低于阈值时,刷新最终的视图并填充数据。

你可以使用与列表视图相关联的 panGestureRecognizer 属性获取速度值。

// 列表视图的速度
-(void)scrollViewDidScroll:(UIScrollView *)scrollView { 
    CGPoint velocity = [tableView.panGestureRecognizer 
                        velocityInView:self.view]; 
    self.velocity = velocity;
}
-(UITableViewCell *)tableView:(UITableView *)tableView 
    cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if(fabs(self.velocity.y) > 2000) { 
        //返回界面外壳
    } else {
        //返回真正的单元格 
    }
}

避免离屛渲染。

开启离屛渲染的代价就是需要新开辟一块新的缓冲区,在渲染的过程中还会多次的切换上下文,这些都是很消耗性能的。以下情况均会造成离屛渲染:

1. shadows(阴影)

其原因在于需要显示在所有layer内容的下方,因此必须被渲染在先。但此时阴影的本体(layer和其子layer)都还没有被组合到一起,只能另外申请一块内存,把本体内容都先画好,再根据渲染结果的形状,添加阴影到帧缓冲区frame buffer,最后把内容画上去。不过如果我们能够预先告诉CoreAnmation(通过 shadowPath属性 )阴影的几何形状,那么阴影当然可以先被独立渲染出来,不需要依赖layer本体,也就不再需要离屏渲染了。

2. 设置了组透明度为YES,并且透明度不为1的layer(不透明)

产生离屏渲染的条件是layer.opacity != 1.0并且有 子layer 或者背景图。alpha并不是分别应用在每一层之上,而是只有到 整个layer树 画完之后,再统一加上alpha,最后和底下其他layer的像素进行组合。显然也无法通过一次遍历就得到最终结果。

3. masks(遮罩)

我们知道mask是应用在layer和其所有子layer的组合之上的,而且可能带有透明度,那么其实和group opacity的原理类似,不得不在离屏渲染中完成。

4. cornerRadius+clipsToBounds

容器的子layer因为父容器有圆角,那么也会需要被裁剪,而这时它们还在渲染队列中排队,尚未被组合到一块画布上,自然也无法统一裁剪,不得已只能另开一块内存来操作。而如果只是设置cornerRadius,并不会触发离屏渲染。

5. shouldRasterize(光栅化)

如果layer不是静态的,我们更新已光栅化的layer,会造成大量的离屏渲染。 例如UITableViewCell因为复用的原因,重绘是很频繁的。如果此时设置了光栅化,会造成大量离屏渲染,降低性能。 不要过度使用,系统限制了缓存的大小为 2.5 * Screen Size。超出缓存之后,同样会造成大量的离屏渲染。 离屏渲染缓存内容有时间限制,被光栅化的图片(即缓存内容)如果超过100ms没有被使用,那么它就会丢弃,无法进行复用。(所以光栅化只能用在图像内容不变的前提下,且只对连续不断使用的图片进行缓存:用于避免静态内容的复杂特效的重绘,例如UIBlurEffect用于避免多个View嵌套的复杂View的重绘。)

6. edge antialiasing(抗锯齿)

分页加载数据,预先异步请求数据 - Prefetching API

viewDidLoad 中先请求网络数据来获取一些初始化数据,然后再利用 UITableViewPrefetching API 来对数据进行预加载,从而来实现数据的无缝加载。

UITableViewDataSourcePrefetching 协议
// this protocol can provide information about cells before they are displayed on screen.

@protocol UITableViewDataSourcePrefetching <NSObject>

@required

// indexPaths are ordered ascending by geometric distance from the table view
- (void)tableView:(UITableView *)tableView prefetchRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths;

@optional

// indexPaths that previously were considered as candidates for pre-fetching, but were not actually used; may be a subset of the previous call to -tableView:prefetchRowsAtIndexPaths:
- (void)tableView:(UITableView *)tableView cancelPrefetchingForRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths;

@end

第一个函数会基于当前滚动的方向和速度对接下来的 IndexPaths 进行 Prefetch,通常我们会在这里实现预加载数据的逻辑。

第二个函数是一个可选的方法,当用户快速滚动导致一些 Cell 不可见的时候,你可以通过这个方法来取消任何挂起的数据加载操作,有利于提高滚动性能, 在下面我会讲到。

实现这俩个函数的逻辑代码为:

swift复制代码extension ViewController: UITableViewDataSourcePrefetching {
    // 翻页请求
    func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
        let needFetch = indexPaths.contains { $0.row >= viewModel.currentCount}
        if needFetch {
            // 1.满足条件进行翻页请求
            indicatorView.startAnimating()
            viewModel.fetchImages()
        }
        
        for indexPath in indexPaths {
            if let _ = viewModel.loadingOperations[indexPath] {
                return
            }
            
            if let dataloader = viewModel.loadImage(at: indexPath.row) {
                print("在 \(indexPath.row) 行 对图片进行 prefetch ")
                // 2 对需要下载的图片进行预热
                viewModel.loadingQueue.addOperation(dataloader)
                // 3 将该下载线程加入到记录数组中以便根据索引查找
                viewModel.loadingOperations[indexPath] = dataloader
            }
        }
    }

    
    func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]){
        // 该行在不需要显示的时候,取消 prefetch ,避免造成资源浪费
        indexPaths.forEach {
            if let dataLoader = viewModel.loadingOperations[$0] {
                print("在 \($0.row) 行 cancelPrefetchingForRowsAt ")
                dataLoader.cancel()
                viewModel.loadingOperations.removeValue(forKey: $0)
            }
        }
    }
}

最后,再加上俩个有用的方法该功能就大功告成了:

   // 用于计算 tableview 加载新数据时需要 reload 的 cell
    func visibleIndexPathsToReload(intersecting indexPaths: [IndexPath]) -> [IndexPath] {
        let indexPathsForVisibleRows = tableView.indexPathsForVisibleRows ?? []
        let indexPathsIntersection = Set(indexPathsForVisibleRows).intersection(indexPaths)
        return Array(indexPathsIntersection)
    }
    
    // 用于确定该索引的行是否超出了目前收到数据的最大数量
    func isLoadingCell(for indexPath: IndexPath) -> Bool {
        return indexPath.row >= (viewModel.currentCount)
    }

滑动TableView时,按需加载内容

有些情况下我们可能会去快速的滑动列表,这时候其实会有大量的cell对象被创建、被重用,但其实我们可能只是去浏览列表停止的那一页的上下一定范围内的信息,前面快速划过的那些信息对我们来说都是无用的。此时我们可以通过ScrollView的代理方法

scrollViewWillEndDragging: withVelocity: targetContentoffset:

来按需加载内容。

#pragma mark - UIScrollViewDelegate  
//按需加载 - 如果目标行与当前行相差超过指定行数,只在目标滚动范围的前后指定3行加载。  
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {  

    NSIndexPath *targetPath = [_myTableView indexPathForRowAtPoint:CGPointMake(0, targetContentOffset->y)];  
    NSIndexPath *firstVisiblePath = [[_myTableView indexPathsForVisibleRows] firstObject];  
    NSInteger skipCount = 8;  
    if (labs(firstVisiblePath.row - targetPath.row)>  skipCount) {  
        NSArray *temp = [_myTableView indexPathsForRowsInRect:CGRectMake(0, targetContentOffset->y, _myTableView.frame.size.width, _myTableView.frame.size.height)];  
        NSMutableArray *arr = [NSMutableArray arrayWithArray:temp];  
        if (velocity.y<0) {  
            NSIndexPath *indexPath = [temp lastObject];  
            if (indexPath.row+33) {  
                [arr addObject:[NSIndexPath indexPathForRow:indexPath.row-3 inSection:0]];  
                [arr addObject:[NSIndexPath indexPathForRow:indexPath.row-2 inSection:0]];  
                [arr addObject:[NSIndexPath indexPathForRow:indexPath.row-1 inSection:0]];  
            }  
        }  
        [_dataList addObjectsFromArray:arr];  
    }  
}  

targetContentOffsetTableView 减速到停止的地方, velocity 表示速度向量。

如何避免滚动时的卡顿: 异步化UI,不要阻塞主线程

当你遇到滚动卡顿的应用程序时,通常是由于任务长时间运行阻碍了 UI 在主线程上的更新,想让主线程有空来响应这类更新事件,第一步就是要将消耗时间的任务交给子线程去执行,避免在获取数据时阻塞主线程。

苹果提供了很多为应用程序实现并发的方式,例如 GCD,我在这里对 Cell 上的图片进行异步加载使用的就是它。 代码如下:

swift复制代码class DataLoadOperation: Operation {
    var image: UIImage?
    var loadingCompleteHandle: ((UIImage?) -> ())?
    private var _image: ImageModel
    private let cachedImages = NSCache<NSURL, UIImage>()
    
    init(_ image: ImageModel) {
        _image = image
    }
    
    public final func image(url: NSURL) -> UIImage? {
        return cachedImages.object(forKey: url)
    }
    
    override func main() {
        if isCancelled {
            return
        }
        
        guard let url = _image.url else {
            return
        }
        downloadImageFrom(url) { (image) in
            DispatchQueue.main.async { [weak self] in
                guard let ss = self else { return }
                if ss.isCancelled { return }
                ss.image = image
                ss.loadingCompleteHandle?(ss.image)
            }
        }
        
    }
    
    // Returns the cached image if available, otherwise asynchronously loads and caches it.
    func downloadImageFrom(_ url: NSURL, completeHandler: @escaping (UIImage?) -> ()) {
        // Check for a cached image.
        if let cachedImage = image(url: url) {
            DispatchQueue.main.async {
                print("命中缓存")
                completeHandler(cachedImage)
            }
            return
        }
        
        URLSession.shared.dataTask(with: url as URL) { data, response, error in
            guard
                let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
                let mimeType = response?.mimeType, mimeType.hasPrefix("image"),
                let data = data, error == nil,
                let _image = UIImage(data: data)
                else { return }
            // Cache the image.
            self.cachedImages.setObject(_image, forKey: url, cost: data.count)
            completeHandler(_image)
            }.resume()
    }
}

在willDisplayCell:forRowAtIndexPath:代理方法中绑定数据

那具体如何使用呢!别急,听我娓娓道来,这里我再给大家一个小建议,大家都知道 UITableView 实例化 Cell 的方法是:tableView:cellForRowAtIndexPath: ,相信很多人都会在这个方法里面去进行数据绑定然后更新 UI,其实这样做是一种比较低效的行为,因为这个方法需要为每个 Cell 调用一次,它应该快速的执行并返回重用 Cell 的实例,不要在这里去执行数据绑定,因为目前在屏幕上还没有 Cell。我们可以在 tableView:willDisplayCell:forRowAtIndexPath: 这个方法中进行数据绑定,这个方法在显示cell之前会被调用。

为每个 Cell 执行下载任务的实现代码如下:

swift复制代码 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: "PreloadCellID") as? ProloadTableViewCell else {
            fatalError("Sorry, could not load cell")
        }
        
        if isLoadingCell(for: indexPath) {
            cell.updateUI(.none, orderNo: "\(indexPath.row)")
        }
        
        return cell
    }
    
    
    func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        // preheat image ,处理将要显示的图像
        guard let cell = cell as? ProloadTableViewCell else {
            return
        }

        // 图片下载完毕后更新 cell
        let updateCellClosure: (UIImage?) -> () = { [unowned self] (image) in
            cell.updateUI(image, orderNo: "\(indexPath.row)")
            viewModel.loadingOperations.removeValue(forKey: indexPath)
        }

        // 1. 首先判断是否已经存在创建好的下载线程
        if let dataLoader = viewModel.loadingOperations[indexPath] {
            if let image = dataLoader.image {
                // 1.1 若图片已经下载好,直接更新
                cell.updateUI(image, orderNo: "\(indexPath.row)")
            } else {
                // 1.2 若图片还未下载好,则等待图片下载完后更新 cell
                dataLoader.loadingCompleteHandle = updateCellClosure
            }
        } else {
            // 2. 没找到,则为指定的 url 创建一个新的下载线程
            print("在 \(indexPath.row) 行创建一个新的图片下载线程")
            if let dataloader = viewModel.loadImage(at: indexPath.row) {
                // 2.1 添加图片下载完毕后的回调
                dataloader.loadingCompleteHandle = updateCellClosure
                // 2.2 启动下载
                viewModel.loadingQueue.addOperation(dataloader)
                // 2.3 将该下载线程加入到记录数组中以便根据索引查找
                viewModel.loadingOperations[indexPath] = dataloader
            }
        }
    }

对预加载的图片进行异步下载(预热):

swift复制代码func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
        let needFetch = indexPaths.contains { $0.row >= viewModel.currentCount}
        if needFetch {
            // 1.满足条件进行翻页请求
            indicatorView.startAnimating()
            viewModel.fetchImages()
        }
        
        for indexPath in indexPaths {
            if let _ = viewModel.loadingOperations[indexPath] {
                return
            }
            
            if let dataloader = viewModel.loadImage(at: indexPath.row) {
                print("在 \(indexPath.row) 行 对图片进行 prefetch ")
                // 2 对需要下载的图片进行预热
                viewModel.loadingQueue.addOperation(dataloader)
                // 3 将该下载线程加入到记录数组中以便根据索引查找
                viewModel.loadingOperations[indexPath] = dataloader
            }
        }
    }

取消 Prefetch 时,cancel 任务,避免造成资源浪费

swift复制代码func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]){
        // 该行在不需要显示的时候,取消 prefetch ,避免造成资源浪费
        indexPaths.forEach {
            if let dataLoader = viewModel.loadingOperations[$0] {
                print("在 \($0.row) 行 cancelPrefetchingForRowsAt ")
                dataLoader.cancel()
                viewModel.loadingOperations.removeValue(forKey: $0)
            }
        }
    }

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux 使用 Anaconda+Uwsgi 部署 Django项目和前端项目

一、安装Anaconda 使用Anaconda创建python环境的优点&#xff1a; virtualenv只能创建系统原有的python版本&#xff0c;而不能创建创建任意版本的环境 而Anaconda的虚拟环境中&#xff0c;你可以指定任意现存可使用的python环境&#xff08;包括比原环境版本高的python版本&a…

产品入门第五讲:Axure交互和情境

目录 一.Axure交互和情境的介绍 1.交互介绍 概念 常见的Axure交互设计技巧 2.情境介绍 概念 常见的Axure情境设计技巧&#xff1a; 二.实例展示 1.ERP登录页到主页的跳转 2.ERP的菜单跳转到各个页面 &#x1f4da;&#x1f4da; &#x1f3c5;我是默&#xff0c;一个…

String类的hashCode()方法源码分析

Object类中的hashCode()方法&#xff1a; 同一个对象&#xff0c;hashCode必须相同&#xff1b;如果两个对象的equals相等&#xff0c;那么hashCode也必须要相等&#xff01;hashCode()方法是native本地方法&#xff0c;是C代码&#xff0c;hashCode的值&#xff0c;不一定是…

Web开发伴侣 Prepros 7.17 Crack

您友好的 Web 开发伙伴&#xff0c;Prepros 编译您的文件、转译您的 JavaScript、重新加载您的浏览器并 使开发变得非常容易测试您的网站&#xff0c;以便您可以专注于制作 他们完美。 编译一切 Prepros 可以编译 Sass、Less、Stylus、Pug/Jade、Haml、Slim、CoffeeScript 和 …

Linux 虚拟机复制后如何彻底修改ip共存

Linux那些事儿 1、复制 2、连接 3、cd /etc/sysconfig/network-scripts/ 4、ls -a 5、vi ifcfg-eth0 6、i 7、修改mac地址和ip地址&#xff0c;记住修改后的mac&#xff08;重要&#xff09; 8、关机 9、打开虚拟机设置此镜像&#xff1a;

Kubernetes与Docker:容器编排的未来

在当今快速变化的技术领域&#xff0c;容器化技术已经成为现代应用开发的核心。Docker 提供了一种轻量、可移植、自包含的容器化解决方案&#xff0c;而 Kubernetes&#xff08;简称K8s&#xff09;则崛起为容器编排的事实标准。本文将深入研究 Kubernetes 和 Docker 的关系&am…

武林风云之linux组软raid0

小y可喜欢玩文明系列的游戏了&#xff0c;因为小y也一直喜欢造轮子&#xff0c;属于自己的轮子。 每次小y听到”要向雄鹰一样&#xff0c;定要遨游于天际。”感觉自己给自己打了一针强心剂&#xff0c;要求自己拼搏进取。 众所周知&#xff0c;文明是个原生的linux游戏&#xf…

c++面经总结

C基础语法 C和c的区别 c中new和delete是对内存分配的运算符&#xff0c;取代了c中的malloc和free 标准c中的字符串类取代了标准c函数库头文件中的字符数组处理函数(c中没有字符串类型). 在c中&#xff0c;允许有相同的函数名&#xff0c;不过他们的参数类型不能完全相同&…

LeetCode:2415. 反转二叉树的奇数层(层次遍历 Java)

目录 2415. 反转二叉树的奇数层 题目描述&#xff1a; 实现代码与解析&#xff1a; BFS 原理思路&#xff1a; 2415. 反转二叉树的奇数层 题目描述&#xff1a; 给你一棵 完美 二叉树的根节点 root &#xff0c;请你反转这棵树中每个 奇数 层的节点值。 例如&#xff0c;…

三、Spring IoC 容器和核心概念

本章概要 组件和组件管理概念 什么是组件&#xff1f;我们的期待Spring充当组件管理角色&#xff08;IoC&#xff09;组件交给Spring管理优势 Spring IoC 容器和容器实现 普通和复杂容器SpringIoC 容器介绍SpringIoC 容器具体接口和实现类SpringIoC 容器管理配置方式 Spring I…

【Qt开发流程】之网络编程:`HTTP`和`FTP`的高级网络操作

概述 Qt Network模块提供了可以编写TCP/IP客户端和服务器的类。它提供了较低层次的类&#xff0c;如QTcpSocket、QTcpServer和QUdpSocket&#xff0c;来代表低层次网络概念&#xff0c;以及高级层次类&#xff0c;如QNetworkRequest、QNetworkReply和QNetworkAccessManager&am…

四十七、Redis分片集群

目录 一、分片集群结构 二、散列插槽 1、Redis如何判断某个key应该在哪个实例&#xff1f; 2、如何将同一类数据固定的保存在同一个Redis实例&#xff1f; 三、集群伸缩 四、故障转移 1、当集群中有一个master宕机时 &#xff08;1&#xff09;自动转移 &#xff08;2&…

Go delve调试工具的简单应用

Delve是个啥 Delve is a debugger for the Go programming language. The goal of the project is to provide a simple, full featured debugging tool for Go. Delve should be easy to invoke and easy to use. Chances are if you’re using a debugger, things aren’t go…

基于单片机的智能导盲杖设计 (论文+源码)

1. 系统设计 应用STC89C52单片机微处理器进行研究一种智能手杖系统&#xff0c;需要同时实现超声波自动测距、语音自动报警、距离自动显示、电机震动报警、LED指示灯灯光明灭自动提醒等多种功能&#xff0c;在手机通信提醒模式下手机用户可拨打固定手机电话信号实现手机通信提…

Helplook VS Salesforce:哪个知识库更好?

对于组织来说&#xff0c;选择一个合适的平台来管理在线知识库可能是一个具有挑战性的任务。而Salesforce的知识管理功能可以帮助组织更好地管理和分享他们的知识&#xff0c;从而更好地为客户提供服务。这是一种将知识管理集成到CRM平台中的方法&#xff0c;可以简化知识共享和…

IS-IS原理与配置3

IS-IS原理与配置 • IS-IS&#xff08;Intermediate System to Intermediate System&#xff0c;中间系统到中间系统&#xff09;是ISO &#xff08;International Organization for Standardization&#xff0c;国际标准化组织&#xff09;为它的CLNP &#xff08;ConnectionL…

【深度学习目标检测】六、基于深度学习的路标识别(python,目标检测,yolov8)

YOLOv8是一种物体检测算法&#xff0c;是YOLO系列算法的最新版本。 YOLO&#xff08;You Only Look Once&#xff09;是一种实时物体检测算法&#xff0c;其优势在于快速且准确的检测结果。YOLOv8在之前的版本基础上进行了一系列改进和优化&#xff0c;提高了检测速度和准确性。…

uni-app微信小程序隐藏左上角返回按钮

官方文档链接&#xff1a;uni.setNavigationBarTitle(OBJECT) | uni-app官网 (dcloud.net.cn) 首先要明确的是页面间的跳转方式有几种、每一种默认的作用是什么。 uniapp五种跳转方式 第一&#xff1a;wx.navigatorTo 【新页面打开&#xff0c;默认会有返回按钮】第二&#x…

ClickHouse Kafka 引擎教程

如果您刚开始并且第一次设置 Kafka 和 ClickHouse 需要帮助怎么办&#xff1f;这篇文章也许会提供下帮助。 我们将通过一个端到端示例&#xff0c;使用 Kafka 引擎将数据从 Kafka 主题加载到 ClickHouse 表中。我们还将展示如何重置偏移量和重新加载数据&#xff0c;以及如何更…

「构」向云端 - 我与 2023 亚马逊云科技 re:Invent 大会

授权声明&#xff1a;本篇文章授权活动官方亚马逊云科技文章转发、改写权&#xff0c;包括不限于在 亚马逊云科技开发者社区, 知乎&#xff0c;自媒体平台&#xff0c;第三方开发者媒体等亚马逊云科技官方渠道 2023年亚马逊AWS re:Invent大会宣布一项Amazon Q的创新项目&#x…