记一个复杂组件(Filter)的从设计到开发 (5)

开源版本(Ts+hooks+lerna)还未公布,所以目前还是采用 rax 0.x 的版本编写的代码。这里只做,有坑的地方代码处理讲解。欢迎各位大佬评论留出各位想法

Filter.js

先从 render 方法看起

render() { const { style = {}, styles = {}, navConfig, keepHighlight } = this.props; const { windowHeight, activeIndex } = this.state; if (!windowHeight) return null; return ( <View style={[defaultStyle.container, styles.container, style]}> {this.renderPanels()} <Navbar ref={r => { this.refNavbar = r; }} navConfig={navConfig} styles={styles} keepHighlight={keepHighlight} activeIndex={activeIndex} onNavbarPress={this.handleNavbarPress} onChange={this.handleSearchChange} /> </View> ); }

获取一些基本配置,以及 windowHeight(屏幕高度)和 activeIndex(当前第几个item 处于 active 状态(被点开))。

之所以我们的 renderPanels 写在 NavBar 上面,是因为在 weex 中,zIndex 是不生效的。若想 A 元素在 B 元素上面,则 render 的时候,A 必须在 B 后面。这样写是为了 panel 面板展开的下拉动画,看起来是从 navBar 下面出来的。

renderPanel 方法就是渲染对应的 panel

/** * 渲染 Panel */ renderPanels = () => { const { activeIndex, windowHeight } = this.state; let { children } = this.props; if (!Array.isArray(children)) { children = [children]; } let index = 0; return children.map(child => { let panelChild = null; let hasPanel = this.panelIndexes[index]; if (!hasPanel) { index++; } if (!this.panelManager[index]) { this.panelManager[index] = {}; } let injectProps = { index, visible: activeIndex === index, windowHeight, filterBarHeight: this.filterBarHeight, maxHeight: this.filterPanelMaxHeight, shouldInitialRender: this.panelManager[index].shouldInitialRender, onChange: this.handleSearchChange.bind(this, index), onNavTextChange: this.handleNavTextChange.bind(this, index), onHidePanel: this.setPanelVisible.bind(this, false, index), onMaskClick: this.handleMaskClick, disableNavbarClick: this.disableNavbarClick, }; if (child.type !== Panel) { panelChild = <Panel {...injectProps}>{child}</Panel>; } else { panelChild = cloneElement(child, injectProps); } index++; return panelChild; }); };

准确的说,这是一个 HOC,我们将代理、翻译传给 Filter 的影响或者 panel 面板需要使用的 props 传递给 Panel 面板。比如 onChange 回调,或者面板隐藏的回调以及当前哪一个 panel 需要展开等。

由于 Panel 的面板复杂度我们未知。为了避免不断的展开和收齐不必要的 render,我们采用 transform的方式,将面板不需要显示的面板移除屏幕外,需要展示的在移入到屏幕内部。具体可见 Panel 的render return

return ( <View ref={r => { this.refPanelContainer = r; }} style={[ defaultStyle.panel, styles.panel, this.panelContainerStyle, { transform: `translateX(-${this.containerTransformDes})`, opacity: 0, }, ]}> <View ref="mask" style={[ defaultStyle.mask, styles.mask, showStyle, isWeb ? { top: 0, zIndex: -1 } : { top: 0 }, ]} onClick={this.handleMaskClick} onTouchMove={this.handleMaskTouchMove} /> {cloneElement(child, injectProps)} </View> );

注意: Panel 面板的坑远不止这些,比如,我们都知道,render 是最消耗页面性能的,而页面初始化进来,面板名没有展示出来(此时面板 Panel 在屏幕外),那么是否需要走 Panel 面板的 render 呢?但是目前的这种写法,Panel 组件的生命周期是会都走到的。但是如果遇到 Panel 里面需要请求数据,然后页面 url 里查询参数有 locationId=123 ,navItem 需要展示对应的地理位置.如果不渲染 Panel 如何根据 id 拿到对应的地名传递给 navItem 去展示?对,我们可以拦截 Panel 面板的 render 方法,让 Panel render null,然后别的生命周期照样运行。但是,如果 render 中用户有对 ref 的使用,那么就可能会造成难以排查的 bug。

所以最终,为了提高页面的可交互率但是又不影响页面需求的情况下,我们提供了一个可选的工具:Performance HOC 。 注意,是可选。

export default function performance(Comp) { return class Performance extends Comp { static displayName = `Performance(${Comp.displayName})`; render() { const { shouldInitialRender } = this.props.panelAttributes; if (shouldInitialRender) { return super.render(); } else { return <View />; } } }; }

通过配置Panel 的 shouldInitialRender 属性来告诉我,是否第一次进来,拦截 render。

当然,Panel 也有很多别的坑,比如,现在 Panel 为了重复 render,将 Panel 移除屏幕外,那么,动画从上而下展开设置初始动画闪屏如何处理?

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzzzys.html