1.版本兼容相关问题
又是一次实际上手和教程版本不一致的采坑过程,前后也废了不少周折。
实际开发时React17,react-dom-router 6.x, antd 4.x
教程版本React16,react-dom-router 5.x, antd 3.x
一开始是想安装新版本一点点解决bug的,可是太耗时最终还是选择了降低版本,在package.json里修改相关版本号后terminal中重新npm install
也还是总结下相关的坑
(1)路由相关兼容问题
react-dom-router 5.x -> 6.x
第一个报错需要用routes把route包裹
//5.x
<BrowserRouter>
<div>
{/*list后面有参数,会放到id里面*/}
<Route path="/list/:id" component= {Newlist}/>
<Route path = "/newButton" component = {NewButton}/>
</div>
</BrowserRouter>
//6.x
<BrowserRouter>
<Routes>
<Route path="/list" element={<Newlist />} />
<Route path="/newButton" element={<NewButton />} />
</Routes>
</BrowserRouter>
接下来与取消component对应的是无法从props获取参数
//5.x
<Route path="/list/:id" component= {Newlist}/>
<Route path = "/newButton" component = {NewButton}/>
------------------------------------------------------------------------
class Newlist extends Component{
render() {
//获取props里match参数,url参数
console.log(this.props.match.params.id)
}
}
//6.x
import { useParams } from 'react-router-dom';
export default function Newlist(){
const params = useParams();
return (
<div>
<h1>{params.id}</h1>
</div>
)
}
2.React路由
react-router-dom 5.x的路由配置:
(1)import相关包
import {BrowserRouter, Route, Switch} from 'react-router-dom';
(2)在组件中配置path属性
如果当前path匹配,则会跳转至path相应的组件
另外要注意根目录要放在后面,
如果在几个最前,其他的路径如/detail也是会和/匹配造成错误
<Content className = 'content'>
<Switch>
/
{/* Switch 匹配一个不会继续匹配 */}
<Route path='/detail' component={Detail}/>
{/*访问根目录,如果/在前那么detail和根路径也会匹配,/显示出根路径 现在detail在前,如果是根路径和detail匹配不上 */}
<Route path = '/:id?' component = {List}/>
{/*/id可不传*/}
</Switch>
</Content>
(3) 实现点击按钮显示对应的组件(列表、页面等)
在新闻网站上方的导航栏组件中每个标题的button绑定一个
header
import React, {Component, Fragment} from 'react';
import logo from'./logo.png'
import './style.css'
import {Menu } from "antd";
import { Icon } from '@ant-design/compatible';
import {Link} from 'react-router-dom'
import axios from 'axios';
class AppHeader extends Component{
constructor(props) {
super(props);
this.state = {
list :[]
}
}
getMenuItems(){
return this.state.list.map(item =>{
return (<Menu.Item key={ item.id } >
{/*Link需要在Router内部*/}
<Link to = {`/${item.id}`}>
<Icon type = {item.icon} />
{item.title}
</Link>
</Menu.Item>)
})
}
componentDidMount() {
axios.get('http://www.dell-lee.com/react/api/header.json')
.then((res) =>{
this.setState({
list : res.data.data
})
})
}
render() {
return (
<Fragment>
<img src = {logo} className='app-header-logo'/>
<Menu
mode="horizontal"
className = 'app-header-menu'>
{this.getMenuItems()}
</Menu>
</Fragment>
)
}
}
export default AppHeader;
首页效果图
3.新闻网站的实现
(1)页面
在首页对应的index.js配置页面布局,采用了AntDesign的Layout样式。 中是新闻的展示区,点击
index.js
import React,{Component , Fragment} from 'react';
import ReactDOM from 'react-dom';
import 'antd/dist/antd.css';
import AppHeader from "./components/Header";
import { Layout } from 'antd';
import './style.css'
import {BrowserRouter, Route, Switch} from 'react-router-dom';
import List from './containers/List/'
import Detail from './containers/Detail/'
const { Header, Footer, Content } = Layout;
class App extends Component{
render() {
return(
<BrowserRouter>
<Layout style = {{minWidth:1300, height:'100%' }} >
<Header className = 'header'>
<AppHeader/>
</Header>
<Content className = 'content'>
<Switch>
/
{/* Switch 匹配一个不会继续匹配 */}
<Route path='/detail' component={Detail}/>
{/*访问根目录,如果/在前那么detail和根路径也会匹配,/显示出根路径 现在detail在前,如果是根路径和detail匹配不上 */}
<Route path = '/:id?' component = {List}/>
{/*/id可不传*/}
</Switch>
</Content>
<Footer className = "footer">@copyright Lux.Lin 2022</Footer>
{/* css className*/}
</Layout>
</BrowserRouter>
)
}
}
ReactDOM.render( <App/>, document.getElementById('root'));
(2)List组件
要在挂载完成后componentDidMount()中请求对应的List接口数据,在页面第一次加载时显示id为1对应的list,这里需要后端的协同使得url后面没有id自动请求id=1的数据。
另:url参数:追加到 URL 上的一个名称/值对。参数以问号 (?) 开始并采用 name=value 的格式。如果存在多个 URL 参数,则参数之间用一个 (&) 符隔开。
componentDidMount() {
const id = this.props.match.params.id;
console.log(id)
let url = 'http://www.dell-lee.com/react/api/list.json'
if(id){
url = url + '?id=' + id
}
axios.get(url)
.then(res => {
this.setState({
data:res.data.data
})
console.log(res.data.data)
})
}
}
具体点击不同的标题的相关实现是每个Header的每个标题都有一个id对应,请求相应id下的List信息。这里是通过 componentWillReceiveProps(nextProps) 生命周期函数来完成,因为componentDidMount在挂载完成后就不会再执行,而componentWillReceiveProps会在Props发生改变时执行。
componentWillReceiveProps(nextProps) {
const id = nextProps.match.params.id;
axios.get('http://www.dell-lee.com/react/api/list.json?id=' + id)
.then(res => {
this.setState({
data:res.data.data
})
console.log(res.data.data)
})
}
那么问题来了 id最初是如何来的?
是在header里从接口获取并在index.js中绑定到即浏览器解析这个地址,list组件接受到这个path里的参数从而拿到的
header 中
getMenuItems(){
return this.state.list.map(item =>{
return (<Menu.Item key={ item.id } >
{/*Link需要在Router内部*/}
<Link to = {`/${item.id}`}>
<Icon type = {item.icon} />
{item.title}
</Link>
</Menu.Item>)
})
index 中
<Switch>
/
{/* Switch 匹配一个不会继续匹配 */}
<Route path='/detail' component={Detail}/>
{/*访问根目录,如果/在前那么detail和根路径也会匹配,/显示出根路径 现在detail在前,如果是根路径和detail匹配不上 */}
<Route path = '/:id?' component = {List}/>
{/*/id可不传*/}
</Switch>
当点击一个标题时,当前地址与path = '/:id?'匹配,所以会跳转到List组件,随着把id作为参数传递给List组件,这样完成了组件之间参数的通信。
List
import React, {Component} from 'react';
import { List, Typography, Divider } from 'antd';
import axios from "axios";
class PageList extends Component{
componentWillReceiveProps(nextProps) {
console.log(nextProps)
const id = nextProps.match.params.id;
axios.get('http://www.dell-lee.com/react/api/list.json?id=' + id)
.then(res => {
this.setState({
data:res.data.data
})
console.log(res.data.data)
})
}
constructor(props) {
super(props);
this.state = {
data :[]
}
}
render() {
return <List
bordered
dataSource={this.state.data}
renderItem={item => (
<List.Item>
<Typography.Text mark>[ITEM]</Typography.Text> {item.title}
</List.Item>
)}
/>
}
componentDidMount() {
const id = this.props.match.params.id;
console.log(id)
let url = 'http://www.dell-lee.com/react/api/list.json'
if(id){
url = url + '?id=' + id
}
axios.get(url)
.then(res => {
this.setState({
data:res.data.data
})
console.log(res.data.data)
})
}
}
export default PageList;