添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
听话的牛肉面  ·  c# ...·  1 年前    · 
酒量小的登山鞋  ·  pymysql execute ...·  1 年前    · 
有情有义的蚂蚁  ·  SQL ...·  1 年前    · 

Vue Virtual Scroller 是一个可复用的列表组件,解决了现实列表中的一些通用功能。通过精度它的源代码,学习如何编写可复用的VUE组件。具体使用方法就不搬运了,请查看项目文档。

#了解VUE插槽(slot) Vue 将 <slot> 元素作为承载分发内容的出口。插槽内可以包含任何模板代码,包括 HTML或其它组件。

comp1模板

<!--这是一个叫comp1的VUE组件模版-->
  <slot></slot>
</div>

使用comp1模板

<comp1>
  hello vue
</comp1>
  hello vue

#可复用的列表组件 显然,利用插槽可以制作可复用组件。例如可以实现一个通用的列表组件,列表的行为是公共的、可复用的,列表中显示的内容使用组件时再指定。

基本实现思路是:将列表数据(items)传递给列表组件,组件中用 v-for 生成列表的框架,其中每个item通过 slot 展示,使用组件的代码中指定 slot 的内容。

<li v-for="item in items" :key="item.id">
  <slot></slot>

这里出现1个问题,slot中的内容是和item相关的,所以替换slot的内容时必须能够访问item的数据。

<my-list :items="items">
  <div>{{item.label}}</div>
</my-list>

但是这样写是无效的,因为:

父级模板里的所有内容都是在父级作用域中编译的;子模板里的所有内容都是在子作用域中编译的。

VUE中提供了 作用域插槽 解决这个问题。为了让组件中数据(item)在父级的插槽内容中可用,我们可以将item作为元素的一个特性绑定上去:绑定在元素上的特性被称为 插槽 prop 。现在在父级作用域中,我们可以给 v-slot 带一个值来定义我们提供的插槽 prop 的名字。

<li v-for="item in items" :key="item.id">
  <slot :item="item"></slot>

在父组件中默认访问子组件属性的方式:

<my-list :items="items"  v-slot="slotProps">
  <div>{{slotProps.item.label}}</div>
</my-list>

但是这样写比较啰嗦,可以用 解构插槽prop简化

<my-list :items="items" v-slot="{ item }">
  <div>{{item.label}}</div>
</my-list>

#Vue Virtual Scroller ##index.js 将3个组件(RecycleScroller,DynamicScroller,DynamicScrollerItem)注册为全局组件,也就是说它们在注册之后可以用在任何新创建的 Vue 根实例 (new Vue) 的模板中。

##组件RecycleScroller.vue

<template>
    v-observe-visibility="handleVisibilityChange"
    class="vue-recycle-scroller"
    :class="{
      ready,
      'page-mode': pageMode,
      [`direction-${direction}`]: true,
    @scroll.passive="handleScroll"
      v-if="$slots.before"
      class="vue-recycle-scroller__slot"
        name="before"
      ref="wrapper"
      :style="{ [direction === 'vertical' ? 'minHeight' : 'minWidth']: totalSize + 'px' }"
      class="vue-recycle-scroller__item-wrapper"
        v-for="view of pool"
        :key="view.nr.id"
        :style="ready ? { transform: `translate${direction === 'vertical' ? 'Y' : 'X'}(${view.position}px)` } : null"
        class="vue-recycle-scroller__item-view"
        :class="{ hover: hoverKey === view.nr.key }"
        @mouseenter="hoverKey = view.nr.key"
        @mouseleave="hoverKey = null"
          :item="view.item"
          :index="view.nr.index"
          :active="view.nr.used"
      v-if="$slots.after"
      class="vue-recycle-scroller__slot"
        name="after"
    <ResizeObserver @notify="handleResize" />
</template>

有3个插槽,分别是before,default和after。默认插槽中定义了3个插槽属性:item,index和active。

:item = "view.item" :index = "view.nr.index" :active = "view.nr.used"

全局变量 $slots 可以访问插槽中要分发的内容,命名插槽before和after通过这个变量判断是否有要分发的内容。

<div v-if="$slots.before" class="vue-recycle-scroller__slot">
  <slot name="before" />
</div>

尽管存在 prop 和事件,有的时候你仍可能需要在 JavaScript 里直接访问一个子组件。为了达到这个目的,你可以通过 ref 特性为这个子组件赋予一个 ID 引用,并通过全局变量 $refs 访问。

ref= "wrapper" :style= "{ [direction === 'vertical' ? 'minHeight' : 'minWidth']: totalSize + 'px' }" class= "vue-recycle-scroller__item-wrapper"

组件中使用了自定义指令

v-observe-visibility = "handleVisibilityChange" </ div >
import { ObserveVisibility } from 'vue-observe-visibility'
handleVisibilityChange (isVisible, entry) {
  if (this.ready) {
    if (isVisible || entry.boundingClientRect.width !== 0 || entry.boundingClientRect.height !== 0) {
      this.$emit('visible')
      requestAnimationFrame(() => {
        this.updateVisibleItems(false)
    } else {
      this.$emit('hidden')

第2参数 entry 是实现IntersectionObserverEntry接口的实现。通过这个接口可以判断对象相对于根元素或视窗(viewport)是否可见。这段代码就是当列表自身的可见性发生变化时,进行相应的处理。

if (this.emitUpdate) this.$emit('update', startIndex, endIndex)

更新列表显示内容时可以出发 update 事件,通过startIndex和endIndex就可以知道显示到了哪些数据,这样如果需要就可以实现按需动态加载数据。

##组件DynamicScroller.vue

<template>
  <RecycleScroller
    ref="scroller"
    :items="itemsWithSize"
    :min-item-size="minItemSize"
    :direction="direction"
    key-field="id"
    v-bind="$attrs"
    @resize="onScrollerResize"
    @visible="onScrollerVisible"
    v-on="listeners"
    <template slot-scope="{ item: itemWithSize, index, active }">
      <slot v-bind="{ item: itemWithSize.item, index, active,  itemWithSize }"/>
    </template>
    <template slot="before">
      <slot name="before" />
    </template>
    <template slot="after">
      <slot name="after" />
    </template>
  </RecycleScroller>
</template>

组件DynamicScroller把组件RecycleScroller组件包裹了一层,把自己接收的slot内容再穿入RecycleScroller组件。

系统架构师 13.2k
粉丝