React 全家桶

本文详细介绍了React,包括其基础内容、面向组件编程、React脚手架以及扩展知识。重点讲解了React的组件化、虚拟DOM、事件处理、状态管理和生命周期。还涉及React Router、Redux、Ant Design的使用,以及React应用的打包与性能优化。
摘要由CSDN通过智能技术生成


前言

React 全家桶:React-Router 路由库、PubSub 消息管理库、Redux 集中状态管理库、Ant-Design UI


一、React是什么?

React 是 Facebook 开发的,用于构建用户界面的开源 JavaScript 库(是一个将数据渲染为HTML视图的开源 JavaScript 库)

页面渲染步骤:

  • 1、发送请求获取数据
  • 2、处理数据(过滤、整理格式等)
  • 3、操作 DOM 呈现页面 (React 的工作)

原生 JavaScript 与 React 比较:

  • 原生JavaScript
    操作 DOM 繁琐、效率低
    直接操作 DOM 、进行大量的重绘重排
    没有组件化编码、复用率低
    命令式编码
  • React
    组件化模式、声明式编码、提高开发效率及组件复用率
    React Native 中可以使用 React 语法进行移动端开发
    高效:虚拟 DOM(不总直接操作DOM) + 优秀 Diffing 算法(最小化页面重绘)尽量减少与真实 DOM 的交互

二、基础内容

1. React 相关 js 库

  • react.js:React 核心库
  • react-dom.js: 提供操作 DOM 的 React 扩展库
  • babel.min.js:解析 JSX 语法代码转化为 JS 代码的库(es6 ⇒ es5、import 模块化、JSX ⇒ JS)

2. React 开发者调试工具

React Developer Tools

3. JSX语法规则

  • 1、定义虚拟DOM时,不要写引号
  • 2、标签中混入 JS 表达式时要用 {}

一定要注意区分:【JS表达式】 与 【JS语句(代码)】
JS 表达式: 一个表达式会产生一个值,可以放在任何一个需要值的地方:a、a+b、demo(1)、arr.map、function demo(){}
JS 代码: 控制代码走向,没有返回值:if(){}、 for(){}、switch(){case:xx}

  • 3、样式的类名指定不要用 class,要用 className
  • 4、内联样式,要用 style ={ {key: value}} 的形式去写
  • 5、虚拟 DOM 只能有一个根标签
  • 6、标签必须闭合
  • 7、标签首字母
    若小写字母开头,则将该标签转为 html 中同名元素,若html中无该标签对应的同名元素,则报错
    若大写字母开头,react 渲染对应的组件,若组件没有定义,则报错
	<div id="root"></div>
	<script type="text/babel">
	   const catName = '花姐'
	   // 用 JSX 创建虚拟 DOM
	   const VDOM = (
	       <div>
	           <h2 className="name">
	               <span style={
   {
   color: "orange", fontSize: "20px"}}>{
   catName}</span>
	           </h2>
	       </div>
	   )
	   ReactDOM.render(VDOM, document.getElementById('root'))
	</script>

4. 模块与组件、模块化与组件化

  • 模块(JS模块)
    理解:向外提供特定功能的 js 程序,一般就是一个 js 文件
    作用:复用 js ,简化 js 的编写,提高 js 的运行效率

  • 组件
    理解:用来实现局部功能效果的代码和资源的集合(html、css、js、image等)
    作用:复用编码、简化项目编码、提高运行效率

  • 模块化:当应用的 js 都以模块来编写的,这个应用就是一个 模块化 的应用

  • 组件化:当应用都是以组件的方式实现,这个应用就是一个 组件化 的应用

5. 类的基本知识

	// 创建一个 cat 类
	class Cat{
   
	  // 构造器方法
	  constructor(name){
   
	      // 构造器中的 this 指向类的实例对象: 谁调用了new, this 指向谁
	      this.name = name
	  }
	  // 一般方法:放在了类的原型对象(_proto_)上, 供实例使用
	  // 通过 Cat 实例调用一般方法时,this 指向 Cat 实例
	  sayName(){
   
	      console.log(`我叫${
     this.name}`)
	  }
	}
	// 创建一个 cat 的实例对象
	const HJ = new Cat('花姐')
	
	// 创建一个 Minicat 类,继承于 Cat 类
	class Minicat extends Cat {
   
	  constructor(name, age){
   
	      // super 作用:调用父类的构造器函数;super 必须写在其他参数之前
	      super(name);
	      this.age = age
	  }
	
	}
	// 创建一个 Minicat 的实例对象
	const mini = new Minicat('橘宝', 2)
	
	/*
	  总结:
	      1、类中的构造器不是必须写的,要对实例进行一些初始化操作的时候,如添加属性才写;
	      2、如果 A 类继承了 B 类,且 A 类写了构造器,那 A 类中构造器的 super 是必须调用的;
	      3、类中的方法,都是放在类的原型对象上(_proto_),供实例使用
	*/

三、React 面向组件编程

1. 函数式组件

适用于 简单组件(无state) 的定义
	// 创建函数式组件
    function Demo(){
   
    	// babel 编译后开启了严格模式,严格模式禁止自定义的函数 this 指向 widow 
    	console.log(this);  // undefined
        return <h2>我是函数式组件</h2>
    }
    // 渲染组件到页面
    ReactDOM.render(<Demo/>, document.getElementById('root'));
    /*
      执行了 ReactDOM.render((<Demo />.... 之后:
       1、React 解析组件标签,找到了 Demo 组件;
       2、发现是函数式组件的,随后调用该函数,将返回的虚拟 DOM 转为真实 DOM ,随后呈现在页面中
   */

2. 类式组件

适用于 复杂组件(有state) 的定义
	/*
      创建类式组件
          1、React.Component:React 内置类
          2、内部 render(){} 必须写,放在 Demo 原型对象上,供实例使用
    */
    class Demo extends React.Component {
   
        render(){
   
            // render 中的 this 指向 Demo 组件实例对象
            console.log(this);
            return <h2>我是类式组件</h2>
        }
    }
    // 渲染组件到页面
    ReactDOM.render(<Demo />, document.getElementById('root'))

    /*
      执行了 ReactDOM.render((<Demo />.... 之后:
          1、React 解析组件标签,找到 Demo 组件;
          2、发现组件是使用类定义的,随后 new 出来该类的实例,并通过该实例调用到原型上的 render 方法
          3、将 render 返回的虚拟 DOM 转化为真实的 DOM, 随后呈现在页面中
    */

3. 组件实例的三个核心属性: state、refs、props

- state
		// state 复杂写法
		class Weather extends React.Component {
   
			constructor(props){
   
			    super(props);
			    this.state = {
    isHot: false };
			    // .bind(this)用来解决 this 指向问题
			    this.changeWeather = this.changeWeather.bind(this); 
			}
			/*
			    changeWeather 放在 Weather 原型对象上,供实例使用
			    由于 changeWeather 是作为 onClick 回调,而不是通过实例调用的,是直接调用
			    类中的方法默认开启了举报的严格模式,所以方法中的 this 为 undefined
			*/
			changeWeather(){
    this.setState({
   isHot: !this.state.isHot}) }
			render(){
   
			    const {
    isHot } = this.state;
			    return <p onClick={
   this.changeWeather} >今天天气{
   isHot}</p>
			}
		}
		// state 简写方式: 省略类构造器,函数修改 this 指向
		class Weather extends React.Component {
   
	        // 类中可以直接写赋值语句,给类添加属性(不能写var\let\const)
	        state =  {
    isHot: fasle };
	        /*
	            赋值语句的形式 + 箭头函数,修改函数 this 指向
	            箭头函数没有自己的this, 声明时会向外寻找 this,始终指向函数声明时所在的作用域下的 this 的值
	        */
	        changeWeather = () => this.setState({
   isHot: !this.state.isHot})
	        render(){
   
	            const {
    isHot } = this.state;
	            return <p onClick={
   this.changeWeather} >今天天气{
   isHot}</p>
	        }
	    }
	    /*
	        总结:
	            1、组件中 render 方法中的 this 为组件实例对象
	            2、组件自定义的方法中 this 为undefined, 解决方法:
	                通过函数对象的 bind(), 强制绑定this
	                箭头函数 + 赋值语句,声明方法
	            3、状态数据,通过 setState 修改
	            	this.state.isHot = true 修改 isHot 的值会成功,但是不会触发渲染
	            	需要通过 setState 修改才会触发重新渲染
	            4、标签绑定事件 onClick 要大写
	    */
- props

理解:每个组件都有一个 props(properties) 属性,组件标签的所有属性都保存在 props 中
作用:通过标签属性从组件外部向组件内部传递变化的数据
注意:组件内部不要修改 props 数据,会报错

        let obj ={
   name: '花姐'}

        // 1、函数组件使用 props,参数形式
        function Demofn(props){
   
            const {
   name} = props
            return(
                <p>{
   name}</p>
            )
        }
        // 对标签属性进行类型、必要性限制
        Demofn.propTypes = {
   
            name: PropTypes.string.isRequired, // string 表示为字符串,isRequired 表示必传
        }
        // 指定默认标签属性值
        Demofn.defaultProps = {
   
            name: '喵'
        }
        ReactDOM.render(<Demofn {
   ...obj}/>, document.getElementById('root'))

        // 2、类式组件中的构造器 与 props
        class Democlass extends React.Component {
   
            // 构造器是都接收 props,是否传递给 super 取决于是否希望在构造器中通过 this 访问 props
            // 开发过程中一般不使用构造器
            constructor(props){
   
                super(props);
                console.log(this.props); // 需要调用super(props),才能在构造器中使用 this.props,否则为 undefined
            }
            // 对标签属性进行类型、必要性限制
            static propTypes = {
   
                name: PropTypes.string.isRequired, // string 表示为字符串,isRequired 表示必传
            }
            // 指定默认标签属性值
            static defaultProps = {
   
                name: '喵'
            }
            render(){
   
                let {
   name} = this.props
                return(
                    <p>{
   name}</p>
                )
            }
        }
        // {...obj} 扩展运算符批量传递
        ReactDOM.render(<Democlass {
   ...obj}/>, document.getElementById('root2'))
- refs (注意 ref 与 refs)
  • 字符串形式 (不推荐使用,存在效率问题)
	class Demo extends React.Component {
   
         getRef = () => {
   
         	// this.refs.btn 是真实 DOM 
             const {
    btn } = this.refs
         }
         render(){
   
             return(
                 <button ref="btn" onClick={
   this.getRef}>字符串形式 ref (不推荐使用)</button>
             )
         }
     }
     ReactDOM.render(<Demo/>, document.getElementById('root'))
  • 回调函数形式(内联写法)
	class Demo extends React.Component {
               
         getRef = () => {
   
             const {
    btn } = this
         }
         render(){
   
             return(
                 <button ref={
   c=> this.btn = c} onClick={
   this.getRef}>回调形式 ref, 内联写法</button>
             )
         }
     }
     ReactDOM.render(<Demo/>, document.getElementById('root'))

回调 ref 在页面更新过程中调用次数的问题(可以忽略,影响不大):
第一次传入 null,第二次传入 DOM,每次更新渲染时会创建一个新的函数实例,React 会清空就的 ref 并设置新的 ref
解决:通过 ref 回调函数定义成 class 的绑定函数可以解决

	class Demo extends React.Component {
   
		 saveBtn = (c) => {
    this.btn = c }
         getRef = () => {
    const {
    btn } = this }
         render(){
   
             return(
                 <button ref={
   this.saveBtn} onClick={
   this.getRef}>回调形式 ref, 内联写法</button>
             )
         }
     }
     ReactDOM.render(<Demo/>, document.getElementById('root'))
  • createRef 形式
    React.createRef()调用后可以返回一个容器,该容器可以存储被 ref 所标识的节点
    该容器专人专用,需要多个 ref 需要重复 React.createRef()
	class Demo extends React.Component {
   
       myRefs = React.createRef()
       getRef = () => {
   
           console.log(this.myRefs.current)
       }
       render(){
   
           return(
               <button ref={
   this.myRefs} onClick={
   this.getRef}>createRef 形式</button>
           )
       }
    }
    ReactDOM.render(<Demo/>, document.getElementById('root'))

4. React 中的事件处理

  • 1、通过onXxx 属性指定事件处理函数(注意大小写)
    React 使用的是自定义(合成)事件,而不是使用的原生 DOM 事件,为了更好的兼容性
    React 中的事件是通过事件委托方式处理的(委托给组件最外层的元素)

事件的执行顺序为原生事件先执行,合成事件后执行
合成事件会冒泡绑定到 document 上,所以尽量避免原生事件与合成事件混用
如果原生事件阻止冒泡,可能会导致合成事件不执行,因为需要冒泡到 document 上合成事件才会执行
react 事件不能采用 return false 的方式来阻止浏览器的默认行为,而必须要地明确地调用 event.preventDefault() 来阻止默认行

  • 2、通过 event.target 得到发生事件的 DOM 元素对象 – 不要过度使用 ref
    发生事件的元素跟获取数据的元素是同个时,就省略 ref ,使用event.target

5. 受控组件 与 非受控组件

受控组件:页面所有输入都由 state 控制
非受控组件: 现取现用 ref

6. 高阶函数 - 函数柯理化

  • 高阶函数
    如果一个函数符合下面2个规范中的任何一个,那么函数就是高阶函数
    1、若A函数,接收的参数是一个函数,A就可以称为高阶函数
    2、若A函数,函数调用的返回值依然是一个函数,A就可以称为高阶函数
    例:promise(() => {})、setTimeout(() => {})、arr.map(() => {}) (数据常见的方法)等

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

	function sum(a){
   
       return b => {
   
           return c =>{
   
               return a + b + c
           }
       }
   }
   sum(1)(2)(3)

react 表单柯里化案例:

	class Login extends React.Component {
   
	    state = {
   
	        username: '',
	        password: ''
	    }
	    handleSubmit = () => {
   
	        event.preventDefault();
	        const {
   username, password} = this.state
	    }
	    // 柯里化实现
	    saveFormData = (type) => {
   
	        return (event) => {
   
	            this.setState({
   [type]: event.target.value})
	        }
	    }
	    // 不使用柯里化实现
	    saveTypeData = (type, event) => {
   
	        this.setState({
   [type]: event.target.value})
	    }
	    render(){
   
	        return(
	            <form onSubmit={
   this.handleSubmit}>
	                {
   /* 柯里化实现 */}
	                用户名:<input type="text" name="username" onChange={
   this.saveFormData('username')} />
	                {
   /* 不使用柯里化实现 */}
	                密码:<input type="password" name="password" onChange={
   event => this.saveTypeData('password', event)} />
	                <button>登录</button>
	            </form>
	        )
	    }
	}

7. 组件生命周期

在这里插入图片描述

  • constructor:构造函数
  • render:渲染
  • getDerivedStateFromProps:从props得到一个派生的state(17 新增),返回 null 或 状态对象
  • componentDidMount:组件挂载完毕
  • shouldComponentUpdate:控制组件是否更新 返回值 true 更新, false 不更新 (this.setState() 触发)
  • getSnapshotBeforeUpdate:组件在更新之前获取快照(17 新增)
  • componentDidUpdate:组件更新完毕
  • React.unmountComponentAtNode(document.getElementById(‘root’)): 卸载组件
  • componentWillUnmount:组件将要被卸载

componentWillMount :组件将要挂载(17 废弃)
componentWillUpdate :组件将要更新(17 废弃)(this.forceUpdate() 触发)
componentWillReceiveProp s:子组件将要接收新的 props,首次不调用(17 废弃)

旧生命周期总结:

  1. 初始化阶段(挂载时):由 ReactDOM.render() 触发初次渲染
    constructor()
    componentWillMount()
    render()
    componentDidMount()

  2. 更新阶段:由组件内部 this.setStat

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值