IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> 【react】列表置顶操作导致滚动问题的分析和解决,react dom-diff右移策略,overflowAnchor: ‘none‘遇到滚动锚定问题并且需要关闭行为 -> 正文阅读

[JavaScript知识库]【react】列表置顶操作导致滚动问题的分析和解决,react dom-diff右移策略,overflowAnchor: ‘none‘遇到滚动锚定问题并且需要关闭行为

需求分析

消息列表里面会有一些内容需要置顶,但是点击置顶后,列表直接滚到顶部不利于消息的批量操作。

复现效果

在这里插入图片描述

原因分析

区别参考于这篇文章https://www.cnblogs.com/yadiblogs/p/11129206.html

react 与 vue的diff区别
判断2个节点是否相同:vue认为className不一样,就不同,react则认为相同,只是属性不同,只需要更新其属性。
同一层级对比:Vue从两端至中间对比,react从左至右对比。react策略存在短板,如果一个集合只把最后一个移到第一个,react会移动前面所有节点,vue只移动最后一个节点到最前面。

key 具体是如何起作用的和为什么不能用 index 作为 key 值参考于这篇文章
https://bbs.huaweicloud.com/blogs/297739
为什么不能用 index 作为 key 值呢?
index 作为 key ,如果我们删除了一个节点,那么数组的后一项可能会前移,这个时候移动的节点和删除的节点就是相同的 key ,在react中,如果 key 相同,就会视为相同的组件,但这两个组件是不同的,这样就会出现很麻烦的事情,例如:序号和文本不对应等问题

所以一定要保证 key 的唯一性

那 key 具体是如何起作用的呢?
在这里插入图片描述

首先在 React 中只允许节点右移

因此对于上图中的转化,只会进行 A,C 的移动

则只需要对移动的节点进行更新渲染,不移动的则不需要更新渲染

解决方案

getSnapshotBeforeUpdate() 方法在最近一次渲染输出(提交到 DOM 节点)之前调用。

在 getSnapshotBeforeUpdate() 方法中,我们可以访问更新前的 props 和 state。

getSnapshotBeforeUpdate() 方法需要与 componentDidUpdate() 方法一起使用,否则会出现错误。

 // // 获取更新之前的快照
    // getSnapshotBeforeUpdate(prevProps, prevState) {
    //       // let top =  document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop;
    //       let top = document.querySelector('main').scrollTop;
    //       // console.log('list.scrollHeight - list.scrollTop;', document.body.scrollHeight, top);
    //       if (top !== 0) {
    //         // return document.body.scrollHeight - top;
    //         return top;

    //       }
    //       return null
    //   }
    
    //   componentDidUpdate(prevProps, prevState, snapshot) {
    //     console.log('snapshot', snapshot);
    //     //(这里的 snapshot 是 getSnapshotBeforeUpdate 的返回值)
    //    if(snapshot !== null) {
    //     const rootEle = document.querySelector('main');
    //     rootEle.scrollTop = this.state.top;
    //     this.listRef.current.scrollTop = snapshot
    //    }
    //   }

不足与补充

getSnapshotBeforeUpdate() 与 componentDidUpdate() 方法一起使用只能解决比较简单组件的场景,组件稍微复杂会有明显回弹效果是不佳的。问题根源不在于diff,diff只是个更新的手段和过程。最终都是要在页面上更新掉那些东西,滚动与否,不是diff和react控制的,是chrome控制的。

{/* <main style={{height: '900px', overflow: 'auto', overflowAnchor: 'none'}}  ref={this.listRef}> */}

overflowAnchor: ‘none’
CSS 属性提供一种退出浏览器滚动锚定行为的方法,该行为会调整滚动位置以最大程度地减少内容偏移。

默认情况下,在任何支持滚动锚定行为的浏览器中都将其启用。因此,仅当您在文档或文档的一部分中遇到滚动锚定问题并且需要关闭行为时,才通常需要更改此属性的值。

通过查阅 MDN 发现适配上不支持safari,也发现的确源码在safari上不会出现滚动bug。

源码



class List1 extends React.Component {
    constructor(props) {
        super(props)
        this.listRef= React.createRef();
    }
    state= {
      list: [],
    };
    
    componentDidMount() {
      const list = this.state.list.slice();
      for (let i = 0; i < 100  ; i++) {
        list.push({
          id: i,
          name: i.toString(),
        });
      }
      this.setState({ list });
    }
    // // 获取更新之前的快照
    // getSnapshotBeforeUpdate(prevProps, prevState) {
    //       // let top =  document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop;
    //       let top = document.querySelector('main').scrollTop;
    //       // console.log('list.scrollHeight - list.scrollTop;', document.body.scrollHeight, top);
    //       if (top !== 0) {
    //         // return document.body.scrollHeight - top;
    //         return top;

    //       }
    //       return null
    //   }
    
    //   componentDidUpdate(prevProps, prevState, snapshot) {
    //     console.log('snapshot', snapshot);
    //     //(这里的 snapshot 是 getSnapshotBeforeUpdate 的返回值)
    //    if(snapshot !== null) {
    //     const rootEle = document.querySelector('main');
    //     rootEle.scrollTop = this.state.top;
    //     this.listRef.current.scrollTop = snapshot
    //    }
    //   }
  
    /**
     * 将列表中的项目置顶
     * @param index 要置顶的索引
     */
    toTop(index, e) {
      const list = this.state.list;
      const item = list[index];
      list.splice(index, 1);
      list.unshift(item);
      this.setState({ list });
    }
    render() {
      return (
        <div>
          {/* <main style={{height: '900px', overflow: 'auto', overflowAnchor: 'none'}}  ref={this.listRef}> */}
          <main style={{height: '900px', overflow: 'auto'}}  ref={this.listRef}>

          <hr />
          <ul  className='list' id='mylist'>
          <p ></p>

            {this.state.list.map((item, index) => {
              return (
                <li key={item.id}>
                  <span>{item.name}</span>
                  <span>
                    <button onClick={e => this.toTop(index, e)}>置顶</button>
                  </span>
                </li>
              );
            })}
          </ul>
          <hr />
          </main>
        </div>
      );
    }
  }


export default class App extends Component {
    render() {
        return (
            <div  >
              
               <div style={{height: '20px'}}>hello</div>
               
                 <List1 />
            </div>
        )
    }
}

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-07-03 10:40:34  更:2022-07-03 10:41:02 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 13:22:38-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码