当然,这只是react16的基本使用,里面没说react-hook,和redux,因为说这两个庞然大物又是一篇长文章了
总之一句话总结迭代如此快的前端开发如何轻松面对各种需求,只有足够努力,才能毫不费劲
react16知识点目录:
react的基本概念:
React是Facebook开发出的一款JS库 Facebook认为MVC无法满足他们的扩展需求
特点:
1.react不使用模板
2.react 不是一个MVC框架
3.响应式
4. react是一个轻量级的js库
原理:
虚拟DOM , react把DOM抽象成为一个JS对象
diff算法
1.虚拟dom确保只对界面上真正发生变化的部分进行实际的DOM操作
2.逐层次的来进行节点的比较
react历史:
2013年发布开源
高阶函数:
定义
:如果函数接收的参数是函数或者返回值是函数
例子
:promise()/then()/定时器/数组遍历相关方法/
好处
:更加动态,更加具有扩展性
高阶组件:
定义
:参数为组件,返回值为新的组件的函数,高阶组件是特别的高阶函数
例子
:WithRouter(组件)/connect()(组件)
作用
:react中用于复用组件逻辑的一种高级技巧
使用装饰器优化react中的高阶组件:
@connect(
state => ({
userInfo: state.userInfo }),
{
deleteUserInfo}
)
class Admin extends Component {
constructor(props) {
super(props)
}
}
export default Admin
class原版:
class Admin extends Component {
constructor(props) {
super(props)
}
}
export default connect(
state => ({
userInfo: state.userInfo }),
{
deleteUserInfo}
)(Admin)
特别的:
要想使用装饰器
,需要安装babel
npm install @babel/plugin-proposal-decorators
还需要
改写根目录的配置文件
纯函数:
1.一类特别的函数
:只要是同样的输入(实参),必定得到同样的输出(返回)
2.必须遵守以下一些约束
- a:不得改写参数数据
- b:不会产生任何副作用,例如网络请求,输入和输出设备
- c:不能调用Date.now()或者Math.random()等不纯的方法
3.redux的reducer函数必须是一个纯函数
react开发环境搭建:
- react.js 核心文件
- react-dom.js 渲染页面中的DOM,当前文件依赖于react核心文件
- babel.js ES6转换成es5,JSX转换成javascript,现今浏览器进行代码的兼容
下载:
react核心包 npm i react --save
react-dom npm i react-dom --save
babel npm i babel-standalone --save
jsx的理解:
jsx = javascript + xml 扩展javascript `优点`:1.执行的效率更快
2.类型更安全,编译的过程能及时的发现错误
3.在使用jsx的时候,编写模板更加的快速
注意:jsx中html标签必须按照w3c的规范来写,标签必须闭合
基本的react环境搭建:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<script src="/node_modules/react/umd/react.development.js"></script>
<script src="/node_modules/react-dom/umd/react-dom.development.js"></script>
<script src="/node_modules/babel-standalone/babel.js"></script>
</head>
<body>
<!-- 创建根节点,这个节点下的内容就会被react管理 -->
<div id="demo">
</div>
</body>
<script type="text/babel">
let myDom = <h1>你好世界1</h1>
ReactDOM.render(myDom,document.getElementById("demo"))
</script>
</html>
Render函数:
注意:render里不允许写setState是指在render的return之前不允许写,因为会造成死循环
。return体里面是可以写的
在jsx中的注释语法: {
/* xxxxxxxxxx */}
多行标签的使用:
想要使用多行标签,必须使用以一个父元素包裹,为了更好的识别,还通过()
包裹
<script type="text/babel">
let myDom = (
<div>
<h1>你好世界1</h1>
<h1>你好世界2</h1>
<h1>你好世界3</h1>
{
/*我是注释的内容*/}
</div>
)
ReactDOM.render(myDom,document.getElementById("demo"))
</script>
使用表达式,完成轻量级的数据展示:
<script type="text/babel">
let text = 'nihao'
let num = 34
let obj = {
name: "34332"
}
let arr = [423, 534, 124]
let fun = (data) => {
return "我是函数:" + data
}
let myDom = (
<div>
<h1>{
text}-{
num}-{
obj.name}-{
arr}-{
fun("aaaaa")}</h1>
</div>
)
ReactDOM.render(myDom, document.getElementById("demo"))
</script>
引入外部jsx代码文件
<script src="/js/jsx.js" type="text/babel"></script>
Jsx语法的进阶使用:
<script type="text/babel">
三目运算符
let phone = 42342
let myDom = (<div>
{
phone > 3000 ? "很贵" : "便宜"}
</div>)
渲染数组
let arr = [
<p key="1">1</p>,
<p key="2">2</p>,
<p key="3">3</p>,
<p key="4">4</p>,
]
myDom = (<div>
{
arr}
</div>)
属性设置
let text = '点我去百度'
let url = 'https://www.baidu.com/'
myDom = <a href={
url}>{
text}</a>
修改样式,注意:必须是一个对象{
}
let myStyle = {
color: "red", fontSize: "80px", }
myDom = <p style={
myStyle}>修改样式</p>
设置class,注意:在jsx中不能直接使用class,因为它是js的关键字,需要使用className
myDom = <p className="demo">修改样式</p>
ReactDOM.render(myDom, document.getElementById("demo"))
</script>
react列表渲染,遍历数组:
很灵活 <script type="text/babel">
//react列表渲染 map(),建议使用map对数组进行循环遍历
let arr = ["吃饭", "睡觉", "哈哈"]
let myDom = arr.map((item, index) => {
return (
<p key={
index}>{
item}</p>
)
})
// for in 循环遍历数组
let arrList = () => {
let newArr = ["gsdgsd", "sfsd", "dgsdgs"]
let emptyArr = []
for (let index in newArr) {
emptyArr.push(<p key={
index}>{
newArr[index]}</p>)
}
return emptyArr
}
myDom = arrList()
ReactDOM.render(myDom, document.getElementById("demo"))
</script>
实例:点击字体颜色变红色
<script type="text/babel">
let clickIndex = -1
function fn1() {
let arr = ["吃饭", "睡觉", "哈哈"]
let myDom = arr.map((item, index) => {
return (
<p
key={
index}
style={
{
color: clickIndex == index ? 'red' : '' }}
onClick={
() => {
console.log(index); clickIndex = index; render() }}
>{
item}</p>
)
})
return myDom
}
function render() {
ReactDOM.render(fn1(), document.getElementById("demo"))
}
render()
</script>
遍历对象:
<script type="text/babel">
遍历对象,Object.keys(),Object.values(),map()
let obj = {
name: "EFawfaw",
age: 13
}
let myDom
console.log(Object.keys(obj))
console.log(Object.values(obj))
myDom = Object.keys(obj).map((item, index) => {
return (<p> {
item} ---{
obj[item]} </p>)
})
ReactDOM.render(myDom, document.getElementById("demo"))
</script>
组件化开发:
组件的特点什么?
1.高内聚低耦合
2.高内聚就是把逻辑紧密的内容放在一个组件当中
3.低耦合把不同组件的依赖关系尽量弱化,每个组件尽可能的独立
组件当中的重要内容:
1.构建方式
2.组件内的属性
3.生命周期
组件的演变过程:
1.传统的组件的特点
+简单的封装
+简单生命周期的呈现
+明显的数据流动
当一个项目比较复杂的时候,传统的组件化根本不能很好的把结构样式和行为结合,
让项目难以维护
2.react的组件分为三个部分
+属性
+状态state
+生命周期
react的组件是 一个非常重要的概念,通过组件可以把页面中的ui部分切分成 独立
高复用性的部件,让每个开发者更加专注于一个个独立的部件
组件与组件化:
1.组件就是用来实现页面局部功能的代码集合,简化页面的复杂程度,提高运行效率、
2.组件化 当前程序都是使用组件完成的,那么就是一个组件化的应用
组件的创建:两种方式
1.函数式组件/无状态组件(组件首字母必须大写)
<script type="text/babel">
let myDom
//无状态组件的创建
//注意:组件首字母必须大写
function MyCom() {
return (<p>我是一个无状态的组件</p>)
}
//组件就是自定义标签
//调用组件
myDom = (
<div>
//单标签和双标签都可以调用组件
<MyCom> </MyCom>
<MyCom />
</div>
)
ReactDOM.render(myDom, document.getElementById("demo"))
</script>
函数式组件/无状态组件 父子组件的使用
:
<script type="text/babel">
父子组件的使用
子组件的创建
function MyComA() {
return (<p>我是第1个组件</p>)
}
function MyComB() {
return (<p>我是第2个组件</p>)
}
function MyComC() {
return (<p>我是第3个组件</p>)
}
父组件的创建
function ParentCom() {
return (
<div>
<MyComA />
<MyComB />
<MyComC />
</div>
)
}
let myDom
myDom =ParentCom()
ReactDOM.render(myDom, document.getElementById("demo"))
</script>
2.类组件(组件首字母必须大写)
<script type="text/babel">
//创建类组件
class MyCom extends React.Component{
render(){
return (
<div>类组件</div>
)
}
}
let myDom
myDom = <MyCom/>
ReactDOM.render(myDom, document.getElementById("demo"))
</script>
受控组件和非受控组件:推荐使用受控组件
没交给state管理,而是通过操作dom改变value的值,非受控组件
用户名:<input type="text" ref={
(input)=>{
this.nameInput= input} }/>
交给state管理,不用操作dom去改变value的值,受控组件
密码:<input type="password" value={
this.state.pwd} onChange={
this.handleChange } />
Props数据传递:父传子,子传父
props 是react中一个重要的属性,是组件对外的接口,通过props就可以从组件的外部向组件的内部进行数据的传递,也可以完成父组件给子组件数据的传递
注意:
无论是函数式组件还是类组件,我们都不能修改自身的props
父传子:
1.函数式组件/无状态组件 的props数据传递:
<script type="text/babel">
function Com(props) {
console.log(props)
return (
<div>我是函数式/无状态组件,外部传递的数据是--{
props.text}</div>
)
}
let myDom
传递一个数据,直接写在组件中
let demoVar = "我是数据"
传递多个参数,展开运算符
let obj ={
text:"我是text",
demoVar: "我是数据"
}
myDom = <Com text="我是text" demoVar={
demoVar} />
//等效于
myDom = <Com {
...obj} />
ReactDOM.render(myDom, document.getElementById("demo"))
</script>
2.类组件 的props数据传递:
<script type="text/babel">
class MyCom extends React.Component{
render(){
return (
<div>
我是类组件--{
this.props.text} -- {
this.props.demoVar}
</div>
)
}
}
let myDom
传递一个数据,直接写在组件中
let demoVar = "我是数据"
传递多个参数,展开运算符
let obj ={
text:"我是text",
demoVar: "我是数据"
}
myDom = <MyCom text="gesgse" demoVar={
demoVar} />
//等价于
myDom = <MyCom {
...obj} />
ReactDOM.render(myDom, document.getElementById("demo"))
</script>
子传父:
本质上也是父传子
,只不过是传入的function,通过function巧妙的实现子传父的效果
<script type="text/babel">
class Child extends React.Component {
constructor(props) {
super(props) }
render() {
return (
<div>
<button onClick={
this.props.fun.bind(this, "aaaaa")}>传递数据给父组件</button>
我是child
</div>
)
}
}
class MyCom extends React.Component {
constructor(props) {
super(props) }
func = (data) => {
console.log(data)
}
render() {
return (
<div>
<Child fun={
this.func} />
</div>
)
}
}
let myDom
myDom = <MyCom />
ReactDOM.render(myDom, document.getElementById("demo"))
</script>
props验证和默认值:
1.函数式组件/无状态组件的props验证和默认值
引用prop-types库:
npm install --save prop-types
注意: 上线模式中取消props验证
<script type="text/babel">
function MyCom(props) {
// react 15x版本设置默认值方法: ||的方式完成 , 16之后不允许修改props了
props.name = props.name || "我是name的默认值"
return (
<div>我是无状态组件--{
props.name}--{
props.age}</div>
)
}
// react 16版本之后的设置默认值方法
MyCom.defaultProps = {
name: "我是name的默认值",
age:12
}
MyCom.propTypes={
name:PropTypes.string, //验证name这个props,传递进来的必须是number类型
age:PropTypes.number.isRequired //是数字类型,且不能为空
}
let myDom
myDom = <MyCom />
ReactDOM.render(myDom, document.getElementById("demo"))
</script>
2.类组件的props验证和默认值
<script type="text/babel">
class Mycom extends React.Component{
//类中设置props默认值 (推荐)
static defaultProps={
name:"类中设置默认值"
}
//类组件的props验证(方式1)
static propTypes={
name:PropTypes.string
}
render(){
return (
<div>我是类组件--{
this.props.name}</div>
)
}
}
//类组件中 传统的设置默认值
// Mycom.defaultProps ={
// name:"我是name的默认值"
// }
//类组件的props验证(方式2)
Mycom.propTypes={
name:PropTypes.string
}
let myDom
myDom = <Mycom />
ReactDOM.render(myDom, document.getElementById("demo"))
</script>
案例:点击显示隐藏
<script type="text/babel">
let bool = true //用来保存内容显示隐藏的状态
let MyCom = (props) => {
return (
<div style={
{
display: bool ? "block" : "none" }}>
{
props.arr.map((item, index) => {
return (<p key={
index}>{
item}</p>)
})}
</div>
)
}
let dataArr = ["gesgse", "gsergse", "Stse"]
let handleClick = () => {
bool = !bool
render() //刷新页面
}
let ParentCom = (props) => {
return (
<div>
<h1 onClick={
handleClick}>我是父组件</h1>
<MyCom arr={
dataArr} />
</div>
)
}
function render() {
ReactDOM.render(<ParentCom />, document.getElementById("demo"))
}
render()
</script>
state状态:
注意:如果用使用state状态,不能使用函数式/无状态组件
state和props的主要区别:
-
state是可以修改的
-
props对于当前页面的组件来说,是只读的,如果想要修改props,只能修改父组件中的数据
-
props是组件对外的接口,state是组件对内的接口
声明式编程/渲染:
react中我们只需要关心的是数据,当数据改变的时候会自动发生变化,通过state管理的数据,不再需要手动的render()刷新页面,state状态等同于页面中的数据,状态/数据 改变–页面中对应的数据绑定内容就会被react自动的进行改变,这种方式也称之为 声明式渲染—-一切的数据改变都不用关心,只需要我们声明好数据
,react会自动管理更新数据
类组件中state状态的使用: 只有类组件才能使用state进行数据管理
-
this.setState({key:newValue},()=>{})
-
this.state.xxxx
<script type="text/babel">
class MyCom extends React.Component{
//es6中的写法,不管子类写不写constructor,new实例的时候,都会补上constructor
//我们可以不写,但如果写了constructor后,必须在其中写上super(),super就是指向父类constructor的构造方法
constructor(props){
//如果想在constructor中使用props,那么super必须写上props
super(props)
//定义state
this.state={
name:"xixixixixixixi"
}
}
render(){
return(
<div>
{
/*
修改state数据,一定不能直接修改,需要调用this.setState({key:newValue}), this.setState({key:newValue}) 是异步的,react会自动的触发render进行数据渲染
*/}
<button onClick={
()=>this.setState({
name:"aaaaaaaaaaaaaa"})}>
点我改变state数据</button>
我是一个组件--{
this.state.name}
</div>
)
}
}
let myDom
myDom = <MyCom />
ReactDOM.render(myDom,document.getElementById("demo"))
</script>
this.setState异步回调转同步:
import React, {
Component } from 'react'
class App extends Component {
state = {
count: 0
}
btnClick = async () => {
await this.setStateAsync({
count: this.state.count + 1 })
console.log(this.state.count)
}
setStateAsync = (state) => {
return new Promise((reslove) => {
this.setState(state, reslove)
})
}
render() {
return (
<div>
<p>{
this.state.count}</p>
<button onClick={
this.btnClick}>按钮</button>
</div>
)
}
}
export default App;
ref引用:
表示当前组件实例的引用,它会返回绑定当前ref属性的元素
作用:
标记组件内部的元素–方便我们查找
注意:
不能在无状态组件当中进行使用,因为无状态组件没有实例
react给我们提供了三种方式进行ref的使用:
-
字符串的方式
-
回调函数( 推荐 )
就是在dom节点上或者组件上挂载函数,函数的形参是dom节点,它达到的效果和字符串方式是一样的,都是获取值的引用 -
React.createRef() (react 16.3新提供的一种方式)
把值赋给一个变量,通过ref挂载在节点或者组件上,使用ref的current属性拿到这个节点
官方建议:不要过度使用refs对逻辑进行处理,需要优先考虑state
<script type="text/babel">
class MyCom extends React.Component {
constructor(props) {
super(props)
this.myRef = React.createRef()
}
fun = () => {
//1.字符串的方式
//需要注意的是,这里不能使用function 定义函数,因为this指向问题
// console.log(this.refs.demoInput.value)
//2.回调函数方式
// console.log(this.textinput.value)
//3.React.createRef()方式
console.log(this.myRef)
console.log(this.myRef.current)
}
//1.字符串的方式
render(){
return (
<div>
我是组件
<input type="text" ref="demoInput" placeholder="请输入" />
<button onClick={
this.fun }>点击我得到输入框的值</button>
</div>
)
}
//2.回调函数的方法(推荐)
render(){
return (
<div>
我是组件
<input type="text" ref={
(input)=>{
this.textinput = input }}
placeholder="请输入" />
<button onClick={
this.fun }>点击我得到输入框的值</button>
</div>
)
}
//3.React.createRef() 方式
render() {
return (
<div>
我是组件
<input type="text" ref={
this.myRef} placeholder="请输入" />
<button onClick={
this.fun}>点击我得到输入框的值</button>
</div>
)
}
}
let myDom
myDom = <MyCom />
ReactDOM.render(myDom, document.getElementById("demo"))
</script>
react事件处理:this指向,传递参数,传递事件对象
注意:react绑定事件使用的是小驼峰命名法,在绑定函数的时候不能加(),
否则函数会立即执行
修改this指向四种方式:
1.bind方式原地绑定
2.函数通过箭头函数创建 (推荐)
3.constructor中提前绑定
4.把事件的调用写成箭头函数的调用方式
函数实参传递两种方式:
1.bind onClick={this.fune.bind(this, "我是参数1")}
2.箭头函数 onClick={() => { this.fune("我是参数2") }}
传递事件对象event两种方式:
1. onClick={(e) => { this.fune("我是参数3", e) }}
2. onClick={this.fune} //什么都不传,默认参数为event
注意: 不能使用onClick={this.changeTitle(“xxx”)} 传递参数
<script type="text/babel">
class MyCom extends React.Component {
constructor(props) {
super(props)
3.constructor中提前绑定
this.func = this.func.bind(this)
}
1.bind方式原地绑定
funa() {
console.log(this)
}
2.函数通过箭头函数创建 (推荐)
fund = () => {
console.log(this)
}
3.constructor中提前绑定
func() {
console.log(this)
}
4.把事件的调用写成箭头函数的调用方式
fun() {
console.log(this)
}
传递事件参数event 和 传递普通参数
fune = (str, e) => {
console.log(str)
console.log(e)
}
render() {
return (
<div>
{
/* 1.bind方式原地绑定 */}
<button onClick={
this.funa.bind(this)}>bind的方式绑定</button>
{
/* 2.函数通过箭头函数创建 */}
<button onClick={
this.fund}>函数通过箭头函数创建</button>
{
/* 3.constructor中提前绑定 */}
<button onClick={
this.func}>constructor中提前绑定 </button>
{
/* 4.把事件的调用写成箭头函数的调用方式 */}
<button onClick={
() => {
this.fun() }}>把事件的调用写成箭头函数的调用方式 </button>
<h1>函数实参传递 和事件对象event传递 </h1>
{
/* 传递参数方式1:bind */}
<button onClick={
this.fune.bind(this, "我是参数1")}>点我传递实参</button>
{
/* 传递参数方式2:箭头函数 */}
<button onClick={
() => {
this.fune("我是参数2") }}>点我传递实参</button>
{
/* 传递事件对象event方式一: */}
<button onClick={
(e) => {
this.fune("我是参数3", e) }}>点我传递实参</button>
{
/* 传递事件对象event方式二:默认就是接收的参数就是事件对象e */}
<button onClick={
this.fune}>点我传递实参</button>
</div>
)
}
}
let myDom
myDom = <MyCom />
ReactDOM.render(myDom, document.getElementById("demo"))
</script>
React条件渲染:
条件渲染是什么?
根据状态的变化只渲染其中的一部分
条件渲染的方式:
1.if,else语句 jsx中不允许有if
2. 三目运算符
<script type="text/babel">
class MyCom extends React.Component {
constructor(props) {
super(props)
this.state = {
bool: true
}
}
click() {
this.setState({
bool: !this.state.bool
})
}
render() {
let text
if (this.state.bool) {
text = "ni hao"
} else {
text = 'xixixixixi'
}
return (
<div>
<button onClick={
this.click.bind(this)}>点我改变</button>
<p> if,else: {
text}</p>
<p>三元运算符:{
this.state.bool ? "哈哈" : "呵呵"}</p>
</div>
)
}
}
let myDom
myDom = <MyCom />
ReactDOM.render(myDom, document.getElementById("demo"))
</script>
React状态提升:
状态提升:
多个组件需要反映相同的变化数据,提升到他们共同的最近的一个父组件中
应用场景:
多个子组件需要利用到对方状态的情况下,那么这个时候就需要使用到状态提升
<script type="text/babel">
class Child1 extends React.Component {
constructor(props) {
super(props) }
render() {
return (
<div>
我是child1--{
this.props.text}
</div>
)
}
}
class Child2 extends React.Component {
constructor(props) {
super(props) }
render() {
return (
<div>
我是child2--{
this.props.text}
</div>
)
}
}
class MyCom extends React.Component {
constructor(props) {
super(props)
this.state = {
context: "我是两个子组件都想使用的数据"
}
}
func = () => {
this.setState({
context: "我要改变了" })
}
render() {
return (
<div>
<button onClick={
this.func}>点我修改两个子组件共用的数据</button>
<Child1 text={
this.state.context} />
<Child2 text={
this.state.context} />
</div>
)
}
}
let myDom
myDom = <MyCom />
ReactDOM.render(myDom, document.getElementById("demo"))
</script>
React脚手架的安装:
create-react-app 是 facebook官方推出的一款react的脚手架
npm install -g create-react-app
全局安装脚手架环境
create-react-app --version
查看版本
create-react-app 项目名
创建项目
npm start
启动项目
空标签:
在react中,空标签<></>或者Fragment标签都可以实现,不在dom中显示的效果
<>
你好我是组件{
parseInt(Math.random()*10)}
</>
<Fragment>
你好我是组件{parseInt(Math.random()*10)}
</Fragment>
img图片的引入:
- 在public中可以直接引入
- 不在public中,需要导入后,通过变量引入 或者 通过require引入
import React, {
Component,Fragment } from 'react';
import a1 from '../assets/a1.png'
class Home extends Component {
render() {
return (
<Fragment>
你好我是组件{
parseInt(Math.random()*10)}
在public中直接引入,自动会找到
<img src="logo512.png" />
<img src={
a1} />
<img src={
require("../assets/a2.png") } />
</Fragment>
);
}
}
export default Home;
实例应用:
this.headerList = Array.from(new Array(20)).map((_val, i) => ({
icon: require(`./images/头像${
i+1}.png`), //不能使用import
text: `头像${
i+1}`,
}));
React常用的代码段:
rcc: 基本的类结构
rccp:基本的类结构 + propTypes类型验证
react中同级传值/兄弟传值:
`方式一:` 安装发布订阅库:npm i --save pubsub-jsimport Pubsub from 'pubsub-js'
class Phone extends Component {
fun = () => {
Pubsub.publish('evt',"aaaaaa")
}
render() {
return (
<div> 我是phone
<button onClick={
this.fun}>进行同级传递数据</button>
</div>
);
}
}
import Pubsub from 'pubsub-js'
class News extends Component {
constructor(props){
super(props)
Pubsub.subscribe('evt',(msg,data)=>{
msg是触发事件的事件名称
console.log("phone传递过来数据:"+ data)
})
}
render() {
return (
<div>我是news</div>
);
}
}
方式二:
可以通过react内置的包导入事件中间:
import {
EventEmitter} from 'events'
const myEmitter = new EventEmitter();
export default myEmitter
event.on('event', (e) => {
console.log('触发事件');
console.log(e) //gfseg
});
event.emit('event',"gfseg");
React中跨域:
1.正向代理—开发环境 (前端可以做到)
一个位于客户端和目标服务器之间的代理服务器,为了获取到目标服务器的内容,客户端向代理服务器发送一个请求,代理服务器帮助客户端去目标服务器中获取数据,并且返回给客户端
2.反向代理—上线环境 (需要后端配置)
可以通过代理服务器来接收网络的请求,然后将这个请求转发给内部的网络服务器上,并且把这个服务器上得到的数据返回给网络请求的客户端,这个时候代理服务器对外的表现就是一个反向代理(掩盖后端服务器真正的地址)
正向代理:
方案一:
找到文件 项目
\node_modules\react-scripts\config\ webpackDevServer.config.js
proxy:{
"/api":{
target:"http://www.weather.com.cn/data/cityinfo",
changeOrigin:true,
"pathRewrite":{
"^/api":"/"
}
}
},
方案二:(推荐)
比如,我请求"http://localhost:3000/news"的时候跨域了
fetch("http://localhost:3000/news")
在package.json文件中配置跨域的地址
"proxy":"http://localhost:3000"
然后代码改为fetch("/news")
方案三: 这种方式可以配置多个跨域
安装http-proxy-middleware
npm i http-proxy-middleware
在src/下新建setupProxy.js
const proxy = require("http-proxy-middleware");
module.exports = function(app) {
app.use(proxy("/api", {
target: "http://localhost:3333" , //配置你要请求的服务器地址
changeOrigin: true,
}))
app.use(proxy("/manage/api", {
target: "http://admintest.happymmall.com:7000" ,
changeOrigin: true,
}))
};
React路由:
路由 ----- 根据url的不同来切换对应的组件,实现spa,在页面切换的时候不会刷新整个页面,更接近原生体验路由有两个库:
react-router: 只提供了一些核心的api
react-router-dom: 提供了更多的一些选项(使用这个居多)
下载:npm install --save react-router-dom
路由模式:
1.hash (HashRouter)
哈希模式
,使用url中的hash值来切换的,带#,刷新时页面不会丢失
2.browser (BrowserRouter )
历史记录模式
,通过h5历史记录api来切换路由,刷新会丢失页面,本地模式不会
如果是HashRouter模式,可以通过 <a href="#/list">
来跳转
index.js中引入使用: 路由模式包裹使用
import {
BrowserRouter, HashRouter } from 'react-router-dom'
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>
,
document.getElementById('root')
);
配置使用路由: Route标签展示组件
import {
Route} from 'react-router-dom'
import News from './News'
import Phone from './Phone'
class Home extends Component {
constructor(props) {
super(props) }
render() {
return (
<div>
<Route path="/news" component={
News} ></Route>
<Route path="/phone" component={
Phone} ></Route>
</div>
);
}
}
路由导航: 切换路由组件
1. Link标签,切换路由时不会动态的加上class=”active”
<Link to="/news">跳转到news</Link>
<Link to="/phone">跳转到phone</Link>
2. NavLink标签,切换路由时会动态的加上class=”active”
<NavLink to="/news">跳转到news</NavLink>
<NavLink to="/phone">跳转到phone</NavLink>
自定义路由的激活class,class=”active”:
<NavLink to="/CommentAdd" activeClassName="xixixi">跳转Add</NavLink>
<NavLink to="/CommentItem" activeClassName="xixixi">跳转Item</NavLink>
实例应用封装: 将外部传入的所有属性传递给NavLink
import React, {
Component } from 'react'
import {
Route,NavLink} from 'react-router-dom'
export default class MyNvaLink extends Component {
render() {
将外部传入的所有属性传递给NavLink(很灵活)
return (
<NavLink {
...this.props} activeClassName="xixi" />
)
}
}
exact精准匹配:
<Route path="/" exact component={
HOME} ></Route>
<Route path="/news" component={
News} ></Route>
<Route path="/phone" component={
Phone} ></Route>
Route 标签加上 exact 表示,只有url http://localhost:3000/ 才匹配到HOME,否则http://localhost:3000/news 也会匹配到 HOME 路由
strict更加精准的匹配: strict存在的时候,exact必须也存在才有效
<Route path="/news" component={
News} ></Route>
<Route strict exact path="/phone" component={
Phone} ></Route>
http://localhost:3000/phone 可以匹配到phone组件,但是http://localhost:3000/phone/ 不行
Switch标签: 避免组件多次渲染,Switch下从上到下只会匹配一个路由组件
import {
Route,Link,NavLink ,Switch } from 'react-router-dom'
<Switch>
<Route path="/news" component={
News} ></Route>
<Route path="/news" component={
News} ></Route>
</Switch>
渲染的时候只会出现一次News组件
实例应用:404页面
import {
Route,Link,NavLink ,Switch } from 'react-router-dom'
<Switch>
//此时/news可以匹配到home,从而不再继续渲染其他路由,exact就是来填这个坑的
<Route path="/" component={
Home} ></Route>
<Route path="/news" component={
News} ></Route>
<Route path="/Nine" component={
Nine} ></Route>
<Route component={
Nofound404} ></Route>
</Switch>
News和Nine匹配不到了就会显示404页面
路由重定向:Redirect
import {
Route, Switch ,Redirect} from 'react-router-dom'
<Redirect from="/" to="/home" exact />
二级路由/嵌套路由:
在一级路由的组件中,再嵌套路由,并且重定向标签改为
<Redirect from="/" to="/home/homeChild" exact />
withRouter:是一个高阶组件
让不是路由切换的组件也具有路由切换组件的三个属性( location,match,history )
,监控路由的变化
加上withRouter后,props中会多出location,match,history三个对象
import {
Route,Redirect,withRouter} from 'react-router-dom'
class Home extends Component {
constructor(props) {
super(props)
props.history.listen((link)=>{
console.log(link)
})
}
render() {
return (
<div>
<Route path="/news" component={
News} ></Route>
<Route path="/phone" component={
Phone} ></Route>
<Redirect from="/" to="/news" exact />
);
}
}
export default withRouter(Home);
History:监控路由的变化(前提是需要包裹在withRouter中)
constructor(props) {
super(props)
props.history.listen((link)=>{
console.log(link)
})
}
this.props.history还具有以下方法:
push(location)
replace(location)
go(n)
goBack()
goForward()
编程式导航: 通过history.push(‘/xxxx’),history.replace(‘/xxxx’)
实现
前进,可以后退回来
<button onClick={
()=>{
this.props.history.push('/phone') }}>编程式导航</button>
替换,后退不回来
<button onClick={
()=>{
this.props.history.replace('/phone') }}>编程式导航</button>
路由传参:
params方式进行传参:match
1.需要在路由规则中设置传递的接受参数 :xxx
2.发送参数,直接在跳转路径后进行编写
3.接受props.match.params.参数名
优点: 刷新地址,参数依然存在
缺点: 只能传递字符串,并且参数过多的时候url会变得丑陋
<NavLink to="/phone/我是参数121321">跳转到phone</NavLink>
<Route path="/phone/:id" component={
Phone} ></Route>
class Phone extends Component {
componentDidMount(){
console.log(this.props.match.params)
console.log(this.props.match.params.id)
}
render() {
return ( <div>我是Phone</div> ); }
}
query方式:location (推荐)
1.不需要在路由规则中进行传递参数的配置
2.直接发送数据
3.使用this.props.location.queury.xxx
<NavLink to={
{
pathname:"/phone",query:{
name:"小明"}}}>
跳转到phone
</NavLink>
<Route path="/phone" component={
Phone} ></Route>
class Phone extends Component {
componentDidMount(){
console.log(this.props.location.query)
}
render() {
return ( <div>我是Phone</div> ); }
}
生命周期:
`react生命周期的理解:`- 组件对象从创建到死亡它会经历特定的生命周期阶段
- React组件对象包含一系列的勾子函数(生命周期回调函数), 在生命周期特定时刻回调
- 我们在定义组件时, 可以
重写
特定的生命周期回调函数, 做特定的工作
生命周期流程图:
生命周期详述:
- 组件的三个生命周期状态:
Mount
:插入真实 DOM
Update
:被重新渲染
Unmount
:被移出真实 DOM
2) React 为每个状态都提供了勾子(hook)函数
componentWillMount()
组件将要被挂载在dom上
componentDidMount()
组件已经被挂载在dom上
componentWillUpdate()
组件将要被更新
componentDidUpdate()
组件已经被更新
componentWillUnmount()
组件将要被卸载
生命周期的流程:
第一次初始化渲染显示的流程:
第一次初始化渲染显示: ReactDOM.render() 调用
constructor(): 创建对象初始化state
componentWillMount() : 将要插入回调
render() : 用于插入虚拟DOM回调
componentDidMount() : 已经插入回调
每次更新state的流程:
每次更新state: this.setSate() 调用
componentWillUpdate() : 将要更新回调
render() : 更新(重新渲染)
componentDidUpdate() : 已经更新回调
移除组件的流程:
移除组件: ReactDOM.unmountComponentAtNode(containerDom)
componentWillUnmount() : 组件将要被移除回调
重要的勾子:
1) render(): 初始化渲染或更新渲染调用
2) componentDidMount(): 开启监听, 发送ajax请求
3) componentWillUnmount(): 做一些收尾工作, 如: 清理定时器
4) componentWillReceiveProps(): 监视接收到新的props,第一次不触发
特殊的勾子:
shouldComponentUpdate():
重写此方法return false,true可以控制页面是否更新this.forceUpdate():
调用此方法,就算state,接收的props没有改变,也可以强制刷新一次页面
即将废弃的钩子: 现在使用会出现警告,下个版本需要加上UNSAFE_前缀
componentWillMount()
componentWillReceiveProps()
componentWillUpdate()
注意:
componentsDidMount
只会在组件加载完后执行一次,之后更新state、props都不会执行,除非重新加载组件。componentWillReceiveProps
在组件传进来的props
被更改时,将被调用。所以可以在componentWillReceiveProps
函数里改变state来重新获取数据。
新的生命周期图:
新钩子:
getDerivedStateFromProps(props,state):钩子用于取代 componentWillMount、
componentWillUpdate
componentWillReceiveProps
getSnapshotBeforeUpdate(props,state):介于componentDidUpdate和render之间
componentDidUpdate(props,state,arg)可以接收到getSnapshotBeforeUpdate返回的参数
实例:
static getDerivedStateFromProps(props,state){
console.log('--getDerivedStateFromProps--',props,state);
//return {data:props.name}
return props //会把props里的对象追加到this.state中
}
注意:
想使用getSnapshotBeforeUpdate,必须和componentDidUpdate一起使用,
componentDidUpdate第三个参数接收getSnapshotBeforeUpdate的返回值
getSnapshotBeforeUpdate(props,state){
console.log('--getSnapshotBeforeUpdate--',props,state);
return 'peiqi'
}
componentDidUpdate(props,state,data){
//data是'peiqi'
console.log('--componentDidUpdate--',props,state,data);
}
生命周期的流程:
1.【初始化】
触发条件:ReactDOM.render(<MyComponent/>)
constructor()
static getDerivedStateFromProps(Props, prevState) ----- 【新增】
render():提供虚拟DOM,可能会调用多次(1+n)。
componentDidMount():启动定时器、发送Ajax请求、只执行一次。
2.【更新】
触发条件:this.setState({
})
static getDerivedStateFromProps(Props, prevState)
shouldComponentUpdate()
render()
getSnapshotBeforeUpdate() ---- 【新增】
componentDidUpdate()
3.【卸载】
触发条件:ReactDOM.unmountComponentAtNode()
componentWillUnmount():做收尾工作,例如:清除定时器等,该回调只会执行一次。
玩react有段时间了,有需求就做一次总结吧,为同学,为友人,为自己,也为前端爱好者,更为大环境,如有错误还望指出