-
Notifications
You must be signed in to change notification settings - Fork 390
feat: RN 环境支持 sectionList 组件 #2511
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
yandadaFreedom
wants to merge
22
commits into
master
Choose a base branch
from
feat-section-list-review
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 18 commits
Commits
Show all changes
22 commits
Select commit
Hold shift + click to select a range
849b546
feat: add rn section-list component
f8694a7
feat: 支持section footer
24646a8
docs: 补充section footer 文档
8a531ce
chore: 删除单测
0b8c742
fix: 手势协同支持原生手势
86f6243
chore: 删除占位文件
fa10339
docs: 更新section-list相关文档
e862610
docs: 更新section-list相关文档
3b3dc96
Merge branch 'master' into feat-section-list-review
cf8a6be
docs: 更新section-list相关文档
1ee12fe
docs: 更新section-list相关文档
d4a9fe1
fix: 修复 section-list 滚动索引与头尾高度计算
7863954
feat: 内建 sticky 组件并完善 RN section-list
5bfb839
feat: 完善 RN sticky 组件与 section-list 头尾渲染
a0a478a
docs: 更新sticky相关文档
35f1557
refactor(webpack-plugin): 重构extendComponentPlugin注册逻辑
88293aa
refactor(webpack-plugin): remove unused section-list original inde
4d05fe5
fix(webpack-plugin): 修复 mpx-section-list height/width 不生效的问题
7d78e91
fix(webpack-plugin): 调整
f58fe15
fix(webpack-plugin): 优化 ExtendComponentsPlugin
c7b288f
refactor(webpack-plugin): 简化 mpx-section-list 组件实现
ccf0a3d
Merge branch 'master' into feat-section-list-review
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,195 @@ | ||
| # Mpx 扩展组件 | ||
|
|
||
| 除基础组件外,Mpx 额外提供一些扩展组件。扩展组件需要在页面或组件的 `usingComponents` 中注册后使用。 | ||
|
|
||
| ```html | ||
| <script type="application/json"> | ||
| { | ||
| "usingComponents": { | ||
| "section-list": "@mpxjs/webpack-plugin/lib/runtime/components/extends/section-list" | ||
| } | ||
| } | ||
| </script> | ||
| ``` | ||
|
|
||
| Mpx 会根据当前编译的目标平台(wx/ali/web/ios/android/harmony),自动解析到对应平台的扩展组件实现。 | ||
|
|
||
|
|
||
| ## section-list | ||
|
|
||
| 跨端虚拟列表组件,可自定义分组头、列表头、列表项,自动分段渲染兼容各端。 | ||
|
|
||
| 支持平台:RN | ||
|
|
||
| ### 属性 | ||
|
|
||
| | 属性名 | 类型 | 默认值 | 说明 | | ||
| |-----------------------|-------------|----------|------------------------| | ||
| | height | String/Number | 100% | 组件高度 | | ||
| | width | String/Number | 100% | 组件宽度 | | ||
| | list-data | Array | [] | 列表数据,如需使用列表分组头 `section-header`,对应 item 的数据需要包含 `isSectionHeader: true` 标识;如需使用列表分组尾 `section-footer`,对应 item 的数据需要包含 `isSectionFooter: true` 标识 | | ||
| | enable-sticky | Boolean | false | 启用分组吸顶 | | ||
| | scroll-event-throttle | Number | 0 | RN 环境特有属性,控制 scroll 事件触发频率 | | ||
| | enhanced | Boolean | false | RN 环境特有属性,开启滚动增强能力 | | ||
| | bounces | Boolean | true | RN 环境特有属性,iOS 下边界弹性控制,需同时开启 `enhanced` | | ||
| | use-list-header | Boolean | false | 使用自定义列表头 | | ||
| | list-header-data | Object | {} | 列表头数据 | | ||
| | use-list-footer | Boolean | false | 使用自定义列表页脚 | | ||
| | list-footer-data | Object | {} | 列表页脚数据 | | ||
| | generic:recycle-item | String | | 列表项,抽象节点组件名,对应组件需要通过 usingComponents 注册 | | ||
| | generic:section-header | String | | 列表分组头,抽象节点组件名,对应组件需要通过 usingComponents 注册 | | ||
| | generic:section-footer | String | | 列表分组尾,抽象节点组件名,对应组件需要通过 usingComponents 注册 | | ||
| | generic:list-header | String | | 列表头,抽象节点组件名,对应组件需要通过 usingComponents 注册 | | ||
| | generic:list-footer | String | | 列表页脚,抽象节点组件名,对应组件需要通过 usingComponents 注册 | | ||
| | item-height | Object | {} | 列表项高度配置(支持 getter/value),必须配置 | | ||
| | section-header-height | Object | {} | 分组头部高度配置(getter/value),若使用了自定义分组头必须配置 | | ||
| | section-footer-height | Object | {} | 分组尾部高度配置(getter/value),若使用了自定义分组尾必须配置 | | ||
| | list-header-height | Number | 0 | 列表头部高度,若使用了列表头必须配置 | | ||
| | enable-back-to-top | Boolean | false | 点击状态栏时滚动到顶部,仅 iOS 环境支持 | | ||
| | end-reached-threshold | Number | 0.1 | 触底事件触发阈值 | | ||
| | refresher-enabled | Boolean | false | 开启自定义下拉刷新 | | ||
| | refresher-triggered | Boolean | false | 设置当前下拉刷新状态,true 表示已触发 | | ||
| | show-scrollbar | Boolean | true | 滚动条显隐控制 | | ||
| | simultaneous-handlers | Array\<Object> | [] | RN 环境特有属性,允许多个手势同时识别和处理 | | ||
| | wait-for | Array\<Object> | [] | RN 环境特有属性,允许延迟激活处理某些手势 | | ||
|
|
||
| #### `item-height`/`section-header-height`/`section-footer-height` 格式说明 {#section-list-height-config} | ||
|
|
||
| `item-height`、`section-header-height`、`section-footer-height` 支持如下格式: | ||
|
|
||
| ```js | ||
| height: { | ||
| value: 400, // 定高 | ||
| getter: function (item, index) { | ||
| const seed = item.id % 2 || 0 | ||
| const heights = [100, 300] | ||
| return heights[seed] | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| **说明:** | ||
| - `value`:默认高度(所有项相同高度时直接用 value 即可)。 | ||
| - `getter`:函数形式,可接收每一项的数据和索引,按需返回不同高度(动态高度需求时使用)。 | ||
| - `getter` 优先级 大于 value。 | ||
|
|
||
| > 建议性能要求较高(如超大数据集)优先使用 `value` 定高。 | ||
|
|
||
| `list-header-height` 仅支持 Number 类型,用于声明列表头部固定高度,不支持 `getter` / `value` 配置对象。 | ||
|
|
||
| ### 事件 | ||
|
|
||
| | 事件名 | 说明 | | ||
| |-----------------------|-----------------------------------| | ||
| | bindscroll | 滚动时触发,返回滚动信息 | | ||
| | bindscrolltolower | 滚动到底部/触底通知 | | ||
| | bindrefresherrefresh | 自定义下拉刷新被触发 | | ||
|
|
||
| ### 方法 {#section-list-methods} | ||
|
|
||
| | 方法名 | 说明 | | ||
| |-------|------| | ||
| | scrollToIndex | 滚动到指定索引 | | ||
|
|
||
| `scrollToIndex({ index, animated, viewOffset, viewPosition })` 参数说明: | ||
| - `index`:目标索引 | ||
| - `animated`:是否滚动动画 | ||
| - `viewOffset`:滚动偏移量 | ||
| - `viewPosition`:滚动定位,0:顶部, 0.5:中间, 1:底部 | ||
|
|
||
| ### 用法示例 | ||
|
|
||
| ```js | ||
| <section-list | ||
| generic:recycle-item="normal-recycle-item" | ||
| generic:section-header="section-header" | ||
| generic:section-footer="section-footer" | ||
| generic:list-header="list-header" | ||
| width="{{width}}" | ||
| height="{{height}}" | ||
| list-data="{{dataList}}" | ||
| item-height="{{ itemHeight }}" | ||
| section-header-height="{{headerHeight}}" | ||
| section-footer-height="{{footerHeight}}" | ||
| list-header-height="{{listHeaderHeight}}" | ||
| use-list-header="{{true}}" | ||
| enable-sticky="{{true}}" | ||
| /> | ||
| <script> | ||
| import mpx, { createPage, createComponent } from '@mpxjs/core' | ||
|
|
||
| const generateData = (itemsPerSection) => { | ||
| const data = [] | ||
|
|
||
| for (let i = 0; i < 10; i++) { | ||
| data.push({ | ||
| isSectionHeader: true, // 标识该行使用 section-header 对应的抽象节点渲染 | ||
| title: `Section ${i + 1}` | ||
| }) | ||
|
|
||
| // 添加该 section 的 items | ||
| for (let j = 0; j < itemsPerSection; j++) { | ||
| const itemNumber = i * itemsPerSection + j + 1 | ||
| data.push({ | ||
| id: itemNumber, | ||
| title: `Item ${itemNumber}`, | ||
| description: `This is item number ${itemNumber} in section ${i + 1}` | ||
| }) | ||
| } | ||
| data.push({ | ||
| isSectionFooter: true, // 标识该行使用 section-footer 对应的抽象节点渲染 | ||
| title: `Section ${i + 1} Footer` | ||
| }) | ||
| } | ||
| return data | ||
| } | ||
|
|
||
| createPage({ | ||
| data: { | ||
| width: 0, | ||
| height: 0, | ||
| bufferScale: 5, | ||
| dataList: generateData(100), | ||
| itemHeight: { | ||
| getter: function (item, index) { | ||
| const seed = item.id % 2 || 0 | ||
| const heights = [100, 300] | ||
| return heights[seed] | ||
| } | ||
| }, | ||
| headerHeight: { | ||
| value: 50 | ||
| }, | ||
| footerHeight: { | ||
| value: 50 | ||
| }, | ||
| listHeaderHeight: 100 | ||
| }, | ||
| onLoad() { | ||
| this.height = mpx.getWindowInfo().windowHeight | ||
| this.width = mpx.getWindowInfo().windowWidth | ||
| } | ||
| }) | ||
| </script> | ||
| <script type="application/json"> | ||
| { | ||
| "usingComponents": { | ||
| "section-list": "@mpxjs/webpack-plugin/lib/runtime/components/extends/section-list", | ||
| "normal-recycle-item": "@/components/recycle-item", | ||
| "section-header": "@/components/section-header", | ||
| "section-footer": "@/components/section-footer", | ||
| "list-header": "@/components/list" | ||
| } | ||
| } | ||
| </script> | ||
| ``` | ||
|
|
||
| ### 其它说明 | ||
|
|
||
| - 当使用了列表项、列表头、自定义分组头或者自定义分组尾,必须配置对应 `item-height`、`section-header-height`、`section-footer-height`、`list-header-height` 高度参数,否则会出现滚动异常情况。 | ||
| - 可通过 ref 获取实例并调用 `scrollToIndex` 方法实现滚动。 | ||
| - 如果用户滑动的速度超过渲染的速度,则会先看到空白的内容,这是为了长列表优化不得不作出的妥协。 | ||
| - 当某行滑出渲染区域之外后,其内部状态将不会保留。 | ||
| - 在 RN 环境,section-list 通过 RN 提供的 SectionList 实现分组吸顶。受 RN 底层实现机制限制,开启 `enable-sticky` 且快速滑动时,自定义分组头有时会出现闪烁现象。此问题需要等待 RN 官方修复,我们会持续关注并跟进。 | ||
| - 若某行需要使用 `section-header` 对应的抽象节点渲染,则该行数据必须包含 `isSectionHeader: true` 字段;若某行需要使用 `section-footer` 对应的抽象节点渲染,则该行数据必须包含 `isSectionFooter: true` 字段;否则默认使用 `recycle-item` 对应的抽象节点渲染 | ||
| - `isSectionFooter: true` 的数据建议放在对应分组的最后一行、下一个 `isSectionHeader: true` 数据之前,用于表示当前分组的尾部。 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
70 changes: 70 additions & 0 deletions
70
packages/webpack-plugin/lib/resolver/ExtendComponentsPlugin.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| const toPosix = require('../utils/to-posix') | ||
| const EXTEND_COMPONENT_RELATIVE_PATH = './lib/runtime/components/extends/' | ||
| const EXTEND_COMPONENT_TARGET_PATH = '@mpxjs/webpack-plugin/lib/runtime/components/react/dist' | ||
| const EXTEND_COMPONENTS = { | ||
| 'section-list': ['ios', 'android', 'harmony'] | ||
| } | ||
|
|
||
| /** | ||
| * 扩展组件路径解析插件 | ||
| * 将 @mpxjs/webpack-plugin/lib/runtime/components/extends/[component-name] 格式的路径 | ||
| * 解析为对应平台的实际组件路径 | ||
| */ | ||
| module.exports = class ExtendComponentsPlugin { | ||
| constructor (source, mode, target) { | ||
| this.source = source | ||
| this.target = target | ||
| this.mode = mode | ||
| } | ||
|
|
||
| apply (resolver) { | ||
| const target = resolver.ensureHook(this.target) | ||
| const mode = this.mode | ||
|
|
||
| resolver.getHook(this.source).tapAsync('ExtendComponentsPlugin', (request, resolveContext, callback) => { | ||
| const componentName = getComponentName(request) | ||
| if (!componentName) { | ||
| return callback() | ||
| } | ||
|
|
||
| // 检查组件是否在配置中 | ||
| const supportedModes = EXTEND_COMPONENTS[componentName] | ||
| if (!supportedModes) { | ||
| return callback(new Error(`Extended component "${componentName}" was not found. Available extended components: ${Object.keys(EXTEND_COMPONENTS).join(', ')}`)) | ||
| } | ||
|
|
||
| // 获取当前模式下的组件路径 | ||
| if (!supportedModes.includes(mode)) { | ||
| return callback(new Error(`Extended component "${componentName}" cannot be used on the ${mode} platform. Supported platforms include: ${supportedModes.join(', ')}`)) | ||
| } | ||
| const newRequest = `${EXTEND_COMPONENT_TARGET_PATH}/mpx-${componentName}.jsx` | ||
|
|
||
| const redirectRequest = Object.assign({}, request, { | ||
| request: newRequest, | ||
| fullySpecified: false, | ||
| __mpxResolvedExtendComponent: true | ||
| }) | ||
|
|
||
| resolver.doResolve( | ||
| target, | ||
| redirectRequest, | ||
| `resolve extend component: ${componentName} to ${newRequest}`, | ||
| resolveContext, | ||
| (err, result) => { | ||
| if (err) return callback(err) | ||
| if (!result) return callback(new Error(`Extended component "${componentName}" resolved to "${newRequest}", but the target file was not found.`)) | ||
| callback(null, result) | ||
| } | ||
| ) | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| function getComponentName (request) { | ||
|
yandadaFreedom marked this conversation as resolved.
|
||
| const descriptionFileData = request.descriptionFileData | ||
| const relativePath = request.relativePath && toPosix(request.relativePath) | ||
|
|
||
| if (!descriptionFileData || descriptionFileData.name !== '@mpxjs/webpack-plugin' || !relativePath || !relativePath.startsWith(EXTEND_COMPONENT_RELATIVE_PATH)) return | ||
|
|
||
| return relativePath.slice(EXTEND_COMPONENT_RELATIVE_PATH.length).replace(/\.[^/.]+$/, '') | ||
| } | ||
1 change: 1 addition & 0 deletions
1
packages/webpack-plugin/lib/runtime/components/extends/section-list.mpx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| <!-- section-list placeholder --> | ||
|
yandadaFreedom marked this conversation as resolved.
Outdated
|
||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.