文章目录
- 前言
- React
- 1 简介
- 2 笔记
- 创建一个简单的JSX元素
- 创建一个复杂的 JSX 元素
- 在JSX中添加注释
- 渲染 HTML 元素为 DOM 树
- 在 JSX 中定义一个 HTML Class
- 了解自闭合 JSX 标签
- 创建一个无状态的函数组件
- 创建一个React组件
- 用组合的方式创建一个 React 组件
- 将class组件渲染到DOM树
- 将 Props 传递给无状态函数组件
- 传递一个数组作为Props
- 使用默认的Props
- 覆盖默认的props
- 使用 PropTypes 来定义 Props 的类型
- 使用this.props 访问props
- 创建一个有状态的组件
- 以另一种方式在用户界面中渲染状态
- 用this.setState设置状态
- 将this绑定到Class方法上
- 使用 State 切换元素
- 创建一个可以控制的表单
- 生命周期方法
- 介绍内联样式
- 在 React 中添加内联样式
- && 代替if/else进行更加简洁的条件渲染
- 使用三元表达式进行条件渲染
- 创建一个简单的"To Do List"应用程序
- 给同级元素一个唯一的键属性
- 使用 Array.Filter() 动态过滤数组
- 用 renderToString 在服务器上渲染 React
前言
前端开发库学习笔记,仅供参考交流,如有侵权,请联系删除。
React
1 简介
React 是一个流行的 JavaScript 库,用于为网页或应用程序构建可重用的组件驱动的用户界面。
React 将 HTML 与 JavaScript 结合在了一起,以此创建出一个新的标记语言 JSX。React 还使得管理整个应用程序的数据流变得更容易。
2 笔记
创建一个简单的JSX元素
React 使用名为 JSX 的 JavaScript 语法扩展,可以直接在 JavaScript 中编写 HTML。 这有几个好处。 可以在 HTML 中使用 JavaScript 的完整程序功能,并有助于保持代码的可读性。
因为 JSX 是 JavaScript 的语法扩展,所以实际上可以直接在 JSX 中编写 JavaScript。 要做到这一点,只需在花括号中包含希望被视为 JavaScript 的代码:{ ‘this is treated as JavaScript code’ }(这被视为 JavaScript 代码)。
由于浏览器不能解析 JSX,因此必须将 JSX 代码编译为 JavaScript。 在这个过程中,转换器 Babel 是一个很受欢迎的工具。
const JSX = <h1>Hello JSX!</h1>;
创建一个复杂的 JSX 元素
关于嵌套的 JSX,必须返回单个元素。用父元素包裹所有其他级别的嵌套元素。
<div>
<p>Paragraph One</p>
<p>Paragraph Two</p>
<p>Paragraph Three</p>
</div>
在JSX中添加注释
const JSX = (
<div>
{/*这里是注释*/}
<h1>This is a block of JSX</h1>
<p>Here's a subtitle</p>
</div>
);
渲染 HTML 元素为 DOM 树
const JSX = (
<div>
<h1>Hello World</h1>
<p>Lets render this to the DOM</p>
</div>
);
// 在这行下面添加代码
ReactDOM.render(JSX,document.getElementById('challenge-node'));
render函数的第一个参数是要渲染的React元素或组件,第二个参数是组件将要渲染到的DOM节点。
在 JSX 中定义一个 HTML Class
JSX 的一个关键区别是你不能再使用 class 这个单词来做为 HTML 的 class 名。 这是因为 class 是 JavaScript 中的关键字。 而 JSX 使用 className 来代替。
事实上,JSX 中所有 HTML 属性和事件引用的命名约定都变成了驼峰式。 例如,JSX 中的单击事件是 onClick,而不是 onclick。 同样,onchange 变成了onChange。 虽然这是一个微小的差异,但请你一定要记住。
了解自闭合 JSX 标签
任何 JSX 元素都可以使用自闭合标签编写,并且每个元素都必须关闭。 例如,为了通过编译换行标签必须始终编写为 <br />
。 另一方面 <div>
可以写成 <div />
或者 <div></div>
。 不同之处在于,在第一个语法版本中,无法在 <div />
中包含任何内容。 在后面的挑战中你会发现,这种语法在渲染 React 组件时非常有用。
创建一个无状态的函数组件
const MyComponent = function() {
// 修改这行下面的代码
return (
<div>zzzz</div>
);
// 修改这行上面的代码
}
有两种方法可以创建 React 组件。 第一种方法是使用 JavaScript 函数。 以这种方式定义组件会创建无状态函数组件。
要用函数创建组件,只需编写一个返回 JSX 或 null 的 JavaScript 函数。 需要注意的一点是,React 要求你的函数名以大写字母开头。
创建一个React组件
class MyComponent extends React.Component {
constructor(props) {
super(props);
}
render() {
// 修改这行下面的代码
return (
<div><h1>Hello React!</h1></div>
)
// 修改这行上面的代码
}
};
创建了一个ES6类MyComponent ,它扩展了React.Component类,因此MyComponent 类现在可以访问许多有用的React功能,例如本地状态和生命周期钩子。
MyComponent 类定义了一个调用super()方法的constructor。它使用super()调用父类的构造函数,即本例中的React.Component。
构造函数是使用 class 关键字创建的特殊方法,它在实例初始化之前调用。 最佳做法是在组件的 constructor 里调用 super,并将 props 传递给它们, 这样可以保证组件能够正确地初始化。
用组合的方式创建一个 React 组件
const ChildComponent = () => {
return (
<div>
<p>I am the child</p>
</div>
);
};
class ParentComponent extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>I am the parent</h1>
{ /* 修改这行下面的代码 */ }
<ChildComponent />
{ /* 修改这行上面的代码 */ }
</div>
);
}
};
将class组件渲染到DOM树
class TypesOfFood extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>Types of Food:</h1>
{/* 修改这行下面的代码 */}
<Fruits/>
<Vegetables/>
{/* 修改这行上面的代码 */}
</div>
);
}
};
// 修改这行下面的代码
ReactDOM.render(<TypesOfFood/>,document.getElementById('challenge-node'))
对于 JSX 元素,传入的是要渲染的元素的名称。 但是,对于 React 组件,需要使用与渲染嵌套组件相同的语法,即传入<TypesOfFood/>
将 Props 传递给无状态函数组件
const CurrentDate = (props) => {
return (
<div>
{ /* 修改这行下面的代码 */ }
<p>The current date is: {props.date}</p>
{ /* 修改这行上面的代码 */ }
</div>
);
};
class Calendar extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h3>What date is it?</h3>
{ /* 修改这行下面的代码 */ }
<CurrentDate date={Date()}/>
{ /* 修改这行上面的代码 */ }
</div>
);
}
};
在 React 中,可以把创建的 React 支持的自定义 HTML 属性传递给组件。调用 props 这个值是常见做法,当处理无状态函数组件时,基本上可以将其视为返回 JSX 的函数的参数。 这样就可以在函数体中访问该值。 但对于类组件,访问方式会略有不同。
传递一个数组作为Props
const List = (props) => {
{ /* 修改这行下面的代码 */ }
return <p>{props.tasks.join(',')}</p>
{ /* 修改这行上面的代码 */ }
};
class ToDo extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>To Do Lists</h1>
<h2>Today</h2>
{ /* 修改这行下面的代码 */ }
<List tasks={["walk dog","workout","read"]}/>
<h2>Tomorrow</h2>
<List tasks={["walk dog","workout","read"]}/>
{ /* 修改这行上面的代码 */ }
</div>
);
}
};
使用默认的Props
const ShoppingCart = (props) => {
return (
<div>
<h1>Shopping Cart Component</h1>
</div>
)
};
// 修改这行下面的代码
ShoppingCart.defaultProps = {
items:0
}
在组件ShoppingCart上定义默认props,它指定一个items
prop,其值为0。
覆盖默认的props
const Items = (props) => {
return <h1>Current Quantity of Items in Cart: {props.quantity}</h1>
}
Items.defaultProps = {
quantity: 0
}
class ShoppingCart extends React.Component {
constructor(props) {
super(props);
}
render() {
{ /* 修改这行下面的代码 */ }
return <Items quantity={10}/>
{ /* 修改这行上面的代码 */ }
}
};
由于 quantity 的值是整数,所以它不会加引号,但应该用花括号括起来, 例如{100}。 这个语法告诉 JSX 直接将花括号中的值解释为 JavaScript。
使用 PropTypes 来定义 Props 的类型
const Items = (props) => {
return <h1>Current Quantity of Items in Cart: {props.quantity}</h1>
};
// 修改这行下面的代码
Items.propTypes = {
quantity:PropTypes.number.isRequired
}
// 修改这行上面的代码
Items.defaultProps = {
quantity: 0
};
class ShoppingCart extends React.Component {
constructor(props) {
super(props);
}
render() {
return <Items />
}
};
当提前知道 prop 的类型时,最佳实践是设置其 propTypes。 当数据是任何其它类型时,都会抛出警告。
optionalArray: PropTypes.array,
optionalBool: PropTypes.bool,
optionalFunc: PropTypes.func,
optionalNumber: PropTypes.number,
optionalObject: PropTypes.object,
optionalString: PropTypes.string,
optionalSymbol: PropTypes.symbol,
使用this.props 访问props
class App extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
{ /* 修改这行下面的代码 */ }
<Welcome name={"Cat"}/>
{ /* 修改这行上面的代码 */ }
</div>
);
}
};
class Welcome extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
{ /* 修改这行下面的代码 */ }
<p>Hello, <strong>{this.props.name}</strong>!</p>
{ /* 修改这行上面的代码 */ }
</div>
);
}
};
倘若接收 prop 的子组件不是无状态函数组件,而是一个 ES6 类组件的时候,要访问类组件中的 props,需要在在访问它的代码前面添加 this。
任何时候,如果要引用类组件本身,可以使用 this 关键字。
创建一个有状态的组件
class StatefulComponent extends React.Component {
constructor(props) {
super(props);
// 只修改这一行下面的代码
this.state = {
firstName:'Xiaoming'
}
// 只修改这一行上面的代码
}
render() {
return (
<div>
<h1>{this.state.firstName}</h1>
</div>
);
}
};
React 中最重要的主题之一是 state。 state 包含应用程序需要了解的任何数据,这些数据可能会随时间而变化。 应用程序能够响应 state 的变更,并在必要时显示更新后的 UI。
可以在组件的整个生命周期内访问 state 对象, 可以更新它、在 UI 中渲染它,也可以将其作为 props 传递给子组件。这里通过{this.state.firstName}渲染状态。
以另一种方式在用户界面中渲染状态
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'freeCodeCamp'
}
}
render() {
// 修改这行下面的代码
const name = this.state.name
// 修改这行上面的代码
return (
<div>
{ /* 修改这行下面的代码 */ }
<h1>{name}</h1>
{ /* 修改这行上面的代码 */ }
</div>
);
}
};
用this.setState设置状态
this.setState()方法,传入键值对的对象,其中键是state属性,值是更新后的state数据。
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
name: 'Initial State'
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
// 修改这行下面的代码
this.setState({
name:'React Rocks!'
});
// 修改这行上面的代码
}
render() {
return (
<div>
<button onClick={this.handleClick}>Click Me</button>
<h1>{this.state.name}</h1>
</div>
);
}
};
React要求永远不要直接修改state
,而是在state发生改变时始终使用this.setState()。此外,React可以批量处理多个state更新以提高性能,这意味着通过setSate方法进行的state更新可以是异步的。
handleClick() {
this.setState((prevState) => {
return {
counter: prevState.counter + 1
};
});
console.log("counter", this.state.counter);
}
在这里,我们将一个函数作为 setState 方法的第一个参数传递,并返回一个新的状态对象,其中counter 在 counter的上一个值的基础上增加1。这样可以正确地获得UI上 counter 的更新值。 但在控制台中获取的是以前的 counter 值,尽管我们在调用 this.setState 之后添加了console.log,这便是异步
的表现。
handleClick() {
this.setState(
(prevState) => {
return {
counter: prevState.counter + 1
};
},
() => console.log("counter", this.state.counter)
);
}
尽管React提供了一个回调函数来立即获取更新后的状态值,但建议只在快速测试或记录日志时使用它。React建议使用componentDidUpdate
方法:
componentDidUpdate(prevProps, prevState) {
if (prevState.counter !== this.state.counter) {
// do something
console.log("counter", this.state.counter);
}
}
学着学着发现不太懂了,可能是内容太多了,先学到这里。
将this绑定到Class方法上
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
text: "Hello"
};
// 修改这行下面的代码
this.handleClick = this.handleClick.bind(this);
// 修改这行上面的代码
}
handleClick() {
this.setState({
text: "You clicked!"
});
}
render() {
return (
<div>
{ /* 修改这行下面的代码 */ }
<button onClick={this.handleClick}>Click Me</button>
{ /* 修改这行上面的代码 */ }
<h1>{this.state.text}</h1>
</div>
);
}
};
让类方法访问this,使用this.handleClick = this.handleClick.bind(this);
显式绑定。
使用 State 切换元素
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
this.setState(state => ({
counter: state.counter + 1
}));
给 setState 传入一个函数,这个函数可以访问 state 和 props。 给 setState 传入函数可以保证 state 和 props 是正确的值,即上一个状态。
创建一个可以控制的表单
class MyForm extends React.Component {
constructor(props) {
super(props);
this.state = {
input: '',
submit: ''
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({
input: event.target.value
});
}
handleSubmit(event) {
// 修改这行下面的代码
event.preventDefault();
this.setState({
submit: this.state.input
});
// 修改这行上面的代码
}
render() {
return (
<div>
<form onSubmit={this.handleSubmit}>
{/* 修改这行下面的代码 */}
<input value={this.state.input} onChange={this.handleChange}/>
{/* 修改这行上面的代码 */}
<button type='submit'>Submit!</button>
</form>
{/* 修改这行下面的代码 */}
<h1>{this.state.submit}</h1>
{/* 修改这行上面的代码 */}
</div>
);
}
}
在提交处理程序中调用event.preventDefault();
,防止将会刷新网页的默认的表单提交行为。
生命周期方法
使用生命周期方法 componentWillMount
当组件被挂载到 DOM 时,componentWillMount() 方法在 render() 方法之前被调用。
注意: componentWillMount 生命周期方法会在版本 16.X 废弃,在版本 17 移除。
class MyComponent extends React.Component {
constructor(props) {
super(props);
}
componentWillMount() {
// 修改这行下面的代码
console.log("今天周一")
// 修改这行上面的代码
}
render() {
return <div />
}
};
使用生命周期方法:componentDidMount
某些时候,大多数 web 开发人员需要调用 API 接口来获取数据。 如果正在使用 React,知道在哪里执行这个动作是很重要的。
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
activeUsers: null
};
}
componentDidMount() {
setTimeout(() => {
this.setState({
activeUsers: 1273
});
}, 2500);
}
render() {
return (
<div>
{/* 修改这行下面的代码 */}
<h1>Active Users:{this.state.activeUsers} </h1>
{/* 修改这行上面的代码 */}
</div>
);
}
}
React 的最佳实践是在生命周期方法 componentDidMount() 中对服务器进行 API 调用或任何其它调用。 将组件装载到 DOM 后会调用此方法。 此处对 setState() 的任何调用都将触发组件的重新渲染。 在此方法中调用 API 并用 API 返回的数据设置 state 时,一旦收到数据,它将自动触发更新。
vue的最佳实践是在created()里吗?现在工作中好像只在这个方法中调用过api以获取数据,其他的生命周期方法中确实比较少见…
添加事件侦听器
componentDidMount() 方法也是添加特定功能所需的任何事件监听器的最佳位置。
React提供了一个合成事件系统,它封装了浏览器中的事件系统。不管用户用的是什么浏览器,合成事件系统的行为都完全相同–即使不同浏览器之间的本地事件的行为可能不同。
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
message: ''
};
this.handleEnter = this.handleEnter.bind(this);
this.handleKeyPress = this.handleKeyPress.bind(this);
}
// 修改这行下面的代码
componentDidMount() {
document.addEventListener(this.keydown ,this.handleKeyPress);
}
componentWillUnmount() {
document.removeEventListener(this.keydown
,this.handleKeyPress)
}
// 修改这行上面的代码
handleEnter() {
this.setState((state) => ({
message: state.message + 'You pressed the enter key! '
}));
}
handleKeyPress(event) {
if (event.keyCode === 13) {
this.handleEnter();
}
}
render() {
return (
<div>
<h1>{this.state.message}</h1>
</div>
);
}
};
使用 shouldComponentUpdate 优化重新渲染
到目前为止,如果任何组件接收到新的 state 或新的 props,它会重新渲染自己及其所有子组件。 这通常是好的。 但是 React 提供了一种生命周期方法shouldComponentUpdate(),当子组件接收到新的 state 或 props 时,可以调用该方法,并特别声明组件是否应该更新。
class OnlyEvens extends React.Component {
constructor(props) {
super(props);
}
shouldComponentUpdate(nextProps, nextState) {
console.log('Should I update?');
// OnlyEvens仅在其新 props 的 value 为偶数时更新
if(nextProps.value%2 == 0){
return true;
}else{
return false;
}
// 修改这行上面的代码
}
componentDidUpdate() {
console.log('Component re-rendered.');
}
render() {
return <h1>{this.props.value}</h1>;
}
}
class Controller extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 0
};
this.addValue = this.addValue.bind(this);
}
addValue() {
this.setState(state => ({
value: state.value + 1
}));
}
render() {
return (
<div>
<button onClick={this.addValue}>Add</button>
<OnlyEvens value={this.state.value} />
</div>
);
}
}
这种方法是优化性能的有效方法。 例如,默认行为是,当组件接收到新的 props 时,即使 props 没有改变,它也会重新渲染。 可以通过使用 shouldComponentUpdate() 比较 props 来防止这种情况发生。 该方法必须返回一个 boolean(布尔值),该值告诉 React 是否更新组件。 可以比较当前的 props(this.props)和下一个 props(nextProps),以确定你是否需要更新,并相应地返回 true 或 false。
介绍内联样式
class Colorful extends React.Component {
render() {
return (
<div style={{color:"red",fontSize:72}}>Big Red</div>
);
}
};
使用style属性为JSX添加内联样式。
注意驼峰拼写 fontSize 属性。
在 React 中添加内联样式
const styles = {
color:"purple",
fontSize:40,
border:'2px solid purple'
}
// 修改这行上面的代码
class Colorful extends React.Component {
render() {
// 修改这行下面的代码
return (
<div style={styles}>Style Me!</div>
);
// 修改这行上面的代码
}
};
有大量样式时,可以将样式 object(对象)分配给一个常量,以保持代码组织有序。
内联样式可以给一个元素单独添加样式,实际工作中,很多时候是要给一组元素设置样式,比如把系统全部蓝色按钮的亮度调暗。
&& 代替if/else进行更加简洁的条件渲染
render() {
// 修改这行下面的代码
return (
<div>
<button onClick={this.toggleDisplay}>Toggle Display</button>
{this.state.display && <h1>Displayed!</h1>}
</div>
);
}
};
{condition && <p>markup</p>}
如果condition为true,则返回标记;如果为false,则在评估condition后操作将立即返回false,并且不返回任何内容。
可以将这些语句直接包含在 JSX 中,并通过在每个条件后面写 && 来将多个条件串在一起。 这允许你在 render() 方法中处理更复杂的条件逻辑,而无需重复大量代码,从而减少出错的可能。
使用三元表达式进行条件渲染
const inputStyle = {
width: 235,
margin: 5
};
class CheckUserAge extends React.Component {
constructor(props) {
super(props);
// 修改这行下面的代码
this.state = {
input:'',
userAge:''
};
// 修改这行上面的代码
this.submit = this.submit.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
this.setState({
input: e.target.value,
userAge: ''
});
}
submit() {
this.setState(state => ({
userAge: state.input
}));
}
render() {
const buttonOne = <button onClick={this.submit}>Submit</button>;
const buttonTwo = <button>You May Enter</button>;
const buttonThree = <button>You Shall Not Pass</button>;
return (
<div>
<h3>Enter Your Age to Continue</h3>
<input
style={inputStyle}
type='number'
value={this.state.input}
onChange={this.handleChange}
/>
<br />
{/* 修改这行下面的代码 */}
{
this.state.userAge === ''
? buttonOne
: this.state.userAge >= 18
? buttonTwo
: buttonThree
}
{/* 修改这行上面的代码 */}
</div>
);
}
}
该三元表达式实现的逻辑:当页面首次加载时,将提交按钮 buttonOne 渲染到页面。 然后,当用户输入年龄并点击该按钮时,根据年龄渲染不同的按钮。 如果用户输入的数字小于18,则渲染buttonThree。 如果用户输入的数字大于或等于18,则渲染buttonTwo。
2024.5.15 上面介绍了三种条件渲染技术,其中三元运算符在React开发人员中非常流行,使用它可以在JSX中实现条件逻辑。三元运算符有三个部分,但可以将多个三元表达式组合在一起(就像上面的例子)。
创建一个简单的"To Do List"应用程序
const textAreaStyles = {
width: 235,
margin: 5
};
class MyToDoList extends React.Component {
constructor(props) {
super(props);
// 修改这行下面的代码
this.state = {
userInput : "",
toDoList :[]
}
// 修改这行上面的代码
this.handleSubmit = this.handleSubmit.bind(this);
this.handleChange = this.handleChange.bind(this);
}
handleSubmit() {
const itemsArray = this.state.userInput.split(',');
this.setState({
toDoList: itemsArray
});
}
handleChange(e) {
this.setState({
userInput: e.target.value
});
}
render() {
const items = this.state.toDoList.map(i => <li>{i}</li>); // 修改这一行
return (
<div>
<textarea
onChange={this.handleChange}
value={this.state.userInput}
style={textAreaStyles}
placeholder='Separate Items With Commas'
/>
<br />
<button onClick={this.handleSubmit}>Create List</button>
<h1>My "To Do" List:</h1>
<ul>{items}</ul>
</div>
);
}
}
技术点:使用Array.map()动态渲染元素。将存储在组件内部state中的toDoList数组一一遍历,并相应地动态呈现在li元素中。
给同级元素一个唯一的键属性
const frontEndFrameworks = [
'React',
'Angular',
'Ember',
'Knockout',
'Backbone',
'Vue'
];
function Frameworks() {
const renderFrameworks = frontEndFrameworks.map(i => <li key={i}>{i}</li>); // 修改这一行
return (
<div>
<h1>Popular Front End JavaScript Frameworks</h1>
<ul>
{renderFrameworks}
</ul>
</div>
);
};
注意:键只需要在兄弟元素之间是唯一的,它们不需要在应用程序中是全局唯一的;最好不要用数组索引。
使用 Array.Filter() 动态过滤数组
如果有一个 users 数组,每个数组元素都有一个可以设置为 true 或 false 的 online 属性,可以这样只过滤那些在线的用户:
let onlineUsers = users.filter(user => user.online);
用 renderToString 在服务器上渲染 React
class App extends React.Component {
constructor(props) {
super(props);
}
render() {
return <div/>
}
};
// 修改这行下面的代码
ReactDOMServer.renderToString(<App />);
这创造了更快的初始页面加载体验,因为渲染的 HTML 代码量要比整个应用程序的 JavaScript 代码小。