w3c / chinese-ig

Web中文兴趣组
https://www.w3.org/2018/chinese-web-ig/
Other
536 stars 192 forks source link

CSS Display Sliver 提案的讨论 #239

Open wssgcg1213 opened 3 years ago

wssgcg1213 commented 3 years ago

Hi All,

这个 Issue 抛出一个关于 CSS 的提案,我希望能够在 Web 中文兴趣组内先进行一些讨论,收集意见和建议,最终目标是能够贡献到 CSSWG。

需要注意的是这个文档目前还只是一个"提议",还不算是比较完整的提案。

提案的目的是解决 Web 标准下滚动视图的复用回收问题,客户端开发中有 RecyclerView/UITableView 来实现滚动回收的布局容器,提案的 Display Sliver 定义了容器的布局方式以及当子元素滚动出 viewport 后的回收特性。

属性

增加 CSS Display 属性值: sliver

sliver 属于 W3C Box Layout Modes 中定义的 Inner Display Layout Models 类型,详见 https://www.w3.org/TR/css-display-3/#inner-model

当 display 为 sliver 时,该元素的 overflow 表现为 auto,越界的内容会被裁减,容器可滚动并显示滚动条。

增加 CSS 属性 sliver-direction : row|column 默认 column

sliver-direction 指定了 Sliver 容器的主轴方向 (Main Axis),Sliver 容器的主轴方向即滚动方向;Sliver 容器的交叉轴 (Cross Axis) 方向与主轴方向交叉,Sliver 容器在交叉轴方向不可滚动。

布局特性

在主轴方向 (Main Axis) 上可滚动, 其值由 sliver-direction 指定, 默认值为 column

主轴方向上的尺寸需要由 CSS 显式的 width/height 定义,否则默认值为 0。

交叉轴 (Cross Axis) 方向上的尺寸会填充满父容器尺度上的剩余空间, 与 flow layout 中的 block-level 的表现相同。

对容器的每一个子元素,其主轴方向上的尺寸需要由子元素在 CSS 中定义,否则默认值为 0;其交叉轴方向上的尺寸会填充满 SliverContainer 的剩余空间;

回收特性

当 Sliver 容器中的子元素滚动出该容器的 Viewport 时,可以将该子元素中用于渲染的 renderobject 回收以达到节省内存占用的目的。当子元素重新出现时,根据 DOM 描述重新生成 renderobject。

一些补充

  1. 具体实现中, 滚动时当子元素边界出 viewport 后, 可以等待一个 remainDistance 的距离再销毁; 当子元素处于 viewport 外,将要移入 viewport 时,可以在 remainDistance 的时候就开始创建 renderobject; 这些措施是出于性能和体验考虑
  2. 具体可销毁的对象, 是除了 Element 本身描述(attr,styleDeclaration)外的对象, 这里应该存在不同实现的差异性
  3. 我们这边已经基于 Flutter RenderObject 实现了类似的布局能力

我希望讨论的内容

  1. 必要性
  2. display 新属性的命名, sliver 的出处是参考了 Flutter
  3. 提案的描述
  4. 关于实现可行性
  5. 提案的流程
flyingzl commented 3 years ago

貌似google已经有了草案 content-visibility

说明请参考: https://web.dev/content-visibility/

wssgcg1213 commented 3 years ago

貌似google已经有了草案 content-visibility

说明请参考: https://web.dev/content-visibility/

@flyingzl 我们有注意到这个 draft, 也已在我们这边实现了 content-visibility 的能力, https://drafts.csswg.org/css-contain/#using-cv-auto 此处提到了 content-visibility: auto 可以用来实现 virtual-list,仍提出提案的必要性出于以下的想法:

  1. 出发点是考虑减少内存和 CPU 计算, content-visibility 的描述侧重于节点是否参与 layout/style/paint (here), 我们对 c-v 的实现中也并没有 dispose renderobject;
  2. 新增布局的必要性在于简化 off-screen 的计算逻辑,c-v auto 需要依赖父层的布局容器计算其布局位置后再决定是否显示, 这可能会导致实际计算中至少要有一次布局计算参与, 也就没有办法做到延迟创建 renderobject 了。sliver 布局的设计需要指定子元素在主轴上的尺度, 也就是只依赖 style declaration 即可, 这样也就不需要在一开始就创建 renderobject.
yuanyan commented 3 years ago

适用场景上有比较明显区别,display: sliver 适用长列表的场景,content-visibility 因为是基于节点当前是否可见来决定渲染,在长列表场景下需要给每一行都加上 content-visibility: auto,因为长列表的行数都会非常多,快速滚动时会导致大量的计算,所以 content-visibility 更适用大面积的以屏级为粒度的场景,像 SPA 里 tabbar 切换页面时、初始化时首屏之外的内容等。

jincdream commented 3 years ago
yuanyan commented 3 years ago
  • 如果父元素设定了display:sliver,当子元素内容布局过于复杂的时候,就不能再使用display:grid进行布局了,而需要再嵌套一个子节点并将它设定为布局容器才行。那么对于容器的子元素优化的场景是不是就过于局限了?

Inner Display Layout Models (参考 https://www.w3.org/TR/css-display-3/#inner-model )这是无法避免的

Gaubee commented 3 years ago

我粗略了看了flutter那边的文档,好像对于sliver是为了能够共享滚动行为? 但看提案描述,好像是想解决虚拟滚动的问题? 如果想不依赖js,只是想用纯html分析解决问题,也许应该用这样的代码来解决? (这里用template与microdata来做组合,以确保seo也能正确识别出来)

<virtual-scroll for="some-data-id">
    <div itemscope itemtype="a">
      <span itemprop="title"></span>
    </div>
    <div itemscope itemtype="b">
      <span itemprop="age"></span>
    </div>
</virtual-scroll>
<script id="some-data-id" type="text/json">
[
{
  "type": "a",
  "prop": {
    "title": "Microdata"
  }
},
{
  "type": "b",
  "prop": {
    "age": 10
  }
}
]
</script>
wssgcg1213 commented 2 years ago

我粗略了看了flutter那边的文档,好像对于sliver是为了能够共享滚动行为? 但看提案描述,好像是想解决虚拟滚动的问题? 如果想不依赖js,只是想用纯html分析解决问题,也许应该用这样的代码来解决? (这里用template与microdata来做组合,以确保seo也能正确识别出来)

<virtual-scroll for="some-data-id">
    <div itemscope itemtype="a">
      <span itemprop="title"></span>
    </div>
    <div itemscope itemtype="b">
      <span itemprop="age"></span>
    </div>
</virtual-scroll>
<script id="some-data-id" type="text/json">
[
{
  "type": "a",
  "prop": {
    "title": "Microdata"
  }
},
{
  "type": "b",
  "prop": {
    "age": 10
  }
}
]
</script>

共享滚动行为指的应该是 ScrollContext 吧,sliver 是一种类似 ListView 的回收模型。virtual-scroll 的实现上势必会有频繁 DOM op 的问题,所以建议直接基于渲染引擎的实现会好一些