分类
javascript vue 性能 算法 解决方案

做虚拟列表时所遇到的坑

页面跳动

出现这种情况的原因是虚拟列表所渲染出的Dom是有限的,向下滚动时需要将上方的Dom移除,向上滚动时需要添加上方的Dom

解决方案

降低Dom渲染的频率

  1. 我有一个当前所处位置的检测值命名为topIndex,以3的倍数进行检测,当我所处位置为1值为0,所处位置为4值为3
  2. 我有一个判断当前滚动方向的检测值,命名为slideArrow,当向下滑动屏幕是置为down,向上滑动屏幕时值为up
  3. 我有一个由topIndex和slideArrow组合而成的计算值,命名为topAndArrowChange
  4. 监听topAndArrowChange,当发生改变时判断滑动方向,改变虚拟列表的值
  5. 当快要到底部时会触发查找列表,数据会放进源列表recommendList中
  6. 当滑动方向为up向上滑动,看下面的内容。或者滑动方向为down向下滑动,看上面的内容。那么虚拟列表为源数据的截取值。
topAndArrowChange(newVal) {
      const circle = 30
      const circleNum = parseInt(this.topIndex / circle, 10)
      console.log(newVal)
      if (this.slideArrow === 'up') {
        this.virtualRecommendList = this.recommendList.slice(circleNum * circle, circleNum * circle + 60)
      } else {
        const start = Math.max((Math.round(this.topIndex / circle) - 1) * circle, 0)
        const end = Math.round(this.topIndex / circle) * circle + circle
        this.virtualRecommendList = this.recommendList.slice(start, end)
      }
    },

看下面的内容,截取的部分

如果当前的topIndex为40,则截取30至90,最大截取60条

看上面的内容,截取的部分

如果当前的topIndex是0,则截取0-30

如果当前的topIndex是36,则截取0-60

如果当前的topIndex是119,则截取90-150

如果当前的topIndex是129,则截取90-150

无限触发查找数据

出现原因是初始时虚拟列表没有赋值,滚动过快触底,虚拟列表来不急赋值

解决方案

  1. 添加源数据列表监听,发现源数据列表更新的同时修改虚拟列表的值
  2. 只有在查找数据时才会触发源列表数据更新,因此虚拟列表的截取值为
 // 防止无限触发查找列表
    recommendList() {
      const circle = 30
      const circleNum = parseInt(this.topIndex / circle, 10)
      this.virtualRecommendList = this.recommendList.slice(circleNum * circle)
    },

当还是以30为一个循环,最多截取不超过60个Dom

总结

综上,此方案虚拟列表最多渲染60条数据

分类
javascript regex 常识

用户环境判断

在开发过程中,我们可能会针对不同的环境做不同的跳转等操作,因此判断环境也是一个比较必要的技术。

const matchEquipment = reg => reg.test(window.navigator.userAgent)

export const isAndroid = () => matchEquipment(/Android/i)

export const isIOS = () => matchEquipment(/ip(hone|od|ad)/i)

// 企业微信
export const isWorkWeiXin = () => matchEquipment(/wxwork/i)

export const isWeiXin = () => matchEquipment(/MicroMessenger/i)

export const isQQ = () => matchEquipment(/QQ/i)

export const isWeiBo = () => matchEquipment(/WeiBo/i)

export const isWindows = () => matchEquipment(/IEMobile/i)

export const isMobile = () => matchEquipment(/Android|webOS|iPhone|iPod|BlackBerry/i)

export const isWeiXinMiniPrograme = () => return matchEquipment(/miniProgram/i)
分类
CSS javascript

超出一行标签内打点

在移动端中,屏幕的宽度通常会不足,在这样的宽度中可能要放下几个标签,这几个标签需要在一行内显示,标签数量不确定,标签内文字数量不确定。现要求是标签尽可能的往一行内放,如果放不下一个完整的标签,那么该标签会以打点的方式在一行的最后一个。

只有一个标签,字数超出一行打点

该标签只有需要最基础的overflow:hidden;white-space:nowrap;text-overflow:ellipsis;就能实现。

两个标签,字数超出一行打点

 .line-box {
        box-sizing: border-box;
        --column-gap: 4px;
        --text-wrap-padding: 4px;
        --text-wrap-border-radius: 4px;
        --text-font-size: 12px;
        --text-color: white;
        --text-background: blue;
        width: 100px;
        height: 40px;
        resize: horizontal;
        padding: 10px;
      }
      .w {
        display: flex;
        flex-wrap: wrap;
        overflow: hidden;
      }
      .tag {
        white-space: nowrap;
        text-overflow: ellipsis;
        /* 第一个作用是使打点生效,第二个作用是作为flex子元素使得在最小尺寸由最小内容宽度决定的时候使最小尺寸失效从而可以无视最小尺寸限制去缩放 */
        overflow: hidden;
        background-color: var(--text-background);
        margin: 0 calc(var(--column-gap) / 2);
        color: var(--text-color);
        font-size: var(--text-font-size);
        border-radius: var(--text-wrap-border-radius);
        padding: var(--text-wrap-padding);
        /* 使下方的tag超出容器高度隐藏 */
        margin-bottom: 50px;
      }
      .z {
        flex: 1;
        min-width: 30px;
        display: flex;
      }
      .f1 {
        flex: 1;
      }


<!-- 两个标签超过打点 -->
    <div class="line-box w">
      <div class="tag">第一个</div>
      <div class="z">
        <div class="tag">第二个</div>
        <div class="f1"></div>
      </div>
    </div>

首先要实现宽度超过一个标签,但是不超过两个标签时第一个标签不打点,第二个标签打点。因此第一个标签的宽度需要尽可能的不缩小,第二个标签缩小完之前不能缩小第一个标签。首先两个标签的容器是flex,flex-wrap:wrap是为了第二个标签缩小到一定程度可以换行。因此需要第一个标签的基础尺寸是标签的长度,最小尺寸是0,第二个标签的基础尺寸是0,最小尺寸是30px。并且可以占据除第一个标签外的所有空间。根据flex弹性伸缩规则,当容器的宽度大于子元素的基础尺寸之和,子元素不会出现宽度小于基础尺寸的情况。因此会出现先缩小基础尺寸是0的元素,当容器的宽度比基础尺寸小时,再根据弹性缩放规则是子元素的宽度缩放的比基础尺寸还小。由于第二个标签与第一个标签所拥有的属性应该是一致的,所以需要给第二个标签套一个容器来实现第二个标签所需要的额外功能。该容器就是z,z所具有的功能是让第二个标签占据除第一个标签外的所有宽度,且具有最小尺寸,如果小于最小尺寸,第二个标签会被换到下一行去,这样就看不见了。并且让第二个标签的基础尺寸是0(flex:1 = flex: 1 1 0%;),如果宽度足够宽第二个标签也不会占据太大宽度。因为标签本身的属性是基础宽度为不换行的文本宽度,且不会弹性扩大。

三个标签字数超过一行打点

<div class="line-box w">
      <div class="w">
        <div class="tag">第一个</div>
        <div class="z">
          <div class="tag">第二个</div>
        </div>
      </div>
      <div class="z">
        <div class="tag">第三个</div>
      </div>
    </div>

我们已经知道两个标签超过一行打点的方法。这个方法我们直接借用。那么还需要一个标签做成三个标签字数超过一行打点。由数学归纳法得,三个标签超过一行打点=两个标签超过一行打点+一个标签超过一行打点。因此还是需要一个容器,yoi容器的左侧是两个标签的容器,右侧是一个标签的容器。左侧的容器的基础宽度是两个标签不换行的宽度,右侧标签容器的基础宽度是0。因此右侧标签的实现与二个标签超出一行打点的第二个标签的实现是一致的。需要改变的是给两个标签超出一行打点加一个基础宽度是不换行宽度,最小尺寸是0,因此容器作为flex子元素,flex默认值就是0 1 auto。基础宽度是达标的。加入overflow:hidden是为了让最小尺寸变为0;加入flex-wrap:wrap;是为了保持原有的标签宽度太小就换行不显示的功能。

n个标签超过一行打点

现在我们已经找出规律

n个标签超过一行打点 = 容器w_line-box(容器w((n-1)个标签超出一行点) +容器z(标签) )

使用递归得

const fac = (n, arr) => {
        if (n <= 0) {
          return ''
        }
        if (n === 1) {
          return `<div class="w"><div class="tag">${arr[n]}</div></div>`
        }
        const len = arr.length
        return `<div class="w${n === len ? ' line-box' : ''}"><div class="w">${fac(
          n - 1,
          arr,
        )}</div><div class="z"><div class="tag">${arr[len - n]}</div></div></div>`
      }
      const arr = [
        '塑料科技',
        'sdkjfhdsf',
        '莱克斯顿',
        '塑料科技',
        'sdkjfhdsf',
        '莱克斯顿',
        '塑料科技',
        'sdkjfhdsf',
        '莱克斯顿',
      ]
      const fragment = document.createDocumentFragment()
      document.getElementById('container').innerHTML = fac(arr.length, arr)
分类
javascript

好用的插件

生成随机数-nanoid

分类
javascript

去除当前所有setTimeout

const t=setTimeout(()=>{})
for(let i=t; i>0;i-=0) {
  clearTimeout(i)
}
分类
javascript

算法基础知识

这里会收集一些提高算法性能的小技巧

内容互换的时间复杂度与空间复杂度最低算法

两个下标互换

image - 算法基础知识

时间复杂度n(1),空间复杂度n(1),无需算法

swap(a,b)

求逆

image 1 - 算法基础知识

reverse()

部分求逆

image 2 - 算法基础知识

reverse(1,4)

同数量相邻块互换

image 3 - 算法基础知识

swap(1,3),swap(2,4)

同数量不相邻块互换

image 4 - 算法基础知识

swap(0,3),swap(1,4)

不同数量相邻块互换

image 5 - 算法基础知识

reverse(0,5)-> reserve(0,3),reserve(3,5)

不同数量非相邻块互换

image 6 - 算法基础知识

reverse() -> reverse(0,3) ,reverse(4,6)

分类
javascript

层叠上下文与事件流

事件流

image - 层叠上下文与事件流

层叠上下文

若子元素的层叠上下文在父元素下方,那么点击子元素位置时target只能到父元素这,即子元素无法触发到点击监听

分类
javascript react

React函数式编程使用lodash的防抖

import React, { FC, useRef } from 'react'
import copy from 'copy-to-clipboard'
import { message } from 'hljd'
import { debounce } from 'lodash'
interface ColorPickerInputNativeProps {
  value?: string
  onChange?: (value: string) => void
}
const ColorPickerInputNative: FC<ColorPickerInputNativeProps> = ({ value, onChange }) => {
  const eRef = useRef('')
  const funcRef = useRef(
    debounce(() => {
      copy(eRef.current)
      onChange?.(eRef.current)
      message.success('颜色值已复制')
    }, 200)
  )
  const handleChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
    eRef.current = e.target.value
    funcRef.current()
  }
  return <input type="color" value={value} onChange={handleChange} />
}
export { ColorPickerInputNative }

要点:

  • 改变的值存入useRef中
  • 在useRef中放置debounce
分类
javascript

postMessage没接收到值只接收到webpackOk

原因

当时的iframe是动态生成的,未加载好就发送postMessage,webpack帮助了发送,所以出现webpackOk

解决方案

const targetIframe = document.getElementById('targetIframe')
const info = { sign: '标识' }
targetIframe.onload = () => {
    targetIframe.contentWindow.postMessage(info,'*')
}
分类
javascript

vue(js)转react(ts)

慢慢更新。。。。

定义

定义函数

// vue
methods: {
   fn(){},
}
// react
fn(){};
fb = ()=>{};

父子通信

子组件改父组件的值

vue

// 子组件
this.$emit('change',2);
// 父组件
<father @change="handleChange"></father>

handleChange(e) {
    console.log(e);
}

react

// 子组件
render() {}
this.props.handleChange(k)
// 父组件
fn(k){};
render() {
    return (<children handleChange={fn}></children>)
}

路由

react使用的是react-router-dom

class

如果要在同一个dom中写多个class

// vue
<div :class="['a',active?'b':'c']"></div>
// react
import cls from 'classnames';
return (
  <div className={cls(a,{b:active,c:!active})}></div>
)