React:用于构建用户界面的 JavaScript 库。由Facebook
开发且开源。
原生 JavaScript 的痛点:
React 的特点:
React Native
中可用 React 语法进行移动端开发相关 JS 库:
react.development.js
:React 核心库react-dom.development.js
:提供 DOM 操作的 React 扩展库babel.min.js
:解析 JSX 语法,转换为 JS 代码<!-- 准备好一个“容器” -->
<div id="test"></div>
<!-- 引入react 核心库 -->
<script
type="text/javascript"
src="../../react-js/react.development.js"
></script>
<!-- 引入react-dom 用于支持react操作dom -->
<script
type="text/javascript"
src="../../react-js/react-dom.development.js"
></script>
<!-- 引入babel 用于jsx转为js -->
<script type="text/javascript" src="../../react-js/babel.min.js"></script>
<!-- 此处一定要写babel,表示写的不是 JS,而是 JSX,并且靠 babel 翻译 -->
<script type="text/babel">
//1.创建虚拟DOM
// 不要写引号,因为不是字符串
const VDOM = <h1>Hello,React</h1>;
//2.渲染虚拟DOM到页面
// 导入核心库和扩展库后,会有 React 和 ReactDOM 两个对象
ReactDOM.render(VDOM, document.getElementById('test'));
</script>
<script type="text/javascript">
//1.使用 React 提供的 API 创建虚拟DOM
const VDOM = React.createElement(
'h1',
{ id: 'title' },
React.createElement('span', {}, 'Hello,React')
);
//2.渲染虚拟DOM到页面
ReactDOM.render(VDOM, document.getElementById('test'));
</script>
<script type="text/babel">
//1.创建虚拟DOM
const VDOM = (
<h1 id='title'>
<span>Hello,React</span>
</h1>
);
//2.渲染虚拟DOM到页面
ReactDOM.render(VDOM, document.getElementById('test'));
</script>
关于虚拟 DOM:
<script type="text/babel">
const VDOM = (
<h1 id='title'>
<span>Hello,React</span>
</h1>
);
ReactDOM.render(VDOM, document.getElementById('test'));
const TDOM = document.getElementById('demo');
console.log('虚拟DOM', VDOM);
console.log('真实DOM', TDOM);
</script>
React.createElement()
方法的语法糖/:<input type="text" />
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>jsx语法规则</title>
<style>
.title {
background-color: orange;
width: 200px;
}
</style>
</head>
<body>
<div id="test"></div>
...
<script type="text/babel">
const myId = 'aTgUiGu';
const myData = 'HeLlo,rEaCt';
const VDOM = (
<div>
<h2 className='title' id={myId.toLowerCase()}>
<span style={{ color: 'white', fontSize: '19px' }}>
{myData.toLowerCase()}
</span>
</h2>
<input type='text' />
// <good>very good</good>
// <Child></Child>
</div>
);
ReactDOM.render(VDOM, document.getElementById('test'));
</script>
</body>
</html>
注意区分:JS 语句(代码) 与 JS 表达式
a;
a + b;
demo(1);
arr.map();
function test() {}
if(){}
for(){}
switch(){case:xxxx}
<script type="text/babel">
let list = ['Angular', 'React', 'Vue'] const VDOM = (
<div>
<h1>前端js框架列表</h1>
<ul>
// React 会自动遍历数组
{list.map((item, index) => {
// Each child in a list should have a unique "key" prop.
return <li key={index}>{item}</li>
})}
</ul>
</div>
) ReactDOM.render(VDOM, document.getElementById('test'))
</script>
<script type="text/babel">
// 1.创建函数式组件
function MyComponent() {
console.log(this); //此处的this是undefined,因为babel编译后开启了严格模式
return <h2>我是使用函数定义的组件(适用于【简单组件】的定义)</h2>;
}
// 2.渲染组件到页面
ReactDOM.render(<MyComponent />, document.getElementById('test'));
/*
执行了ReactDOM.render(<MyComponent />....)之后,发生了什么?
1.React解析组件标签,找到了MyComponent组件
2.发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真实DOM,随后呈现在页面中
*/
</script>
要点:
渲染组件的过程:
<script type="text/babel">
// 1.创建类式组件
class MyComponent extends React.Component {
render() {
// render是放在哪里的?—— MyComponent的原型对象上,供实例使用。
// render中的this是谁?—— MyComponent的实例对象 <=> MyComponent组件实例对象。
return <h2>我是使用类定义的组件(适用于【复杂组件】的定义)</h2>;
}
}
// 2.渲染组件到页面
ReactDOM.render(<MyComponent />, document.getElementById('test'));
/*
执行了ReactDOM.render(<MyComponent />....)之后,发生了什么?
1.React解析组件标签,找到了MyComponent组件。
2.发现组件是使用类定义的,随后new出该类的实例,并通过该实例调用到原型上的render方法。
3.将render返回的虚拟DOM转为真实DOM,随后呈现在页面中。
*/
</script>
组件渲染过程:
state
是组件实例对象最重要的属性,值为对象。又称为状态机,通过更新组件的 state 来更新对应的页面显示。
要点:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>state</title>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="test"></div>
<!-- 引入react 核心库 -->
<script
type="text/javascript"
src="../../react-js/react.development.js"
></script>
<!-- 引入react-dom 用于支持react操作dom -->
<script
type="text/javascript"
src="../../react-js/react-dom.development.js"
></script>
<!-- 引入babel 用于jsx转为js -->
<script type="text/javascript" src="../../react-js/babel.min.js"></script>
<script type="text/babel">
// 1.创建类组件
class Weather extends React.Component {
// constructor调用几次? ———— 1次
constructor(props) {
console.log('constructor');
super(props);
// 初始化状态
this.state = { isHot: true, 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() {
console.log('changeWeather');
// changeWeather方法放在了哪里 —— Weather的原型对象上,供实例使用
// 由于changeWeather是作为onClick的回调,所以不是通过实例调用的,是直接调用
// 类中的方法默认开启了局部的严格模式,所以changeWeather中的this为undefined
// 获取原来的值
const isHot = this.state.isHot;
// 严重注意:状态必须通过setState进行更新,且更新是一种合并,不是替换
this.setState({ isHot: !isHot });
//严重注意:状态(state)不可以直接修改,下面这行代码就是直接修改
// this.state.isHot = !isHot; //这是错误的写法
}
}
// 2.渲染组件到页面
ReactDOM.render(<Weather />, document.getElementById('test'));
</script>
</body>
</html>
简化版:
<script>
class Weather extends React.Component {
state = { isHot: true, wind: '微风' };
render() {
const { isHot } = this.state;
return (
<h2 onClick={this.changeWeather}>天气{isHot ? '炎热' : '凉爽'}</h2>
);
}
// 采用箭头函数 + 赋值语句形式
changeWeather = () => {
const isHot = this.state.isHot;
this.setState = { isHot: !isHot };
};
}
ReactDOM.render(<Weather />, document.getElementById('test'));
</script>
总结:
- 组件的 render 方法中的 this 为组件实例对象
- 组件自定义的方法中的 this 为 undefined,如何解决?
1. 强制绑定this:通过函数对象的bind()方法
2. 箭头函数
- 状态数据,不能直接修改或更新
每个组件对象都有
props
属性,组件标签的属性都保存在props
中。props
是只读的,不能修改。
<script type="text/babel">
class Person extends React.Component {
render() {
const { name, age, sex } = this.props;
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age}</li>
</ul>
);
}
}
// 类似于标签属性传值
ReactDOM.render(
<Person name='Lily' age={19} sex='男' />,
document.getElementById('test')
);
</script>
<script type="text/babel">
class Person extends React.Component {
render() {
const { name, age, sex } = this.props;
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age}</li>
</ul>
);
}
}
const obj = { name: 'Ben', age: 21, sex: '女' };
ReactDOM.render(<Person {...obj} />, document.getElementById('test'));
</script>
在
React 15.5
以前,React
身上有一个PropTypes
属性可直接使用,即name: React.PropTypes.string.isRequired
,没有把PropTypes
单独封装为一个模块。
从
React 15.5
开始,把PropTypes
单独封装为一个模块,需要额外导入使用。
<!-- 引入prop-types,用于对组件标签属性进行限制 -->
<script type="text/javascript" src="../../react-js/prop-types.js"></script>
<script type="text/babel">
// 1.创建组件
class Person extends React.Component {
render() {
const { name, age, sex } = this.props;
return (
<ul>
<li>姓名:{name}</li>
<li>年龄:{age + 1}</li>
<li>性别:{sex}</li>
</ul>
);
}
}
// 对标签属性进行类型,必要性的限制
Person.propTypes = {
name: PropTypes.string.isRequired, //限制name必传,且为字符串
age: PropTypes.number, //限制age为数值
sex: PropTypes.string, //限制sex为字符串
speak: PropTypes.func, //限制speak为函数
};
// 指定默认标签属性值
Person.defaultProps = {
sex: '男', //sex默认值为男
age: 18, //age默认值为18
};
// 2.渲染组件到页面
ReactDOM.render(
<Person name='jerry' speak={speak} />,
document.getElementById('test')
);
function speak() {
console.log('我说话了');
}
</script>
Person.propTypes
和Person.defaultProps
可以看作在类身上添加属性,利用static
关键词就能在类内部进行声明。因此所谓简写只是从类外部移到类内部。
<!-- 引入prop-types,用于对组件标签属性进行限制 -->
<script type="text/javascript" src="../../react-js/prop-types.js"></script>
<script type="text/babel">
// 1.创建组件
class Person extends React.Component {
// 对标签属性进行类型,必要性的限制
static propTypes = {
name: PropTypes.string.isRequired, //限制name必传,且为字符串
age: PropTypes.number, //限制age为数值
sex: PropTypes.string, //限制sex为字符串
speak: PropTypes.func, //限制speak为函数
};
// 指定默认标签属性值
static defaultProps = {
sex: '男', //sex默认值为男
age: 18, //age默认值为18
};
render() {
// props是只读的
const { name, age, sex } = this.props;
// this.props.name = 'jack'; //此行代码会报错,因为props是只读的
return (
<ul>
<li>姓名:{name}</li>
<li>年龄:{age + 1}</li>
<li>性别:{sex}</li>
</ul>
);
}
}
// 2.渲染组件到页面
ReactDOM.render(
<Person name='jerry' speak={speak} />,
document.getElementById('test')
);
function speak() {
console.log('我说话了');
}
</script>
构造函数一般用在两种情况:
constructor(props) {
super(props)
// 初始化 state
this.state = { isHot: true, wind: '微风' }
// 解决 this 指向问题
this.changeWeather = this.changeWeather.bind(this)
}
因此构造器一般都不需要写。如果要在构造器内使用 this.props 才声明构造器,并且需要在最开始调用 super(props) :
constructor(props) {
super(props)
console.log(this.props)
}
由于函数可以传递参数,因此函数式组件可以使用
props
。
<!-- 引入prop-types,用于对组件标签属性进行限制 -->
<script type="text/javascript" src="../js/prop-types.js"></script>
<script type="text/babel">
function Person(props) {
const { name, age, sex } = props;
return (
<ul>
<li>姓名:{name}</li>
<li>性别:{sex}</li>
<li>年龄:{age}</li>
</ul>
);
}
Person.propTypes = {
name: PropTypes.string.isRequired,
sex: PropTypes.string,
age: PropTypes.number,
};
Person.defaultProps = {
sex: '男',
age: 18,
};
ReactDOM.render(<Person name='jerry' />, document.getElementById('test'));
</script>
通过定义 ref 属性可以给标签添加标识。
这种形式已过时,效率不高,官方不建议使用。
<script type="text/babel">
// 创建组件
class RefDom extends React.Component {
// 展示左侧输入框数据
showData = () => {
const { input1 } = this.refs;
alert(input1.value);
};
// 展示右侧输入框数据
showData2 = () => {
const { input2 } = this.refs;
alert(input2.value);
};
render() {
return (
<div>
<input ref='input1' type='text' placeholder='点击按钮提示数据' />
<button onClick={this.showData}>点击我提示左侧输入框内容</button>
<input
ref='input2'
onBlur={this.showData2}
type='text'
placeholder='失去焦点提示数据'
/>
</div>
);
}
}
// 渲染组件到页面
ReactDOM.render(<RefDom />, document.getElementById('test'));
</script>
要点:
<script type="text/babel">
// 创建组件
class RefDom extends React.Component {
// 展示左侧输入框数据
showData = () => {
console.log(this);
const { input1 } = this;
alert(input1.value);
};
// 展示右侧输入框数据
showData2 = () => {
const { input2 } = this;
alert(input2.value);
};
render() {
return (
<div>
<input
ref={(c) => (this.input1 = c)}
type='text'
placeholder='点击按钮提示数据'
/>
<button onClick={this.showData}>点击我提示左侧输入框内容</button>
<input
ref={(c) => (this.input2 = c)}
onBlur={this.showData2}
type='text'
placeholder='失去焦点提示数据'
/>
</div>
);
}
}
// 渲染组件到页面
ReactDOM.render(<RefDom />, document.getElementById('test'));
</script>
关于回调 ref 执行次数的问题,官网描述:
TIP 如果
ref
回调函数是以内联函数的方式定义的,在更新过程中它会被执行两次,第一次传入参数null
,然后第二次会传入参数 DOM 元素。这是因为在每次渲染时会创建一个新的函数实例,所以 React 清空旧的ref
并且设置新的。通过将ref
的回调函数定义成class
的绑定函数的方式可以避免上述问题,但是大多数情况下它是无关紧要的。
即内联函数形式,在更新过程中 ref 回调会被执行两次,第一次传入 null ,第二次传入 DOM 元素。若是下述形式,则只执行一次。但是对功能实现没有影响,因此一般也是用内联函数形式。
<script type="text/babel">
//创建组件
class Demo extends React.Component {
state = { isHot: false };
changeWeather = () => {
const { isHot } = this.state;
this.setState({ isHot: !isHot });
};
saveInput = (c) => {
this.input1 = c;
console.log('@', c);
};
render() {
const { isHot } = this.state;
return (
<div>
<h2>今天天气很{isHot ? '炎热' : '凉爽'}</h2>
<input ref={this.saveInput} type='text' />
</div>
);
}
}
ReactDOM.render(<Demo />, document.getElementById('test'));
</script>
该方式通过调用 React.createRef 返回一个容器用于存储节点,且一个容器只能存储一个节点。
<script type="text/babel">
class Demo extends React.Component {
/*
React.createRef()调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是“专人专用”
*/
myRef = React.createRef();
myRef2 = React.createRef();
showData = () => {
alert(this.myRef.current.value);
};
showData2 = () => {
alert(this.myRef2.current.value);
};
render() {
return (
<div>
<input ref={this.myRef} type='text' placeholder='点击按钮提示数据' />
<button onClick={this.showData}>点我提示左侧的数据</button>
<input
onBlur={this.showData2}
ref={this.myRef2}
type='text'
placeholder='失去焦点提示数据'
/>
</div>
);
}
}
ReactDOM.render(<Demo />, document.getElementById('test'));
</script>
当触发事件的元素和需要操作的元素为同一个时,可以不使用 ref :
class Demo extends React.Component {
showData2 = (event) => {
alert(event.target.value);
};
render() {
return (
<div>
<input
onBlur={this.showData2}
type='text'
placeholder='失去焦点提示数据'
/>
</div>
);
}
}
包含表单的组件分类:
尽量使用受控组件,因为非受控组件需要使用大量的ref
。
// 非受控组件
class Login extends React.Component {
handleSubmit = (event) => {
event.preventDefault();
const { username, password } = this;
alert(`你输入的用户名是${username.value},你输入的密码是${password.value}`);
};
render() {
return (
<form onSubmit={this.handleSubmit}>
用户名:
<input ref={(c) => (this.username = c)} type='text' name='username' />
密码:
<input
ref={(c) => (this.password = c)}
type='password'
name='password'
/>
<button>登录</button>
</form>
);
}
}
// 受控组件
class Login extends React.Component {
state = {
username: '',
password: '',
};
saveUsername = (event) => {
this.setState({ username: event.target.value });
};
savePassword = (event) => {
this.setState({ password: event.target.value });
};
handleSubmit = (event) => {
event.preventDefault();
const { username, password } = this.state;
alert(`用户名是:${username}, 密码是:${password}`);
};
render() {
return (
<form onSubmit={this.handleSubmit}>
用户名:
<input onChange={this.saveUsername} type='text' name='username' />
密码:
<input onChange={this.savePassword} type='password' name='password' />
<button>登录</button>
</form>
);
}
}
对上述受控组件的代码进行优化,希望把 saveUsername 和 savePassword 合并为一个函数。
要点:
// 函数柯里化
function sum(a) {
return (b) => {
return (c) => {
return a + b + c;
};
};
}
// 使用高阶函数和柯里化写法
class Login extends React.Component {
state = {
username: '',
password: '',
};
saveFormData = (dataType) => {
return (event) => {
this.setState({ [dataType]: event.target.value });
};
};
handleSubmit = (event) => {
event.preventDefault();
const { username, password } = this.state;
alert(`用户名是:${username}, 密码是:${password}`);
};
render() {
return (
<form onSubmit={this.handleSubmit}>
用户名:
<input
onChange={this.saveFormData('username')}
type='text'
name='username'
/>
密码:
<input
onChange={this.saveFormData('password')}
type='password'
name='password'
/>
<button>登录</button>
</form>
);
}
}
// 不使用柯里化写法
class Login extends React.Component {
state = {
username: '',
password: '',
};
saveFormData = (dataType, event) => {
this.setState({ [dataType]: event.target.value });
};
handleSubmit = (event) => {
event.preventDefault();
const { username, password } = this.state;
alert(`用户名是:${username}, 密码是:${password}`);
};
render() {
return (
<form onSubmit={this.handleSubmit}>
用户名:
<input
onChange={(event) => this.saveFormData('username', event)}
type='text'
name='username'
/>
密码:
<input
onChange={(event) => this.saveFormData('password', event)}
type='password'
name='password'
/>
<button>登录</button>
</form>
);
}
}
不使用函数柯里化实现
<script type="text/babel">
// 创建组件
class Login extends React.Component {
// 初始化状态
state = {
username: '',
password: '',
};
// 保存表单数据到状态中【高阶函数&&函数柯里化】
saveFormData = (typeData, event) => {
this.setState({ [typeData]: event.target.value });
};
// 表单提交的回调
handleSubmit = (event) => {
event.preventDefault();
const { username, password } = this.state;
alert(`你输入的用户名是${username},你输入的密码是${password}`);
};
render() {
return (
<form action='#' onSubmit={this.handleSubmit}>
用户名:
<input
onChange={(event) => {
this.saveFormData('username', event);
}}
type='text'
name='username'
/>
密码:
<input
onChange={(event) => {
this.saveFormData('password', event);
}}
type='text'
name='password'
/>
<button>登录</button>
</form>
);
}
}
// 渲染组件
ReactDOM.render(<Login />, document.getElementById('test'));
</script>
要点: 高阶函数
如果一个函数符合下面 2 个规范中的任何一个,那该函数就是高阶函数。
1. 若A函数,接收的参数是一个函数式,那么A函数就可以称之为高阶函数。
2. 若A函数,调用的返回值依然是一个函数,那么A函数就可以称之为高阶函数。
常见的高阶函数:Promise,setTimeout,arr.map()等等
函数柯里化
通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>高阶函数&&函数柯里化</title>
</head>
<body>
<!-- 准备好一个容器 -->
<div id="test"></div>
<!-- 引入react 核心库 -->
<script
type="text/javascript"
src="../../react-js/react.development.js"
></script>
<!-- 引入react-dom 用于支持react操作dom -->
<script
type="text/javascript"
src="../../react-js/react-dom.development.js"
></script>
<!-- 引入babel 用于jsx转为js -->
<script type="text/javascript" src="../../react-js/babel.min.js"></script>
<script type="text/babel">
/*
高阶函数:如果一个函数符合下面2个规范中的任何一个,那该函数就是高阶函数
1. 若A函数,接收的参数是一个函数式,那么A函数就可以称之为高阶函数。
2. 若A函数,调用的返回值依然是一个函数,那么A函数就可以称之为高阶函数。
常见的高阶函数:Promise,setTimeout,arr.map()等等
函数柯里化:通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码形式。
function sum1(a) {
return (b) => {
return (c) => {
return a + b + c;
};
};
}
*/
// 创建组件
class Login extends React.Component {
// 初始化状态
state = {
username: '',
password: '',
};
// 保存表单数据到状态中【高阶函数&&函数柯里化】
saveFormData = (typeData) => {
console.log(typeData);
return (event) => {
this.setState({ [typeData]: event.target.value });
};
};
// 表单提交的回调
handleSubmit = (event) => {
event.preventDefault();
const { username, password } = this.state;
alert(`你输入的用户名是${username},你输入的密码是${password}`);
};
render() {
return (
<form action='#' onSubmit={this.handleSubmit}>
用户名:
<input
onChange={this.saveFormData('username')}
type='text'
name='username'
/>
密码:
<input
onChange={this.saveFormData('password')}
type='text'
name='password'
/>
<button>登录</button>
</form>
);
}
}
// 渲染组件
ReactDOM.render(<Login />, document.getElementById('test'));
</script>
</body>
</html>
<script type="text/babel">
// 创建组件
// 生命周期回调函数 <=> 生命周期钩子函数 <=> 生命周期函数 <=> 生命周期钩子
class Life extends React.Component {
state = { opacity: 1 };
giveUp = () => {
// 卸载组件
ReactDOM.unmountComponentAtNode(document.getElementById('test'));
};
// 组件挂载完毕
componentDidMount() {
this.timer = setInterval(() => {
// 获取原来的状态
let { opacity } = this.state;
// 减小0.1
opacity -= 0.1;
if (opacity <= 0) opacity = 1;
// 更新状态
this.setState({ opacity });
}, 200);
}
// 组件将要卸载
componentWillUnmount() {
// 清除定时器
clearInterval(this.timer);
}
// 初始化渲染,状态更新之后
render() {
console.log('@');
return (
<div>
<h2 style={{ opacity: this.state.opacity }}>React学不会呀!</h2>
<button onClick={this.giveUp}>放弃吧</button>
</div>
);
}
}
// 渲染组件
ReactDOM.render(<Life />, document.getElementById('test'));
</script>
初始化阶段:ReactDOM.render()
触发的初次渲染
constructor
componentWillMount
render
componentDidMount
更新阶段
render
触发的更新componentWillReceiveProps
shouldComponentUpdate
:控制组件是否更新的阀门,返回值为布尔值,默认为true
。若返回false
,则后续流程不会进行。componentWillUpdate
render
componentDidUpdate
this.setState()
修改状态shouldComponentUpdate
componentWillUpdate
render
componentDidUpdate
this.forceUpdate()
强制更新componentWillUpdate
render
componentDidUpdate
卸载阶段:ReactDOM.unmountComponentAtNode()
触发
componentWillUnmount
案例:
<script type="text/babel">
// 创建组件
class Count extends React.Component {
// 构造器
constructor(props) {
console.log('Count---constructor');
super(props);
// 初始化状态
this.state = { count: 0 };
}
// 加1按钮的回调
add = () => {
// 获取原状态
const { count } = this.state;
// 更新状态
this.setState({ count: count + 1 });
};
// 卸载组件按钮的回调
death = () => {
ReactDOM.unmountComponentAtNode(document.getElementById('test'));
};
// 强制更新按钮的回调
force = () => {
this.forceUpdate();
};
// 组件将要挂载的钩子
componentWillMount() {
console.log('Count---componentWillMount');
}
// 组件挂载完毕的钩子
componentDidMount() {
console.log('Count---componentDidMount');
}
// 组件将要卸载的钩子
componentWillUnmount() {
console.log('Count---componentWillUnmount');
}
// 控制组件更新的阀门
shouldComponentUpdate() {
console.log('Count---shouldComponentUpdate');
return true;
}
// 组件将要更新的钩子
componentWillUpdate() {
console.log('Count---componentWillUpdate');
}
// 组件更新完毕的钩子
componentDidUpdate() {
console.log('Count---componentDidUpdate');
}
// 初始化,更新状态时
render() {
console.log('Count---render');
const { count } = this.state;
return (
<div>
<h2>当前求和的值为{count}</h2>
<button onClick={this.add}>点我+1</button>
<button onClick={this.death}>卸载组件</button>
<button onClick={this.force}>
不更新状态中的数据,强制更新一下
</button>
</div>
);
}
}
class A extends React.Component {
// 初始化状态
state = { carName: '奔驰' };
changeCarname = () => {
this.setState({ carName: '奥迪' });
};
render() {
return (
<div>
<div>我是A组件</div>
<button onClick={this.changeCarname}>换车</button>
<B carName={this.state.carName} />
</div>
);
}
}
class B extends React.Component {
// 组件将要接收新的props的钩子
componentWillReceiveProps() {
console.log('B---componentWillReceiveProps');
}
//控制组件更新的阀门
shouldComponentUpdate() {
console.log('B---shouldComponentUpdate');
return true;
}
// 组件将要更新的钩子
componentWillUpdate() {
console.log('B---componentWillUpdate');
}
//组件更新完毕的钩子
componentDidUpdate() {
console.log('B---componentDidUpdate');
}
render() {
console.log('B---render');
return (
<div>
我是B组件,喜欢的车是
<span style={{ color: 'orangered' }}>{this.props.carName}</span>
</div>
);
}
}
// 渲染组件
ReactDOM.render(<A />, document.getElementById('test'));
</script>
componentWillMount
、componentWillReceiveProps
、componentWillUpdate
。在新版本中这三个钩子需要加UNSAFE_
前缀才能使用,后续可能会废弃。getDerivedStateFromProps
、getSnapshotBeforeUpdate
`
static getDerivedStateFromProps(props, state):
getSnapshotBeforeUpdate(prevProps, prevState):
// static getDerivedStateFromProps 案例
static getDerivedStateFromProps(props,state){
console.log('getDerivedStateFromProps',props,state);
return null
}
getSnapshotBeforeUpdate(){
console.log('getSnapshotBeforeUpdate');
return 'atguigu'
}
componentDidUpdate(preProps,preState,snapshotValue){
console.log('componentDidUpdate',preProps,preState,snapshotValue);
}
// getSnapshotBeforeUpdate 案例
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'));
render
:初始化渲染和更新渲染componentDidMount
:进行初始化,如开启定时器、发送网络请求、订阅消息componentWillUnmount
:进行收尾,如关闭定时器、取消订阅消息
key
的作用:
key
是虚拟 DOM 对象的标识,可提高页面更新渲染的效率。
当状态中的数据发生变化时,React 会根据新数据生成新的虚拟 DOM ,接着对新旧虚拟 DOM 进行 Diff 比较,规则如下:
使用 index 作为 key 可能引发的问题:
// 使用 index 作为 key 引发的问题
class Person extends React.Component {
state = {
persons: [
{ id: 1, name: '小张', age: 18 },
{ id: 2, name: '小李', age: 19 },
],
}
add = () => {
const { persons } = this.state
const p = { id: persons.length + 1, name: '小王', age: 20 }
this.setState({ persons: [p, ...persons] })
}
render() {
return (
<div>
<h2>展示人员信息</h2>
<button onClick={this.add}>添加小王</button>
<h3>使用index作为key</h3>
<ul>
{this.state.persons.map((personObj, index) => {
return (
<li key={index}>
{personObj.name}---{personObj.age}
<input type="text" />
</li>
)
})}
</div>
)
}
}
npm i -g create-react-app
create-react-app 项目名称
cd 项目名称
npm start
上述方式已经过时,改用下方命令。详见官方说明。
npx create-react-app my-app
cd my-app
npm start
public
:静态资源文件
favicon.icon
:网站页签图标index.html
:主页面manifest.json
:应用加壳(把网页变成安卓/IOS 软件)的配置文件robots.txt
:爬虫协议文件src
:源码文件
App.css
:App 组件的样式App.js
:App 组件App.test.js
:用于给 App 组件做测试,一般不用index.css
:样式index.js
:入口文件reportWebVitals.js
:页面性能分析文件,需要 web-vitals
库支持setupTests.js
:组件单元测试文件,需要 jest-dom
库支持index.html
代码分析
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<!-- %PUBLIC_URL% 代表 public 文件夹的路径 -->
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<!-- 开启理想视口,用于做移动端网页的适配 -->
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!-- 用于配置浏览器页签+地址栏的颜色(仅支持安卓手机浏览器) -->
<meta name="theme-color" content="red" />
<!-- 网站描述 -->
<meta
name="description"
content="Web site created using create-react-app"
/>
<!-- 用于指定网页添加到手机主屏幕后的图标 -->
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!-- 应用加壳时的配置文件 -->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>React App</title>
</head>
<body>
<!-- 若浏览器不支持 js 则展示标签中的内容 -->
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
样式的模块化可用于解决样式冲突的问题。该方法比较麻烦,实际开发用的比较少。用 less
就能解决了。
component/Hello
文件下的 index.css
改名为 index.module.css
.title {
background-color: orange;
}
Hello
组件导入样式:
import { Component } from 'react';
import hello from './index.module.css';
export default class Hello extends Component {
render() {
return <h2 className={hello.title}>Hello,React!</h2>;
}
}
className
、style
的写法state
中state
中,即状态提升props
传递props
给子组件传递一个函数,子组件调用该函数// 父组件
class Father extends Component {
state: {
todos: [{ id: '001', name: '吃饭', done: true }],
flag: true,
};
addTodo = (todo) => {
const { todos } = this.state;
const newTodos = [todo, ...todos];
this.setState({ todos: newTodos });
};
render() {
return <List todos={this.state.todos} addTodo={this.addTodo} />;
}
}
// 子组件
class Son extends Component {
// 由于 addTodo 是箭头函数,this 指向父组件实例对象,因此子组件调用它相当于父组件实例在调用
handleClick = () => {
this.props.addTodo({ id: '002', name: '敲代码', done: false });
};
render() {
return <button onClick={this.handleClick}>添加</button>;
}
}
注意defaultChecked
和checked
的区别,类似的还有:defaultValue
和value
状态在哪里,操作状态的方法就在哪里
方法一:
在 package.json
文件中进行配置:
"proxy": "http://localhost:5000" //服务器地址
方法二: 在 src 目录下创建代理配置文件 setupProxy.js ,进行配置:
const proxy = require('http-proxy-middleware')
module.exports = function (app) {
app.use(
//api1是需要转发的请求(所有带有/api1前缀的请求都会转发给5000)
proxy('/api1', {
//配置转发目标地址(能返回数据的服务器地址)
target: 'http://localhost:5000',
//控制服务器接收到的请求头中host字段的值
/*
changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000
changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:3000
changeOrigin默认值为false,但一般将changeOrigin改为true
*/
changeOrigin: true,
//去除请求前缀,保证交给后台服务器的是正常请求地址(必须配置)
pathRewrite: { '^/api1': '' },
}),
proxy('/api2', {
target: 'http://localhost:5001',
changeOrigin: true,
pathRewrite: { '^/api2': '' },
})
)
}
即 React 中兄弟组件或任意组件之间的通信方式。
使用的工具库:PubSubJS
下载安装 PubSubJS :npm install pubsub-js --save
基础用法:
import PubSub from 'pubsub-js'
// 订阅消息
var token = PubSub.subscribe('topic', (msg, data) => {
console.log(msg, data)
})
// 发布消息
PubSub.publish('topic', 'hello react')
// 取消订阅
PubSub.unsubscribe(token)
//server.js
const express = require('express');
const axios = require('axios');
const app = express();
// 设置代理,以便可以从服务器端直接请求GitHub的API
app.use('/search/users', (req, res) => {
const query = req.query.q;
const url = `https://api.github.com/search/users?q=${query}`;
axios
.get(url)
.then((response) => {
res.json(response.data);
})
.catch((error) => {
console.error('Error fetching data: ', error);
res.status(500).send('Server error');
});
});
app.get('/search/users2', (request, response) => {
// 随机生成100条用户头像数据
const users = Array.from({ length: 100 }, (_, i) => ({
id: i + 1,
avatar: `https://picsum.photos/200/300?random=${i}`,
}));
response.send(users);
});
app.listen(5000, (err) => {
if (!err) {
console.log(`Server running on 5000`);
}
});
//setupProxy.js
let obj = { a: { b: 1 } }
//传统解构赋值
const { a } = obj
//连续解构赋值
const {
a: { b },
} = obj
//连续解构赋值 + 重命名
const {
a: { b: value },
} = obj
try {
// 先看服务器是否联系得上
const response = await fetch(`/api1/search/users2?q=${keyWord}`)
// 再获取数据
const data = await response.json()
console.log(data)
} catch (error) {
console.log('请求出错', error)
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。