> For the complete documentation index, see [llms.txt](https://edsign.gitbook.io/ed-mobile/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://edsign.gitbook.io/ed-mobile/gong-gong-bu-ju-shi-yong-shou-ce/ye-mian-bu-ju.md).

# 页面布局

这里提供的布局只是一种布局思路或者说策略，需要根据实际项目进行调整。

页面分割为三块内容，头部-header，填充内容- page-content，底部-footer，这三部分都不需要使用定位就可以固定在顶部和底部，这样就不会有软键盘弹出时fixed失效的问题。可以滚动的只有填充内容- page-content部分。

#### 根元素的样式：

```css
#root {
  height: 100vh;
  width: 100vw;
  background-color: $ed-page-bgcolor-primary;
  font-size: pxToRem(16);
  font-family: $fontFamily;
  overflow: hidden;
  position: relative;
}
```

需要注意的是，根元素设置了高度（屏幕高度），宽度（屏幕宽度），定位，以及元素溢出屏幕时隐藏而不是滚动。

#### 头部-header的样式：

```css
.header {
  width: 100vw;
  height: size(45);
}
```

修改header的高度时，需要在.page-content和.page-content.no-footer样式中修改height减去的高度。

#### 底部-footer的样式

```css
.footer {
  height: size(45);

  .footer-menu-active {
    color: #FFF;
  }
}
```

修改footer的高度时，需要在.page-content样式中修改height属性减去的高度。

#### 填充内容- page-content的样式：

```css
.page-content {
  position: relative;
  background-color: #FFF;
  height: calc(100vh - 45px - 45px);
  overflow-y: scroll;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch
}

.page-content.no-header {
  height: calc(100vh - 45px);
}

.page-content.no-footer {
  height: calc(100vh - 45px);
}
```

只需要头部添加类名header ，填充内容添加类名page-content，根据是否有头部和底部添加对应的类名，底部添加类名footer，这个布局就完成了。

page-content的高度=屏幕高度-头部高度-底部高度，这样保证三块内容高度相加等于根元素的高度且不溢出，page-content设置了Y轴方向的滚动，同时具有position属性和高度，所以其子元素除了fixed定位外的所有定位不会是body和根元素，子元素使用百分比设置高度也会生效。需要X轴滚动的情况，在滚动区域外加一层div，不建议修改这个类。

样式上定义了没有头部时page-content的高度和没有底部时page-content的高度，没有头部也没有底部的页面，请不要用这个高阶组件，自己定义一个样式吧，当然，添加一个.page-content.no-header.no-footer的类名也可以。

#### **关于适配样式**

适配需要留出空白的部分，只需要调整根元素的高度或者内边距，和填充内容的高度就可以完成，以IPhoneX适配为例，需要在底部留出20px的高度，这个时候只需要让根元素的高度和填充内容page-content的高度都减少20px就可以了。

```css
#root.adapt-iosX {
  height: calc(100vh - 20px);

  .page-content {
    height: calc(100vh - 45px - 45px - 20px);
  }
  
  .page-content.no-header {
    height: calc(100vh - 45px - 20px);
  }

  .page-content.no-footer {
    height: calc(100vh - 45px - 20px);
  }
}
```

#### 使用高阶组件

如果每个页面都需要手动添加这些样式是比较麻烦的，尤其是当需要修改或者添加一些公共类名的时候，所以有了高阶组件PublicPage.jsx，PublicPage组件内头部显示内容使用的Header组件，底部显示内容使用的EdTabBar组件，填充内容显示的是传进来的组件，头部显示内容和底部显示内容可以根据实际项目需要进行修改。

显示的控制：UI库中，header默认是一定会显示的，后续可能会做一些优化，把头部也变成通过读取配置控制显示，底部是根据传参footerKey，读取底部内容的配置footerConfig，获取底部显示内容（如问文字，图标，路由）。

参数传递：像是Header组件需要传入title和backFn，为了PublicPage组件能够简便的从传入的组件中获取参数，除了函数外，都是从静态变量中获取，比如

```jsx
// 页面，LayoutFlex就是高阶组件接收参数中的WrappedComponent
class LayoutFlex extends Component {
  static title = 'flex公共布局';

  constructor(props) {
    super(props);
  }
  ...
}
// 高阶组件
export default (WrappedComponent, pageParam) => {
  class PublicPage extends Component {
    ...
    render(){
      // WrappedComponent.title就可以获取到LayoutFlex设置的静态变量title
      const title = WrappedComponent.title || pageParam.title || '';
    }
  }
  return PublicPage;
}
```

&#x20;为什么不通过静态变量的方式获取组件内的函数呢？因为静态变量无法获取到组件任何的props和state，所以使用了通过ref的方式，如下：

```jsx
// 页面，LayoutFlex就是高阶组件接收参数中的WrappedComponent
class LayoutFlex extends Component {
  static title = 'flex公共布局';

  constructor(props) {
    super(props);
  }
  
  backFn = () => {
    console.log('backFn')
  };
  
  ...
}
// 高阶组件
export default (WrappedComponent, pageParam) => {
  class PublicPage extends Component {
    constructor(props) {
      ...
      this.wrappedRef = React.createRef();
    }
    
    backFn = () => {
      this.wrappedRef.current.back();
    }
    
    ...
    
    render(){
      <WrappedComponent ref={this.wrappedRef} />
    }
  }
  return PublicPage;
}
```

但是，ref这种方式不适用于函数声明的组件，所以高阶组件还接收一个参数对象pageParam，pageParam的属性名与组件设置的静态变量一致，且行为一致，函数声明的组件需要设置pageParam.isFunComponent = true，此时不会创建ref，且Header组件的backFn和rightFn触发时执行 window\.history.back()。

#### 组件静态变量说明：

| 属性名       | 作用                                                                         |
| --------- | -------------------------------------------------------------------------- |
| title     | Header组件的children，默认值为空                                                    |
| isGoback  | Header组件isGoback的属性值，默认为true                                               |
| right     | Header组件right的属性值，无默认值                                                     |
| footerKey | 底部配置footerConfig的key，若footerKey或footerConfig\[footerKey]布尔值为false，则页面不显示底部 |

#### &#x20;高阶组件内还可以做什么？

&#x20;这个高阶组件可能拿到项目里还需要少量更改，因为引入了Header和EdTabBar这两个组件，大多数时候替换头部和底部为项目自己的组件就可以了。

&#x20;PublicPage.jsx文件内的footerConfig，是底部显示内容的配置，需要显示底部的页面只需要设置footerKey就可以了。

&#x20;高阶组件内还添加了componentDidCatch声明周期，用于捕获页面级别的错误，同样的，只需要在高阶组件内写一遍就可以了。

在AppRouter中可以看到有两个路由配置，区别是RouterHOCConfig中的页面会使用高阶组件，RouterConfig中的页面不会使用高阶组件，为了使用方便，封装了Router，代码如下：

```jsx
const Router = (props) => {
  const { path, exact = true, component, pageParam = {} } = props;
  return (
    <Route path={path} exact={exact} component={PublicPage(component, pageParam)} />
  );
};

export default Router;
```

这样页面就和平时开发的就没有区别了，只需要设置一些静态变量就可以，而且不需要每个页面引入Header组件。
