React-class类篇


react特点

优势

  • 声明式设计 −React采用声明范式,可以轻松描述应用。

    //命令式编程:命令“机器”如何去做事情(how),这样不管你想要的是什么(what),它都会按照你的命令实现。类似jquery 操作DOM,创建一个页面,一点点的告诉DOM 怎么去挂载,你要怎么去做;JQ\原生 都是命令式编程,都是在做DOM 操作。
    
    //声明式编程:告诉“机器”你想要的是什么(what),让机器想出如何去做(how)。
    
  • 高效 −React通过对DOM的模拟,最大限度地减少与DOM的交互。

           //1.虚拟DOM(Virtual  DOM)
          用js 对象来模拟页面上的DOM 和DOM嵌套**实现DOM 元素的高效更新
          //2.Diff 算法
             tree diff
             component diff
             element diff
    

  • 灵活 −React可以与已知的库或框架很好地配合。

  • JSX − JSX 是 JavaScript 语法的扩展。React 开发不一定使用 JSX ,但我们建议使用它。

  • 组件 − 通过 React 构建组件,使得代码更加容易得到复用,能够很好的应用在大项目的开发中。模块化

    vue的组件是通过.vue 文件(template/script/style)实现的

    react中没有像vue中那样的模板文件,在react里一切都是以js来实现的

  • 单向响应的数据流 − React 实现了单向响应的数据流,从而减少了重复代码,这也是它为什么比传统数据绑定更简单。

劣势

  • 学习成本很高,学习曲线很陡
  • react本身能做的事并不多,想做大东西必须得用react插件(全家桶)

jsx

介绍

全称: JavaScript XML, React 使用 JSX 来替代常规的 JavaScript。

JSX 是一个看起来很像 XML 的 JavaScript 语法扩展。

我们不需要一定使用 JSX,但它有以下优点:

  • JSX 执行更快,因为它在编译为 JavaScript 代码后进行了优化。

  • 它是类型安全的,在编译过程中就能发现错误。

  • 使用 JSX 编写模板更加简单快速。

  • 作用: 用来创建react虚拟DOM(元素)对象

    • var ele = <h1>Hello JSX!</h1>;
    • 注意1: 它不是字符串, 也不是HTML/XML标签
    • 注意2: 它最终产生的就是一个JS对象
  • 标签名任意: HTML标签或其它标签

  • 标签属性任意: HTML标签属性或其它

  • 基本语法规则

    • 遇到 <开头的代码, 以标签的语法解析: html同名标签转换为html同名元素, 其它标签需要特别解析
    • 遇到以 { 开头的代码,以JS的语法解析: 标签中的js代码必须用{ }包含
  • babel.js的作用

    • 浏览器的js引擎是不能直接解析JSX语法代码的, 需要babel转译为纯JS的代码才能运行
    • 只要用了JSX,都要加上type=“text/babel”, 声明需要babel来处理

入口文件index.js的理解

//在index.js文件中App变量中保存的数据渲染到root关联的根元素中了
//但是js文件中应该写js语法,js语法变量中只能保存:6种基本数据 和2种引用数据
//let App=<div>666</div>   保存的是一个标签  这种语法就是JSX语法
//JSX语法就是一种插值语法  它经过打包工具编译后本质上还是JS语法 

//JSX:
// 1.遇到了<>就会解析为标签:
// 1.1如果是html语法标准的标签就会直接解析为同名元素  <nav/>   <div/>   <article/> 
// 1.2如果是其它标签需要特别解析--组件<App>
// 1.3 <App/> 这个代码的意义决定了能不能真正入门react框架: 可以理解为:<App titel="200"/> ===> new App(200)  (这里只是方便理解,不是等价的)

使用样式

React 推荐使用内联样式。React 会在指定元素数字后自动添加 px

render() {
    let mystyle={color:'red',fontSize:33}
    return (
      <div style={mystyle}>我的样式</div>
    )
  }

在这里插入图片描述

注释

注释需要写在花括号中,需要注意的是:

  • 在标签内部的注释需要花括号
  • 在标签外的的注释不能使用花括号

条件渲染

在 JSX 内部不能使用 if else 语句,但可以使用 三元运算 表达式来替代。

render() {
    let i=0  
    return (
      <div>
       {i==1? <h1>正确</h1>:<h1>错误</h1>}
      </div>
    )
  }

在这里插入图片描述
可以在JSX外部使用if语句

render() {
    let islogin=true
    let Box=''
    if(islogin){
        Box=<h1>已登录</h1>
    }else{
        Box=<h1>未登录</h1>
    }
    return (
      <div>
       {Box}
      </div>
    )
  }

在这里插入图片描述

数组

JSX 允许在模板中插入数组,数组会自动展开所有成员

render() {
    let arr=[1111,2222,3333,4444,5555]
    return (
      <div>
       {arr}
      </div>
    )
  }

在这里插入图片描述

列表渲染

通过数组的map函数
map函数的返回值是一个数组,数组里面的值为处理后的值

render() {
    let arr=[{title:'ljy',age:22},{title:'jack',age:30},{title:'marry',age:25}]
    return (
      <div>
       {arr.map(el=><p>{el.title}--{el.age}</p>)}
      </div>
    )
  }

在这里插入图片描述

组件

React.js 中一切皆组件,用 React.js 写的其实就是 React.js 组件。

我们在编写 React.js 组件的时候,一般都需要继承 React.js 的 Component(类定义)。一个组件类必须要实现一个 render 方法,这个 render 方法必须要返回一个 JSX 元素。但这里要注意的是,必须要用一个外层的 JSX 元素把所有内容包裹起来。返回并列多个 JSX 元素是不合法的。

定义单个组件

方式1:通过React自定义组件(DOM元素):类定义

import React, { Component } from 'react'

export default class Box4 extends Component {
  render() {
    return (
      <div>Box4</div>
    )
  }
}

方式2:通过React自定义组件(DOM元素):函数定义

import React from 'react'

export default function Box4(props) {
  return (
    <div>Box4</div>
  )
}

方式3:不建议使用,React.createClass创建组件的方式在react 16版本中去除。

var MyApp = React.createClass({
  render: function() {
    return <h1>Hello World!</h1>;
  }
});

使用组件

import React from 'react';
import ReactDOM from 'react-dom/client';
import Box4 from './Box4.jsx';  导入自定义组件
const root = ReactDOM.createRoot(document.getElementById('root'));   //关联页面根元素
root.render(<Box4 />);   //给根元素内部渲染组件模板

定义复合组件

我们可以通过创建多个组件来合成一个组件,即把组件的不同功能点进行分离

import Box5 from './Box5'
import React, { Component } from 'react'
export default class Box4 extends Component {
  render() {
    return (
        <div>
        <h1>Box4组件中</h1>
        <Box5></Box5>
        </div>
    )
  }
}

在这里插入图片描述

传值Props

定义和使用props 传值

通过React类定义组件时:

  • 在父组件render 方法中调用组件时使用key/value 的形式来指定属性。
  • 在自定义子组件中通过this.props.key 来获得组件属性的值,需要使用{}括起来。
//父组件Box4中
render() {
    let a='父组件Box4传递的数据'
    return (
        <div>
        <h1>Box4组件中</h1>
        <Box5 mypro={a}></Box5>   //定义属性名绑定数据传递给子组件
        </div>
    )
  }
  
 //子组件Box5中
 render() {
    return (
      <div>
          <h2>Box5中-{this.props.mypro}</h2>  //通过this.props.父组件传递的属性名 来接收
      </div>
    )
  }

在这里插入图片描述

通过React函数定义 组件时:

  • 在父组件render 方法中调用组件时使用key/value 的形式来指定属性。
  • 在自定义子组件中通过函数接收参数propsprops.key来获得组件属性的值,需要使用{}括起来
// 函数定义组件时,在组件内部使用属性值:props.属性名称
function Welcome(props) { // 函数需要传递一个参数props
    return(<h2>{props.title}</h2>)
}

ReactDOM.render(
	<Welcome title="张三" />, 
	document.getElementById('root')
);

定义默认props:

export default class Box5 extends Component {
  render() {
    return (
      <div>
          <h2>Box5中-{this.props.name}</h2>
      </div>
    )
  }
}
//由于是用ES6 class语法创建组件,其内部只允许定义方法,而不能定义属性,class的属性只能定义在class之外。所以defaultProps要写在组件外部。
Box5.defaultProps = {
	name: 'Box5'
};

多属性传值

(1)定义一个this.props对象,在对象中声明多个键值对,用于表示组件的属性
(2)在组件中使用**{…this.props}**的方式传递属性。“…”表示JSX的延展操作符,这种方式可以很方便的为组件指定多个属性,并且为属性的值指定数据类型。

//父组件ox4中
render() {
    let a="父组件传递的值"
    let b=1111111111
    let c=[111,222,333]
    let props={
        a,b,c
    }
    return (
        <div>
        <h1>Box4组件中</h1>
        <Box5 {...props}></Box5>
        </div>
    )
  }
  
  //子组件Box5中
  
  render() {
    return (
      <div>
          <h2>Box5中-{this.props.a}--{this.props.b}--{this.props.c}</h2>
      </div>
    )
  }

State

React 的核心思想是组件化的思想,应用由组件搭建而成,而组件中最重要的概念是State(状态),State是一个组件的UI数据模型,是组件渲染时的数据依据。

如何定义State

组件中用到的一个变量是不是应该作为组件State,可以通过下面的4条依据进行判断:

  1. 这个变量是否是通过Props从父组件中获取?如果是,那么它不是一个状态。
  2. 这个变量是否在组件的整个生命周期中都保持不变?如果是,那么它不是一个状态。
  3. 这个变量是否可以通过其他状态(State)或者属性(Props)计算得到?如果是,那么它不是一个状态。
  4. 这个变量是否在组件的render方法中使用?如果不是,那么它不是一个状态。这种情况下,这个变量更适合定义为组件的一个普通属性。

注意:并不是组件中用到的所有变量都是组件的状态!

import Box5 from './Box5'
import React, { Component } from 'react'
export default class Box4 extends Component {
  constructor(arg){
      super(arg)
      this.state={
          msg:0
      }
      //写法一
      this.change1=()=>{
        this.setState({msg:this.state.msg+1})  //使用setState函数更改状态
      }
      //写法二 
      this.change2=()=>{
          this.state.msg++
          this.setState(this.state)   //修改变量之后,调用setState刷新render  从而页面重新取到已经修改后的值
      }
  }
  render() {
    return (
        <div>
        <h1>Box4组件中</h1>
        <h2>{this.state.msg}</h2>
        <button onClick={this.change1}>change1</button><button onClick={this.change2}>change2</button>
        </div>
    )
  }
}

注意:React在ES6的实现中,规定state在constructor中实现

正确定义State的方式如下:

(1)在constructor中实现state

(2)在constructor中通过bind绑定事件函数(事件函数是用来改变状态)

(3)在事件函数内部使用setState函数更改状态

(4)在组件中的render函数中使用该状态

(5)在组件上需要设置监听事件,去触发事件函数的执行

setState设置状态

(1)语法:

setState(object nextState[, function callback])

(2)说明:

  • setState是React事件处理函数中和回调函数中触发UI更新的主要方法。
  • 不能在组件内部通过this.state修改状态,因为该状态会在调用setState()后被替换。
  • setState()不一定是同步的,为了性能提升,React会批量执行state和DOM渲染。
  • setState()总是会触发一次组件重绘,但可在shouldComponentUpdate()中实现一些条件渲染逻辑来解决。

同步设置多个状态时(不常用),可以在setState函数的第二个参数可以传递一个function回调函数,如下:

this.change1=()=>{
        this.setState({msg:this.state.msg+1},()=>{
             this.state.num+= this.state.msg
        })
      }

**状态上移:**当存在多个组件共同依赖一个状态,或是当子组件的props 数据需要被修改时,将这个状态放到对应组件的父组件中:

State 与 Props 区别

除了State, 组件的Props也是和组件的UI有关的。他们之间的主要区别是:

  • props 中的数据都是外界传递过来的
  • state 中的数据都是组件私有的;(通过Ajax 获取回来的数据,一般都是私有数据)
  • props 中的数据都是只读的,不能重新赋值
  • state 中的数据都是可读可写的
  • State定义在constructor内部,在super(props)代码后面;Props默认值定义在类(组件)的外部

当子组件的属性值是可变值时,采用状态上移:

状态上移通过属性将父组件的状态传递到子组件,那么父组件的状态发生变化时,子组件的属性也会改变

state/props 实现父子组件通信

  • 子组件获取父组件整个组件进行传参
    • 父组件在调用子组件时,传入一整个组件给子组件**<Children parent={ this } />**
    • 父组件中定义一个方法getChildrenMsg(resulet, msg),用来获取子组件传来的值以及执行其他操作
    • 子组件在通过this.props来获取到一整个组件this.props.parent 或者this.props[parent]
    • 子组件调用父组件步骤2里定义的方法进行传值this.props.parent.getChildrenMsg(this,val)

Parent:

import Box5 from './Box5'
import React, { Component } from 'react'
export default class Box4 extends Component {
  constructor(arg){
      super(arg)
      this.state={
          msg:0,
          num:0
      }
      //写法一
      this.change1=()=>{
        this.setState({msg:this.state.msg+1},()=>{
             this.state.num+= this.state.msg
        })
      }
      //写法二 
      this.change2=()=>{
          this.state.msg++
          this.setState(this.state)
      }
      this.change3=(arg,arg2)=>{
        this.state.msg=arg
        this.state.num=arg2
        this.setState(this.state)
      }
  }
  render() {
    let props={
        msg:this.state.msg,
        num:this.state.num,
        change3:this.change3
    }
    return (
        <div>
        <h1>Box4组件中</h1>
        <h2>msg--{this.state.msg}</h2>
        <h2>num--{this.state.num}</h2>
        <Box5 {...props}></Box5>
        <button onClick={this.change1}>change1</button><button onClick={this.change2}>change2</button>
        </div>
    )
  }
}
Children:
import React, { Component } from 'react'

export default class Box5 extends Component {
  render() {
    return (
      <div>
          <h2>Box5中---msg:{this.props.msg}----num:{this.props.num}</h2>
          <button onClick={()=>{this.props.change3('子组件修改了msg','子组件修改了num')}}>点击更改父组件传递的数据</button>
      </div>
    )
  }
}

在这里插入图片描述

Redux

我们为什么需要状态管理器Redux?为了方便的组织和管理组件之间的数据。

Redux是什么?
Redux是一个流行的JavaScript框架,为应用程序提供一个可预测的状态容器。Redux基于简化版本的Flux框架,Flux是Facebook开发的一个框架。在标准的MVC框架中,数据可以在UI组件和存储之间双向流动,而Redux严格限制了数据只能在一个方向上流动。

请添加图片描述
Store:存储数据的公告区域;eg–图书管理员;

React Component:页面组件;eg–借书的用户;

Reducer:数据存放位置;eg–图书记录本;

Action Creators:动作;eg–结束的时候,跟图书管理员说的话;

  • Redux基本原则

    • 单一数据源;
    • state是只读的;
    • 使用纯函数来执行修改;
  • Redux核心API

    • createStore(reducer):创建数据仓库: import {createStore} from “redux”;
    • store.getState():用于获取store里面的数据;
    • store.dispatch(action):用于派发action,触发reducer,执行修改动作;
    • store.subscribe(componentMethods):订阅store的修改,只要store发生改变,组件中的回调函数就会被执行;

store的创建和使用

使用Redux需要安装该模块

npm i redux –save

创建reducer(store/reducer.js),提供数据源,是一个纯函数

// src/store/reducer.js
const defaultState = {
    username: "ljy",
    stulist: [1, 2, 3]
}

//1、state用于提供为仓库提供数据源,需要给定一个初始值
//2、action用于对state 进行更新处理,表示要执行的动作,通过dispatch(action)触发;action被传递到reducer内部来处理,action中必须有一个type字段

const reducer = (state=defaultState) => {
//action……
return state;//必须返回处理后的state
}

export default reducer;


- 创建数据仓库(store/index.js):createStore()

  ```react
  // src/store/index.js
  import {createStore} from "redux";
  import reducer from "./reducer";

  const store = createStore(reducer);
  export default store;

组件内部使用数据:getState()

//组件内部引入仓库
import store from "./store/index";

//获取数据
<h2>{store.getState().username}</h2>

数据的修改和订阅

在组件内部定义要执行的动作action,并触发该动作dispatch。action中type字段是必须的,值通常大写:

addValue() {
	const action = {
            type: "ADD",
            data: "测试数据"
    }
    store.dispatch(action);
}

接收动作并修改数据:通过dispatch触发动作后,会将action传递到reducer函数的第二个参数中

const reducer = (state=defaultState, action) => {
    switch(action.type) {
        case "ADD": 
            state.stulist.push(action.data);
            break;
        default: 
            break;
    }
    return state;
}

组件中及时响应更新的数据:订阅

componentDidMount() {
    // 订阅store改变事件
	store.subscribe(this.changeValue.bind(this));
}
changeValue() {
    this.setState(store.getState())
}

subscribe函数可以监听Redux中state的变化,一旦Redux中的state发生了变化,render函数就会被调用,页面就会被重新渲染


版权声明:本文为qq_56973906原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。