别人的总结
React
React 是 facebook 出的一款针对前端 view 视图层的 library 库。react 使用了 jsx 语法(就是在 js 中写 html 标签)
https://zh-hans.reactjs.org/ react 官网地址
7/27 第一天
react js 文件的引入
<div id="app"></div>
<script src="./libs/babel.min.js"></script>
<!-- js文件引入的顺序,先引入react.js,再引入react-dom.js -->
<script src="./libs/react.js"></script>
<script src="./libs/react-dom.js"></script>
<!-- <script src="./db.js"></script> -->
<!-- script标签必须加上type="text/babel",因为我们需要解析jsx语法 -->
<script type="text/babel">
// var list = [];
// console.log(pets);
// 在react中组件需要使用大写开头
// react传参使用props
function HelloWorld(props) {
return <h1>你好,世界!,count值为:{props.count}</h1>
}
// 使用了class类的继承
class HelloMesage extends React.Component {
// render表示渲染组件,所有的组件中都有这个方法
render() {
return <h5>我是一句话</h5>
}
}
// render 参数一 表示展示的组件内容,参数二表示位置
// react 中花括号 {} 里面是js表达式
ReactDOM.render(<HelloMesage count={2} />, document.getElementById('app'))
</script>
第一个 react 循环数据
// React中所有的组件都是一个function对象
function Person(props) {
// 在react中设置样式的时候使用style
// 在react中设置class样式名的时候需要写成className,因为class是js的保留字
return (
<li className="person" style={{ color: props.color }}>
{props.name}
</li>
);
}
function People() {
const renwu = ["Tom", "Jerry", "马冬梅"];
// 使用jsx循环生成数据的时候一定要使用map
return (
<ul>
{renwu.map((p, i) => (
<Person key={i} color={i % 2 == 0 ? "red" : "blue"} name={p} />
))}
</ul>
);
}
// render 参数一 表示展示的组件内容,参数二表示位置
ReactDOM.render(<People />, document.getElementById("app"));
</script>
// 请求接口渲染数据
// fetch原生js自带的一种http数据请求方式
fetch('http://localhost:3009/api/v1/products')
.then((res) => res.json())
.then((res) => {
function Products() {
//使用jsx循环生成数据的时候一定要使用map
return (
<ul>
{res.products.map((p, i) => (
<li key={p._id}>{p.name}</li>
))}
</ul>
)
}
//render 参数一 表示展示的组件内容,参数二表示位置
ReactDOM.render(<Products />, document.getElementById('app'))
})
7/28 第二天
react 组件定义
组件定义的两种常见形式
1、function,无状态组件。此种方法定义的组件没有 this 指向问题,但是在 react16.8 之后的版本中可以通过 hooks 实现组件的局部状态和生命周期,是目前官方推荐的组件写法
// 语法
// Name:组件名 props:父组件传的参数
function Name(props) {
return jsx语法写的内容
}
// React中所有的组件都是一个function对象
function HelloWord(props) {
return <h1>你好,世界!,count值为:{props.count}</h1>
}
2、class,可以继承自 React.Component 或者 React.PureComponent,相对来说继承自 React.PureComponent 的组件性能更快一些
// 使用了class类的继承
class HelloMessage extends React.Component {
// render 表示渲染组件,所有的组件中都有这个方法
render() {
return <h5>只是一句话</h5>
}
}
function 定义的组件案例
function Count(props) {
console.log(props)
const step = props.step || 1
// useState可以在function定义的组件中设置一个局部状态
// 此方法返回一个数组
// 第一个值为可以使用的变量名
// 第二个值表示改变当前数据的方法(可选)
// 此处用到了解构赋值:
// const arr=["小白","小红",'小绿']
// const [a,b] = arr // a="小白",b="小红"
// const [count, setCount] = React.useState(0)
// const [title, setTitle] = useState('当前的标题')
const [count, setCount] = useState(0)
const [list, setList] = useState([])
const keyUpHandle = (e) => {
if (e.keyCode === 13) {
setList([{ id: Date.now(), content: e.target.value }, ...list])
}
}
return (
<div>
<h5>当前的步长为:{step}</h5>
<ul>
{list.map((item) => (
<li key={item.id}>{item.content}</li>
))}
</ul>
<input type="text" placeholder="请输入内容" onKeyUp={keyUpHandle} />
<button onClick={() => setCount(count + step)}>计数值为:{count}</button>
</div>
)
}
ReactDOM.render(
<div>
<Count step={5} />
<Count step="5" />
</div>,
document.getElementById('app')
)
class 定义的组件
const { Component } = React
class Counter extends Component {
constructor(props) {
super(props)
// class定义的组件可以通过设置state来定义局部状态
this.state = {
count: 1,
title: '',
}
// 第一种改变this的方法,官方推荐写法
// this.clickHandle = this.clickHandle.bind(this)
this.keyUpHandle = this.keyUpHandle.bind(this)
}
clickHandle() {
console.log(this)
// 在class定义的组件中改变state时需要调用setState方法
// setState方法是异步的
//setState的第二个参数是数据改变成功之后的回调函数
this.setState(
{
count: this.state.count + 1,
},
function () {
console.group('count值已经改变成功了')
}
)
}
keyUpHandle(e) {
this.setState({
title: e.target.value,
})
}
// 每一个class组件都有一个render函数,表示当前组件输出的内容,需要有一个返回值,返回一个jsx标签,class定义的组件方法中获取属性时使用this.props固定写法
// 当state或者props发生改变之后都会执行render方法
// 不能在这个方法中改变state数据(会造成死循环)
render() {
return (
<div>
<h5>当前计数值为:{this.props.step}</h5>
<button onClick={() => this.clickHandle()}>
计数值:{this.state.count}
</button>
<button onClick={this.clickHandle.bind(this)}>
计数值2:{this.state.count}
</button>
<h5>{this.state.title}</h5>
<input type="text" onKeyUp={this.keyUpHandle} />
</div>
)
}
}
ReactDOM.render(<Counter step={6} />, document.getElementById('app'))
const { Component } = React
class App extends Component {
constructor(props) {
super(props)
this.state = {
list: [],
page: 1,
}
}
componentDidMount() {
// console.log("组件挂载成功之后的处理函数");
// 此处主要用来获取数据操作
this.loadMore()
}
loadMore() {
const { page, list } = this.state // 解构赋值
fetch('http://localhost:3009/api/v1/books?page=' + page)
.then((res) => res.json())
.then((res) => {
// console.log(res);
this.setState({
list: [...list, ...res.books],
page: page + 1,
})
})
}
// 当组件的state或者props改变之后render方法会触发执行
render() {
const { list } = this.state
console.count('执行了render方法')
return (
<div className="main">
<h1>我是一个App</h1>
<ul>
{list.map((book) => (
<li key={book.id}>{book.title}</li>
))}
</ul>
<button onClick={() => this.loadMore()}>加载更多</button>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'))
react 组件传参
父组件向子组件传参使用 props 属性进行
子组件向父组件传参使用方法调用
父组件向子组件传参列子
.ball {
width: 200px;
height: 200px;
border-radius: 50%;
display: block;
margin: 1rem auto;
/* background-color: deeppink; */
transition: all 2s;
}
const { Component } = React
class Ball extends Component {
constructor(props) {
super(props)
}
render() {
return (
<div style={{ backgroundColor: this.props.color }} className="ball" />
)
}
}
class App extends Component {
constructor(props) {
super(props)
this.state = {
color: 'yellowgreen',
}
}
changeColor(color) {
this.setState({
color, // color:color
})
}
// 当组件的state或者props改变之后会触发执行
render() {
return (
<div className="app">
<button onClick={() => this.changeColor('red')}>红色</button>
<button onClick={() => this.changeColor('orange')}>橙色</button>
<button onClick={() => this.changeColor('green')}>绿色</button>
<button onClick={() => this.changeColor('blue')}>蓝色</button>
<Ball color={this.state.color} />
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'))
父组件掉数据传递给子组件渲染页面
const { Component } = React
class Book extends Component {
render() {
// 接收book属性传递的内容,此处用了解构赋值{book:book}
const { book } = this.props
return (
<div className="card text-white bg-primary">
<img className="card-img-top" src={book.coverImg} alt="" />
<div className="card-body">
<h4 className="card-title">{book.title}</h4>
<p className="card-text">{book.descriptions}</p>
</div>
</div>
)
}
}
class App extends Component {
constructor(props) {
super(props)
this.state = {
list: [],
}
}
componentDidMount() {
this.loadMore()
}
loadMore() {
fetch('http://localhost:3009/api/v1/books')
.then((res) => res.json())
.then((res) => {
console.log(res.books)
this.setState({
list: res.books,
})
})
}
render() {
return (
// 遍历list数组取得单个书籍信息book,将book信息赋值给book属性,通过book属性传给Book组件
// 遍历list数组循环创建Book组件渲染页面
<div className="row">
{this.state.list.map((book) => (
<div key={book._id} className="col-md-4">
<Book book={book} />
</div>
))}
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'))
子组件向父组件传参示例
const { Component } = React
class Counter extends Component {
constructor(props) {
super(props)
this.state = { count: 0 }
}
render() {
return (
<div
onClick={() => {
this.setState({ count: this.state.count + 1 }, function () {
// 将count值作为changeF的参数传递给changeFromChild方法
this.props.changeF(this.state.count)
})
}}
>
点一下:{this.state.count}
</div>
)
}
}
class App extends Component {
constructor(props) {
super(props)
this.state = { fromChild: 0 }
this.changeFromChild = this.changeFromChild.bind(this)
}
//父组件调用changeFromChild方法获取到子组件的count值
changeFromChild(c) {
this.setState({
fromChild: c,
})
}
render() {
// 子组件Counter创建changeF属性绑定changeFromChild方法
return (
<div>
<h5>子组件传递的数据为:{this.state.fromChild} </h5>
<br />
<Counter changeF={this.changeFromChild} />
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'))
hooks
- useState 可以定义一个局部状态数据
- useEffect 副作用,当一个数据或者属性发生改变之后会引起的一些变化
useEffect 和 useState 的简单使用示例
const { useState, useEffect } = React
const App = (props) => {
// 当属性或者state改变之后function会重新执行
console.count('组件重新渲染了')
// useState可以在function定义的组件中设置一个局部状态
// 此方法返回一个数组
// 第一个值为可以使用的变量名,第二个值表示改变当前数据的方法
// useState会对我们定义好的数据进行缓存,当数据改变之后他们不会更新,否则会造成死循环
const [list, setList] = useState([])
const [count, setCount] = useState(0)
const [page, setPage] = useState(1)
// useEffect()叫做副作用,它有两个参数,第一个参数是一个回调函数 ,第二个参数是依赖项
// 当依赖项数据发生改变,会执行第一个参数中的回调函数
// 如果依赖项是一个空数组[]表示只有初始化的时候会执行一次
useEffect(() => {
console.count('初始化')
}, [])
useEffect(() => {
loadData()
}, [page])
const loadData = () => {
fetch('http://localhost:3009/api/v1/books?page=' + page)
.then((res) => res.json())
.then((res) => setList([...list, ...res.books]))
}
// useEffect(() => {
// console.count('list count值变了')
// }, [list, count])
return (
<div>
<h3 onClick={() => setCount(count + 1)}>这是一个组件-{count}</h3>
<ul>
{list.map((item) => (
<li key={item._id}>{item.title}</li>
))}
</ul>
<button onClick={() => setPage(page + 1)}>加载数据</button>
</div>
)
}
ReactDOM.render(<App />, document.getElementById('app'))
7/29 第三天
react 组件生命周期
常用生命周期钩子函数
- componentWillMount 组件挂载完成加载数据
- shouldComponentUpdate 主要用来对组件做性能优化的时候使用,通过 return 布尔值来判断组件是否更新
class Ball extends React.Component {
componentWillMount() {
console.log('组件Ball将要挂载')
}
componentDidMount() {
console.log('组件Ball挂载完成')
}
componentWillUnmount() {
console.log('组件Ball将要卸载')
}
componentWillReceiveProps(p) {
console.log(p)
console.log('组件将要接收新的属性')
}
render() {
return <div className="ball" />
}
}
class App extends React.Component {
// 初始化阶段
constructor(props) {
super(props)
this.state = {
title: '标题',
count: 0,
list: ['小凡', '陆雪琪', '碧瑶'],
}
}
// 挂载阶段
componentWillMount() {
console.log('组件将要挂载')
}
componentDidMount() {
console.log('组件挂载完成')
}
// shouldComponentUpdate 主要用来对组件做性能优化的时候使用
// 更新阶段,当状态或者属性发生改变之后会触发更新阶段的钩子函数,在更新阶段 不能修改数据
shouldComponentUpdate(nextProps, nextState) {
// console.log(p);
// console.log(s);
console.log('组件是否需要更新')
if (nextState.count % 2 === 0) {
return false // 返回值为false,组件不需要更新,不会执行render方法
} else {
return true
}
}
componentWillUpdate() {
console.log('组件将要更新')
}
componentDidUpdate() {
console.log('组件更新完成')
}
componentWillReceiveProps() {
console.log('组件将要接收新的属性')
}
// 卸载阶段
componentWillUnmount() {
console.log('组件将要卸载')
}
render() {
console.log('render执行了')
return (
<div>
<h5>{this.state.title}</h5>
<button
onClick={() =>
this.setState({
count: this.state.count + 1,
})
}
>
{this.state.count}
</button>
{this.state.count % 2 == 0 ? <></> : <Ball r={Math.random()} />}
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'))
ref 操作
class 定义的组件中可以使用 this.refs
function 定义的组件中可以使用 useRef
- 非受控组件,组件的数据不受 state 状态管理,在获取的时候直接通过 ref 属性进行获取
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
name: '',
skills: '',
desc: '',
}
}
render() {
return (
<ul>
<li>
<input ref="name" type="text" placeholder="请输入名字" />
</li>
<li>
<input ref="desc" type="text" placeholder="请输入简介" />
</li>
<li>
<input ref="skills" type="text" placeholder="请输入技能" />
</li>
<li>
<button
onClick={() => {
// 非受控组件,组件的数据不受state状态管理,在获取的时候直接通过ref属性进行获取
console.log(this.refs)
console.log(this.refs.name.value)
console.log(this.refs.skills.value)
console.log(this.refs.desc.value)
}}
>
提交
</button>
</li>
</ul>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'))
获取表单数据
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
name: '',
skills: '',
desc: '',
}
this.keyUpHandle = this.keyUpHandle.bind(this)
}
keyUpHandle(e) {
console.log(e.target.dataset.id)
const obj = {}
// 对象动态赋值,获取input输入的内容
obj[e.target.dataset.id] = e.target.value
this.setState(obj)
}
render() {
return (
<ul>
<li>
<input
onKeyUp={this.keyUpHandle}
data-id="name"
type="text"
placeholder="请输入名字"
/>
</li>
<li>
<input
onKeyUp={this.keyUpHandle}
data-id="desc"
type="text"
placeholder="请输入简介"
/>
</li>
<li>
<input
onKeyUp={this.keyUpHandle}
data-id="skills"
type="text"
placeholder="请输入技能"
/>
</li>
<li>
<button
onClick={() => {
console.log(this.state)
}}
>
提交
</button>
</li>
</ul>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'))
ref-function 操作
const { useRef } = React
function App() {
const name = useRef() // 可以为标签定义一个ref属性,在组件中直接获取他的内容
const desc = useRef()
const skills = useRef()
return (
<ul>
<li>
<input ref={name} type="text" placeholder="请输入名字" />
</li>
<li>
<input ref={desc} type="text" placeholder="请输入简介" />
</li>
<li>
<input ref={skills} type="text" placeholder="请输入技能" />
</li>
<li>
<button
onClick={() => {
console.log(name.current.value)
console.log(desc.current.value)
console.log(skills.current.value)
}}
>
提交
</button>
</li>
</ul>
)
}
ReactDOM.render(<App />, document.getElementById('app'))
context
Context 提供了一种在组件之间共享此类值的方式,而不必显式地通过组件树的逐层传递 props。
Context 主要应用场景在于很多不同层级的组件需要访问同样一些的数据。请谨慎使用,因为这会使得组件的复用性变差。
如果你只是想避免层层传递一些属性,组件组合(component composition)有时候是一个比 context 更好的解决方案。
// 创建一个 Context 对象。当 React 渲染一个订阅了这个 Context 对象的组件,这个组件会从组件树中离自身最近的那个匹配的 Provider 中读取到当前的 context 值。
const mainContext = React.createContext()
const { Provider } = mainContext
class B extends React.Component {
constructor(props) {
super(props)
this.state = {}
}
render() {
console.log(this.context)
return (
<>
<h1>我是B组件</h1>
</>
)
}
}
B.contextType = mainContext //第一种调用Context内参数的方法
class App extends React.Component {
constructor(props) {
super(props)
this.state = {}
}
// static 这个类属性来初始化你的 contextType。
static contextType = mainContext //第二种调用Context内参数的方法
render() {
return (
<>
<B />
</>
)
}
}
//每个 Context 对象都会返回一个 Provider React 组件,它允许消费组件订阅 context 的变化。
// Provider 接收一个 value 属性,传递给消费组件。一个 Provider 可以和多个消费组件有对应关系。多个 Provider 也可以嵌套使用,里层的会覆盖外层的数据。
// 当 Provider 的 value 值发生变化时,它内部的所有消费组件都会重新渲染。Provider 及其内部 consumer 组件都不受制于 shouldComponentUpdate 函数,因此当 consumer 组件在其祖先组件退出更新的情况下也能更新。
ReactDOM.render(
<Provider value={{ title: '我是一个标题', color: 'orange' }}>
<App />
</Provider>,
document.getElementById('app')
)
useContext
useContext 可以获取当前组件的上下文内容
const mainContext = React.createContext()
const { Provider } = mainContext
function B() {
// useContext可以获取当前组件的上下文内容
const c = React.useContext(mainContext)
console.log(c)
return (
<>
<h1>这是一个组件B</h1>
</>
)
}
function App() {
return (
<>
<B />
</>
)
}
ReactDOM.render(
<Provider value={{ title: '标题', color: 'orange' }}>
<App />
</Provider>,
document.getElementById('app')
)
使用 context 上下文进行跨组件传参
const mainContext = React.createContext()
const { Provider } = mainContext
function B() {
// 跨组件传参,使用context上下文
// useContext可以获取当前组件的上下文内容
const { counter } = React.useContext(mainContext)
// console.log(c)
return (
<>
<h1 onClick={() => counter.setCount(counter.count + 1)}>这是一个组件B</h1>
</>
)
}
function App(props) {
const { counter } = React.useContext(mainContext)
// App组件里面的所有子标签,可以通过{props.children}展示,类似于vue组件里面的插槽
return (
<>
<h1>当前的count值为:{counter.count}</h1>
<B />
{props.children}
</>
)
}
function AppProvider(props) {
const [count, setCount] = React.useState(0)
// 创建的局部状态
return (
<Provider value={{ counter: { count, setCount } }}>
{props.children}
</Provider>
)
}
ReactDOM.render(
<AppProvider>
<App />
</AppProvider>,
document.getElementById('app')
)
7/30 第四天
全选/反选
const { useState, useEffect } = React
function CheckAll() {
const [checkAll, setCheckAll] = useState(false)
const [isCheckAllClicked, setCheckAllClick] = useState(false)
const [list, setList] = useState([
{
id: 1,
name: '从你的全世界路过',
checked: true,
},
{
id: 2,
name: '沙丘',
checked: false,
},
{
id: 3,
name: '三体',
checked: false,
},
{
id: 4,
name: '球状闪电',
checked: false,
},
])
useEffect(() => {
// 设置全选
setCheckAll(list.filter((item) => item.checked).length == list.length)
}, [list])
// 全选按钮改变之后
useEffect(() => {
if (isCheckAllClicked) {
setList(
list.map((item) => {
item.checked = checkAll
return item
})
)
setCheckAllClick(false)
}
}, [checkAll])
return (
<div>
<p>
<label
onClick={() => {
setCheckAllClick(true)
setCheckAll(!checkAll)
}}
>
<input type="checkbox" onChange={() => {}} checked={checkAll} />
全选
</label>
</p>
<ul>
{list.map((item) => (
<li key={item.id}>
<label
onClick={() => {
list.find((i) => i.id === item.id).checked = !list.find(
(i) => i.id === item.id
).checked
setList([...list])
}}
>
<input
onChange={() => {}}
type="checkbox"
checked={item.checked}
/>
{item.name}
</label>
</li>
))}
</ul>
</div>
)
}
function App() {
return <CheckAll />
}
ReactDOM.render(<App />, document.getElementById('app'))
使用 react 脚手架搭建项目
npx create-react-app XXXX
cd XXXX #进入文件夹
npm start #启动项目
创建.jsx文件后在文件内输入rfce按Tap键快速生成function组件(rcc快速生成class组件,但是function组件用起来更为方便)
Ant Design - 一套企业级 UI 设计语言和 React 组件库:https://ant-design.gitee.io/index-cn
8/3 第五天
react路由
npm i react-router-dom #安装路由插件
Router 表示所有需要使用路由的部分都必须包含在此节点内部,一个项目只需要有一个此节点就好
此Router有两种常见的形式hash browser
Route 表示一个路由对象,需要属性path component
render方法,可以使用一个组件渲染路由内容,注意:此种方式渲染的组件是没有路由相关的属性信息传递的,需要使用withRouter方法把路由属性带到组件内部
Link 生成一个a标签
withRouter是一个函数,接收一个组件作为参数,返回一个新的组件,为组件添加一些路由属性信息
const About = (props)=>{
console.log(props);
return <h1>About us</h1>
}
// 此处为高阶组件
const AboutPage = withRouter(About)
*** 高阶组件 ***:把一个组件当作参数传递到一个方法里面为它进行封装,添加一些新的属性,返回一个新的组件 ,相当于js中的高阶函数(回调函数)
function App() {
return (
<Router>
<div className="App">
<ul>
<li>
<Link exact to="/">首页</Link>
</li>
<li>
<Link to="/list?t=cart">列表</Link>
</li>
<li>
<Link to="/p">People</Link>
</li>
</ul>
<hr />
{/* Route是一个组件 */}
<div className="container">
{/* exact 表示完全匹配 */}
<Route path="/" exact component={Home} />
<Route path="/list" component={List} />
<Route path="/p" component={People} />
{/* render方式渲染组件,没有直接使用component指定组件
这时候在路由对应的组件中没有history和location等路由的属性信息
*/}
<Route path="/about" render={() => <About />} />
</div>
</div>
</Router>
);
}
// hsitory.push('/list') 编程跳转
const Home = (props) => {
const { history } = props;
return (
<div>
<h1>首页</h1>
<Button
onClick={() => {
// 使用编程式跳转
history.push("/list");
}}
>
列表页
</Button>
<Button
onClick={() => {
// 使用编程式跳转
history.push("/about");
}}
>
关于我们
</Button>
</div>
);
};
URLSearchParams
路由组件中会包含history\location\match可以打印props查看
使用new URLSearchParams 获取当前查询条件
使用模块qs也能获取查询条件
function List(props){
console.log(props);
const sq = new URLSearchParams(props.location.search);
// get方法通过url中?后面的键名
console.log(sq.get('t'))
return ()
}
路由嵌套
所谓路由嵌套就是在路由跳转的组件内部编写Route组件
私有路由
import React from "react";
import { Route, Redirect } from "react-router-dom";
// 此处定义一个私有路由 需要登录之后才能访问
// PrivateRoute(props)也可以,该方法在对应的私有路由博客那篇文章里有展示
function PrivateRoute({ children, ...rest }) {
const isLogined = localStorage.getItem("token") ? true : false;
// console.group("私有路由判断");
// console.log(isLogined);
// console.groupEnd();
return (
<Route
{...rest}
render={() => (isLogined ? children : <Redirect to="/login" />)}
/>
);
}
export default PrivateRoute;
私有路由的使用
通过封装好的权限验证组件将需要进行路由验证的的组件包裹
import React from "react";
import {
HashRouter as Router,
Route,
Link,
NavLink,
Switch,
} from "react-router-dom";
import List from "./components/List";
import People from "./components/People";
import PrivateRoute from "./components/PrivateRoute";
import Login from "./pages/Login";
function App() {
return (
<Router>
<div className="App">
{/* Route是一个组件 */}
<div className="container">
{/* Switch 只会匹配一个路由内容 */}
<Switch>
<Route path="/login" render={() => <Login />} />
{/* exact 表示完全匹配 */}
<Route path="/" exact component={Home} />
// 此处使用了路由权限验证
<PrivateRoute path="/list">
<List />
</PrivateRoute>
// params传参需要使用/:占位符
<PrivateRoute path="/p/:o">
<People />
</PrivateRoute>
{/* render方式渲染组件,没有直接使用component指定组件
这时候在路由对应的组件中没有history和location等路由的属性信息
*/}
</Switch>
</div>
</div>
</Router>
);
}
NavLink生成的a标签多一个active属性,可以利用此特性给选中的a标签添加样式
<NavLink>是<Link>的一个特定版本,会在匹配上当前的url的时候给已经渲染的元素添加参数,组件的属性有
activeClassName(string):设置选中样式,默认值为active
activeStyle(object):当元素被选中时,为此元素添加样式
exact(bool):为true时,只有当导致和完全匹配class和style才会应用
strict(bool):为true时,在确定为位置是否与当前URL匹配时,将考虑位置pathname后的斜线
isActive(func)判断链接是否激活的额外逻辑的功能
8/4 第四天
redux
状态管理工具,遵循的单项数据流。所谓单项数据流就是数据单向流动
reducer是用来改变数据的
action是用来组织数据,每一个action都需要包含一个type属性,type的类型不能重复是唯一的
state是用来存储数据的
如果要改变数据是通过dispatch派发一个action在reducer中改变数据
redux和vuex的区别:
vuex只能在vue中使用
redux可以和任何一个前端框架进行结合
redux使用
npm i redux #安装依赖项
import { createStore, combineReducers } from 'redux'
// 定义reducer的时候它是一个function接收两个参数
// 参数一 初始状态
// 参数二 action 表示以什么方式改变数据
function counter(state = { count: 1 }, action) {
switch (action.type) {
case 'ADD':
return {
...state,
count: state.count + action.payload.step,
}
default:
return state
}
}
const store = createStore(rootReducer) // 初始化创建一个store
console.log(store.getState())
combineReducers 方法
Redux 提供了一个combineReducers
方法,用于 Reducer 的拆分。你只要定义各个子 Reducer 函数,然后用这个方法,将它们合成一个大的 Reducer。
import { combineReducers } from 'redux';
// 还要引入其中reducer各个分组件,此处没写,懒得:import...
const chatReducer = combineReducers({
chatLog,
statusMessage,
userName
})
export default todoApp;
关联redux和react
需要安装插件:
- 注意:引入第三方模块要放在自己创建的模块前面
npm i react-redux
Provider 提供者
使用Provider 提供者 方法 通过store属性将redux内容提供给组件
import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'
import App from './App'
import store from './store'
ReactDOM.render(
// 此处第一个store是Provider的属性,第二个store是变量名
<Provider store={store}>
<App />,
</Provider>,
document.getElementById('root')
)
connect 映射
connect 连接 react-redux模块中的方法
import { connect } from 'react-redux'
function App(props) {
console.log(props)
return (
<div className="App">
<People />
</div>
)
}
// 1️⃣connect方法完整版
// state表示所有数据
function mapStateToProps(state) {
return state
}
// 通过connect可以把redux中的state数据和dispatch方法映射到组件的属性中
export default connect(mapStateToProps)(App)
// 2️⃣connect方法简写
export default connect((state)=>state)(App)
dispatch 派发
function App(props) {
console.log(props)
const { count, dispatch } = props //将count内容从props中解构出来
return (
<div className="App">
<h1> {count}</h1>
<button
// 通过事件回调dispatch方法派发一个action在reducer中改变数据
onClick={() =>
dispatch({
type: 'ADD',
payload: {
step: 2,
},
})
}
>
加
</button>
<Counter />
</div>
)
}
redux-thunk 处理异步
第三方插件
作用:解决redux里面处理异步操作的事情
npm i redux-thunk
import { createStore, compose, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import rootReducers from './reducers'
// combineReducers 合并多个reducer为一个
//使用了redux-thunk插件之后可以返回一个function作为dispatch的参数
// 判断当前的action是什么
//如果是一个function那么会自动的把dispatch当作参数传递到function内部,否则直接dispatch
//初始化创建store时
第一步我们要在index.js中让middleware可用。
我们将使用到compose和applyMiddleware
compose 其作用是把一系列的函数,组装生成一个新的函数,并且从后到前,后面参数的执行结果作为其前一个的参数。compose(f, g, h) is identical to doing
(...args) => f(g(h(...args))).
applyMiddleware 最常见的使用场景是无需引用大量代码或依赖类似 Rx 的第三方库实现异步 actions。这种方式可以让你像 dispatch 一般的 actions 那样 dispatch 异步 actions。
const store = createStore(
rootReducers,
compose(
applyMiddleware(...[thunk]),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
)
) // 初始化创建一个store
8/5 react后台管理项目搭建
需要使用的技术
antd :4.x版本 UI框架
react-router-dom:5.x版本 路由
redux:全局状态管理
axios:封装
项目创建
npx create-react-app XXXX #项目名称
安装插件
npm i antd axios redux react-redux react-router-dom redux-thunk
文件夹封装
src
-
assets
- images
-
utils 封装网络请求和常用的类
- request.js 发网络请求
- config.js 配置信息
-
pages 所有的页面
-
components 组件
-
services 封装的网络请求
-
store
- index.js 入口文件
- actions
- reducers
-
routes/index.js 路由