JSX的语法规则
1、全称:JavaScript XML
2、是react定义的一种类似于XML的JS扩展语法
3、 作用:用来简化创建虚拟DOM
(1)写法:var ele = <h1>Hello JSX!</h1>
(2)注意:它不是字符串,也不是HTML/XML标签,它最终产生的是一个JS对象
4、语法规则:
(1)定义虚拟DOM时,不要写引号
(2)标签中需要写JS表达式时要用{}包裹
(3)样式的类名指定不再使用class而是使用className
(4)内联样式,要用style={{key:value}}的形式来写
(5)当有很多标签的时候,需要有一个根标签将它们包裹着
(6)标签必须闭合
(7)标签首字母:
- 若为小写字母,会将该标签转换为html中同名的标签,若html中没有该标签对应的标签元素,则会报错;
- 若为大写字母,react会将其渲染为对应的组件,若组件未定义,则会报错;
<div>
<h1 className='title'>Hello World</h1>
<p style={{ "marginTop": '16px' }}>This is a simple React app</p>
{/* 此处react将Hello当做组件,而Hello组件未声明,会报错 */}
<Hello />
</div>
React定义组件
在React中组件分为函数组件和类组件
函数组件:
function App() {
return (
<div>
<h1>Hello World</h1>
</div>
)
}
类组件:
class App extends React.Component {
render() {
return (
<div>
<h1>Hello World</h1>
</div>
)
}
}
函数组件和类组件之间的区别:
- 在写法上类组件会比函数组件多一个render,而这个render会放在该组件的原型对象上,供实例使用;
- 函数组件没有this,而类组件不仅有this并且该this指向了这个组件的实例对象
- 解析过程,当组件是函数组件时,react发现该组件是函数定义的,随后就会调用该函数,将返回的虚拟DOM转换为真实的DOM,随后呈现在页面上;
当组件是类组件时,react发现该组件是类定义的,随后会new出来该类的实例,并通过该实例调用原型上的render方法,然后将render返回的虚拟DOM转换为真实DOM,随后呈现在页面上;
state属性
1、state是组件对象中最重要的属性,它的值是一个对象(可以包含多个key-value的组合)
2、组件被称为“状态机”,会通过更新组件中的state来更新对应的页面显示(即重新渲染组件)
3、state中的数据不能直接修改,需要使用setState方法来进行更新,并且更新是一种合并,不是替换
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
name: 'John',
age: 18
}
}
// 当没有从父组件传来的props时,可以不写构造函数,此时的state能直接写在类组件中
// 即为简写形式
// state = {
// name: 'John',
// age: 18
// }
handleChangeAge = () => {
// 通过使用setState来改变state
this.setState({ age: this.state.age + 1 })
}
render() {
const { name, age } = this.state
return (
<div>
<h1>Hello {name},你今年{age}岁了</h1>
{/* 点击按钮来改变年龄 */}
<button onClick={this.handleChangeAge}>年龄加1</button>
</div>
)
}
}
props属性
1、每个组件对象都会有props(properties)属性
2、组件标签中的所有属性都会保存在props中
3、可以通过props属性来进行组件之间的数据传递
4、无论是函数组件还是类组件,都不能修改自身的props
class App extends React.Component {
render() {
return (
<div>
<Person name='张三' age='18' sex='男' />
<Teacher name='李老师' age='38' sex='女' />
</div>
)
}
}
// 类组件使用props
class Person extends React.Component {
constructor(props) {
super(props)
console.log(props);
}
render() {
const { name, age, sex } = this.props
return (
<div>
<h1>{name},{age},{sex}</h1>
</div>
)
}
}
// 函数组件使用props
function Teacher(props) {
const { name, sex, age } = props
return (
<div>
<h1>{name},{age},{sex}</h1>
</div>
)
}


Refs属性
1、组件内的标签可以使用ref属性来标识自己,即访问DOM节点
何时使用Refs
- 管理焦点,文本选择或者媒体播放
- 触发强制性的动画
- 集成第三方DOM库
字符串形式的ref(已过时)
<input ref='firstInput'/>
class App extends React.Component {
showFirstData = () => {
const { firstInput } = this.refs;
alert(firstInput.value);
}
showSecondData = () => {
const { secondInput } = this.refs;
alert(secondInput.value);
}
render() {
return (
<div>
<input ref='firstInput' type="text" placeholder='点击按钮提示数据' />
<button onClick={this.showFirstData}>点击提示左侧数据</button>
<input ref='secondInput' type="text" placeholder='失去焦点提示数据' onBlur={this.showSecondData} />
</div>
)
}
}

回调函数形式的ref
<input ref={(c)=>this.firstInput = c}/>
在回调函数形式的ref中,会传递一个函数。这个函数中会接收React 组件实例或者HTML DOM元素作为参数,以使它们能在其他地方被存储和访问
class App extends React.Component {
showFirstData = () => {
console.log(this);
const { firstInput } = this
alert(firstInput.value)
}
showSecondData = () => {
const { secondInput } = this
alert(secondInput.value)
}
render() {
return (
<div>
<input ref={c => this.firstInput = c} type="text" placeholder='点击按钮提示数据' />
<button onClick={this.showFirstData}>点击提示左侧数据</button>
<input ref={c => this.secondInput = c} type="text" placeholder='失去焦点提示数据' onBlur={this.showSecondData} />
</div>
)
}
}

createRef的使用
firstInput = React.createRef()
<input ref={this.firstInput} />
React会在组件挂载时将current属性传入DOM元素,并在组件卸载的时候传入null值。ref会在componentDidMount或componentDidUpdate生命周期钩子触发前更新。
class App extends React.Component {
// 创建新的ref实例
firstInput = React.createRef()
secondInput = React.createRef()
showFirstData = () => {
//可以获得当前节点
console.log(this.firstInput.current);
alert(this.firstInput.current.value)
}
showSecondData = () => {
alert(this.secondInput.current.value)
}
render() {
return (
<div>
<input ref={this.firstInput} type="text" placeholder='点击按钮提示数据' />
<button onClick={this.showFirstData}>点击提示左侧数据</button>
<input ref={this.secondInput} type="text" placeholder='失去焦点提示数据' onBlur={this.showSecondData} />
</div>
)
}
}

生命周期
1、组件从创建到死亡会经历一些特定的阶段
2、React组件中包含了一系列钩子函数(生命周期回调函数),并且会在特定的时刻调用
3、我们在定义组件时,可以在一些特定的生命周期的回调函数中做一些特定的工作

挂载阶段:
当组件实例被创建并插入DOM中时,其生命周期调用顺序如下:
constructor()static getDerivedStateFromProps()render()componentDidMount()
更新阶段:
当组件中的props或者state发生变化时就会触发更新。组件更新的生命周期调用顺序如下:
static getDerivedStateFromProps()shouldComponentUpdate()render()getSnapshotBeforeUpdate()componentDidUpdate()
卸载:
componentWillUnmount()
//创建组件
class App extends React.Component {
//构造器,通常适用于以下两种情况,如果不需要以下两种情况可以不用为组件实现该构造函数
//通过给this.state赋值对象来初始化内部state
//为事件处理函数绑定实例
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('root'))
}
//强制更新按钮的回调
force = () => {
this.forceUpdate()
}
//若state的值在任何时候都取决于props,那么可以使用getDerivedStateFromProps
static getDerivedStateFromProps(props, state) {
console.log('getDerivedStateFromProps', props, state);
// 若返回的是null则不会更新任何内容
return null
}
//组件挂载完毕的钩子
componentDidMount() {
console.log('Count---componentDidMount');
}
//控制组件更新的“阀门”
shouldComponentUpdate(nextProps, nextState) {
console.log('Count---shouldComponentUpdate', nextProps, nextState);
// 若返回值为false则不会调用componentDidUpdate()
return true
}
//组件更新完毕的钩子
componentDidUpdate(preProps, preState, snapshotValue) {
console.log('Count---componentDidUpdate', preProps, preState, snapshotValue);
}
//在更新之前获取快照
getSnapshotBeforeUpdate() {
console.log('getSnapshotBeforeUpdate');
// 该返回值会传递给componentDidUpdate()中的第三个参数
return 'nihao'
}
//组件将要卸载的钩子
componentWillUnmount() {
console.log('Count---componentWillUnmount');
}
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>
)
}
}
页面第一次挂载:

点击+1更新state来更新组件:

强制更新:

组件卸载:

生命周期总结:
重要的钩子:
-
render:初始化渲染或者更新渲染时调用
-
componentDidMount:开启监听,发送Ajax请求
-
componentWillUnmount:做一些收尾工作,如:清理定时器
废弃的钩子:
-
componentWillMount
-
componentWillReceiveProps
-
componentWillUpdate
现在使用会出现警告,下一个大版本需要加上UNSAFE_前缀才能使用,以后可能会被彻底废弃,不建议使用。
事件处理
1、通过onXxx属性可以指定事件处理函数(注意大小写)
- React中使用的是自定义事件(合成)事件,而不是使用原生的DOM事件-----为了更好的兼容性
- React中的事件是通过事件委托的方式处理的(委托给组件最外层的元素)-----为了更高效
2、通过event.target可以得到发生事件的DOM元素对象-----不要过度使用ref
class App extends React.Component {
showFirstData = (e) => {
console.log(e);
console.log(e.target);
}
showSecondData = (e) => {
console.log(e);
console.log(e.target);
}
render() {
return (
<div>
<input type="text" placeholder='点击按钮提示数据' />
<button onClick={this.showFirstData}>点击提示左侧数据</button>
<input type="text" placeholder='失去焦点提示数据' onBlur={this.showSecondData} />
</div>
)
}
}

关于事件处理的三种写法以及传值,请参考
React中的事件处理
条件渲染
在React中,可以使用JavaScript中运算符if或者条件运算符去创建元素来表现当前的状态,然后让React根据他们来更新UI
例如有两个组件,通过isLogin的真假来渲染不同的组件内容
function UserGreeting() {
return <h1>Welcome back!</h1>;
}
function GuestGreeting() {
return <h1>Please sign up.</h1>;
}
function App(props) {
if (props.isLogin) {
return <UserGreeting />;
} else {
return <GuestGreeting />
}
}
与运算符&&
通过{}包裹代码,可以在JSX中写入任何表达式
例如
function App(props) {
const unreadMessage = props.unreadMessage
return (
<div>
<h1>Hello!</h1>
{unreadMessage.length > 0 &&
<h1>
You have {unreadMessage.length} unread messages.
</h1>
}
</div>
)
}
const messages = ['React', 'Re: React', 'Re:Re: React']
ReactDOM.render(<App unreadMessage={messages} />, document.getElementById("root"));
在JavaScript中,当true&&expression时,将会返回expression,而false&&expression时总会返回false。
因此在上述例子中如果前者条件为true,则会渲染右侧的元素,若为false,则会忽略并跳过它
三目运算符
在React中不仅可以使用&&运算符来进行条件渲染,也可以使用三目运算符来完成
例如
function App(props) {
const isLogin = props.isLogin;
return (
<h1>
The user is <b>{isLogin ? 'currently' : 'not'}</b> login in
</h1>
)
}
列表&key
我们可以像JavaScript中的一些遍历的方法来渲染列表组件,通过使用{}在JSX中构建一个元素集合
例如
function App() {
const numbers = [1, 2, 3, 4, 5];
return (
<h1>
{numbers.map((number) => (
<div>{number}</div>
))}
</h1>
)
}
但是这样写会有一个警告Each child in a list should have a unique "key" prop.,意思是当创建一个元素时,必须包括一个特殊的key属性
即改为:
function App() {
const numbers = [1, 2, 3, 4, 5];
return (
<h1>
{numbers.map((number) => (
<div key={number}>{number}</div>
))}
</h1>
)
}
DOM的Diffing算法
当setState()更新状态了之后,会重新创建一个新的虚拟DOM树,这个新的虚拟DOM会去跟之前旧的虚拟DOM树进行比较差异,并且更新有差异的部分再将新的数据渲染到真实的DOM上面。
key的作用
key可以帮React识别出来哪些元素改变了,比如被添加或者删除。因此应当给一个数组的每一个元素赋予一个确定的标识。
同时一个元素的key最好是这个元素在列表中拥有一个独一无二的字符串。通常可以使用数据中的id作为元素的key,当元素没有id的时候,也可以将index作为key,但是这样会引起一些问题的出现。
在进行新虚拟DOM和旧虚拟DOM的diff比较时,会有以下规则:
1、如果新虚拟DOM从旧虚拟DOM中找到了相同的key:
-
若新虚拟DOM中的内容没有变,则直接使用旧虚拟DOM中的内容和之前的真实DOM来渲染到页面上
-
若新虚拟DOM中的内容发生了变化,则使用新虚拟DOM中的内容,创建新的真实DOM,然后替换掉之前的真实DOM来渲染到页面上
2、如果新虚拟DOM中没有找到与旧虚拟DOM中相同的key,则会直接使用新虚拟DOM中的内容来创建新的真实DOM,然后渲染到页面上
为什么遍历列表,key最好不要使用index?
当使用index作为key时可能引发的问题:
(1)若对数据进行了逆序添加,逆序删除等破坏顺序的操作的话,会产生没有必要的真实DOM更新
(2)若结构中还包含了输入类的DOM,会产生错误的DOM更新,导致页面有问题
class App extends React.Component {
state = {
numbers: [1, 2, 3, 4, 5]
}
addItem = () => {
this.setState({
numbers: [this.state.numbers.length + 1, ...this.state.numbers]
})
}
render() {
const { numbers } = this.state;
return (
<>
<h3>
{numbers.map((number, index) => (
// 使用index值作为key值
<div key={index}>
{number}:
<input type="text" />
</div>
))}
</h3>
<button onClick={this.addItem}>Add Item</button>
</>
)
}
}
若使用index作为key值时,对数据进行了逆序的添加删除,此时页面就会出现以下的错误


class App extends React.Component {
state = {
numbers: [1, 2, 3, 4, 5]
}
addItem = () => {
this.setState({
numbers: [this.state.numbers.length + 1, ...this.state.numbers]
})
}
render() {
const { numbers } = this.state;
return (
<>
<h3>
{numbers.map((number) => (
// 使用唯一的值作为key值
<div key={number}>
{number}:
<input type="text" />
</div>
))}
</h3>
<button onClick={this.addItem}>Add Item</button>
</>
)
}
}
当使用唯一的值作为key值时,无论是正序还是逆序都不会影响到页面。


表单
非受控组件
不需要React来管理,现取现用
class App extends React.Component {
handleSubmit = (e) => {
e.preventDefault();
alert(this.name.value)
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
名字:
<input type="text" ref={c => this.name = c} />
</label>
<input type="submit" value="提交" />
</form>
)
}
}
受控组件
渲染表单的React组件还控制着用户输入过程中表单发生的操作。被React以这样的方式控制取值的表单输入元素就叫做“受控组件”
例
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
value: ''
}
}
handleChange = (e) => {
this.setState({
value: e.target.value
})
}
handleSubmit = (e) => {
e.preventDefault();
alert('提交成功!提交的名字为:' + this.state.value)
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
名字:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="提交" />
</form>
)
}
}
本文详细介绍了ReactJS的基础知识,包括JSX语法、组件、状态管理和props的使用。讲解了虚拟DOM、事件处理、条件渲染以及列表与key的概念。此外,还探讨了生命周期方法、表单处理和非受控及受控组件。文章强调了正确使用key的重要性,以及在处理列表时避免使用index作为key的原因。
608

被折叠的 条评论
为什么被折叠?



