react学习

React 入门(超详细) 本文会引导我们进入一段 React 学习之旅。我们将逐步了解有关它的背景和用例的一些细节,在自己的电脑上建起基本的 React 工具链,创建并使用一个简单的入门应用程序,以学习一些关于 React 在此过程中如何工作的知识。React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设Instagram 的网站。做出来以后,发现这套东西很好用,就在2013年5月开源了。 阅读详情

1. react基础

1.1 es6语法

ES 6是JavaScript语言的新一代标准。React的项目一般都是用ES 6语法来写的。

1.let const

let和const是ES 6中新增的两个关键字,用来声明变量。
const声明一个只读的常量,一旦声明,常量的值就不能改变。

2.箭头函数

ES 6允许使用“箭头”(=>)定义函数。
例如:

var f = (a, b) => a + b;
3.模板字符串

模板字符串是增强版的字符串,用反引号(`)标识字符串。
它可以用来定义多行字符串,以及在字符串中嵌入变量,功能很强大。

//多行字符串
'JS is wonderful !
React is wonderful! '
//字符串中嵌入变量
var name = "React";
'Hello, ${name} ! ';
4.解构赋值

ES 6允许按照一定模式从数组和对象中提取值,对变量进行赋值,
这被称为解构。

//数组解构
let [a,b,c] = [1, 2, 3];

//对象解构
let name = 'Lily';
let age = 4;
let person = {name, age};
person // Object {name: "Lily", age: 4}

//对象解构的另一种形式
let person = {name: 'Lily', age: 4};
let {name, age} = person;
name // "Lily"
age //4

//嵌套结构的对象解构
let {person: {name, age}, foo} = {person: {name: 'Lily',
age: 4}, foo: 'foo'};
name //"Lily"
age //4
foo //"foo"
5. rest参数

ES 6引入rest参数(形式为…变量名)用于获取函数的多余参数,以代替arguments对象的使用。rest参数是一个数组,数组中的元素是多余的参数。

function languages(lang, ...types){
console.log(types);
}
languages('JavaScript', 'Java', 'Python'); //["Java",
"Python"]
6. 扩展运算符

扩展运算符是三个点(…),它将一个数组转为用逗号分隔的参数序列,类似于rest参数的逆运算。

function sum(a, b, c){
return a + b + c;
}
let numbers = [1, 2, 3];
sum(...numbers); //6

扩展运算符还常用于合并数组以及与解构赋值结合使用。

//合并数组
let arr1 = ['a'];
let arr2 = ['b', 'c'];
let arr3 = ['d', 'e'];
[...arr1, ...arr2, ...arr3]; //['a', 'b', 'c', 'd', 'e'];

//与解构赋值结合
let [a, ...rest] = ['a', 'b', 'c'];
rest //['b', 'c']

扩展运算符还可以用于取出参数对象的所有可遍历属性,复制到当
前对象之中。

let bar = {a: 1, b: 2};
let foo = {...bar};
foo //Object {a: 1, b: 2};
foo === bar //false
7. class

ES 6引入了class(类)这个概念,新的class写法让对象原型的写法
更加清晰,也更像传统的面向对象编程语言的写法。

//定义一个类
class Person {
	constructor(name, age) {
		this.name = name;
		this.age = age;
	}
	getName(){
		return this.name;
	}
	getAge(){
		return this.age;
	}
}

//根据类创建对象
let person = new('Lily', 4);

class之间可以通过extends关键字实现继承

class Man extends Person {
	constructor(name, age) {
		super(name, age);
}
	getGender() {
		return 'male';
	}
}
let man = new Man('Jack', 20);
8. import、export

ES 6实现了自己的模块化标准。
export用于规定模块对外暴露的接口,import用于引入其他模块提供的接口。

//a.js,导出默认接口和普通接口
const foo = () => 'foo';
const bar = () => 'bar';
export default foo; //导出默认接口
export {bar}; //导出普通接口

//b.js(与a.js在同一目录下),导入a.js中的接口
//注意默认接口和普通接口导入写法的区别
import foo, {bar} from './a';
foo(); //"foo"
bar(); //"bar"

1.2 JSX

JSX 是一种用于描述UI的JavaScript扩展语法,React使用这种语法
描述组件的UI。
React 认为,一个组件应该是具备UI描述和UI数据的完整体,不应该将它们分开处理,于是发明了JSX,作为UI描述和UI数据之间的桥梁。

1.2.1 JSX语法
1 基本语法

JSX的基本语法和XML语法相同,都是使用成对的标签构成一个树
状结构的数据

const element = (
<div>
<h1>Hello, world!</h1>
</div>
)

const element = <h1>Hello, world!</h1>;
2 标签类型

在JSX语法中,使用的标签类型有两种:DOM类型的标签(div、
span等)和React组件类型的标签。

当使用DOM类型的标签时,标签的首字母必须小写;当使用React组件类型的标签时,组件名称的首字母必须大写。React 正是通过首字母的大小写判断渲染的是一个DOM类型的标签还是一个React组件类型的标
签。

// DOM类型标签
const element = <h1>Hello, world!</h1>;
// React组件类型标签
const element = <HelloWorld />;
// 二者可以互相嵌套使用
const element = (
	<div>
		<HelloWorld />
	</div>;
)
3 JavaScript表达式

在JSX中使用JavaScript表达式需要将表达式用大括号“{}”包起来。
表达式在JSX中的使用场景主要有两个:通过表达式给标签属性赋值和通过表达式定义子组件。

// 通过表达式给标签属性赋值
const element = <MyComponent foo={ 1 + 2 } />
// 通过表达式定义子组件(map虽然是函数,但它的返回值是JavaScript表达
式)
const todos = ['item1', 'item2', 'item3'];
const element = (
<ul>
{todos.map(message => <Item key={message} message=
{message} />)}
</ul>
);

注意,JSX中只能使用JavaScript表达式,而不能使用多行JavaScript语句。

// 错误
const element = <MyComponent foo={const val = 1 + 2; return val; } />

// 错误
let complete;
const element = (
	<div>
		{
			if(complete){
				return <CompletedList />;
			}else{
				return null;
			}
		}
	</div>
)

不过,JSX中可以使用三目运算符或逻辑与(&&)运算符代替if语
句的作用。

// 正确
let complete;
const element = (
<div>
{
complete ? <CompletedList /> : null
}
</div>
)

//正确
let complete;
const element = (
<div>
{
complete && <CompletedList />
}
</div>
)
4 标签属性

当JSX 标签是DOM类型的标签时,对应DOM标签支持的属性JSX也支持,例如id、class、style、onclick等。但是,部分属性的名称会有所改变,主要的变化有:class 要写成className,事件属性名采用驼峰格式,例如onclick 要写成 onClick。原因是,class是JavaScript的关键字,所以改成className;React对DOM标签支持的事件重新做了封装,
封装时采用了更常用的驼峰命名法命名事件。

<div id='content' className='foo' onClick={() =>
{console.log('Hello, React')}} />
5 注释

JSX中的注释需要用大括号“{}”将/**/包裹起来。

const element = (
<div>
{/* 这里是一个注释 */}
<span>React</span>
</div>
)
6 注释

JSX语法对使用React来说并不是必需的,实际上,JSX语法只是
React.createElement (component, props, …children)的语法糖,所有的JSX
语法最终都会被转换成对这个方法的调用。

//JSX语法
const element = <div className='foo'>Hello, React</div>

//转换后
const element = React.createElement('div', {className:
'foo'}, 'Hello, React')

1.3 组件

1 组件定义

定义一个组件有两种方式,使用ES 6 class(类组件)和使用函数(函数组件)。

使用class定义组件需要满足两个条件:
(1)class继承自React.Component。
(2)class内部必须定义render方法,render方法返回代表该组件UI
的React元素。

假设定义了一个PostList组件。如何使组件显示到页面上呢?
需要使用ReactDOM.render() 完成这一个工作

// index.js
import React from "react";
import ReactDOM from "react-dom";
import PostList from "./PostList";

ReactDOM.render(<PostList />,
document.getElementById("root"));

注意,使用ReactDOM.render() 需要先导入react-dom库,这个库会完成组件所代表的虚拟DOM节点到浏览器的DOM节点的转换。

2 组件的props

组件的props用于把父组件中的数据或方法传递给子组件,供子组件使用。
props是一个简单结构的对象,它包含的属性正是由组件作为JSX标签使用时的属性组成。

<User name='React' age='4' address='America' >

此时User组件的props结构如下:
props = {
	name: 'React',
	age: '4',
	address: 'America'
}
3 组件的state

组件的state是组件内部的状态,state的变化最终将反映到组件UI的变化上。
我们在组件的构造方法constructor中通过this.state定义组件的初始状态,并通过调用this.setState方法改变组件状态(也是改变组件状态的唯一方式),进而组件UI也会随之重新渲染。

// 点赞的例子
import React, { Component } from "react";

class PostItem extends Component {
  constructor(props) {
    super(props);
    this.state = {
      vote: 0
    };
  }

  // 处理点赞逻辑
  handleClick() {
    let vote = this.state.vote;
    vote++;
    this.setState({
      vote: vote
    });
  }

  render() {
    const { title, author, date } = this.props;
    return (
      <li>
        <div>
          {title}
        </div>
        <div>
          创建人:<span>{author}</span>
        </div>
        <div>
          创建时间:<span>{date}</span>
        </div>
        <div>
          <button
            onClick={() => {
              this.handleClick();
            }}
          >
            点赞
          </button>
          &nbsp;
          <span>
            {this.state.vote}
          </span>
        </div>
      </li>
    );
  }
}

export default PostItem;

注意
(1)在组件的构造方法constructor内,首先要调用super(props),这一步实际上是调用了React.Component这个class的constructor方法,用来完成React组件的初始化工作。
(2)在constructor中,通过this.state定义了组件的状态。
(3)在render方法中,我们为标签定义了处理点击事件的响应函数,在响应函数内部会调用this.setState更新组件的点赞数。

组件的props和state都会直接影响组件的UI。事实上,React组件可以看作一个函数,函数的输入是props和state,函数的输出是组件的UI。

UI = Component(props,state)

React组件正是由props和state两种类型的数据驱动渲染出组件UI。props是组件对外的接口,组件通过props接收外部传入的数据(包括方法);state是组件对内的接口,组件内部状态的变化通过state来反映。另外,props是只读的,你不能在组件内部修改props;state是可变的,组件状态的变化通过修改state来实现。

4 有状态组件和无状态组件

如果一个组件的内部状态是不变的,当然就用不到state,这样的组件称之为无状态组件。
一个组件的内部状态会发生变化,就需要使用state来保存变化,这样的组件称之为有状态组件。

定义无状态组件除了使用ES 6 class的方式外,还可以使用函数定义,也就是我们在本节开始时所说的函数组件。
一个函数组件接收props作为参数,返回代表这个组件UI的React元素结构。

function Welcome(props) {
	return <h1>Hello, {props.name}</h1>;
}

React应用组件设计的一般思路是,通过定义少数的有状态组件管理整个应用的状态变化,并且将状态通过props传递给其余的无状态组件,由无状态组件完成页面绝大部分UI的渲染工作。总之,有状态组件主要关注处理状态变化的业务逻辑,无状态组件主要关注组件UI的渲染。

5 属性校验和默认属性

React提供了PropTypes这个对象,用于校验组件属性的类型。

class PostItem extends React.Component {
//......
}
PostItem.propTypes = {
	post: PropTypes.object,
	onVote: PropTypes.func
};

当使用PropTypes.object或PropTypes.array校验属性类型时,我们只知道这个属性是一个对象或一个数组,至于对象的结构或数组元素的类型是什么样的,依然无从得知。这种情况下,更好的做法是使用PropTypes.shape或PropTypes.arrayOf。

style: PropTypes.shape({
	color: PropTypes.string,
	fontSize: PropTypes.number
}),
sequence: PropTypes.arrayOf(PropTypes.number)

表示style是一个对象,对象有color和fontSize两个属性,color是字符串类型,fontSize是数字类型;sequence是一个数组,数组的元素是数字。
如果属性是组件的必需属性,就需要在PropTypes的类型属性上调用isRequired。

PostItem.propTypes = {
	post: PropTypes.shape({
		id: PropTypes.number,
		title: PropTypes.string,
		author: PropTypes.string,
		date: PropTypes.string,
		vote: PropTypes.number
	}).isRequired,
	onVote: PropTypes.func.isRequired
}

提供了defaultProps,为组件属性指定默认值。

function Welcome(props) {
	return <h1 className='foo'>Hello, {props.name}</h1>;
}
Welcome.defaultProps = {
	name: 'Stranger'
};
6 组件样式

为组件添加样式的方法主要有两种:外部CSS样式表和内联样式。

1.外部CSS样式表

这种方式和我们平时开发Web应用时使用外部CSS文件相同,CSS样式表中根据HTML标签类型、ID、class等选择器定义元素的样式。唯一的区别是,React元素要使用className来代替class作为选择器。

// style.css
.foo {
	width:100%;
	height:50px;
	background-color:blue;
	font-size: 20px;
}

function Welcome(props) {
return <h1 className='foo'>Hello, {props.name}</h1>;
}

样式表的引入方式有两种,一种是在使用组件的HTML页面中通过
标签引入:

<link rel="stylesheet" type="text/css" href="style.css">

另一种是把样式表文件当作一个模块,在使用该样式表的组件中,
像导入其他组件一样导入样式表文件:

import './style.css'; //要保证相对路径设置正确

function Welcome(props) {
	return <h1 className='foo'>Hello, {props.name}</h1>;
}
2.内联样式

将CSS样式写到JS文件中,用JS对象表示CSS样式,然后通过DOM类型节点的style属性引用相应样式对象。

function Welcome(props) {
	return (
		<h1
			style={{
				width: "100%",
				height: "50px",
				backgroundColor: "blue",
				fontSize: "20px"
				}}
			>
			Hello, {props.name}
		</h1>
	);
}

style使用了两个大括号,第一个大括号表示style的值是一个JavaScript表达式,第二个大括号表示这个JavaScript表达式是一个对象。
当使用内联样式时,样式的属性名必须使用驼峰格式的命名。所以,在Welcome组件中,background-color写成backgroundColor,font-size写成fontSize

1.4 组件的生命周期

组件从被创建到被销毁的过程称为组件的生命周期。通常,组件的生命周期可以被分为三个阶段:挂载阶段、更新阶段、卸载阶段。

1 挂载阶段

这个阶段组件被创建,执行初始化,并被挂载到DOM中,完成组件的第一次渲染。
依次调用的生命周期方法有:
(1)constructor
(2)componentWillMount
(3)render
(4)componentDidMount

1.constructor

这是ES 6 class的构造方法,组件被创建时,会首先调用组件的构造方法。这个构造方法接收一个props参数,props是从父组件中传入的属性对象,如果父组件中没有传入属性而组件自身定义了默认属性,那么
这个props指向的就是组件的默认属性。你必须在这个方法中首先调用
super(props)才能保证props被传入组件中。constructor通常用于初始化组
件的state以及绑定事件处理方法等工作。

2.componentWillMount

这个方法在组件被挂载到DOM前调用,且只会被调用一次。这个方法在实际项目中很少会用到,因为可以在该方法中执行的工作都可以提前到constructor中。在这个方法中调用this.setState不会引起组件的重新渲染。

3.render

这是定义组件时唯一必要的方法(组件的其他生命周期方法都可以省略)。在这个方法中,根据组件的props和state返回一个React元素,用于描述组件的UI。
render是一个纯函数,在这个方法中不能执行任何有副作用的操作,所以不能在render中调用this.setState,这会改变组件的状态。

4.componentDidMount

在组件被挂载到DOM后调用,且只会被调用一次。这时候已经可以获取到DOM结构,因此依赖DOM节点的操作可以放到这个方法中。这个方法通常还会用于向服务器端请求数据。在这个方法中调用this.setState会引起组件的重新渲染。

2 更新阶段

组件被挂载到DOM后,组件的props或state可以引起组件更新。props引起的组件更新,本质上是由渲染该组件的父组件引起的,也就是当父组件的render方法被调用时,组件会发生更新过程,这个时候,组件props的值可能发生改变,也可能没有改变,因为父组件可以使用相同的对象或值为组件的props赋值。但是,无论props是否改变,父组件render方法每一次调用,都会导致组件更新。
State引起的组件更新,是通过调用this.setState修改组件state来触发的。
组件更新阶段,依次调用的生命周期方法有:
(1)componentWillReceiveProps
(2)shouldComponentUpdate
(3)componentWillUpdate
(4)render
(5)componentDidUpdate

1.componentWillReceiveProps

这个方法只在props引起的组件更新过程中,才会被调用,此方法在子组件中定义。State引起的组件更新并不会触发该方法的执行。方法的参数nextProps是父组件传递给当前组件的新的props。

2.shouldComponentUpdate

这个方法决定组件是否继续执行更新过程,默认返回true。
一般通过比较nextProps、nextState和组件当前的props、state决定这个方法的返回结果。这个方法可以用来减少组件不必要的渲染,从而优化组件的性能。

3.componentWillUpdate

这个方法在组件render调用前执行,可以作为组件更新发生前执行某些工作的地方,一般也很少用到。

shouldComponentUpdate和componentWillUpdate中都不能调用setState,否则会引起循环调用问题,render永远无法被调用,组件也无法正常渲染。

4.render
5.componentDidUpdate

组件更新后被调用,可以作为操作更新后的DOM的地方。这个方
法的两个参数prevProps、prevState代表组件更新前的props和state。

3 卸载阶段

组件从DOM中被卸载的过程,只有一个生命周期方法:
componentWillUnmount
这个方法在组件被卸载前调用,可以在这里执行一些清理工作,比如清除组件中使用的定时器,清除componentDidMount中手动创建的DOM元素等,以避免引起内存泄漏。

只有类组件才具有生命周期方法,函数组件是没有生命周期方法的,因此永远不要在函数组件中使用生命周期方法。

1.5 事件处理

在React元素中绑定事件有两点需要注意:
(1)在React中,事件的命名采用驼峰命名方式,而不是DOM元素中的小写字母命名方式。例如,onclick要写成onClick,onchange要写成onChange等。
(2)处理事件的响应函数要以对象的形式赋值给事件属性,而不
是DOM中的字符串形式。

在DOM中绑定一个点击事件这样写:

<button onclick="clickButton()">
	Click
</button>

而在React元素中绑定一个点击事件变成这种形式:

<button onclick={clickButton}> //clickButton是一个函数
	Click
</button>

ES 6中class并不会为方法自动绑定this到当前对象。

class MyComponent extends React.Component {
  handleClick() {
    console.log('Button clicked!');
    console.log('this:', this); // 这里的this默认可能是undefined,需要通过一些方式绑定
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        Click Me
      </button>
    );
  }
}

React事件处理函数的写法主要有三种方式:

1 使用箭头函数
class MyComponent extends React.Component {
    constructor(props) {
        super(props);
        this.state = {number: 0};
    }

    handleClick(event) {
        const number = ++this.state.number;// 这个方法体的this
        this.setState({
            number: number
        });
    }
    render() {
        return (
            <div>
                <div>{this.state.number}</div>
                <button onClick={(event)=>
                {this.handleClick(event);}}>
                    Click
                </button>
            </div>
        );
    }
}

因为箭头函数中的this指向的是函数定义时的对象,所以可以保证this总是指向当前组件的实例对象。

2 使用组件方法

直接将组件的方法赋值给元素的事件属性,同时在类的构造函数中,将这个方法的this绑定到当前对象。

class MyComponent extends React.Component {
    constructor(props) {
        super(props);
        this.state = {number: 0};
        // 将this绑定到this.handleClick方法
        this.handleClick = this.handleClick.bind(this);
    }

    handleClick(event) {
        const number = ++this.state.number;
        this.setState({
            number: number
        });
    }
    render() {
        return (
            <div>
                <div>{this.state.number}</div>
                <button onClick={this.handleClick}>
                    Click
                </button>
            </div>
        );
    }
}

1.6 表单

1 受控组件

表单元素的值是由React来管理的,那么它就是一个受控组件。
对于不同的表单元素,React的控制方式略有不同。

文本框

文本框包含类型为text的input元素和textarea元素。它们受控的主要原理是,通过表单元素的value属性设置表单元素的值,通过表单元素的onChange事件监听值的变化,并将变化同步到React组件的state中。

class LoginForm extends React.Component {
    constructor(props) {
        super(props);
        this.state = {name: '', password: ''};
        this.handleChange = this.handleChange.bind(this);
        this.handleSubmit = this.handleSubmit.bind(this);
    }
// 监听用户名和密码两个input值的变化
    handleChange(event) {
        const target = event.target;
        this.setState({[target.name]: target.value});
    }
// 表单提交的响应函数
    handleSubmit(event) {
        console.log('login successfully');
        event.preventDefault();
    }
    render() {
        return (
            <form onSubmit={this.handleSubmit}>
                <label>
                    用户名:
                    {/* 通过value 设置input显示内容,通过onChange 监听
value的变化 */}
                    <input type="text" name="name" value=
                        {this.state.name} onChange={this.handleChange} />
                </label>
                <label>
                    密码:
                    <input type="password" name="password" value=
                        {this.state.password} onChange={this.handleChange} />
                </label>
                <input type="submit" value="登录" />
            </form>
        );
    }
}
列表

在React中,对select的处理方式有所不同,它通过在select上定义value属性来决定哪一个option元素处于选中状态。这样,对select的控制只需要在select这一个元素上修改即可,而不需要关注option元素。

class ReactStackForm extends React.Component {
    constructor(props) {
        super(props);
        this.state = {value: 'mobx'};
        this.handleChange = this.handleChange.bind(this);
        this.handleSubmit = this.handleSubmit.bind(this);
    }
// 监听下拉列表的变化
    handleChange(event) {
        this.setState({value: event.target.value});
    }
// 表单提交的响应函数
    handleSubmit(event) {
        alert('You picked ' + this.state.value);
        event.preventDefault();
    }
    render() {
        return (
            <form onSubmit={this.handleSubmit}>
                <label>
                    Pick one library:
                    {/* select的value属性定义当前哪个option元素处于选中状态
*/}
                    <select value={this.state.value} onChange=
                        {this.handleChange}>
                        <option value="react">React</option>
                        <option value="redux">Redux</option>
                        <option value="mobx">MobX</option>
                    </select>
                </label>
                <input type="submit" value="Submit" />
            </form>
        );
    }
}
复选框和单选框

复选框是类型为checkbox的input元素,单选框是类型为radio的input元素,它们的受控方式不同于类型为text的input元素。通常,复选框和单选框的值是不变的,需要改变的是它们的checked状态,因此React控制的属性不再是value属性,而是checked属性。

class ReactStackForm extends React.Component {
    constructor(props) {
        super(props);
        this.state = { react: false, redux: false, mobx: false
        };
        this.handleChange = this.handleChange.bind(this);
        this.handleSubmit = this.handleSubmit.bind(this);
    }
// 监听复选框变化,设置复选框的checked状态
    handleChange(event) {
        this.setState({ [event.target.name]:
            event.target.checked });
    }
// 表单提交的响应函数
    handleSubmit(event) {
        event.preventDefault();
    }
    render() {
        return (
            <form onSubmit={this.handleSubmit}>
                {/* 设置3个复选框 */}
                <label>React
                    <input
                        type="checkbox"
                        name="react"
                        value="react"
                        checked={this.state.react}
                        onChange={this.handleChange}
                    />
                </label>
                <label>Redux
                    <input
                        type="checkbox"
                        name="redux"
                        value="redux"
                        checked={this.state.redux}
                        onChange={this.handleChange}
                    />
                </label>
                <label>MobX
                    <input
                        type="checkbox"
                        name="mobx"
                        value="mobx"
                        checked={this.state.mobx}
                        onChange={this.handleChange}
                    />
                </label>
                <input type="submit" value="Submit" />
            </form>
        );
    }
}
2 非受控组件

非受控组件不再通过React的state来管理组件的值,一般通过ref对象来直接操作DOM的状态。

import React, { useRef } from 'react';

function NameForm() {
  // 使用 useRef 创建一个 ref 对象
  const inputRef = useRef(null);

  // 处理表单提交
  const handleSubmit = (event) => {
    event.preventDefault();
    // 使用 ref 访问 input 元素的值
    alert('A name was submitted: ' + inputRef.current.value);
  }

  return (
    <form onSubmit={handleSubmit}>
      {/* 使用 ref 属性将 ref 对象附加到 input 元素 */}
      <label>
        名字:
        <input type="text" ref={inputRef} />
      </label>
      <input type="submit" value="提交" />
    </form>
  );
}

export default NameForm;

1.7 React 16新特性

1 render新的返回类型

render方法支持两种新的返回类型:数组(由React元素组成)和字符串。

class ListComponent extends Component {
  render() {
    return [
      <li key="A">First item</li>,
      <li key="B">Second item</li>,
      <li key="C">Third item</li>
    ];
  }
}
class StringComponent extends Component {
  render() {
    return "Just a strings";
  }
}

渲染

export default class App extends Component {
  render() {
    return [
      <ul>
        <ListComponent />
      </ul>,
      <StringComponent />
    ];
  }
}
2 Portals

Portals特性让我们可以把组件渲染到当前组件树以外的DOM节点上,这个特性典型的应用场景是渲染应用的全局弹框。

Portals的实现依赖ReactDOM的一个新的API:

ReactDOM.createPortal(child, container)

第一个参数child是可以被渲染的React节点,例如React元素、由React元素组成的数组、字符串等,container是一个DOM元素,child将被挂载到这个DOM节点。

3 错误处理

React 16之前,组件在运行期间如果执行出错,就会阻塞整个应用的渲染,这时候只能刷新页面才能恢复应用。React 16引入了新的错误处理机制,默认情况下,当组件中抛出错误时,这个组件会从组件树中卸载,从而避免整个应用的崩溃。

React 16还提供了一种更加友好的错误处理方式——错误边界(Error Boundaries)。错误边界是能够捕获子组件的错误并对其做优雅处理的组件。

定义了componentDidCatch(error, info)这个方法的组件将成为一个错误边界.

2. react进阶

2.1 组件state

1 state和props

state和props又有什么区别呢?state和props都直接和组件的UI渲染有关,它们的变化都会触发组件重新渲染,但props对于使用它的组件来说是只读的,是通过父组件传递过来的,要想修改props,只能在父组件中修改;而state是组件内部自己维护的状态,是可变的。

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

2 正确修改state
state 的更新是异步的

调用setState时,组件的state并不会立即改变,setState只是把要修改的状态放入一个队列中,React会优化真正的执行时机,并且出于性能原因,可能会将多次setState的状态修改合并成一次状态修改。所以不要依赖当前的state,计算下一个state。
另外,需要注意的是,同样不能依赖当前的props计算下一个状态,因为props的更新也是异步的。

举个例子,在购物车点击购买按钮,购买会增加1.
如果连续点击两次按钮,就会连续调用两次this.setState({quantity: this.state.quantity + 1}),在React合并多次修改为一次的情况下,相当于等价执行了如下代码:

Object.assign(
previousState,
{quantity: this.state.quantity + 1},
{quantity: this.state.quantity + 1}
)

于是,后面的操作覆盖前面的操作,最终购买的数量只增加1。

// 正确
this.setState((preState, props) => ({
counter: preState.quantity + 1;
}))

这个函数有两个参数,第一个是当前最新状态(本次组件状态
修改生效后的状态)的前一个状态preState(本次组件状态修改前的状
态),第二个参数是当前最新的属性props。

state与不可变对象

React官方建议把state当作不可变对象。
当state中的某个状态发生变化时,应该重新创建这个状态对象,而不是直接修改原来的状态。因为在一些场景下,组件会比较state是否有变化,无变化不会重新渲染组件。比较对象时比较的是对象地址,可能会造成对象属性变化了,但是react判定没有变化。

react推荐使用对象扩展语法,生成新对象。

// state是对象
this.setState(preState => ({
	owner: {...preState.owner, name: 'Jason'};
}))
// state 是数组
this.setState(preState => ({
	books: [...preState.books, 'React Guide'];
}))

2.2 组件与服务器通信

1 组件挂载阶段通信

React官方推荐,在componentDidMount中与服务器进行通信。
这时候组件已经挂载,真实DOM也已经渲染完成,是调用服务器API最安全的地方。

class UserListContainer extends React.Component{
    componentDidMount() {
        var that = this;
        fetch('/path/to/user-api').then(function(response) {
            response.json().then(function(data) {
                that.setState({users: data})
            });
        });
    }
}
2 组件更新阶段通信

组件在更新阶段常常需要再次与服务器通信,获取服务器上的最新数据。
componentWillReceiveProps非常适合做这个工作。

class UserListContainer extends React.Component{
    componentWillReceiveProps(nextProps) {
        if(nextProps.category !== this.props.category) {
            fetch('/path/to/user-api?category='+
                nextProps.category). then(function(response) {
                response.json().then(function(data) {
                    that.setState({users: data})
                });
            });
        }
    }
}
3 组件通信
父子组件通信

(1)父组件向子组件通信
是通过父组件向子组件的props传递数据完成的。

// 子组件
class UserList extends React.Component{
    render() {
        return (
            <div>
                <ul className="user-list">
                    {this.props.users.map(function(user) {
                        return (
                            <li key={user.id}>
                                <span>{user.name}</span>
                            </li>
                        );
                    })}
                </ul>
            </div>
        )
    }
}
// 父组件
import UserList from './UserList'
class UserListContainer extends React.Component{
    constructor(props){
        super(props);
        this.state = {
            users: []
        }
    }
    componentDidMount() {
        var that = this;
        fetch('/path/to/user-api').then(function(response) {
            response.json().then(function(data) {
				// 父组件调用setState,触发父组件的重新渲染,进而触发子组件的重新渲染
                that.setState({users: data})
            });
        });
    }
    render() {
        return (
            {/* 通过props传递users */}
        <UserList users={this.state.users} />
    )
    }
}

1. 组件通过 state 改变状态后,组件一定会重新渲染吗?
答案:是的,组件一定会重新渲染。

  • 在 React 中,当组件的状态(state)通过setState发生改变时,React 会触发父组件的重新渲染。
  • 这是因为 React 的核心机制是基于状态驱动的 UI 更新:当状态发生变化时,React 会重新调用父组件的 render 方法(类组件)或重新执行函数组件的主体代码,从而生成新的虚拟 DOM 并与之前的虚拟 DOM 进行比较。
// •每次点击按钮时,setCount 会更新父组件的状态,导致父组件重新渲染。
// •即使 Child 组件没有依赖于父组件的状态,它也会被重新渲染
function Parent() {
  const [count, setCount] = useState(0);

  console.log('Parent rendered');

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <Child />
    </div>
  );
}

function Child() {
  console.log('Child rendered');
  return <div>Child Component</div>;
}

export default Parent;

2. 父组件重新渲染,一定会触发子组件重新渲染吗?
答案:父组件重新渲染通常会导致子组件重新渲染,但可以通过一些优化手段避免不必要的子组件重新渲染。

  • 如果子组件是一个普通的函数组件或类组件,那么即使它的 props 和 state 没有变化,父组件的重新渲染仍然会触发子组件的重新渲染。
  • 这是因为 React 默认会对整个组件树进行递归渲染。

但是可以通过一下手段避免不必要的重新渲染。

  1. 类组件中的 shouldComponentUpdate
    • 如果子组件是一个类组件,可以通过实现 shouldComponentUpdate 方法来控制是否需要重新渲染。
    • shouldComponentUpdate 接收新的 props 和 state,返回 true 表示需要重新渲染,返回 false 表示不需要重新渲染。
示例:class Child extends React.Component {
  shouldComponentUpdate(nextProps, nextState) {
    console.log('shouldComponentUpdate called');
    return false; // 不重新渲染
  }

  render() {
    console.log('Child rendered');
    return <div>Child Component</div>;
  }
}
  1. 使用 PureComponent 或 React.memo
    • 如果子组件是一个类组件,可以继承 React.PureComponent,它会自动对 props 和 state 进行浅比较,避免不必要的重新渲染。
    • 对于函数组件,可以使用 React.memo,效果类似。

(2)子组件向父组件通信
依然是props。
父组件可以通过子组件的props传递给子组件一个回调函数,子组件在需要改变父组件数据时,调用这个回调函数即可。

// 子组件
class UserList extends React.Component{
    constructor(props){
        super(props);
        this.state = {
            newUser : ''
        };
        this.handleChange = this.handleChange.bind(this);
        this.handleClick = this.handleClick.bind(this);
    }
    handleChange(e) {
        this.setState({newUser: e.target.value});
    }
// 通过props调用父组件的方法新增用户
    handleClick() {
        if(this.state.newUser && this.state.newUser.length > 0)
        {
            this.props.onAddUser(this.state.newUser);
        }
    }
    render() {
        return (
            <div>
                <ul className="user-list">
                    {this.props.users.map(function(user) {
                        return (
                            <li key={user.id}>
                                <span>{user.name}</span>
                            </li>
                        );
                    })}
                </ul>
                <input onChange={this.handleChange} value=
                    {this.state.newUser} />
                <button onClick={this.handleClick}>新增</button>
            </div>
        )
    }
}
// 父组件
import UserList from './UserList'
class UserListContainer extends React.Component{
    constructor(props){
        super(props);
        this.state = {
            users: []
        }
        this.handleAddUser = this.handleAddUser.bind(this);
    }
    componentDidMount() {
        var that = this;
        fetch('/path/to/user-api').then(function(response) {
            response.json().then(function(data) {
                that.setState({users: data})
            });
        });
    }
// 新增用户
    handleAddUser(user) {
        var that = this;
        fetch('/path/to/save-user-api',{
            method: 'POST',
            body: JSON.stringify({'username':user})
        }).then(function(response) {
            response.json().then(function(newUser) {
// 将服务器端返回的新用户添加到state中
                that.setState((preState) => ({users:
                        preState.users.concat ([newUser])}))
            });
        });
    }
    render() {
        return (
            {/* 通过props传递users和 handleAddUser方法 */}
        <UserList users={this.state.users}
                  onAddUser={this.handleAddUser}
        />
    )
    }
}

子组件UserList通过调用props.onAddUser方法成功地将待新增的用户传递给父组件UserListContainer的handleAddUser方法执行保存操作,保存成功后,UserListContainer会更新状态users,从而又将最新的用户列表传递给UserList。这一过程既包含子组件到父组件的通信,又包含父组件到子组件的通信,而通信的桥梁就是通过props传递的数据和回调方法。

兄弟组件通信

当两个组件不是父子关系但有相同的父组件时,称为兄弟组件。兄弟组件在整个组件树上并不一定处于同一层级。

兄弟组件不能直接相互传送数据,需要通过state状态提升的方式实现兄弟组件的通信,即把组件之间需要共享的状态保存到距离它们最近的共同父组件内,任意一个兄弟组件都可以通过父组件传递的回调函数来修改共享状态,父组件中共享状态的变化也会通过props向下传递给所有兄弟组件,从而完成兄弟组件之间的通信。

Context

React提供了一个context上下文,让任意层级的子组件都可以获取父组件中的状态和方法。

创建context的方式是:在提供context的组件内新增一个getChildContext方
法,返回context对象,然后在组件的childContextTypes属性上定义context对象的属性的类型信息。

class UserListContainer extends React.Component{
    /** 省略其余代码 **/
// 创建context对象,包含onAddUser方法
    getChildContext() {
        return {onAddUser: this.handleAddUser};
    }
// 新增用户
    handleAddUser(user) {
        this.setState((preState) => ({users:
                preState.users.concat([{'id':'c', 'name': 'cc' }])}))
    }
    render() {
        const filterUsers = this.state.users.filter((user) =>
        {user.id = this.state.currentUserId});
        const currentUser = filterUsers.length > 0 ?
            filterUsers[0] : null;
        return (
            <UserList users={this.state.users}
                      currentUserId = {this.state.currentUserId}
                      onSetCurrentUser = {this.handleSetCurrentUser}
            />
        <UserDetail currentUser = {currentUser} />
    )
    }
}
// 声明context的属性的类型信息
UserListContainer.childContextTypes = {
    onAddUser: PropTypes.func
};
class UserAdd extends React.Component{
    /**省略其余代码**/
    handleChange(e) {
        this.setState({newUser: e.target.value});
    }
    handleClick() {
        if(this.state.newUser && this.state.newUser.length > 0)
        {
            this.context.onAddUser(this.state.newUser);
        }
    }
    render() {
        return (
            <div>
                <input onChange={this.handleChange} value=
                    {this.state.newUser} />
                <button onClick={this.handleClick}>Add</button>
            </div>
        )
    }
}
// 声明要使用的context对象的属性
UserAdd.contextTypes = {
    onAddUser: PropTypes.func
};

增加contextTypes后,在UserAdd内部就可以通过this.context.onAddUser的方式访问context中的onAddUser方法。
这里的示例传递的是组件的方法,组件中的任意数据也可以通过context自动向下传递。另外,当context中包含数据时,如果要修改context中的数据,一定不能直接修改,而是要通过setState修改,组件state的变化会创建一个新的context,然后重新传递给子组件。

2.3 特殊的ref

ref不仅可以用来获取表单元素,还可以用来获取其他任意DOM元素,甚至可
以用来获取React组件实例。
但绝大多数场景下,应该避免使用ref,因为它破坏了React中以props为数据传递介质的典型数据流。

在DOM元素上使用ref
class AutoFocusTextInput extends React.Component {
    componentDidMount() {
// 通过ref让input自动获取焦点
        this.textInput.focus();
    }
    render() {
        return (
            <div>
                <input
                    type="text"
                    ref={(input) => { this.textInput = input; }} />
            </div>
        );
    }
}
在组件上使用ref

React组件也可以定义ref,此时ref的回调函数接收的参数是当前组件的实例,这提供了一种在组件外部操作组件的方式。

class AutoFocusTextInput extends React.Component {
    constructor(props) {
        super(props);
        this.blur = this.blur.bind(this);
    }
    componentDidMount() {
// 通过ref让input自动获取焦点
        this.textInput.focus();
    }
// 让input失去焦点
    blur() {
        this.textInput.blur();
    }
    render() {
        return (
            <div>
                <input
                    type="text"
                    ref={(input) => { this.textInput = input; }} />
            </div>
        );
    }
}
class Container extends React.Component {
    constructor(props) {
        super(props);
        this.handleClick = this.handleClick.bind(this);
    }
    handleClick() {
// 通过ref调用AutoFocusTextInput组件的方法
        this.inputInstance.blur();
    }
    render() {
        return (
            <div>
                <AutoFocusTextInput ref={(input) =>
                {this.inputInstance = input}}/>
                <button onClick={this.handleClick}>失去焦点</button>
            </div>
        );
    }
}

注意,只能为类组件定义ref属性,而不能为函数组件定义ref属性。

2.4 虚拟DOM和性能优化

1 虚拟DOM

在传统的前端开发中,对DOM进行增删改操作,每一次对DOM的修改都会引起浏览器对网页的重新布局和重新渲染,而这个过程是很耗时的。这也是为什么前端性能优化中有一条原则:尽量减少DOM操作。

DOM效率低下的这个问题通过增加一层抽象解决。虚拟DOM就是这层抽象。

虚拟DOM使用普通的JavaScript对象来描述DOM元素。例如,下面是一个DOM结构:

<div className="foo">
	<h1>Hello React</h1>
</div>

可以用这样的一个JavaScript对象来表述:

{
    type: 'div',
        props: {
    className: 'foo',
        children: {
        type: 'h1',
            props:{
            children: 'Hello React'
        }
    }
}
}

虚拟DOM是普通的JavaScript对象,访问JavaScript对象当然比访问真实DOM要快得多。到这里,大家可以发现,虚拟DOM并不是什么神奇的东西,它只是用来描述真实DOM的JavaScript对象而已。

2 Diff算法

React采用声明式的API描述UI结构,每次组件的状态或属性更新,组件的render方法都会返回一个新的虚拟DOM对象,用来表述新的UI结构。React会通过比较两次虚拟DOM结构的变化找出差异部分,更新到真实DOM上,从而减少最终要在真实DOM上执行的操作,提高程序执行效率。这一过程就是React的调和过程(Reconciliation),其中的关键是比较两个树形结构的Diff算法。

尽量不要使用元素在列表中的索引值作为key,因为列表中的元素顺序一旦发
生改变,就可能导致大量的key失效,进而引起大量的修改操作。

2 性能优化

1.使用生产环境版本的库
2.通过shouldComponentUpdate避免不必要的组件渲染
3.对列表元素使用key,避免顺序错乱导致大量的重新渲染

2.5 高阶组件

高阶组件(简称HOC)接收React组件作为参数,并且返回一个新的React组件。高阶组件本质上也是一个函数。
例如:

class MyComponent extends Component {
    render() {
        return <div>{this.props.data}</div>
    }
}
// 组件给所有的传入组件增加componentWillMount逻辑,不用每个组件有单独写逻辑
import React, { Component } from 'react'
function withPersistentData(WrappedComponent1) {
    return class extends Component {
        componentWillMount() {
            let data = localStorage.getItem('data');
            this.setState({data});
        }
        render() {
            return <WrappedComponent1 data={this.state.data}
                                     {...this.props} />
        }
    }
}

const MyComponentWithPersistentData = withPersistentData(MyComponent)

通过这个例子可以看出高阶组件的主要功能是封装并分离组件的通用逻辑,让通用逻辑在组件间更好地被复用。

3. react实战

3.1 路由:用React Router开发单页面应用

通过不同的URL标识不同的页面,也就是存在页面间路由的需求,这时候就用React Router。页面路由的控制由服务器端负责,这种路由方式称为后端路由。这种方式,每次页面切换都需要向服务器发送一次请求,页面使用到的静态资源也需要重新请求加载,存在一定的浪费。
而单页面应用,上无论URL如何变化,对应的HTML文件都是同一个。页面内容发生变化,但不会向服务器发送新的请求。
所以“逻辑页面”的路由只能由前端负责,这种路由方式称为前端路由。

3.2 Router、Route

import React, { Component } from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import Home from "./components/Home";
import Login from "./components/Login";

class App extends Component {
  render() {
    return (
      <Router>
        <Switch>
          <Route exact path="/" component={Home} />
          <Route path="/login" component={Login} />
          <Route path="/posts" component={Home} />
        </Switch>
      </Router>
    );
  }
}

export default App;

一个应用中只需要一个Router实例,所有的路由配置组件Route都定义为Router的子组件。
Route提供了3个属性,用于定义待渲染的组件。

  • component
    Route中,component的值是一个组件,当URL和Route匹配时,component属性定义的组件就会被渲染。
  • render
    render的值是一个函数,这个函数返回一个React元素。这种方式可以为待渲染的组件传递额外的属性
    例如
render() {
    const { match, location } = this.props;
    const { userId, username } = this.state;
    return (
      <div>
        <Header
          username={username}
          onLogout={this.handleLogout}
          location={location}
        />
        <Route
          path={match.url}
          exact
          render={props => <PostList userId={userId} {...props} />}
        />
        <Route
          path={`${match.url}/:id`}
          render={props => <Post userId={userId} {...props} />}
        />
      </div>
    );
  }

3.3 Switch、exact

当URL和多个Route匹配时,这些Route都会执行渲染操作。如果只想让第一个匹配的Route渲染,那么可以把这些Route包到一个Switch组件中。如果想让URL和Route完全匹配时,Route才渲染,那么可以使用
Route的exact属性。

3.3 嵌套路由

嵌套路由是指在Route渲染的组件内部定义新的Route。

const Posts = ({ match }) => {
return (
<div>
{/* 这里match.url 等于 /posts */}
<Route path={`${match.url}/:id`} component=
{PostDetail} />
<Route exact path={match.url} component={PostList} />
</div>
);
}

3.4 Link

Link组件定义了当点击该Link时,页面应该如何路由。

<Link to={{ pathname: "/login", state: { from: location } }}>
                登录
              </Link>
1.基本用法

在传统的Web应用中,浏览器根据地址栏的URL向服务器发送一个HTTP请求,服务器根据URL返回一个HTML页面。

4. Redux

4.1

5. 问题

1.react在构造函数中调用bind(this)方法,是为什么

在 React 中,尤其是在类组件中,你可能会看到在构造函数中使用 bind(this) 方法。这种做法的目的是为了解决 JavaScript 中 this 的绑定问题。以下是详细的原因和解释:

1. JavaScript 中的 this 绑定规则在 JavaScript 中,this 的值取决于函数的调用方式,而不是函数定义的位置。这可能导致以下问题:
  • 在类组件中,如果你直接将类的方法(如事件处理函数)传递给子组件或 DOM 元素,那么该方法在调用时的 this 可能会丢失。
  • 如果没有正确绑定 this,函数内部的 this 将指向 undefined 或其他意外的对象。
    示例:this 丢失的问题
class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { message: "Hello, World!" };
  }

  handleClick() {
    console.log(this.state.message); // 这里的 `this` 是 undefined
  }

  render() {
    return <button onClick={this.handleClick}>Click Me</button>;
  }
}

在这个例子中,当你点击按钮时,控制台会报错,提示 Cannot read properties of undefined (reading ‘state’)。这是因为 handleClick 方法被作为回调函数传递给了 onClick,而它的 this 没有绑定到当前的类实例。

2. 使用 bind(this) 解决问题

通过在构造函数中显式地调用 bind(this),可以确保方法在任何地方调用时,this 都指向当前的类实例。
修改后的代码:

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { message: "Hello, World!" };

    // 手动绑定 this
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    console.log(this.state.message); // 正确输出 "Hello, World!"
  }

  render() {
    return <button onClick={this.handleClick}>Click Me</button>;
  }
}
  • 在构造函数中,this.handleClick = this.handleClick.bind(this); 将 handleClick 方法绑定到当前的类实例。
  • 现在,无论 handleClick 被如何调用,this 都会指向 MyComponent 实例。
3. 为什么需要在构造函数中绑定?

虽然你可以在其他地方绑定 this(例如在 render 方法中),但在构造函数中绑定是最高效和推荐的方式,原因如下:

    1. 性能优化:
    • 如果你在 render 方法中绑定 this(例如 ),每次组件重新渲染时都会创建一个新的函数实例。这会增加不必要的性能开销。
    • 在构造函数中绑定只会在组件初始化时执行一次,避免了重复绑定的性能问题。
    1. 代码清晰性:
    • 在构造函数中集中绑定 this,可以让代码更易于维护和理解。
4. 替代方案:使用箭头函数

从 ES6 开始,箭头函数提供了一种更简洁的方式来解决 this 绑定问题。箭头函数不会创建自己的 this,而是继承自外层作用域的 this。
示例:使用箭头函数

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { message: "Hello, World!" };
  }

  // 使用箭头函数定义方法
  handleClick = () => {
    console.log(this.state.message); // 正确输出 "Hello, World!"
  };

  render() {
    return <button onClick={this.handleClick}>Click Me</button>;
  }
}
  • 在这个例子中,handleClick 是一个箭头函数,因此它自动绑定了 this,无需手动调用 bind(this)。
  • 这种方式更加简洁,但需要注意的是,箭头函数是类的实例属性,而不是原型方法。这意味着每个组件实例都会有自己的 handleClick 方法副本,可能稍微增加内存消耗。
5. 总结
  • 为什么需要 bind(this)?
    • 在 JavaScript 中,this 的值取决于函数的调用方式。如果类方法被作为回调函数传递,this 可能会丢失。
    • 使用 bind(this) 确保方法中的 this 指向当前的类实例。
  • 为什么在构造函数中绑定?
    • 避免在每次渲染时重复绑定,提高性能。
    • 代码更加清晰和集中。
  • 替代方案:箭头函数
    • 使用箭头函数可以自动绑定 this,无需手动调用 bind(this)。
    • 更加简洁,但需要注意其对内存的影响。
React基础入门 React系列二 - 核心JSX语法一 React系列二 - 核心JSX语法二 React系列三 - 阶段案例练习 阅读详情

相关推荐

【ReactJs学习笔记】【编译生产环境的React应用】

目录 目录 package.json 本地服务器打开编译好的项目 可以快速以当前路径为根目录建立服务的工具pushstate-server 快捷链接 package.json &quot;scripts&quot;: { &quot;start&quot;: &quot;react-scripts start&quot;, &quot;build&quot;: &quot;reac

m0_37136491的博客 1264

React教程(详细版)

一、简介(背景) 1.1、概念 它是一个将数据渲染为HTML视图 的js库 1.2、原生js痛点 用dom的API去操作dom,繁琐且效率低 用js直接操作dom,浏览器会进行大量的回流和重绘 原生jjs没有组件化的编程方案,代码复用性低,哪怕有模块话的概念,但模块化也只能拆解一个个js,对样式和结构也没办法拆解,组件化就相当于3剑客整体拆解,成为一个个的小功能 1.3、React特点 采用组件化模式,声明式编码,提高开发效率和组件复用性 在React Native中可以用react预发进行安卓、io

Ronychen的博客 20万+

React学习教程,介绍,基础概念,简介

格式化配置 目标任务: 基于vscode配置格式化工具,提高开发效率 安装vsCode prettier插件 修改配置文件 setting.json { "git.enableSmartCommit": true, // 修改注释颜色 "editor.tokenColorCustomizations": { "comments": { "fontStyle": "bold", "foreground": "#82e0aa" } }, //

VAN 599

React 入门学习

目录 1. React 介绍 2. React 特点 (1)声明式 (2)组件化 (3)一次学习,跨平台编写 3. React 脚手架 方式 1 方式 2 4. React 基本使用 5. React 创建元素练习 1. React 介绍 了解 react 的历史背景和基本概念 React起源于 Facebook 的内部项目。因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设 Instagram 的网站。做出来以后,发现这套...

m0_62118859的博客 9950

掌握ReactJS的学习之路:书籍内容深度解读

本文深入探讨了《The Road to Learn React》一书的内容,包括学习React的最佳实践、社区支持、内容更新、以及如何在学习中提供帮助。书籍强调通过编码实践来掌握React,并随着内容的不断更新,让读者能够跟上React的最新趋势。作者还提倡通过学习React来支持儿童英语教育项目。

weixin_42576410的博客 517

手把手教你用键盘在Ubuntu上给Lerobot机械臂采集HIL-SERL训练数据(附完整配置文件)

本文详细介绍了在Ubuntu系统中使用键盘控制Lerobot机械臂进行HIL-SERL训练数据采集的完整流程。从环境准备、机械臂校准到键盘遥操系统配置和数据采集优化,提供了实战指南和常见问题解决方案,帮助读者高效获取高质量训练数据,推动具身智能和强化学习应用的发展。

weixin_30733003的博客 479

React学习教程,从入门到精通, ReactJS - 安装:初学者指南(3)

在我们开始安装之前,让我们快速了解一下ReactJS是什么。React是一个流行的JavaScript库,用于构建用户界面,尤其是单页应用程序。它主要是关于创建可重用的UI组件,这些组件管理自己的状态,使得构建复杂的界面变得更加容易。

qq_45746668的博客 2459

Unity游戏开发实战:用Flow Field实现RTS百人军团流畅寻路(附完整代码)

本文深入探讨了在Unity中利用流场寻路技术解决RTS游戏大规模单位移动性能瓶颈的实战方案。通过解析从Dijkstra算法生成成本场、积分场到最终向量场的核心原理,并结合分块、缓存及与DOTS/ECS架构集成的优化策略,实现了百人军团的流畅群体寻路。文章提供了完整的代码示例,助力开发者构建高性能的RTS寻路系统。

weixin_28363123的博客 587

入门指南:从零开始学习ReactJS

ReactJS是一个强大的JavaScript库,用于构建用户界面。它由Facebook开发,并于2013年首次发布。自那时以来,React已成为前端开发中最受欢迎的库之一,许多公司和开发者都在使用它来构建高性能、交互式的Web应用程序。本文将向您介绍ReactJS的基础知识,并通过实践示例来帮助您入门。我们将从React的核心概念开始,逐步深入,直到您能够构建简单的React应用为止。

努力是为了站在万人之中,成为别人的光 2万+

帕德伯恩大学轴承数据集(PU)介绍

PU数据集介绍一、帕德伯恩大学轴承数据集故障类别说明二、关于损伤描述2.1 损伤组合:2.2 重复和多重损伤的安排:2.3 几何尺寸2.4 损伤程度2.5损伤特征数据集损伤类别三、人为损坏轴承3.1 最常用的人为损伤制造方法是:3.2 本数据集使用的人工损坏是由三种不同方法引起的四、通过加速寿命试验产生真正的轴承损坏样本 一、帕德伯恩大学轴承数据集 数据集下载地址 从帕德伯恩大学获得的PU数据集是一个6203轴承数据集,包括人为诱发的和真实的损害。采用压电加速度计采集轴承座的振动信号,采样频率为64 kHz

m0_47180208的博客 3万+

【JTAG】1500协议详解

随着集成电路的规模不断扩大,1149.1的板级测试无法满足要求越来越高的芯片测试需求。芯片规模变大后,测试需求不在是一次测试整片芯片,而是芯片分为好几个区域去测试,而1500协议正是对1149.1在该方面的一个补充即1149.1主要测试板级间的互连,而1500用来测试芯片内不同core之间的互连。IEEEStd1500已经为包含不同模块的集成电路(ICs)开发了一种可测试性设计标准方法。............

xuhe0206的博客 7022

从零开始学习ReactJS -- 01 --首先要搭建一个react项目啊!

从头开始学习ReactJS - 01 开始学习第一步 看文档 文档地址:https://react.docschina.org/docs/getting-started.html 主要介绍了ReactJS的基本内容与基本语法 开始按照文档进行搭建 npx create-react-app my-app #my-app 是项目名称 自己定义 下同 cd my-app npm start 执行成功后显示页面 编辑器打开代码查看内容 使用vscode编辑器打开项目 项目目录结构显示 入口文件 src/

GASG的博客 321

读懂 SAP S/4HANA 里的 SAP Fiori 架构:前端服务器、搜索链路、传统应用接入与内容组织全景解析

摘要:SAP Fiori架构并非简单的系统连接图,而是明确区分界面层与业务数据层的三层分工体系:用户交互层(浏览器与Fiori launchpad)、前端技术层(SAPUI5运行环境)和业务数据层(SAP S/4HANA后端)。这种分离实现了生命周期解耦,前端专注于统一体验治理,后端处理核心业务逻辑。值得注意的是,SAP Fiori front-end server本质仍是ABAP系统,负责UI技术组件运行。虽然SAP S/4HANA推荐嵌入式部署,但独立部署在多系统场景中仍有价值。典型Fiori应用通过O

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 179

reactjs 分模块学习

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_34072458的博客 161

【SystemVerilog基础】SystemVerilog Assertion 概述

断言主要是描述复杂的设计行为;是传统验证方法学一个很好的补充,可以检查到电路内部细节,相当于一种白盒验证,传统的验证方法学是纯粹的黑盒验证!property跟上面的sequence差别不大,唯一有差别可能多了两个implication(|->,|=>)注意,当有前置条件时,不满足前置条件不会fail,而是伪真!上面这个因为没有前置条件,每个周期都会去检查!注意:当没有前置条件时,不满足条件会直接fail!

MoorePlus的博客 333

42、InfiniBand技术详解:QP状态机、操作及错误处理

本文深入解析InfiniBand技术中的核心组件QP(Queue Pair)状态机、工作请求处理机制、支持的操作类型及错误处理策略。详细介绍了QP的创建与状态转换流程,包括INIT、RTR、RTS等关键状态,并提供了状态修改的代码示例。文章还探讨了工作请求的发布、资源使用限制、围栏机制以及重试和RNR流程,帮助开发者理解通信可靠性保障机制。此外,涵盖了多播组的应用场景与API使用方法,并对比了用户空间与内核级RDMA API的调用差异,最后给出了实践建议和常见问题解答,为构建高性能分布式系统提供技术指导。

oauth7security的博客 132

React学习教程,从入门到精通, ReactJS - 优点与缺点(5)

ReactJS是一个流行的前端框架,具有显著优点和部分缺点。其核心优势包括虚拟DOM技术提升性能、可复用组件提高开发效率、单向数据流简化状态管理,以及庞大的生态系统支持。然而,React也存在学习曲线陡峭、JSX语法带来的困惑、频繁版本更新和文档不完善等问题。开发者应根据项目需求权衡这些特性,合理选择技术栈。React作为强大工具,适合需要高效交互和复杂状态管理的应用场景。

qq_45746668的博客 700

大模型之Spring AI实战系列(一):基础认知篇 - 开启智能应用开发之旅

Spring AI 是一个轻量级开源框架,旨在简化 AI 模型在企业级 Java 应用中的集成与管理。它提供了跨多个 AI 服务提供商的便携式 API,支持聊天、文本转图像、嵌入式模型等多种功能,并兼容主流的 AI 模型提供商如 OpenAI、Google 等。Spring AI 的核心功能包括结构化输出、向量数据库支持、工具/函数调用、可观察性、文档摄取 ETL 框架、AI 模型评估等。

寻道AI,探索AI无限可能! 6366

React学习教程,从入门到精通, ReactJS - 特性:初学者的指南(4)

ReactJS是一个用于构建用户界面的JavaScript库,由Facebook于2011年创建。其核心特性包括:1)虚拟DOM实现高效更新;2)JSX语法允许在JavaScript中编写类似HTML的代码;3)组件化架构使代码可重用;4)单向数据流确保应用可预测性;5)Hooks让函数组件拥有类组件功能。React通过虚拟DOM比较算法优化性能,组件化开发提高代码复用性,JSX简化UI编写,单向数据流便于调试,Hooks增强函数组件能力。这些特性使React成为构建现代Web应用的强大工具。

qq_45746668的博客 629

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

React源码深度解析终极指南:Under-the-hood-ReactJS完整学习路线图

想要真正掌握React的内部工作机制吗?Under-the-hood-ReactJS项目为你提供了完整的React源码学习路径!这个开源项目通过可视化流程图详细解析了React的挂载和更新两大核心流程,让复杂的源码变得清晰易懂。 ## 🎯 项目核心价值 Under-the-hood-ReactJS是一个专门为React开发者设计的源码学习项目。它通过精美的视觉图表,将React的内部架构和运

gitblog_01402的博客 1057

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

Web前端:开始学习ReactJS需要知道什么?

毫无疑问,ReactJS是前端开发者中最著名的库之一,它的受欢迎程度与日俱增。用ReactJS构建的网站看起来非常棒,大多数开发新手都被它吸引住了。然而,许多新人和有经验的开发人员在没有首先了解先决条件的情况下,就直接用ReactJS是错误的。

yueqian_edu的博客 992

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

【ReactJs学习笔记】【Chrome安装react审查插件&&组件】

目录 目录 Chrome安装react审查插件 组件 创建并导出组件 引用组件 尝试直接将新组件放置到对应标签 快捷链接 Chrome安装react审查插件 react developer tools拓展安装包下载 ClickHere&amp;amp;gt;&amp;amp;gt; 拖动下载了的.crx文件到拓展程序,显示如下 打开react项目,并用开发者工具,即可看到 ...

m0_37136491的博客 1601

reactJS学习日记——IDEA上的react项目创建

IDEA创建react项目的流程,此文仅以学生角度记录一些操作步骤以及遇到过的问题,以供将来查阅。

rinw20的博客 1915

8月中旬之后的学习计划 --- react

  这段时间快活了,放纵了,玩hi了,接下来该好好的学习新知识了。   鉴于目前业界比较火的前端js框架有react、vue,决定先从react开始学习。之前有简单的接触过它的一些基本的语法知识,这次准备全面的学习一下,从基础到深入,从简单到高级,为期一个半月,每周做好学习笔记,加油!   之后准备学习研究一下nodejs。   reactjs学习资料:   1、基础语法:...

dbl1134的博客 141

reactJS 学习指南——汇总

React学习资源汇总 React是如今最热门的前端框架,它设计思路独特,性能卓越,逻辑简单,受到了广泛开发者的喜爱。一旦接触她,你就会被她深深吸引。 我断断续续的学了将近两个月,今天终于有时间把自己学习React的一些学习资料总结一下。 React 官方 官网地址: http://facebook.github.io/react/ Github地址: ht...

PINGER007的博客 454

如何学习ReactJS:

文章目录1、基本条件2、HTML,CSS,JavaScript3、Javascript的ES6功能4、NodeJS基础和代码编辑器导入:一旦在项目中使用NPM安装了Node模块,就必须使用'import'关键字来使用该模块。导出:在创建模块/组件时使用此关键字,而您只需要返回一部分,而不必返回所有方法和变量。5、学习ReactJS 1、基本条件 HTML,CSS和JavaScript的基本知识。...

X W F 486
上一篇: 知识点查漏补缺
下一篇: java8 版本+ 新特性
modouwu
博客等级 码龄12年 86粉丝 29原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值