第二章 构建第一个页面
文章目录
项目结构
经过上一章学习,我们的项目结构应该是
# 在 src 文件夹下面
│ App.tsx
│ index.tsx
ok,我们应当如何学习管理我们的项目结构呢?
- github上找一个已有项目,学习它的结构。
下面是一个开源项目,react-pxq
我们用它来学习一下目录结构。
# git@github.com:bailicangdu/react-pxq.git
├── api
│ ├── api.js
│ └── server.js
├── assets
│ └── iconfonts
│ ├── iconfont.css
│ ├── iconfont.eot
│ ├── iconfont.svg
│ ├── iconfont.ttf
│ └── iconfont.woff
├── components
│ ├── alert
│ │ ├── alert.jsx
│ │ └── alert.less
│ ├── header
│ │ ├── header.jsx
│ │ └── header.less
│ └── TouchableOpacity
│ ├── TouchableOpacity.jsx
│ └── TouchableOpacity.less
├── envconfig
│ └── envconfig.js
├── index.js
├── pages
│ ├── balance
│ │ ├── balance.jsx
│ │ └── balance.less
│ ├── helpcenter
│ │ ├── helpcenter.jsx
│ │ └── helpcenter.less
│ ├── home
│ │ ├── home.jsx
│ │ └── home.less
│ ├── production
│ │ ├── production.jsx
│ │ └── production.less
│ └── record
│ ├── components
│ │ ├── recordList.jsx
│ │ └── recordList.less
│ ├── record.jsx
│ └── record.less
├── registerServiceWorker.js
├── router
│ └── index.js
├── store
│ ├── home
│ │ ├── action.js
│ │ ├── action-type.js
│ │ └── reducer.js
│ ├── production
│ │ ├── action.js
│ │ ├── action-type.js
│ │ └── reducer.js
│ └── store.js
├── style
│ ├── base.css
│ └── mixin.less
└── utils
├── asyncComponent.jsx
├── mixin.js
└── setRem.js
有可取之处,但也有一些我们用不到的东西,就比如*.less
文件,Material提倡JSS
写法,这会在以后讲到,在这之前,我们先来规定一下我们的目录结构。
# in src
mkdir components
cd components
mkdir layout
现在,你的目录应该是这样的结构。
│ App.tsx
│ index.tsx
└─components
└─layout
ok,继续,我们在layout
创建两个文件:Footer.tsx
、Header.tsx
。
//Footer.tsx
import React from 'react'
const Footer: React.FunctionComponent = () => {
return <div>
</div>
}
export default Footer;
//Header.tsx
import React from 'react'
const Header:React.FunctionComponent = ()=>{
return <div>
</div>
}
export default Header;
为了能直观看到修改,我们先把这两个组件导出到App.tsx
中去。但是,如果我们不做任何修改,在App.tsx
中,我们会使用两条import
语句。
这在现在看起来不是问题,但是,随着我们layout
,含有越来越多的组件,那我们的引入语句将会越来越多,所以我们在layout
文件夹中创建index.tsx
这样,我们就可以通过它,引用 layout
文件夹中所有组件。
//src/components/layout/index.tsx
import Header from './Header'
import Footer from './Footer'
export {
Header,
Footer
}
然后修改我们的App.tsx
文件,引入我们的Header.tsx
、Footer.tsx
组件。
//App.tsx
import React from 'react'
import {Header,Footer} from './components/layout'
const App:React.FunctionComponent = ()=>{
return <>
<Header></Header>
<Footer></Footer>
</>
}
export default App;
使用AppBar(应用栏)组件
应用栏用来显示与当前屏幕相关的信息和操作。
cool!,看到漂亮的组件总是让人心潮澎湃对吧?
让我们开始在自己的页面上使用AppBar
组件!
//Header.tsx
import React from 'react'
import { AppBar, Toolbar, IconButton, Typography, Button } from '@material-ui/core'
import { Menu as MenuIcon } from '@material-ui/icons'
const Header: React.FunctionComponent = () => {
return <div>
<AppBar position="static">
<Toolbar>
<IconButton edge="start" color="inherit" aria-label="menu">
<MenuIcon />
</IconButton>
<Typography variant="h5">
New Page !
</Typography>
</Toolbar>
</AppBar>
</div>
}
export default Header;
有关App Bar
的相关说明你可以在这找到。
我们暂时不写任何css相关,为了更好的学习Material的思想,我们先把大致组件学习一遍。
我在文件中使用了React.FunctionComponent
组件,也是React所推崇的函数组件。
class组件带来了一些开发上的难题,比如生命周期的强耦合,我们无法理清思路、细分组件,所以新版本的React建议我们使用函数组件,并且提供了Hook API来解决函数组件无法保存state的问题。
Header.tsx
我们见到一些新元素比如AppBar, Toolbar, IconButton, Button
,想要一开始就完全弄清楚每个组件在什么时候用,使用什么参数,并不是好的学习习惯,我们因该先像喝水、睡觉去习惯它们,而不是去搞清楚水的成分、睡眠的作用。
使用 Tabs(选项卡)组件
下面,我们同样修改Footer.tsx
文件。
import React,{useState} from 'react'
import { Paper, Tab, Tabs } from '@material-ui/core'
const Footer: React.FunctionComponent = () => {
const [value,setValue]= useState(0)
/**
* tab选项卡切换时,触发事件
* @param event 事件对象
* @param val value值 选择的下标
*/
function handChage(event:React.ChangeEvent<{}>,val:any){
setValue(val)
}
return <div>
<Paper>
<Tabs value={value} onChange={handChage} aria-label="simple tabs example">
<Tab label="Item One" />
<Tab label="Item Two" />
<Tab label="Item Three" />
</Tabs>
</Paper>
</div>
}
export default Footer;
这里就要解释一些东西了,Tabs
组件,通过value
参数决定,哪一个Tab
选项被选择。
很明显,我们需要class组件的state成员变量了,但是我们应该追随官方,我们使用Hook API。
对Hook API 不清楚的同学,点击这里学习详细的知识。我在这只简单讲解一下。
useState(init_value)//返回[value,setValue],我们可以使用es6的数组结构方法,接受返回值。
//它相当我们
//state:{value:init_value}
//setValue(10)相当于setState({value:10})
通过使用Hook API在函数组件也能达到存放state。
使用Container、Grid、Box组件
现在,我们的layout
算是暂时搭建好了,现在我们开始构建内容。含有内容的页面已经算是一个page
了,所以我们再创建一个page
文件夹,存放我们所有页面。
src
└─pages
└─index
index.tsx
如果你在项目中使用了React-router等路由插件,就可以将这些页面放入路由,再将路由放入App.tsx
文件的中间。
现在使用这些组件,我们搭建一个类似于每日一文的网站出来,这是一个浏览型的网站,用来当练手的小项目最好不过。
Container(容器)组件
容器组件会让您页面的内容水平居中。 这是最基本的布局元素。
Name | Type | Default | Description |
---|---|---|---|
children * | node | ||
classes | object | 覆盖或扩展应用于组件的样式。 | |
component | elementType | ‘div’ | 用于根节点的组件。要么是使用DOM元素的字符串,要么是组件。 |
fixed | bool | false | 设置max-width以匹配当前断点的最小宽度。如果您希望设计一组固定大小而不是尝试容纳完全流体视口,这将非常有用。它默认是流动的。 |
maxWidth | ‘xs’ | ‘sm’ | ‘md’ | ‘lg’ | ‘xl’ | false | ‘lg’ | 确定容器的最大宽度。容器宽度随着屏幕的大小而增长。设置false 为禁用maxWidth 。 |
引入
import Container from '@material-ui/core/Container';
// or
import { Container } from '@material-ui/core';
一般来说,我们都是从@material-ui/core
库中引入组件,从@material-ui/icons
引入图标。只要大家记好,不使用文档也能开发页面。
显然,我们使用Container
作为主体显示最合适不过,因为它自动居中,且自适应,当然我们也可以自己写组件实现这些功能,不过使用Material-UI
就是要追求快,写轮子当然是好事,但不能在学习使用轮子的时候,去造轮子,这样只会导致自己学习速度过慢。
在index.tsx
中放入下面代码。
import React from 'react'
import { Paper, Typography, Container, Grid, Box } from '@material-ui/core'
const Index: React.FunctionComponent = () => {
return <>
<Paper>
<Container maxWidth="lg">
<Box border={1} p={3} m={4} borderColor="grey.500">
<Grid>
<Typography variant="h3" align="center" component="p">
街
</Typography>
</Grid>
<br />
</Box>
</Container>
</Paper>
</>
}
export default Index;
相信聪明的大伙都能看懂,我们在Paper
组件中放入了Container
组件,并使最大宽度不超出lg(1280px)
ok,接下来介绍Grid
组件
Grid(栅格)组件
Material Design 响应式布局的栅格可适应屏幕大小和方向,确保布局之间的一致性。
栅格(Grid)组件能确保不同布局间的视觉一致性,同时在众多不同设计中保持灵活性。 Material Design 的响应式 UI 是基于12列的栅格布局。
ok,明白了,12列又是你。接着看
响应式栅格侧重于一致的间距宽度,而不是列宽。 Material design 外边距和列遵循8px的方块形基线栅格。 spacing 属性设置为一个在0和10之间的整数,且并包括0和10。 默认情况下,两个网格项之间的间距遵循这样的线性函数: output(spacing) = spacing * 8px
,例如 spacing={2}
会创建一个 16px 的宽间距。
[外链图片转存失败(img-HLJz50mz-1568532470145)(C:\Users\Wilin\Desktop\yhy\assets\QQ图片20190915110606.png)]
Box(分组)组件
Box 组件充当大多数 CSS 实用程序所需求的包装器组件。
说明上来看,Box并没有给我们提供过多的样式,更多的是让我们使用它的语义化,告诉其他开发者,这是我们自定义的div,用来包装其他组件。
这几个组件差不多了解,我们开始写代码。
Index 页面
首先,我们需要大量的文字,用来填充主页使其达到每日一文网站的效果。想象一下我们有很多段落,但是都要通过同种方式渲染,所以我们可以封装一下,这段的逻辑。
第二,我们用js数组来存放我们所有的文字。效果应该是这样的
我们如果要使用,直接通过import TextJson from './text.json.js'
。
然后,我们来编写渲染的组件。
interface ITextPorps {
text: string
}
const TextLine: React.FunctionComponent<ITextPorps> = (porps) => {
const { text } = porps
return <>
<Grid>
<Typography variant="h5" gutterBottom component="p">
{text}
</Typography>
</Grid>
<br />
</>
}
我们的TextLine
组件,接收text
属性,并将其渲染。
最后,我们的文件应该是这样子的。
import React from 'react'
import { Paper, Typography, Container, Grid, Box } from '@material-ui/core'
import TextJson from './index.json'
interface ITextPorps {
text: string
}
const TextLine: React.FunctionComponent<ITextPorps> = (porps) => {
const { text } = porps
return <>
<Grid>
<Typography variant="h5" gutterBottom component="p">
{text}
</Typography>
</Grid>
<br />
</>
}
const Index: React.FunctionComponent = () => {
return <>
<Paper>
<Container maxWidth="lg">
<Box border={1} p={3} m={4} borderColor="grey.500">
<Grid>
<Typography variant="h3" align="center" component="p">
街
</Typography>
</Grid>
<br />
{TextJson.map((item, key) => {
return <TextLine text={item.text} key={key} />
})}
</Box>
</Container>
</Paper>
</>
}
export default Index;
最最后,我们需要把页面放到App.tsx
文件中,这样才能看到我们的效果。
//App.tsx
import React from 'react'
import {Header,Footer} from './components/layout'
import Index from './pages/index'
const App:React.FunctionComponent = ()=>{
return <>
<Header></Header>
<Index></Index>
<Footer></Footer>
</>
}
export default App;