React学习宝典

1. 第一个React页面

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <div id="test"></div>

  <!-- 引入核心库 -->
  <script type="text/javascript" src="../js/react.development.js"></script>

  <!-- 引入react-dom,用于支持react操作dom -->
  <script type="text/javascript" src="../js/react-dom.development.js"></script>

  <!-- 将jsx转为js -->
  <script type="text/javascript" src="../js/babel.min.js"></script>

  <script type="text/babel">
    const VDOM = <h1>Hello</h1>
    ReactDOM.render(VDOM, document.getElementById('test'))
  </script>
</body>
</html>

2. JSX语法规则

  • 定义虚拟dom时不要写引号
  • 标签中混入js表达式时要用{}
  • 样式类名指定要用className
  • 内联样式style={{color: ‘red’, fonsSize: ‘20px’}}
  • 只有一个根标签
  • 标签必须闭合
  • 标签首字母
    • 首字母小写则转为html同名标签
    • 大写就去渲染对应组件,如无则报错
<script type="text/babel">
    const data = 'aaa'
    const VDOM = (
    <div>
        <h1 className="title">
            <span style={{color: 'red', fonsSize: '20px'}}>{data}</span>
        </h1>
    </div>
    )
    ReactDOM.render(VDOM, document.getElementById('test'))
</script>

3. 组件类别

3.1 函数式组件

function MyComponent () {
    return <h2>我是一个组件</h2>
}
ReactDOM.render(<MyComponent />, document.getElementById('test'))

3.2 类式组件

class MyComponent extends React.Component {
    render () {
        return <h2>我是一个组件</h2>
    }
}
ReactDOM.render(<MyComponent />, document.getElementById('test'))
  • render是MyComponent的原型对象上,供实例使用

  • render中的this指向MyComponent组件实例对象

  • React解析组件标签,找到MyComponent组件,当发现改组件是类定义的时,new出来改类的实例,并通过改实例调用到原型上的render方法,将render返回的虚拟DOM转为真是DOM,随后呈现在页面中

4. 状态State

4.1 详细版

//1.创建组件
class Weather extends React.Component{

    //构造器调用几次? ———— 1次
    constructor(props){
        console.log('constructor');
        super(props)
        //初始化状态
        this.state = {isHot:false,wind:'微风'}
        //解决changeWeather中this指向问题
        this.changeWeather = this.changeWeather.bind(this)
    }

    //render调用几次? ———— 1+n次 1是初始化的那次 n是状态更新的次数
    render(){
        console.log('render');
        //读取状态
        const {isHot,wind} = this.state
        return <h1 onClick={this.changeWeather}>今天天气很{isHot ? '炎热' : '凉爽'}{wind}</h1>
    }

    //changeWeather调用几次? ———— 点几次调几次
    changeWeather(){
        //changeWeather放在哪里? ———— Weather的原型对象上,供实例使用
        //由于changeWeather是作为onClick的回调,所以不是通过实例调用的,是直接调用
        //类中的方法默认开启了局部的严格模式,所以changeWeather中的this为undefined

        console.log('changeWeather');
        //获取原来的isHot值
        const isHot = this.state.isHot
        //严重注意:状态必须通过setState进行更新,且更新是一种合并,不是替换。
        this.setState({isHot:!isHot})
        console.log(this);

        //严重注意:状态(state)不可直接更改,下面这行就是直接更改!!!
        //this.state.isHot = !isHot //这是错误的写法
    }
}
//2.渲染组件到页面
ReactDOM.render(<Weather/>,document.getElementById('test'))

4.2 简写版

class MyComponent extends React.Component {
    state = {
        isHot: true
    }
changeWeather = () => {
    console.log(this)
    const isHot = this.state.isHot
    this.setState({isHot: !isHot})
}
render () {
    return <h2 onClick={this.changeWeather}>今天天气很{this.state.isHot ? '炎热' : '凉爽'}</h2>
}

}
ReactDOM.render(<MyComponent />, document.getElementById('test'))

5. props

5.1 基本使用

class MyComponent extends React.Component {
    render () {
        return (
            <ul>
                <li>姓名:{this.props.name}</li>
                <li>性别:{this.props.sex}</li>
                <li>年龄:{this.props.age}</li>
            </ul>
        )
    }
}
const obj = {name:"tyh", age:"12", sex:"male"}
ReactDOM.render(<MyComponent {...obj} />, document.getElementById('test'))

5.2 传递限制

class MyComponent extends React.Component {
    render () {
        return (
            <ul>
                <li>姓名:{this.props.name}</li>
                <li>性别:{this.props.sex}</li>
                <li>年龄:{this.props.age}</li>
            </ul>
        )
    }
}
// 属性类型限制
MyComponent.propTypes = {
    name: PropTypes.string.isRequired
}
// 指定默认属性值
MyComponent.defaultProps = {
    name: 'aaa',
    sex: 'female',
    age: 12
}
const obj = {name:'111', sex:"male"}
ReactDOM.render(<MyComponent {...obj} />, document.getElementById('test'))

5.3 简写方式

class MyComponent extends React.Component {
    static propTypes = {
        name: PropTypes.string.isRequired
    }
static defaultProps = {
    name: 'aaa',
    sex: 'female',
    age: 12
}
render () {
    return (
        <ul>
            <li>姓名:{this.props.name}</li>
            <li>性别:{this.props.sex}</li>
            <li>年龄:{this.props.age}</li>
        </ul>
    )
}
}
const obj = {sex:"male"}
ReactDOM.render(<MyComponent {...obj} />, document.getElementById('test'))

6. ref使用方式

尽可避免字符串形式的ref

class MyComponent extends React.Component {
    showInfo = () => {
        const {input1} = this
        alert(input1.value)
    }
    render () {
        return (
            <div>
                <input type="text" ref={c => this.input1 = c} />
                <button onClick={this.showInfo}>点击获取</button>
            </div>
        )
    }
}
ReactDOM.render(<MyComponent />, document.getElementById('test'))

7. 非受控组件

现用现取的称为非受控组件

class MyComponent extends React.Component {
    submitInfo = (e) => {
        e.preventDefault()
        const {username, password} = this
        alert(`用户名为:${username.value},密码为:${password.value}`)
    }
    render () {
        return (
            <form onSubmit={this.submitInfo}>
                <input type="text" ref={c => this.username = c} />
                <input type="password" ref={c => this.password = c} />
                <button type="submit">登录</button>
            </form>
        )
    }
}
ReactDOM.render(<MyComponent />, document.getElementById('test'))

8. 受控组件

页面中所有输入的dom,随着用户的输入,可以将所输入的内容存在状态中,等需要使用的时候再调用,称为受控组件

class MyComponent extends React.Component {
    state = {
        username: '',
        password: ''
    }
submitInfo = (e) => {
    e.preventDefault()
    const {username, password} = this.state
    alert(`用户名为:${username},密码为:${password}`)
}
saveUsername = (e) => {
    this.setState({
        username: e.target.value
    })
}
savePassword = (e) => {
    this.setState({
        password: e.target.value
    })
}
render () {
    return (
        <form onSubmit={this.submitInfo}>
            <input type="text" onChange={this.saveUsername} />
            <input type="password" onChange={this.savePassword} />
            <button type="submit">登录</button>
        </form>
    )
}
}
ReactDOM.render(<MyComponent />, document.getElementById('test'))

9. 高阶函数的使用

9.1 高阶函数

  • 函数接收的参数是一个函数
  • 函数的返回值依然是一个函数
class MyComponent extends React.Component {
    state = {
        username: '',
        password: ''
    }
submitInfo = (e) => {
    e.preventDefault()
    const {username, password} = this.state
    alert(`用户名为:${username},密码为:${password}`)
}
saveInfo = (dataType) => {
    return (e) => {
        this.setState({
            [dataType]: e.target.value
        })
    }
}
render () {
    return (
        <form onSubmit={this.submitInfo}>
            <input type="text" onChange={this.saveInfo('username')} />
            <input type="password" onChange={this.saveInfo('password')} />
            <button type="submit">登录</button>
        </form>
    )
}
}
ReactDOM.render(<MyComponent />, document.getElementById('test'))

9.2 函数的柯里化

通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式

10. 生命周期

生命周期引入

class MyComponent extends React.Component {
    state = {
        opacity: 1
    }
death = () => {
    // 卸载组件
    ReactDOM.unmountComponentAtNode(document.getElementById('test'))
}
// 组件挂载完毕
componentDidMount () {
    this.timer = setInterval (() => {
        let {opacity} = this.state
        opacity -= 0.1
        if (opacity <= 0) opacity = 1
        this.setState({opacity})
    }, 200)
}
// 当组件即将被卸载时
componentWillUnmount () {
    clearInterval(this.timer)
}
render () {
    return (
        <div>
            <h1 style={{opacity: this.state.opacity}}>我是一段文字</h1>
            <button onClick={this.death}>卸载改组件</button>
        </div>
    )
}
}

ReactDOM.render(<MyComponent />, document.getElementById('test'))

10.1 旧的生命周期图

总共分为三个阶段

  • 初始化阶段(由ReactDOM.render()触发—初次渲染)
    • 1.constructor()
    • 2.componentWillMount()
    • 3.render()
    • 4.componentDidMount() ===> 一般在改钩子中做一些初始化的事
  • 更新阶段(由组件内部this.setState()或父组件重新render触发)
    • 1.shouldComponentUpdate()
    • 2.componentWillUpdate()
    • 3.render()
    • 4.componentDidUpdate()
  • 卸载组件(由ReactDOM.unmountComponentAtNode()触发)
    • componentWillUnmount() ===> 取消订阅消息等

10.2 新的生命周期

生命周期的三个阶段(新)

  • 1. 初始化阶段: 由ReactDOM.render()触发—初次渲染

    • constructor()
    • getDerivedStateFromProps
    • render()
    • componentDidMount()
  • 2. 更新阶段: 由组件内部this.setSate()或父组件重新render触发

    • getDerivedStateFromProps
    • shouldComponentUpdate()
    • render()
    • getSnapshotBeforeUpdate
    • componentDidUpdate()
  • 3. 卸载组件: 由ReactDOM.unmountComponentAtNode()触发

10.3 getSnapshotBeforeUpdate的使用场景

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>4_getSnapShotBeforeUpdate的使用场景</title>
	<style>
		.list{
			width: 200px;
			height: 150px;
			background-color: skyblue;
			overflow: auto;
		}
		.news{
			height: 30px;
		}
	</style>
</head>
<body>
	<!-- 准备好一个“容器” -->
	<div id="test"></div>
	
	<!-- 引入react核心库 -->
	<script type="text/javascript" src="../js/17.0.1/react.development.js"></script>
	<!-- 引入react-dom,用于支持react操作DOM -->
	<script type="text/javascript" src="../js/17.0.1/react-dom.development.js"></script>
	<!-- 引入babel,用于将jsx转为js -->
	<script type="text/javascript" src="../js/17.0.1/babel.min.js"></script>

	<script type="text/babel">
		class NewsList extends React.Component{

			state = {newsArr:[]}

			componentDidMount(){
				setInterval(() => {
					//获取原状态
					const {newsArr} = this.state
					//模拟一条新闻
					const news = '新闻'+ (newsArr.length+1)
					//更新状态
					this.setState({newsArr:[news,...newsArr]})
				}, 1000);
			}

			getSnapshotBeforeUpdate(){
				return this.refs.list.scrollHeight
			}

			componentDidUpdate(preProps,preState,height){
				this.refs.list.scrollTop += this.refs.list.scrollHeight - height
			}

			render(){
				return(
					<div className="list" ref="list">
						{
							this.state.newsArr.map((n,index)=>{
								return <div key={index} className="news">{n}</div>
							})
						}
					</div>
				)
			}
		}
		ReactDOM.render(<NewsList/>,document.getElementById('test'))
	</script>
</body>
</html>

11. Diff算法

(1)react/vue中key有什么作用,key的原理是什么

a.key时DOM对象的标识,在更新显示时key起着极其重要的作用

b.当状态中的数据发生变化的时候,react会根据新的数据生成新的虚拟DOM,然后新的虚拟DOM与旧的虚拟DOM进行比较,它们先根据key绑定的值进行比较,若新旧虚拟DOM中有两个key值相同的则比较DOm中的内容,若内容相同则直接使用旧的虚拟DOM,真是DOM不发生变化,若内容不同则使用新的虚拟DOM,新的虚拟DOM再生成新的真实DOM替换旧的真是DOM,若原本新旧两个虚拟DOM的内容完全一样,但key值不同,则新的虚拟DOM直接替换旧的虚拟DOM,会产生效率问题。

(2)用index作为key引发的问题

如果对数据进行逆序增加或者插入的时候,会产生没有必要的DOM更新,效率很低。

如果结构中还有用户可输入内容的DOM会发生内容的错乱

12. 配置代理解决跨域

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:5000
      	changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:3000
      	changeOrigin默认值为false,但我们一般将changeOrigin值设为true
      */
      pathRewrite: {'^/api1': ''} //去除请求前缀,保证交给后台服务器的是正常请求地址(必须配置)
    }),
    proxy('/api2', { 
      target: 'http://localhost:5001',
      changeOrigin: true,
      pathRewrite: {'^/api2': ''}
    })
  )
}

13. 组件之间的通信

13.1 父子组件之间的通信

子给父–> 自定义函数,通过函数传参

父给子–> props

13.2 兄弟之间的通信

消息订阅与发布或者取兄弟之间最近的公共父组件

yarn add pubsub-js --save

发布消息

PubSub.publish('myMsg', e)

接收消息

PubSub.subscribe('myMsg', (msg, data) => {
    console.log(msg, data)
})

14. 路由的基本使用

import React, { Component } from 'react';
import {Route, Link} from 'react-router-dom'
import About from './components/About'
import Home from './components/Home'

export default class App extends Component {
  render() {
    return (
      <div>
        <Link to="/about">About</Link>
        <Link to="/home">Home</Link>
        <Route path="/about" component={About}></Route>
        <Route path="/home" component={Home}></Route>
      </div>
    );
  }
}

在index.js中包裹一个路由器

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import {BrowserRouter} from 'react-router-dom'

ReactDOM.render(
  <React.StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </React.StrictMode>,
  document.getElementById('root')
);

15. 向路由组件传递参数

15.1 params参数

  • 路由链接(携带参数):

    <Link to='/demo/test/tom/18'}>详情</Link>
    
  • 注册路由(声明接收):

    <Route path="/demo/test/:name/:age" component={Test}/>
    
  • 接收参数:

    this.props.match.params
    

15.2 search参数

  • 路由链接(携带参数):

    <Link to='/demo/test?name=tom&age=18'}>详情</Link>
    
  • 注册路由(声明接收):

    <Route path="/demo/test" component={Test}/>
    
  • 接收参数:

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

15.3 state参数

  • 路由链接(携带参数):

    <Link to={{pathname:'/demo/test',state:{name:'tom',age:18}}}>详情</Link>
    
  • 注册路由(声明接收):

    <Route path="/demo/test" component={Test}/>
    
  • 接收参数:

    this.props.location.state
    // 备注:刷新也可以保留住参数
    

16. 编程式路由导航

借助this.prosp.history对象上的API对操作路由跳转、前进、后退

  • this.props.history.push()
  • this.props.history.replace()
  • this.props.history.goBack()
  • this.props.history.goForward()
  • this.props.history.go()

17. 按需引入样式

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' },
        }
    }),
);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

boboj1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值