React应用(基于react脚手架)

 react脚手架

1.xxx脚手架:用来帮助程序员快速创建一个基于xxx库的模板项目

  • 包含了所有需要的配置(语法检查,jsx编译,devServer)
  • 下载好了所有相关的依赖
  • 可以直接运行一个简单结果

2.react提供了一个用于创建react项目的脚手架库:create-react-app

3.项目的整体技术架构为:react+webpack+es6+eslint

4.使用脚手架开发的项目的特点:模块化,组件化,工程化

创建项目并启动

第一步,全局安装:npm i -g create-react-app

第二步,切换到想创项目的目录,使用命令:create-react-app hello-react

第三步,进入项目文件夹,cd hello-react

第四步,启动项目:npm start

react脚手架项目结构

public ---- 静态资源文件夹

                        favicon.icon ------ 网站页签图标

                        index.html -------- 主页面

                        logo192.png ------- logo图

                        logo512.png ------- logo图

                        manifest.json ----- 应用加壳的配置文件

                        robots.txt -------- 爬虫协议文件

src ---- 源码文件夹

                        App.css -------- App组件的样式

                        App.js --------- App组件

                        App.test.js ---- 用于给App做测试

                        index.css ------ 样式

                        index.js ------- 入口文件

                        logo.svg ------- logo图

                        reportWebVitals.js

                                       --- 页面性能分析文件(需要web-vitals库的支持)

                        setupTests.js

                                       ---- 组件单元测试的文件(需要jest-dom库的支持)

样式的模块化

将组件中样式的类名改为index.module.css

index.jsx

import React,{Component} from 'react'
import hello from './index.module.css'export default class Hello extends Component{render(){return <h2 className={hello.title}>Hello,React!</h2>}
}

todoList案例

App.jsx

import React, { Component } from 'react'
import Header from './components/Header'
import List from './components/List'
import Footer from './components/Footer'
import './App.css'export default class App extends Component {//状态在哪里,操作状态的方法就在哪里//初始化状态state = {todos:[{id:'001',name:'吃饭',done:true},{id:'002',name:'睡觉',done:true},{id:'003',name:'打代码',done:false},{id:'004',name:'逛街',done:false}]}//addTodo用于添加一个todo,接收的参数是todo对象addTodo = (todoObj)=>{//获取原todosconst {todos} = this.state//追加一个todoconst newTodos = [todoObj,...todos]//更新状态this.setState({todos:newTodos})}//updateTodo用于更新一个todo对象updateTodo = (id,done)=>{//获取状态中的todosconst {todos} = this.state//匹配处理数据const newTodos = todos.map((todoObj)=>{if(todoObj.id === id) return {...todoObj,done}else return todoObj})this.setState({todos:newTodos})}//deleteTodo用于删除一个todo对象deleteTodo = (id)=>{//获取原来的todosconst {todos} = this.state//删除指定id的todo对象const newTodos = todos.filter((todoObj)=>{return todoObj.id !== id})//更新状态this.setState({todos:newTodos})}//checkAllTodo用于全选checkAllTodo = (done)=>{//获取原来的todosconst {todos} = this.state//加工数据const newTodos = todos.map((todoObj)=>{return {...todoObj,done}})//更新状态this.setState({todos:newTodos})}//clearAllDone用于清除所有已完成的clearAllDone = ()=>{//获取原来的todosconst {todos} = this.state//过滤数据const newTodos = todos.filter((todoObj)=>{return !todoObj.done})//更新状态this.setState({todos:newTodos})}render() {const {todos} = this.statereturn (<div className="todo-container"><div className="todo-wrap"><Header addTodo={this.addTodo}/><List todos={todos} updateTodo={this.updateTodo} deleteTodo={this.deleteTodo}/><Footer todos={todos} checkAllTodo={this.checkAllTodo} clearAllDone={this.clearAllDone}/></div></div>)}
}

Header.jsx

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import {nanoid} from 'nanoid'
import './index.css'export default class Header extends Component {//对接收的props进行:类型、必要性的限制static propTypes = {addTodo:PropTypes.func.isRequired}//键盘事件的回调handleKeyUp = (event)=>{//解构赋值获取keyCode,targetconst {keyCode,target} = event//判断是否是回车按键if(keyCode !== 13) return//添加的todo名字不能为空if(target.value.trim() === ''){alert('输入不能为空')return}//准备好一个todo对象const todoObj = {id:nanoid(),name:target.value,done:false}//将todoObj传递给Appthis.props.addTodo(todoObj)//清空输入target.value = ''}render() {return (<div className="todo-header"><input onKeyUp={this.handleKeyUp} type="text" placeholder="请输入你的任务名称,按回车键确认"/></div>)}
}

List.jsx

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Item from '../Item'
import './index.css'export default class List extends Component {//对接收的props进行:类型、必要性的限制static propTypes = {todos:PropTypes.array.isRequired,updateTodo:PropTypes.func.isRequired,deleteTodo:PropTypes.func.isRequired,}render() {const {todos,updateTodo,deleteTodo} = this.propsreturn (<ul className="todo-main">{todos.map( todo =>{return <Item key={todo.id} {...todo} updateTodo={updateTodo} deleteTodo={deleteTodo}/>})}</ul>)}
}

Item.jsx

import React, { Component } from 'react'
import './index.css'export default class Item extends Component {state = {mouse:false} //标识鼠标移入、移出//鼠标移入、移出的回调handleMouse = (flag)=>{return ()=>{this.setState({mouse:flag})}}//勾选、取消勾选某一个todo的回调handleCheck = (id)=>{return (event)=>{this.props.updateTodo(id,event.target.checked)}}//删除一个todo的回调handleDelete = (id)=>{if(window.confirm('确定删除吗?')){this.props.deleteTodo(id)}}render() {const {id,name,done} = this.propsconst {mouse} = this.statereturn (<li style={{backgroundColor:mouse ? '#ddd' : 'white'}} onMouseEnter={this.handleMouse(true)} onMouseLeave={this.handleMouse(false)}><label><input type="checkbox" checked={done} onChange={this.handleCheck(id)}/><span>{name}</span></label><button onClick={()=> this.handleDelete(id) } className="btn btn-danger" style={{display:mouse?'block':'none'}}>删除</button></li>)}
}

Footer.jsx

import React, { Component } from 'react'
import './index.css'export default class Footer extends Component {//全选checkbox的回调handleCheckAll = (event)=>{this.props.checkAllTodo(event.target.checked)}//清除已完成任务的回调handleClearAllDone = ()=>{this.props.clearAllDone()}render() {const {todos} = this.props//已完成的个数const doneCount = todos.reduce((pre,todo)=> pre + (todo.done ? 1 : 0),0)//总数const total = todos.lengthreturn (<div className="todo-footer"><label><input type="checkbox" onChange={this.handleCheckAll} checked={doneCount === total && total !== 0 ? true : false}/></label><span><span>已完成{doneCount}</span> / 全部{total}</span><button onClick={this.handleClearAllDone} className="btn btn-danger">清除已完成任务</button></div>)}
}

TodoList案例总结

1.拆分组件、实现静态组件,注意className,style({{}})的写法

2.动态初始化列表,如何确定将数据放在哪个组件的state中?

  • 某个组件使用:放在其自身的state中
  • 某些组件使用,放在他们共同的父组件state中(官方称此操作为:状态提升)

3.关于父子之间通信:

  • 父组件给子组件传递数据:通过props传递
  • 子组件给父组件传递数据:通过props传递,要求父提前给子传递一个函数

4.注意defaultChecked(只在第一次指定的时候起作用)和checked的区别,类似的还有:defaultValue和value

"proxy":"http://localhost:5000"

5.状态在哪里,操作状态的方法就在哪里

配置代理

方法一

在package.json中追加如下配置

"proxy":"http://localhost:5000"

说明:

  • 优点:配置简单,前端请求资源时可以不加任何前缀
  • 缺点:不能配置多个代理、
  • 工作方式:上述方式配置代理,当请求了3000不存在的资源时,那么该请求会转发给5000(优先匹配前端资源)

方法二

1.第一步:创建代理配置文件:在src下创建配置文件:src/setupProxy.js

2.编写setupProxy.js配置具体代理规 则:

   const proxy = require('http-proxy-middleware')module.exports = function(app) {app.use(proxy('/api1', {  //api1是需要转发的请求(所有带有/api1前缀的请求都会转发给5000)target: 'http://localhost:5000', //配置转发目标地址(能返回数据的服务器地址)changeOrigin: true, //控制服务器接收到的请求头中host字段的值/*changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:3000changeOrigin默认值为false,但我们一般将changeOrigin值设为true*/pathRewrite: {'^/api1': ''} //去除请求前缀,保证交给后台服务器的是正常请求地址(必须配置)}),proxy('/api2', { target: 'http://localhost:5001',changeOrigin: true,pathRewrite: {'^/api2': ''}}))}

说明:

  • 优点:可以配置多个代理,可以灵活的控制请求是否走代理
  • 缺点:配置繁琐,前端请求资源时必须加前缀

github搜索案例--axios

list.jsx

import React, { Component } from 'react'
import './index.css'export default class List extends Component {render() {const {users,isFirst,isLoading,err} = this.propsreturn (<div className="row">{isFirst ? <h2>欢迎使用,输入关键字,随后点击搜索</h2> :isLoading ? <h2>Loading......</h2> :err ? <h2 style={{color:'red'}}>{err}</h2> :users.map((userObj)=>{return (<div key={userObj.id} className="card"><a rel="noreferrer" href={userObj.html_url} target="_blank"><img alt="head_portrait" src={userObj.avatar_url} style={{width:'100px'}}/></a><p className="card-text">{userObj.login}</p></div>)})}</div>)}
}

search.jsx

import React, { Component } from 'react'
import axios from 'axios'export default class Search extends Component {search = ()=>{//获取用户的输入(连续解构赋值+重命名)const {keyWordElement:{value:keyWord}} = this//发送请求前通知App更新状态this.props.updateAppState({isFirst:false,isLoading:true})//发送网络请求axios.get(`/api1/search/users?q=${keyWord}`).then(response => {//请求成功后通知App更新状态this.props.updateAppState({isLoading:false,users:response.data.items})},error => {//请求失败后通知App更新状态this.props.updateAppState({isLoading:false,err:error.message})})}render() {return (<section className="jumbotron"><h3 className="jumbotron-heading">搜索github用户</h3><div><input ref={c => this.keyWordElement = c} type="text" placeholder="输入关键词点击搜索"/>&nbsp;<button onClick={this.search}>搜索</button></div></section>)}
}

App.jsx

import React, { Component } from 'react'
import Search from './components/Search'
import List from './components/List'export default class App extends Component {state = { //初始化状态users:[], //users初始值为数组isFirst:true, //是否为第一次打开页面isLoading:false,//标识是否处于加载中err:'',//存储请求相关的错误信息} //更新App的stateupdateAppState = (stateObj)=>{this.setState(stateObj)}render() {return (<div className="container"><Search updateAppState={this.updateAppState}/><List {...this.state}/></div>)}
}

github搜索案例---pubsub

list.jsx

import React, { Component } from 'react'
import PubSub from 'pubsub-js'
import './index.css'export default class List extends Component {state = { //初始化状态users:[], //users初始值为数组isFirst:true, //是否为第一次打开页面isLoading:false,//标识是否处于加载中err:'',//存储请求相关的错误信息} componentDidMount(){this.token = PubSub.subscribe('atguigu',(_,stateObj)=>{this.setState(stateObj)})}componentWillUnmount(){PubSub.unsubscribe(this.token)}render() {const {users,isFirst,isLoading,err} = this.statereturn (<div className="row">{isFirst ? <h2>欢迎使用,输入关键字,随后点击搜索</h2> :isLoading ? <h2>Loading......</h2> :err ? <h2 style={{color:'red'}}>{err}</h2> :users.map((userObj)=>{return (<div key={userObj.id} className="card"><a rel="noreferrer" href={userObj.html_url} target="_blank"><img alt="head_portrait" src={userObj.avatar_url} style={{width:'100px'}}/></a><p className="card-text">{userObj.login}</p></div>)})}</div>)}
}

search.jsx

import React, { Component } from 'react'
import PubSub from 'pubsub-js'
import axios from 'axios'export default class Search extends Component {search = ()=>{//获取用户的输入(连续解构赋值+重命名)const {keyWordElement:{value:keyWord}} = this//发送请求前通知List更新状态PubSub.publish('atguigu',{isFirst:false,isLoading:true})//发送网络请求axios.get(`/api1/search/users?q=${keyWord}`).then(response => {//请求成功后通知List更新状态PubSub.publish('atguigu',{isLoading:false,users:response.data.items})},error => {//请求失败后通知App更新状态PubSub.publish('atguigu',{isLoading:false,err:error.message})})}render() {return (<section className="jumbotron"><h3 className="jumbotron-heading">搜索github用户</h3><div><input ref={c => this.keyWordElement = c} type="text" placeholder="输入关键词点击搜索"/>&nbsp;<button onClick={this.search}>搜索</button></div></section>)}
}

App.jsx

import React, { Component } from 'react'
import Search from './components/Search'
import List from './components/List'export default class App extends Component {render() {return (<div className="container"><Search/><List/></div>)}
}

github搜索案例---fetch

list.jsx

import React, { Component } from 'react'
import PubSub from 'pubsub-js'
import './index.css'export default class List extends Component {state = { //初始化状态users:[], //users初始值为数组isFirst:true, //是否为第一次打开页面isLoading:false,//标识是否处于加载中err:'',//存储请求相关的错误信息} componentDidMount(){this.token = PubSub.subscribe('atguigu',(_,stateObj)=>{this.setState(stateObj)})}componentWillUnmount(){PubSub.unsubscribe(this.token)}render() {const {users,isFirst,isLoading,err} = this.statereturn (<div className="row">{isFirst ? <h2>欢迎使用,输入关键字,随后点击搜索</h2> :isLoading ? <h2>Loading......</h2> :err ? <h2 style={{color:'red'}}>{err}</h2> :users.map((userObj)=>{return (<div key={userObj.id} className="card"><a rel="noreferrer" href={userObj.html_url} target="_blank"><img alt="head_portrait" src={userObj.avatar_url} style={{width:'100px'}}/></a><p className="card-text">{userObj.login}</p></div>)})}</div>)}
}

search.jsx

import React, { Component } from 'react'
import PubSub from 'pubsub-js'
// import axios from 'axios'export default class Search extends Component {search = async()=>{//获取用户的输入(连续解构赋值+重命名)const {keyWordElement:{value:keyWord}} = this//发送请求前通知List更新状态PubSub.publish('atguigu',{isFirst:false,isLoading:true})//#region 发送网络请求---使用axios发送/* axios.get(`/api1/search/users2?q=${keyWord}`).then(response => {//请求成功后通知List更新状态PubSub.publish('atguigu',{isLoading:false,users:response.data.items})},error => {//请求失败后通知App更新状态PubSub.publish('atguigu',{isLoading:false,err:error.message})}) *///#endregion//发送网络请求---使用fetch发送(未优化)/* fetch(`/api1/search/users2?q=${keyWord}`).then(response => {console.log('联系服务器成功了');return response.json()},error => {console.log('联系服务器失败了',error);return new Promise(()=>{})}).then(response => {console.log('获取数据成功了',response);},error => {console.log('获取数据失败了',error);}) *///发送网络请求---使用fetch发送(优化)try {const response= await fetch(`/api1/search/users2?q=${keyWord}`)const data = await response.json()console.log(data);PubSub.publish('atguigu',{isLoading:false,users:data.items})} catch (error) {console.log('请求出错',error);PubSub.publish('atguigu',{isLoading:false,err:error.message})}}render() {return (<section className="jumbotron"><h3 className="jumbotron-heading">搜索github用户</h3><div><input ref={c => this.keyWordElement = c} type="text" placeholder="输入关键词点击搜索"/>&nbsp;<button onClick={this.search}>搜索</button></div></section>)}
}

App.jsx

import React, { Component } from 'react'
import Search from './components/Search'
import List from './components/List'export default class App extends Component {render() {return (<div className="container"><Search/><List/></div>)}
}

github搜索案例相关总结

1.设计状态时要考虑全面,例如带有网络请求的组件,要考虑请求失败怎么办

2.ES6知识点:解构赋值+命名

let obj = {a:{b:1}}
const {a} = obj; //传统解构赋值
const {a:{b}} = obj; //连续解构赋值
const {a:{b:value}} = obj; //连续解构赋值+重命名

3.消息订阅与发布机制

  1. 先订阅,再发布(理解:有一种隔空对话的感觉)
  2. 适用于任意组件间通信
  3. 要在组件的componentWillUnmount中取消订阅

4.fetch发送请求(关注分离的设计思想)

try {const response= await fetch(`/api1/search/users2?q=${keyWord}`)const data = await response.json()console.log(data);} catch (error) {console.log('请求出错',error);}

react脚手架中未下载的库:
prop-types  npm add prop-types

react-router-dom npm i react-router-dom@5

路由的基本使用

NavLink的使用

App.jsx

import React, { Component } from 'react'
import {NavLink,Route} from 'react-router-dom'
import Home from './pages/Home' //Home是路由组件
import About from './pages/About' //About是路由组件
import Header from './components/Header' //Header是一般组件export default class App extends Component {render() {return (<div><div className="row"><div className="col-xs-offset-2 col-xs-8"><Header/></div></div><div className="row"><div className="col-xs-2 col-xs-offset-2"><div className="list-group">{/* 原生html中,靠<a>跳转不同的页面 */}{/* <a className="list-group-item" href="./about.html">About</a><a className="list-group-item active" href="./home.html">Home</a> */}{/* 在React中靠路由链接实现切换组件--编写路由链接 */}<NavLink activeClassName="atguigu" className="list-group-item" to="/about">About</NavLink><NavLink activeClassName="atguigu" className="list-group-item" to="/home">Home</NavLink></div></div><div className="col-xs-6"><div className="panel"><div className="panel-body">{/* 注册路由 */}<Route path="/about" component={About}/><Route path="/home" component={Home}/></div></div></div></div></div>)}
}

About.jsx

import React, { Component } from 'react'export default class About extends Component {render() {// console.log('About组件收到的props是',this.props);return (<h3>我是About的内容</h3>)}
}

Home.jsx

import React, { Component } from 'react'export default class Home extends Component {render() {return (<h3>我是Home的内容</h3>)}
}

index.js(入口文件)

//引入react核心库
import React from 'react'
//引入ReactDOM
import ReactDOM from 'react-dom'
//
import {BrowserRouter} from 'react-router-dom'
//引入App
import App from './App'ReactDOM.render(<BrowserRouter><App/></BrowserRouter>,document.getElementById('root')
)

1.明确好界面中的导航区,展示区

2.导航区的a标签改为Link标签

        <Link to="/xxx">Demo</Link>

3.展示区写Route标签进行路径的匹配

        <Route path='/xxx' component={Demo}/>

4.<App>的最外层包裹了一个<BrowserRouter>或<HashRouter>

路由组件与一般组件

1.写法不同:

一般组件:<Demo/>

路由组件:<Route path="/demo" component="{Demo}"/>

2.存放位置不同:

一般组件:components

路由组件:pages

3.接收到的props不同:

一般组件:写组件标签时传递了什么,就能收到什么、

路由组件:接收到三个固定的属性

history:go: ƒ go(n)goBack: ƒ goBack()goForward: ƒ goForward()push: ƒ push(path, state)replace: ƒ replace(path, state)
location:pathname: "/about"search: ""state: undefined
match:params: {}path: "/about"url: "/about"

NavLink与封装NavLink

  • NavLink可以实现路由链接的高亮,通过activeClassName指定样式名
  • 标签体内容是一个特殊的标签属性
  • 通过this.props.children可以获取标签体内容

MyNavLink

import React, { Component } from 'react'
import {NavLink} from 'react-router-dom'export default class MyNavLink extends Component {render() {// console.log(this.props);return (<NavLink activeClassName="atguigu" className="list-group-item" {...this.props}/>)}
}
{/* 在React中靠路由链接实现切换组件--编写路由链接 */}
<MyNavLink to="/about">About</MyNavLink>
<MyNavLink to="/home">Home</MyNavLink>

Switch的使用

1.通常情况下,path和component是一一对应的关系

2.Switch可以提高路由匹配效率(单一匹配) 

<Switch><Route path="/about" component={About}/><Route path="/home" component={Home}/><Route path="/home" component={Test}/>
</Switch>

如果没有Switch包裹,/home路径下会显示Home和Test两个组件,但是如果有Switch,当/home匹配到Home组件时,就不会继续往下匹配

解决多级路径样式丢失问题

当路由组件路径写为多级路径时,浏览器申请css文件的路径就会变化从而找不到该文件导致样式丢失,此时显示的界面默认是public下的index.html解决方法有以下三种方法:

1.public/index.html中引入样式时不写./写/(常用)

2.public/index.html中引入样式时不写./写%PUBLIC_URL%(常用)

3.使用HashRouter(#后面的路径不会传至服务器上,不会和前面的端口号混淆)

路由的严格匹配与模糊匹配

  1. 默认使用的是模糊匹配(简单记:输入的路径必须包含要匹配的路径,且顺序要一致)
  2. 开启严格匹配:<Route exact={true} path="/about" component={About}/>
  3. 严格匹配不要随便开启,需要再开,有时候开启会导致无法继续匹配二级路由
<Switch><Route exact path="/about" component={About}/><Route exact path="/home" component={Home}/>
</Switch>

Redirect的使用

一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到Redirect指定的路由

<Switch><Route path="/about" component={About}/><Route path="/home" component={Home}/><Redirect to="/about"/>
</Switch>

嵌套路由

  1. 注册子路由时要写上父路由的path值
  2. 路由的匹配是按照注册路由的顺序进行的
import React, { Component } from 'react'
import MyNavLink from '../../components/MyNavLink'
import {Route,Switch,Redirect} from 'react-router-dom'
import News from './News'
import Message from './Message'export default class Home extends Component {render() {return (<div><h3>我是Home的内容</h3><div><ul className="nav nav-tabs"><li><MyNavLink to="/home/news">News</MyNavLink></li><li><MyNavLink to="/home/message">Message</MyNavLink></li></ul>{/* 注册路由 */}<Switch><Route path="/home/news" component={News}/><Route path="/home/message" component={Message}/><Redirect to="/home/news"/></Switch></div></div>)}
}

 向路由组件传递参数

Message.jsx

import React, { Component } from 'react'
import {Link,Route} from 'react-router-dom'
import Detail from './Detail'export default class Message extends Component {state = {messageArr:[{id:'01',title:'消息1'},{id:'02',title:'消息2'},{id:'03',title:'消息3'},]}render() {const {messageArr} = this.statereturn (<div><ul>{messageArr.map((msgObj)=>{return (<li key={msgObj.id}>{/* 向路由组件传递params参数 */}{/* <Link to={`/home/message/detail/${msgObj.id}/${msgObj.title}`}>{msgObj.title}</Link> */}{/* 向路由组件传递search参数 */}{/* <Link to={`/home/message/detail/?id=${msgObj.id}&title=${msgObj.title}`}>{msgObj.title}</Link> */}{/* 向路由组件传递state参数 */}<Link to={{pathname:'/home/message/detail',state:{id:msgObj.id,title:msgObj.title}}}>{msgObj.title}</Link></li>)})}</ul><hr/>{/* 声明接收params参数 */}{/* <Route path="/home/message/detail/:id/:title" component={Detail}/> */}{/* search参数无需声明接收,正常注册路由即可 */}{/* <Route path="/home/message/detail" component={Detail}/> */}{/* state参数无需声明接收,正常注册路由即可 */}<Route path="/home/message/detail" component={Detail}/></div>)}
}

Detail.jsx

import React, { Component } from 'react'
// import qs from 'querystring'const DetailData = [{id:'01',content:'你好,中国'},{id:'02',content:'你好,尚硅谷'},{id:'03',content:'你好,未来的自己'}
]
export default class Detail extends Component {render() {console.log(this.props);// 接收params参数// const {id,title} = this.props.match.params // 接收search参数// const {search} = this.props.location// const {id,title} = qs.parse(search.slice(1))// 接收state参数const {id,title} = this.props.location.state || {}const findResult = DetailData.find((detailObj)=>{return detailObj.id === id}) || {}return (<ul><li>ID:{id}</li><li>TITLE:{title}</li><li>CONTENT:{findResult.content}</li></ul>)}
}

        1.params参数

              路由链接(携带参数):<Link to='/demo/test/tom/18'}>详情</Link>

              注册路由(声明接收):<Route path="/demo/test/:name/:age" component={Test}/>

              接收参数:this.props.match.params

        2.search参数

              路由链接(携带参数):<Link to='/demo/test?name=tom&age=18'}>详情</Link>

              注册路由(无需声明,正常注册即可):<Route path="/demo/test" component={Test}/>

              接收参数:this.props.location.search

              备注:获取到的search是urlencoded编码字符串,需要借助querystring解析

        3.state参数

              路由链接(携带参数):<Link to={{pathname:'/demo/test',state:{name:'tom',age:18}}}>详情</Link>

              注册路由(无需声明,正常注册即可):<Route path="/demo/test" component={Test}/>

              接收参数:this.props.location.state

              备注:刷新也可以保留住参数,且地址栏中不会有传递的参数

编程式路由导航

借助this.props.history对象上的API对路由进行跳转,前进,后退等操作

        -this.props.history.push()

        -this.props.history.replace()

        -this.props.history.goBack()

        -this.props.history.goForward()

        -this.props.history.go()

import React, { Component } from 'react'
import {Link,Route} from 'react-router-dom'
import Detail from './Detail'export default class Message extends Component {state = {messageArr:[{id:'01',title:'消息1'},{id:'02',title:'消息2'},{id:'03',title:'消息3'},]}replaceShow = (id,title)=>{//replace跳转+携带params参数//this.props.history.replace(`/home/message/detail/${id}/${title}`)//replace跳转+携带search参数// this.props.history.replace(`/home/message/detail?id=${id}&title=${title}`)//replace跳转+携带state参数this.props.history.replace(`/home/message/detail`,{id,title})}pushShow = (id,title)=>{//push跳转+携带params参数// this.props.history.push(`/home/message/detail/${id}/${title}`)//push跳转+携带search参数// this.props.history.push(`/home/message/detail?id=${id}&title=${title}`)//push跳转+携带state参数  push: ƒ push(path, state)this.props.history.push(`/home/message/detail`,{id,title})}back = ()=>{this.props.history.goBack()}forward = ()=>{this.props.history.goForward()}go = ()=>{this.props.history.go(-2)}render() {const {messageArr} = this.statereturn (<div><ul>{messageArr.map((msgObj)=>{return (<li key={msgObj.id}>{/* 向路由组件传递params参数 */}{/* <Link to={`/home/message/detail/${msgObj.id}/${msgObj.title}`}>{msgObj.title}</Link> */}{/* 向路由组件传递search参数 */}{/* <Link to={`/home/message/detail/?id=${msgObj.id}&title=${msgObj.title}`}>{msgObj.title}</Link> */}{/* 向路由组件传递state参数 */}<Link to={{pathname:'/home/message/detail',state:{id:msgObj.id,title:msgObj.title}}}>{msgObj.title}</Link>&nbsp;<button onClick={()=> this.pushShow(msgObj.id,msgObj.title)}>push查看</button>&nbsp;<button onClick={()=> this.replaceShow(msgObj.id,msgObj.title)}>replace查看</button></li>)})}</ul><hr/>{/* 声明接收params参数 */}{/* <Route path="/home/message/detail/:id/:title" component={Detail}/> */}{/* search参数无需声明接收,正常注册路由即可 */}{/* <Route path="/home/message/detail" component={Detail}/> */}{/* state参数无需声明接收,正常注册路由即可 */}<Route path="/home/message/detail" component={Detail}/><button onClick={this.back}>回退</button>&nbsp;<button onClick={this.forward}>前进</button>&nbsp;<button onClick={this.go}>go</button></div>)}
}

WithRouter

由于一般组件上没有路由组件的API,要想让一般组件使用路由组件的API,必须先让WithRouter处理一般组件,获得拥有了路由组件API新组件。

import React, { Component } from 'react'
import {withRouter} from 'react-router-dom'class Header extends Component {back = ()=>{this.props.history.goBack()}forward = ()=>{this.props.history.goForward()}go = ()=>{this.props.history.go(-2)}render() {console.log('Header组件收到的props是',this.props);return (<div className="page-header"><h2>React Router Demo</h2><button onClick={this.back}>回退</button>&nbsp;<button onClick={this.forward}>前进</button>&nbsp;<button onClick={this.go}>go</button></div>)}
}export default withRouter(Header)//withRouter可以加工一般组件,让一般组件具备路由组件所特有的API
//withRouter的返回值是一个新组件

BrowserRouter与HashRouter的区别

1.底层原理不一样:

  • BrowserRouter使用的是H5的history API,不兼容IE9以下版本
  • HashRouter使用的是URL的哈希值

2.path表现形式不一样

  • BrowerRouter的路径中没有#,例如:localhost:3000/demo/test
  • HashRouter的路径包含#,例如:localhost:3000/#/demo/test

3.刷新后对路由的state参数的影响

  • BrowerRouter没有任何影响,因为state保存在history对象中
  • HashRouter刷新后会导致路由state参数的缺失

4.备注:HashRouter可以用于解决一些路径错误的问题

antd的按需引入+自定主题

      1.安装依赖:yarn add react-app-rewired customize-cra babel-plugin-import less less-loader

      2.修改package.json
 

            "scripts": {"start": "react-app-rewired start","build": "react-app-rewired build","test": "react-app-rewired test","eject": "react-scripts eject"},

      3.根目录下创建config-overrides.js

          //配置具体的修改规则const { override, fixBabelImports,addLessLoader} = require('customize-cra');module.exports = override(fixBabelImports('import', {libraryName: 'antd',libraryDirectory: 'es',style: true,}),addLessLoader({lessOptions:{javascriptEnabled: true,modifyVars: { '@primary-color': 'green' },}}),);

      4.备注:不用在组件里亲自引入样式了,即:import 'antd/dist/antd.css'应该删掉

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/397801.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AWVS——Web 应用漏洞扫描的强大工具

一、引言 在网络安全日益重要的今天&#xff0c;Web 应用的安全性备受关注。Acunetix Web Vulnerability Scanner&#xff08;简称 AWVS&#xff09;作为一款知名的 Web 应用漏洞扫描工具&#xff0c;为保障 Web 应用的安全发挥了重要作用。本文将详细介绍 AWVS 的功能、特点、…

【vulhub靶场之spring】——

简介&#xff1a; Spring是Java EE编程领域的一个轻量级开源框架&#xff0c;该框架由一个叫Rod Johnson的程序员在2002年最早提出并随后创建&#xff0c;是为了解决企业级编程开发中的复杂性&#xff0c;业务逻辑层和其他各层的松耦合问题&#xff0c;因此它将面向接口的编程思…

【Postman工具】

一.接口扫盲 1.什么是接口&#xff1f; 接口是系统之间数据交互的通道。拿小红到沙县点餐为例&#xff1a;小红想吃鸭腿饭。她要用什么语言来表达&#xff1f;跟谁表达&#xff1f;通过什么表达&#xff1f;按照生活习惯应该是&#xff1a;小红根据菜单对服务员用中文表达她想要…

联通数科如何基于Apache DolphinScheduler构建DataOps一体化能力平台

各位小伙伴晚上好&#xff0c;我是联通数字科技有限公司数据智能事业部的王兴杰。 更好的阅读体验可前往原文阅读:巨人肩膀 | 联通数科如何基于Apache DolphinScheduler构建DataOps一体化能力平台 今天&#xff0c;我将和大家聊一聊联通数字科技有限公司是如何基于Apache Dol…

k8s创建secret并在container中获取secret

k8s创建secret并在container中获取secret 本文使用的deployment和service与我的上一篇文章一样。link也放在下面了&#xff0c;如果不懂什么事deployment和service&#xff0c;可以先看我的上一篇文章。 k8s使用kustomize来部署应用 下面我们将通过创建secret开始。secret是我…

保姆教程篇:手把手教你从零开始本地部署Dify

本教程将指导您在个人电脑上安装和配置 Dify。 为什么需要Dify 在开始具体的教程之前&#xff0c;先搞清楚为什么要选择 Dify。 6 月份&#xff0c;阿里巴巴全球数学竞赛中&#xff0c;首次接受AI参赛。结果令人大跌眼镜&#xff1a;AI选手们的表现完全无法与人类选手相提并…

萌啦数据软件价格多少,萌啦数据软件价格是多少

在当今这个数据驱动的时代&#xff0c;无论是企业运营、市场分析还是个人研究&#xff0c;都离不开高效、准确的数据处理与分析工具。萌啦数据软件&#xff0c;作为业界一颗璀璨的新星&#xff0c;凭借其强大的功能、友好的用户界面以及灵活的数据处理能力&#xff0c;赢得了众…

[SWPUCTF 2021 新生赛]PseudoProtocols(构造伪协议)

打开题目所给的环境我们可以看到这样一句话&#xff1a; 这里我先尝试访问/hint.php &#xff0c;但是发现什么都没有发生&#xff0c; F12查看源代码也并没有发现什么&#xff0c;到这里来看的话似乎没有思路了&#xff0c;但是这个题的题目已经给了我们很明显的提示&#xff…

类和对象(中)(1)

类和对象&#xff08;中&#xff09;(1) 类的默认成员函数 默认成员函数就是用户没有显式实现&#xff0c;编译器会⾃动⽣成的成员函数称为默认成员函数。 ⼀个类&#xff0c;我们不写的情况下编译器会默认⽣成以下6个默认成员函数&#xff0c;需要注意的是这6个中最重要的是…

云计算实训24——python基本环境搭建、变量和数据类型、数据集合、py脚本

一、python环境搭建 确保拥有阿里云镜像 查看python环境 [rootpython ~]# yum list installed | grep python 查看epel是否安装 [rootpython ~]# yum list installed | grep epel 安装epel [rootpython ~]# yum -y install epel-release.noarch 查看是否安装python3 [rootpyt…

【数据结构】mapset详解

&#x1f341;1. Set系列集合 Set接口是一种不包含重复元素的集合。它继承自Collection接口&#xff0c;所以可以使用Collection所拥有的方法&#xff0c;Set接口的实现类主要有HashSet、LinkedHashSet、TreeSet等&#xff0c;它们各自以不同的方式存储元素&#xff0c;但都遵…

OceanBase V4.2特性解析:MySQL模式下GIS空间表达式的场景及能力解析

1. 背景 1.1. OceanBase Mysql gis空间表达式的应用场景及能力 在OceanBase 4.1版本中&#xff0c;mysql模式下支持了gis数据类型以及部分空间对象相关的表达式&#xff0c;随着客户使用空间数据的需求日益增长&#xff0c;需要快速地补齐空间数据存储和计算分析的能力&#…

实景三维:解锁地理信息新维度,引领未来城市智慧之钥

在这个信息爆炸与科技日新月异的时代&#xff0c;地理信息与遥感技术正以前所未有的速度改变我们认知世界的方式。在推动“实景三维平台”这一前沿科技的构建上&#xff0c;它不仅是地理信息的立体呈现&#xff0c;更是智慧城市的基石&#xff0c;打开了通往未来城市规划、管理…

C++设计模式(代理模式)

1. 电话虫 在海贼中&#xff0c;有一种神奇的通信工具叫做电话虫&#xff08;Den Den Mushi&#xff09;&#xff0c;外形如蜗牛&#xff0c;身上带有斑点或条纹或通体纯色&#xff0c;壳顶上有对讲机或按键&#xff0c;不接通时会睡觉&#xff0c;接通时会惊醒&#xff0c;并发…

数据结构之链表

写在前面 链表是一种常用的线性数据结构&#xff0c;在jdk中也提供具体的实现类java.util.LinkedList。本文来看下其相关内容。 1&#xff1a;链表的特点 链表是一种由很多个节点组成的线性数据结构&#xff0c;每个节点都有一个指向下一个节点的引用&#xff0c;从而构成链…

Unity(2022.3.38LTS) - 下载,安装

目录 A. 简介 B. 下载和安装UnityHub C. 下载安装unity编辑器 安装页面 选择版本 添加模块 D.总结 A. 简介 Unity 是一款广泛使用的跨平台游戏开发引擎。 一、主要特点 跨平台性&#xff1a; 支持多种主流平台&#xff0c;包括 Windows、Mac、Linux、iOS、Android、Xb…

LeetCode_sql_day15(262.行程与用户)

描述&#xff1a;262. 行程和用户 - 力扣&#xff08;LeetCode&#xff09; 取消率 的计算方式如下&#xff1a;(被司机或乘客取消的非禁止用户生成的订单数量) / (非禁止用户生成的订单总数)。 编写解决方案找出 "2013-10-01" 至 "2013-10-03" 期间非禁止…

Vue 应用实例的关键方法与配置案例一

目录 createApp createSSRApp app.mount app.unmount app.component app.directive Vue3.X自定义全局指令 Vue2.X自定义全局指令 app.use app.mixin 非 VIP 用户能够免费下载博文资源 createApp createApp 是 Vue 3.0 中用于创建应用实例的方法。它接收一个…

127. Go反射基本原理

文章目录 反射基础 - go 的 interface 是怎么存储的&#xff1f;iface 和 eface 的结构体定义&#xff08;runtime/iface.go&#xff09;&#xff1a;_type 是什么&#xff1f;itab 是什么&#xff1f; 反射对象 - reflect.Type 和 reflect.Value反射三大定律Elem 方法reflect.…

mysql中select的执行流程

目录 引言 SELECT查询语句的重要性 ​编辑引言部分重写示例&#xff1a; MySQL架构概览 MySQL架构概述 Server层的核心功能模块 知识点图文结合示例&#xff1a; 连接器的作用 连接器的职责 连接器职责 查询缓存的工作原理 查询缓存的概念 查询缓存的工作机制 查询…