Appearance
antd-mobile 使用
bash
npm i antd-mobile
# https://antd-mobile-v2.surge.sh/components/tab-bar-cn/#components-tab-bar-demo-basic
路由嵌套
js
// 准备父路由
function App() {
return (
<BrowserRouter>
<div className="App">
<Route path="/home" component={Home}></Route>
</div>
</BrowserRouter>
)
}
js
// 配置子路由
import React from 'react'
import News from '../News'
import { Route } from 'react-router-dom'
export default class Home extends React.Component {
render() {
return (
<div>
首页
<Route path="/home/news" component={News}></Route>
</div>
)
}
}
路由重定向
js
// 导入组件
// import { Button } from "antd-mobile";
import { BrowserRouter, Route, Redirect } from 'react-router-dom'
import Home from './pages/Home'
import CityList from './pages/CityList'
function App() {
return (
<BrowserRouter>
<div className="App">
{/* Redirect 可用于路由重定向,如下写法可实现默认路由,当访问 / 路径时自动重定向到 home */}
<Route path="/" render={() => <Redirect to="/home" />}></Route>
<Route path="/home" component={Home}></Route>
</div>
</BrowserRouter>
)
}
在钩子函数中获取路由变化
js
// 这样就能获取到变化前后的路由信息
componentDidUpdate(prevProps) {
console.log(this.props);
console.log(prevProps);
}
配置 css 预编译语言
bash
# 由于 react 脚手架内置了 sass-loader 所以只需要安装 sass 即可,sass是以 .scss结尾的文件
npm install sass # 安装sass
百度地图 API
js
// 百度地图开放平台
https://lbsyun.baidu.com/
// 开发文档
https://lbsyun.baidu.com/index.php?title=jspopularGL/guide/geoloaction
// api 文档
https://mapopen-pub-jsapi.bj.bcebos.com/jsapi/reference/jsapi_webgl_1_0.html
1.在 index.html 中引入 js 文件
html
<script
type="text/javascript"
src="https://api.map.baidu.com/apiv=1.0&&type=webgl&a
k=Ql6g9FMhKC8AQhzbPG7erzSwCEAMmCCP"
></script>
2.在组件中使用
js
import React from 'react'
import './index.scss'
export default class Map extends React.Component {
componentDidMount() {
// 初始化地图实例
// 在 react 脚手架中全局对象需要使用 window 来访问,否则会造成 ESlint 校验错误
const map = new window.BMapGL.Map('container')
// 设置中心点坐标
const point = new window.BMapGL.Point(116.404, 39.915)
// 初始化地图 centerAndZoom 第二个参数用于设置地图展示(放大)级别
map.centerAndZoom(point, 15)
}
render() {
return (
<div className="map">
{/* 地图容器元素 */}
<div id="container">zxczxc</div>
</div>
)
}
}
通过 ip 获取当前城市名
js
const curCity = new window.BMapGL.LocalCity()
curCity.get(res => {
console.log(res)
})
通过城市名转换为坐标并渲染
js
import React from 'react'
import './index.scss'
import NavHeader from '../../components/NavHeader'
export default class Map extends React.Component {
componentDidMount() {
const { label, value } = JSON.parse(sessionStorage.getItem('hkzf_city'))
const map = new window.BMapGL.Map('container')
//创建地址解析器实例
const myGeo = new window.BMapGL.Geocoder()
// 将地址解析结果显示在地图上,并调整地图视野
// getPoint 第一个参数表示需要转换为坐标的地区名称,最后一个参数需要指定地址解析所在的城市
myGeo.getPoint(
label,
point => {
if (point) {
// 通过参数 point 得到转换后的坐标初始化地图和缩放级别
map.centerAndZoom(point, 16)
// map.addOverlay 表示在地图标记点添加一个标记物
// map.addOverlay(new window.BMapGL.Marker(point));
}
},
label
)
}
render() {
return (
<div className="map">
<NavHeader title="地图找房" />
{/* 地图容器元素 */}
<div id="container"></div>
</div>
)
}
}
添加控件
js
import React from 'react'
import './index.scss'
import NavHeader from '../../components/NavHeader'
export default class Map extends React.Component {
componentDidMount() {
const { label, value } = JSON.parse(sessionStorage.getItem('hkzf_city'))
const map = new window.BMapGL.Map('container')
//创建地址解析器实例
const myGeo = new window.BMapGL.Geocoder()
// 将地址解析结果显示在地图上,并调整地图视野
// getPoint 第一个参数表示需要转换为坐标的地区名称,最后一个参数需要指定地址解析所在的城市
myGeo.getPoint(
label,
point => {
if (point) {
// 初始化地图和缩放级别
map.centerAndZoom(point, 11)
// 添加比例尺控件////////////////////////////////////////////////////////////
map.addControl(new window.BMapGL.ScaleControl())
// 添加缩放控件////////////////////////////////////////////////////////////
map.addControl(new window.BMapGL.ZoomControl())
}
},
label
)
}
render() {
return (
<div className="map">
<NavHeader title="地图找房" />
{/* 地图容器元素 */}
<div id="container"></div>
</div>
)
}
}
添加文本标注
js
import React from 'react'
import './index.scss'
import NavHeader from '../../components/NavHeader'
export default class Map extends React.Component {
initMap() {
const { label, value } = JSON.parse(sessionStorage.getItem('hkzf_city'))
const map = new window.BMapGL.Map('container')
//创建地址解析器实例
const myGeo = new window.BMapGL.Geocoder()
// 将地址解析结果显示在地图上,并调整地图视野
// getPoint 第一个参数表示需要转换为坐标的地区名称,最后一个参数需要指定地址解析所在的城市
myGeo.getPoint(
label,
point => {
if (point) {
// 初始化地图和缩放级别
map.centerAndZoom(point, 11)
// 添加比例尺控件
map.addControl(new window.BMapGL.ScaleControl())
// 添加缩放控件
map.addControl(new window.BMapGL.ZoomControl())
// 创建文本标注对象 第二个参数表示配置对象////////////////////////////////////////
const ops = {
position: point, // 文本标注显示位置
offset: new window.BMapGL.Size(30, -30) // 设置偏移量
}
const label = new window.BMapGL.Label('测试文本!', ops)
// 自定义文本标注样式
label.setStyle({
color: 'blue'
// borderRadius: "5px",
// borderColor: "#ccc",
// padding: "10px",
// fontSize: "16px",
// height: "30px",
// lineHeight: "30px",
// fontFamily: "微软雅黑",
})
// 将配置好的文本标注添加到地图中
map.addOverlay(label)
///////////////////////////////////////////////////////////////////////
}
},
label
)
}
componentDidMount() {
this.initMap()
}
render() {
return (
<div className="map">
<NavHeader title="地图找房" />
{/* 地图容器元素 */}
<div id="container"></div>
</div>
)
}
}
自定义覆盖物
js
import React from 'react'
import './index.scss'
import styles from './index.module.css'
import NavHeader from '../../components/NavHeader'
export default class Map extends React.Component {
initMap() {
const { label, value } = JSON.parse(sessionStorage.getItem('hkzf_city'))
const map = new window.BMapGL.Map('container')
//创建地址解析器实例
const myGeo = new window.BMapGL.Geocoder()
// 将地址解析结果显示在地图上,并调整地图视野
// getPoint 第一个参数表示需要转换为坐标的地区名称,最后一个参数需要指定地址解析所在的城市
myGeo.getPoint(
label,
point => {
if (point) {
// 初始化地图和缩放级别
map.centerAndZoom(point, 11)
// 添加比例尺控件
map.addControl(new window.BMapGL.ScaleControl())
// 添加缩放控件
map.addControl(new window.BMapGL.ZoomControl())
// 创建文本标注对象 第二个参数表示配置对象
const ops = {
position: point // 文本标注显示位置
// offset: new window.BMapGL.Size(30, -30), // 设置偏移量
}
// 设置 setContent 后 BMapGL.Label 的第一个参数设置文本内容就被覆盖掉了
const label = new window.BMapGL.Label('', ops)
///////////////////////////////////////////////////////////////////////
// 用于自定义文本标注覆盖物显示内容,传递一段html代码
label.setContent(`<div class="${styles.bubble}">
<p class="${styles.name}">浦东</p>
<p>98套</p>
</div>`)
// 自定义文本标注样式
label.setStyle({
cursor: 'pointer',
border: '0px solid rgb(255,0,0)',
padding: '0',
whiteSpace: 'normal',
fontSize: '12px',
color: 'rgb(252, 228, 228)',
textAlign: 'center'
})
// 添加点击事件
label.addEventListener('click', () => {
console.log('点击')
})
///////////////////////////////////////////////////////////////////////
// 将配置好的文本标注添加到地图中
map.addOverlay(label)
}
},
label
)
}
componentDidMount() {
this.initMap()
}
render() {
return (
<div className="map">
<NavHeader title="地图找房" />
{/* 地图容器元素 */}
<div id="container"></div>
</div>
)
}
}
css
.bubble {
width: 70px;
height: 70px;
line-height: 1;
display: inline-block;
position: absolute;
border-radius: 100%;
background-color: #0ad38b;
color: #fff;
border: 2px solid rgba(255, 255, 255, 0.8);
text-align: center;
cursor: pointer;
}
.name {
padding: 18px 0 6px 0;
}
点击覆盖物放大并定位
js
import React from 'react'
import './index.scss'
import styles from './index.module.css'
import axios from 'axios'
import NavHeader from '../../components/NavHeader'
export default class Map extends React.Component {
initMap() {
const { label, value } = JSON.parse(sessionStorage.getItem('hkzf_city'))
const map = new window.BMapGL.Map('container')
//创建地址解析器实例
const myGeo = new window.BMapGL.Geocoder()
// 将地址解析结果显示在地图上,并调整地图视野
// getPoint 第一个参数表示需要转换为坐标的地区名称,最后一个参数需要指定地址解析所在的城市
myGeo.getPoint(
label,
async point => {
if (point) {
// 初始化地图和缩放级别
map.centerAndZoom(point, 11)
// 添加比例尺控件
map.addControl(new window.BMapGL.ScaleControl())
// 添加缩放控件
map.addControl(new window.BMapGL.ZoomControl())
// 创建文本标注对象 第二个参数表示配置对象
// const ops = {
// position: point, // 文本标注显示位置
// offset: new window.BMapGL.Size(-35, -35), // 设置偏移量
// };
// 创建多个覆盖物
//////////////////////////////////////////////////////////////////////
// 获取房源信息
const { data } = await axios.get(
`http://localhost:8080/area/map?id=${value}`
)
data.body.forEach(item => {
const areaPoint = new window.BMapGL.Point(
item.coord.longitude,
item.coord.latitude
)
const label = new window.BMapGL.Label('', {
position: areaPoint, // 文本标注显示位置
offset: new window.BMapGL.Size(-35, -35) // 设置偏移量
})
// 用于自定义文本标注覆盖物显示内容,传递一段html代码
label.setContent(`<div class="${styles.bubble}">
<p class="${styles.name}">${item.label}</p>
<p>${item.count}套</p>
</div>`)
// 自定义文本标注样式
label.setStyle({
cursor: 'pointer',
border: '0px solid rgb(255,0,0)',
padding: '0',
whiteSpace: 'normal',
fontSize: '12px',
color: 'rgb(252, 228, 228)',
textAlign: 'center'
})
// 添加点击事件
label.addEventListener('click', () => {
// 第一个参数表示放大区域的中心坐标,第二个参数表示放大级别
map.centerAndZoom(areaPoint, 13)
// 清除页面上所有的覆盖物
map.clearOverlays()
})
// 将配置好的文本标注添加到地图中
map.addOverlay(label)
})
//////////////////////////////////////////////////////////////////////
}
},
label
)
}
componentDidMount() {
this.initMap()
}
render() {
return (
<div className="map">
<NavHeader title="地图找房" />
{/* 地图容器元素 */}
<div id="container"></div>
</div>
)
}
}
获取当前点击覆盖物坐标并移动地图
js
// 绘制镇覆盖物
createRect(point, name, count, id) {
const label = new window.BMapGL.Label("", {
position: point, // 文本标注显示位置
offset: new window.BMapGL.Size(-50, -28), // 设置偏移量
});
label.setContent(`<div class="${styles.rect}">
<span class="${styles.housename}">${name}</span>
<span class="${styles.housenum}">${count}套</span>
<i class="${styles.arrow}"></i>
</div>`);
// 自定义文本标注样式
label.setStyle({
cursor: "pointer",
border: "0px solid rgb(255,0,0)",
padding: "0",
whiteSpace: "normal",
fontSize: "12px",
color: "rgb(252, 228, 228)",
textAlign: "center",
});
// 添加点击事件
label.addEventListener("click", (e) => {
//////////////////////////////////////////////////
console.log(e.domEvent.changedTouches[0]);
// panBy 方法用于移动地图位置,第一个参数表示 x 轴方向需要移动的距离,第二个表示 y 轴需要移动的距离
// 这里用于移动到屏幕中心位置
this.map.panBy(
window.innerWidth / 2 - e.domEvent.changedTouches[0].clientX,
(window.innerHeight - 300) / 2 - e.domEvent.changedTouches[0].clientY
);
//////////////////////////////////////////////////
this.getHousesList(id);
});
// 将配置好的文本标注添加到地图中
this.map.addOverlay(label);
}
给地图添加地图移动事件
js
// 初始化地图
initMap() {
const { label, value } = JSON.parse(sessionStorage.getItem("hkzf_city"));
const map = new window.BMapGL.Map("container");
// 保存地图实例供其他地方使用
this.map = map;
//创建地址解析器实例
const myGeo = new window.BMapGL.Geocoder();
// 将地址解析结果显示在地图上,并调整地图视野
// getPoint 第一个参数表示需要转换为坐标的地区名称,最后一个参数需要指定地址解析所在的城市
myGeo.getPoint(
label,
async (point) => {
if (point) {
// 初始化地图和缩放级别
map.centerAndZoom(point, 11);
// 添加比例尺控件
map.addControl(new window.BMapGL.ScaleControl());
// 添加缩放控件
map.addControl(new window.BMapGL.ZoomControl());
// 渲染覆盖物入口
this.renderOverlays(value);
///////////////////////////////////////////////////////////////////////////
// 给地图添加移动事件,可以监听用户手指或鼠标拖动地图事件
map.addEventListener("movestart", () => {
console.log("55555555555555555555555555555");
this.setState({
isshow: false,
});
});
///////////////////////////////////////////////////////////////////////////
}
},
label
);
}
长列表
js
// 使用 react-virtualized 实现长列表渲染
// 原理:只渲染页面可视区域的内容,可视区域之外的不渲染,提高性能
bash
npm i react-virtualized # 安装
js
// 在 index.js 中导入样式文件
import 'react-virtualized/styles.css'
js
// 打开文档找到需要使用的组件
https://github.com/imkf-zhang/react-virtualized/blob/master/docs/README.md
js
import {List} from 'react-virtualized';
// 列表数据
const list = new Array(100).fill("测试一下");
// 函数返回值就是列表最终展示的内容
function rowRenderer({
key, // 渲染时的唯一 key 值
index, // 每条数据的索引号
isScrolling, // 表示当前项是不是在滚动中
isVisible, // 当前项在列表中是否可见
style, // 表示样式对象,将来会被应用到每一项,且必须添加该属性,指定每一行的位置
}) {
return (
<div key={key} style={style}>
{list[index]}
</div>
);
}
export default class CityList extends React.Component {
render() {
return (
<div className="citylist">
{/* 城市列表 */}
<List
width={300}
height={300}
{/* rowCount 指定当前列表行数 */}
rowCount={list.length}
{/* rowHeight 指定每行的高度 */}
rowHeight={20}
rowRenderer={rowRenderer}
/>
</div>
);
}
}
让列表占满屏幕
js
import { List, AutoSizer } from 'react-virtualized'
const list = new Array(100).fill('测试一下')
function rowRenderer({
key, // 渲染时的唯一 key 值
index, // 每条数据的索引号
isScrolling, // 表示当前项是不是在滚动中
isVisible, // 当前项在列表中是否可见
style // 表示样式对象,将来会被应用到每一项,且必须添加该属性,指定每一行的位置
}) {
return (
<div key={key} style={style}>
{list[index]}
</div>
)
}
export default class CityList extends React.Component {
render() {
return (
<div className="citylist">
{/* 城市列表 */}
{/* 使用 AutoSizer 组件时该组件父元素必须有高度 */}
<AutoSizer>
{({ width, height }) => (
<List
width={width}
height={height}
rowCount={list.length}
rowHeight={20}
rowRenderer={rowRenderer}
/>
)}
</AutoSizer>
</div>
)
}
}
动态设置每行的高度
js
// 可以通过解构出来的 index ,表示当前行的索引
getRowHeight = ({ index }) => {
return (this.state.cityList[this.state.cityIndex[index]].length + 1) * 50;
};
render() {
return (
<div className="citylist">
{/* 城市列表 */}
{/* 使用 AutoSizer 组件时该组件父元素必须有高度 */}
<AutoSizer>
{({ width, height }) => (
<List
width={width}
height={height}
rowCount={this.state.cityIndex.length}
{/* rowHeight 可以为一个函数,可以从其形参中解构出当前项索引 */}
rowHeight={this.getRowHeight}
rowRenderer={this.rowRenderer}
/>
)}
</AutoSizer>
</div>
);
}
获取当前页面列表顶部 item 项索引
js
// 获取当前列表顶部 item 项索引
onRowsRendered = ({ startIndex }) => {
console.log(startIndex);
};
render() {
return (
<div className="citylist">
<AutoSizer>
{({ width, height }) => (
<List
width={width}
height={height}
rowCount={this.state.cityIndex.length}
rowHeight={this.getRowHeight}
rowRenderer={this.rowRenderer}
onRowsRendered={this.onRowsRendered}
/>
)}
</AutoSizer>
</div>
);
}
实现点击跳转
js
constructor(props) {
super(props);
////////////////////////////////////////////////////////////////////
// 创建 ref 对象 用于获取页面元素
this.cityListComponent = React.createRef();
////////////////////////////////////////////////////////////////////
}
async getCityList() {
const res = await axios.get(`http://localhost:8080/area/city?level=1`);
const obj = {};
res.data.body.forEach((item) => {
if (obj[item.short.slice(0, 1)]) {
obj[item.short.slice(0, 1)].push(item);
} else {
obj[item.short.slice(0, 1)] = [item];
}
});
const citystr = Object.keys(obj).sort();
citystr.unshift("hot");
citystr.unshift("#");
const res2 = await axios.get(`http://localhost:8080/area/hot`);
obj.hot = res2.data.body;
const label = await getCurrentCity();
obj["#"] = [label];
////////////////////////////////////////////////////////////////////
this.setState(
{
cityList: obj,
cityIndex: citystr,
},
() => {
// 调用 me 提前计算 List 中每一行的高度,解决跳转时精度偏差问题
this.cityListComponent.current.measureAllRows();
}
);
////////////////////////////////////////////////////////////////////
}
renderCityIndex() {
return this.state.cityIndex.map((item, i) => (
////////////////////////////////////////////////////////////////////
<li
className="city-index-item"
key={item}
onClick={() => {
// 注意 scrollToRow 方法只能在已经在页面上渲染过一次的 item 项中跳转,否则会出现位置偏差问题
this.cityListComponent.current.scrollToRow(i);
}}
>
<span className={i === this.state.activeIndex ? "index-active" : ""}>
{item === "hot" ? "热" : item.toUpperCase()}
</span>
</li>
////////////////////////////////////////////////////////////////////
));
}
render() {
return (
<div className="citylist">
<AutoSizer>
{({ width, height }) => (
<List
ref={this.cityListComponent}
width={width}
height={height}
rowCount={this.state.cityIndex.length}
rowHeight={this.getRowHeight}
rowRenderer={this.rowRenderer}
onRowsRendered={this.onRowsRendered}
// 当调用 onRowsRendered 跳转时始终保持跳转项出现在页面顶部
scrollToAlignment="start"
/>
)}
</AutoSizer>
{/* 右侧索引列表 */}
<ul className="city-index">{this.renderCityIndex()}</ul>
</div>
);
}
使用 WindowScroller 高阶组件跟随页面滚动
js
// 默认情况下 react-virtualized 生成的列表会自己生成滚动条而不会跟随页面滚动
// github.com/imkf-zhang/react-virtualized/blob/master/docs/WindowScroller.md
js
export default class HouseList extends React.Component {
// 渲染列表每一行
rowRenderer = ({ key, index, style }) => {
return (
<HouseItem
key={key}
style={style}
src={'http://localhost:8080' + this.state.list[index].houseImg}
title={this.state.list[index].title}
desc={this.state.list[index].desc}
tags={this.state.list[index].tags}
price={this.state.list[index].price}
/>
)
}
render() {
return (
<div>
<div className={styles.houseItems}>
{/* 结合 AutoSizer 和 WindowScroller 实现跟随页面滚动并且设置全屏高宽*/}
<WindowScroller>
{({ height, isScrolling, scrollTop }) => (
<AutoSizer>
{({ width }) => (
<List
autoHeight // 设置高度为 WindowScroller 最终渲染的列表高度
width={width} // 视口宽度
height={height} // 视口高度
rowCount={this.state.count} // list 列表项的行数
rowHeight={120} // 每一行的高度
rowRenderer={this.rowRenderer} // 渲染列表项中的每一行
isScrolling={isScrolling} // 指示表格或列表是否正在滚动
scrollTop={scrollTop} // 从页面滚动距离
/>
)}
</AutoSizer>
)}
</WindowScroller>
</div>
</div>
)
}
}
样式隔离
css in js
js
// 是使用 js 编写 css 的统称,用来解决 css 样式冲突、覆盖的问题
// css in js 具体实现有许多种,其中比较常用的有 css Modules、style-components
// 其中 css Modules 在 React 脚手架中已经集成,可直接使用
css Modules 使用
js
// css Modules 是通过对 css 重命名保证类名的唯一性来避免样式冲突的问题
// 重命名采用:BEM(Block块、Element 元素、Modifier)如:.list__item_active
// 在 react 脚手架中演化成:文件名、类名、hash(随机值)
创建 module.css 文件
在组件中引入并使用
空标签使用
js
// 由于 react 组件默认只能返回一个根节点,但如果需要同时返回多个节点,在最外层加一层 div 也可以,但会使项目多一层无意义的标签元素,这时可以使用空标签解决,类似于 vue 里的 template 标签
// 空标签实际上是 <React.Fragment></React.Fragment> 的简化语法,作用是不添加额外元素返回多个节点
动态添加样式和 ref 的使用
js
import { createRef, useRef } from 'react'
class Sticky extends Component {
// 创建ref对象
placeholder = createRef()
// 还可以使用 ReactHoos 的 useRef 实现
// placeholder = useRef();
content = createRef()
// scroll 事件的事件处理程序
handleScroll = () => {
// 获取DOM对象
const placeholderEl = this.placeholder.current
const contentEl = this.content.current
const { top } = placeholderEl.getBoundingClientRect()
if (top < 0) {
// 吸顶
// 添加class
contentEl.classList.add(styles.fixed)
placeholderEl.style.height = '40px'
} else {
// 取消吸顶
// 移除class
contentEl.classList.remove(styles.fixed)
placeholderEl.style.height = '0px'
}
}
// 监听 scroll 事件
componentDidMount() {
document
.getElementById('home')
.addEventListener('scroll', this.handleScroll)
}
componentWillUnmount() {
document
.getElementById('home')
.removeEventListener('scroll', this.handleScroll)
}
render() {
return (
<div>
{/* 占位元素 */}
<div ref={this.placeholder} />
{/* 内容元素 */}
<div ref={this.content}>{this.props.children}</div>
</div>
)
}
}
react-spring 动画库
js
// https://react-spring.io/ 官方文档
// yarn add react-spring 安装
js
// 基本使用
// 导入 spring 组件
import { Spring, animated } from "react-spring";
export default class Filter extends Component {
renderMask() {
const flag = this.state.openType !== "more" && this.state.openType !== "";
return (
// from 指定初始样式(第一次渲染),to 表示选然后样式,这里通过改变 to 的样式实现淡入淡出动画
<Spring from={{ opacity: 0 }} to={{ opacity: flag ? 1 : 0 }}>
{(props) => {
if (!flag) return "";
return (
<animated.div style={props}>
<div className={styles.mask} onClick={this.onCancel} />
</animated.div>
);
}}
</Spring>
);
}
render() {
return (
<div className={styles.root}>
{this.renderMask()}
);
}
}
路由参数
js
// 定义路由参数
function App() {
return (
<BrowserRouter>
<div className="App">
<Route path="/home" component={Home}></Route>
<Route path="/citylist" component={CityList}></Route>
<Route path="/map" component={Map}></Route>
{/* id 就表示要动态捕获的路由参数 */}
<Route path="/detail/:id" component={HouseDetail}></Route>
{/* 路由参数后加上 ? 表示参数可选 */}
<Route path="/detail2/:id?" component={HouseDetail2}></Route>
<Route path="/" exact render={() => <Redirect to="/home" />}></Route>
</div>
</BrowserRouter>
)
}
// 在组件中获取
export default class HouseDetail extends Component {
componentDidMount() {
console.log(this.props.match.params)
}
}
页面访问控制
js
// 封装 AuthRoute 组件
import React from 'react'
import { Route, Redirect } from 'react-router-dom'
// <AuthRoute path="..." component={...}></AuthRoute>
const AuthRoute = ({ component: Component, ...rest }) => {
return (
<Route
{...rest}
render={props => {
if (sessionStorage.getItem('hkzf_token')) {
// 已登录
// 将 props 传递给组件,组件中才能获取到路由相关信息
return <Component {...props} />
} else {
// 未登录
return (
// Redirect 重定向组件,to 属性指定需要重定向的地址,state 表示需要传递的数据,这里将路由信息传递给 /login 对应的组件,该组件可以通过 props.location.state 拿到数据
<Redirect
to={{
pathname: '/login',
state: {
from: props.location
}
}}
/>
)
}
}}
/>
)
}
export default AuthRoute
js
// 使用该组件
// 这样就能实现再未登录时访问 map 页面会重定向回登录页
import AuthRoute from './components/AuthRoute'
function App() {
return (
<BrowserRouter>
<div className="App">
<AuthRoute path="/map" component={Map}></AuthRoute>
</div>
</BrowserRouter>
)
}
export default App
最简单的实现
js
import styles from './App.module.css'
import { Home, Login, Detail, Search, Shopping } from './page'
import { BrowserRouter, Route, Switch } from 'react-router-dom'
import { Redirect } from 'react-router-dom'
import { useSelector } from './redux/hooks'
// 创建页面访问控制组件,isAuthenticated 表示权限标识
const PrivateRoute = ({ isAuthenticated, ...rest }) => {
if (isAuthenticated) {
return <Route {...rest} />
} else {
return <Redirect to={{ pathname: 'signIn' }} />
}
}
function App() {
// 获取权限标识
const jwt = useSelector(s => s.UserState.token)
return (
<div className={styles.App}>
<BrowserRouter>
{/* 使用组件 */}
<PrivateRoute
path="/shopping"
component={Shopping}
isAuthenticated={jwt}
/>
</BrowserRouter>
</div>
)
}
export default App
路由传参
js
onTipsClick = ({ communityName, community }) => {
// 通过 replace 或者 push 跳转时可以添加第二个参数表示需要传递的参数,这种方式传递的参数不在地址栏显示且刷新还在
this.props.history.replace('/rent/add', {
name: communityName,
id: community
})
}
// 在跳转的页面中获取数据
export default class RentAdd extends Component {
constructor(props) {
super(props)
console.log(props)
}
}
组件懒加载
js
// 导入组件 lazy
// Suspense 组件用于当异步组件未加载完成时显示(loading)fallback指定显示内容
import { lazy, Suspense } from 'react'
// 这种导入方法也行
// import React, { lazy } from "react";
// 指定懒加载组件
const News = lazy(() => import('../News'))
function App() {
return (
<BrowserRouter>
<Suspense fallback={<div>loading...</div>}>
<div className="App">
<Route path="/News" component={News}></Route>
</div>
</Suspense>
</BrowserRouter>
)
}