[React] 손글씨 가상화 무한 스크롤 구성 요소

27357 단어 React

전언

  • react의 가상화 무한 스크롤은 다른 사람이 쓴 것이 있고 antd의list 구성 요소에도 이런 가상화 구성 요소, antd 가상화 목록 주소가 있다.
  • 지난번에 간단명료한 스크롤 성능 최적화를 한 편 쓴 후에 이런 기술이 효율을 더욱 높일 수 있다는 것을 발견했고 지난번의 힘겹게 실현된 사고가 막다른 골목으로 들어갔기 때문에 이런 방안은 나로 하여금 즉시 흥미를 가지게 하여 실현하고 싶다.

  • 의 원리

  • 원리는 내가 그 평론에 남긴 것과 차이가 많지 않다. 바로 매번 고정된 몇 개의div만 표시하고 굴러갈 때div를 바꾸는 것이다.
  • 이 물건을 실현하기 어려운 점은 다음과 같다. 첫째, 스크롤 바의 출현 둘째, 가시 구역의 원활한 이동 셋째, 플래시 화면 최적화
  • 이 장난감은 순서대로 쓰지 않고 주로 이 몇 가지 난점을 어떻게 해결했는지 말해 봅시다.
  • 스크롤 막대가 나타납니다.

  • 이 난점은 가장 간단하지만 사고방식이 열리지 않으면 이런 가상화 실현 방법을 생각하지 못하기 쉽다.공백div로 만든 스크롤 바입니다.
  • 이것은 주로 레이아웃과 관련이 있는데 현재 몇 가지 레이아웃을 참고할 수 있다.
  • 첫 번째는 스크롤 막대와 목록을 나란히 하는 것이다. 이렇게 하면 완전히 버틸 수 있다.
  • 두 번째는 스크롤 바가 바닥에 있고 목록 항목은 절대적으로 위치를 정한다. 원래 목록 항목은 스크롤 바에 맞추기 위해 이동해야 하기 때문에 목록 항목은 절대적으로 위치를 정할 수 있다.
  • 내 조립품에서 첫 번째 방식을 채택한다.

  • 가시 영역의 원활한 이동

  • 두 번째 난점은 가시 영역의 이동에 있다. 가상화 방안에 따르면 가시 영역은 스크롤 바와 함께 걷기 때문이다. 그러나 시작 항목의 높이와 각 항목의 높이를 고려해야 한다. 그렇지 않으면 가시 구역fixed가 화면에 있는 이상한 현상이 된다.
  • 더욱 원활하게 굴러가기 위해서는 원소마다 굴러가는 비율을 추산해야 한다. 그렇지 않으면 가시 구역이 순간적으로 모든 항목이 바뀌는 이상한 현상이 나타날 수 있다.백분율은 여수를 사용하여 계산하면 현재 원소의 몇 퍼센트에 스크롤 바가 굴러가고 이 백분율은 줄마다 높이를 곱해서 빼야 할 거리를 얻을 수 있다.

  • 플래시 화면 최적화

  • 이게 제일 어려워요. 몇 시간 걸렸어요. 오점도 많았어요.
  • 앞의 두 가지 난점을 해결했고 기본적으로 일을 할 수 있지만 플래시가 나온다.
  • 내가 끊은 지점에 의하면 플래시 스크린의 원인은 scroll의 함수에 색인을 설정한 다음에 페이지를 새로 고침하고 색인이 바뀐 후에useEffect가 실행을 촉발하여 이 렌더링된 요소를 페이지에 렌더링하는 것이다.이렇게 하면 2차 렌더링을 일으키지만, 감청 스크롤 함수에서 프롬프트를 가져올 수 없습니다.children, 왜냐하면 이 children이 전해온 값은 처음부터 얻을 수 있는 것이 아니기 때문이다.children이 바뀌면scroll의props.children이 최초의 값이야.
  • useState에 값을 저장해 봤는데scroll이useState에props를 가져왔어요.children, 결과가 잘못되었습니다.
  • usestate 메모리 함수를 시험해 보았는데 결과는 무한 귀속을 초래할 수 있습니다.
  • 함수 작용역을 바꾸어 보았는데bind로this를 찾았는데 반나절을 해도 안 된다.
  • 마지막에 갑자기 생각났어요. 왜 굳이 스크롤에 children을 집착해야 하는지. 제가 과장을 늦추면 되잖아요. 결과적으로 set Timeout을 넣어서 문제를 완벽하게 해결했어요.

  • 효과

  • 스크롤 바를 차지하는div를 빨간색으로 만들었어요. 보기 편하도록 스타일을 지우면 돼요.세그먼트 로드와 Intersection Observer가 완벽하게 배합되어 있어 이 몇 가지를 함께 사용하면 충돌이 전혀 없습니다.이 사례는 이 세 가지 기술을 동시에 사용했다.

  • 코드

  • 사용하면 몇 개의 값을 전달하면 됩니다. 중간에 리스트를 순환해서 쓰는 대로 쓰세요.
  •             <Virtualize itemHeight={rootSize*(4.5596)} columnNumber={2}
                    insightNumber={6} startHeight={rootSize*6}
                    scrollDom={document.querySelector('.home-main-container')}
                >
                {
                    props.renderProduct.productList.map((item:Product,index:number)=>{
                      return (                     
                            <Link key={item.id} to={{pathname:`/productdetail/${item.id}`,state:item}}
                            >
                            <Card
                                hoverable
                                key={item.id}
                                cover={
                                 <div  data-src={item.poster}
                                 className='list-item'
                                 ref={(ref)=>{
                                    if(ref){
                                        io!.observe(ref)
                                    }}}                              
                                 >
                                    <Icon type="picture"></Icon>
                                 </div>                                                        
                                }
                            ><Card.Meta title={item.title} description={`  :${item.price} `} />
                            </Card>
                            </Link>
                        )                 
                      })
                }
               </Virtualize>
    
  • 들어오는 매개 변수에 주석이 있음
  • type Props = PropsWithChildren<{
        itemHeight:number//     
        columnNumber:number//      
        insightNumber:number//         
        startHeight:number//           
        scrollDom:HTMLDivElement|null //     dom
        scaleRow?:number//    
    }>
    
    function  Virtualize(props:Props){
        const [costomHeight,setCostomHeight]=useState()
        const [visbleHeight,setVisibleHeight]=useState()
        const [renderChildren,setRenderChildren]=useState()
        const [indexNumber,setIndexNumber]=useState({
            startIndex:0,
            endIndex:props.insightNumber,
            overScroll:0
        })
        const [scaleRow,setScaleRow]=useState(2)
        useEffect(()=>{
            if(props.children instanceof Array){
                let childrenLen = props.children.length
                if(childrenLen%props.columnNumber!=0){//        
                    let remain = childrenLen%props.columnNumber
                    childrenLen=childrenLen+remain
                }
                let fullheight = childrenLen/props.columnNumber*props.itemHeight
                setCostomHeight(fullheight)
                let insightHeight
                if(childrenLen<props.insightNumber){
                    insightHeight = fullheight
                }else{
                    insightHeight = props.insightNumber/props.columnNumber*props.itemHeight
                }
                setVisibleHeight(insightHeight)
                setRenderChildren(props.children.slice(indexNumber.startIndex,indexNumber.endIndex))
            }
        },[props.children,indexNumber])
        const scrollFunc=(e:Event)=>{
            let target= e.target as HTMLDivElement
            let overScroll = target.scrollTop-props.startHeight//    
            let timer = overScroll/props.itemHeight*props.columnNumber
            let startIndex =Math.floor(timer)//      0  
            startIndex = startIndex<0?0:startIndex;
            timer = timer%props.columnNumber/props.columnNumber//       
            if(timer<0)timer=0;
            if(overScroll<0)overScroll=0
            if(startIndex%props.columnNumber!=0){//     
                startIndex=startIndex-startIndex%props.columnNumber
            }
            let endIndex = startIndex+props.insightNumber+scaleRow
            overScroll=overScroll-timer*props.itemHeight
            setTimeout(() => {
                setIndexNumber({
                    startIndex,
                    endIndex,
                    overScroll
                })
            });
          
        }
        useEffect(()=>{
            props.scaleRow?setScaleRow(props.scaleRow):null;
            if(props.scrollDom)
            props.scrollDom.addEventListener('scroll',throttle(scrollFunc,50))
            return ()=>{
                if(props.scrollDom)
                props.scrollDom.removeEventListener('scroll',throttle(scrollFunc,50))
            }
        },[])
        return (
           <>
           <div style={{display:'flex'}}>
           <div style={{height:costomHeight?costomHeight:0,backgroundColor:'red',width:'20px'}} ></div>
           <div className='virtual-custom-item' 
           style={{
               height:visbleHeight?visbleHeight:0,
               position:"relative",
               transform:`translate3d(0px, ${indexNumber.overScroll}px, 0px)`
            }}>
           {renderChildren}
           </div>
           </div>
         
           </>
        )
    }
    export default Virtualize
    
  • 코드에 빨간색 스크롤 바가 없으면 양식을 없앨 수 있다.

  • 넓히다

  • 이 구성 요소는 고정폭이 높은 가상화 구성 요소로 구성 요소가 고정폭이 높지 않으면 더 많은 자원 계산을 소모한다.시간 나면 다음에 놀자고 써.
  • 좋은 웹페이지 즐겨찾기